forked from mirror/openmw-tes3mp
Merge branch 'master' of https://github.com/zinnschlag/openmw.git into inventoryGUI
Conflicts: apps/openmw/CMakeLists.txtactorid
commit
a38db39e88
@ -0,0 +1,123 @@
|
||||
Bitstream Vera Fonts Copyright
|
||||
|
||||
The fonts have a generous copyright, allowing derivative works (as
|
||||
long as "Bitstream" or "Vera" are not in the names), and full
|
||||
redistribution (so long as they are not *sold* by themselves). They
|
||||
can be be bundled, redistributed and sold with any software.
|
||||
|
||||
The fonts are distributed under the following copyright:
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
||||
Vera is a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute
|
||||
the Font Software, including without limitation the rights to use,
|
||||
copy, merge, publish, distribute, and/or sell copies of the Font
|
||||
Software, and to permit persons to whom the Font Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Bitstream" or the word "Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Bitstream Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
||||
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font
|
||||
Software without prior written authorization from the Gnome Foundation
|
||||
or Bitstream Inc., respectively. For further information, contact:
|
||||
fonts at gnome dot org.
|
||||
|
||||
Copyright FAQ
|
||||
=============
|
||||
|
||||
1. I don't understand the resale restriction... What gives?
|
||||
|
||||
Bitstream is giving away these fonts, but wishes to ensure its
|
||||
competitors can't just drop the fonts as is into a font sale system
|
||||
and sell them as is. It seems fair that if Bitstream can't make money
|
||||
from the Bitstream Vera fonts, their competitors should not be able to
|
||||
do so either. You can sell the fonts as part of any software package,
|
||||
however.
|
||||
|
||||
2. I want to package these fonts separately for distribution and
|
||||
sale as part of a larger software package or system. Can I do so?
|
||||
|
||||
Yes. A RPM or Debian package is a "larger software package" to begin
|
||||
with, and you aren't selling them independently by themselves.
|
||||
See 1. above.
|
||||
|
||||
3. Are derivative works allowed?
|
||||
Yes!
|
||||
|
||||
4. Can I change or add to the font(s)?
|
||||
Yes, but you must change the name(s) of the font(s).
|
||||
|
||||
5. Under what terms are derivative works allowed?
|
||||
|
||||
You must change the name(s) of the fonts. This is to ensure the
|
||||
quality of the fonts, both to protect Bitstream and Gnome. We want to
|
||||
ensure that if an application has opened a font specifically of these
|
||||
names, it gets what it expects (though of course, using fontconfig,
|
||||
substitutions could still could have occurred during font
|
||||
opening). You must include the Bitstream copyright. Additional
|
||||
copyrights can be added, as per copyright law. Happy Font Hacking!
|
||||
|
||||
6. If I have improvements for Bitstream Vera, is it possible they might get
|
||||
adopted in future versions?
|
||||
|
||||
Yes. The contract between the Gnome Foundation and Bitstream has
|
||||
provisions for working with Bitstream to ensure quality additions to
|
||||
the Bitstream Vera font family. Please contact us if you have such
|
||||
additions. Note, that in general, we will want such additions for the
|
||||
entire family, not just a single font, and that you'll have to keep
|
||||
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
|
||||
glyphs to the font, they must be stylistically in keeping with Vera's
|
||||
design. Vera cannot become a "ransom note" font. Jim Lyles will be
|
||||
providing a document describing the design elements used in Vera, as a
|
||||
guide and aid for people interested in contributing to Vera.
|
||||
|
||||
7. I want to sell a software package that uses these fonts: Can I do so?
|
||||
|
||||
Sure. Bundle the fonts with your software and sell your software
|
||||
with the fonts. That is the intent of the copyright.
|
||||
|
||||
8. If applications have built the names "Bitstream Vera" into them,
|
||||
can I override this somehow to use fonts of my choosing?
|
||||
|
||||
This depends on exact details of the software. Most open source
|
||||
systems and software (e.g., Gnome, KDE, etc.) are now converting to
|
||||
use fontconfig (see www.fontconfig.org) to handle font configuration,
|
||||
selection and substitution; it has provisions for overriding font
|
||||
names and subsituting alternatives. An example is provided by the
|
||||
supplied local.conf file, which chooses the family Bitstream Vera for
|
||||
"sans", "serif" and "monospace". Other software (e.g., the XFree86
|
||||
core server) has other mechanisms for font substitution.
|
@ -0,0 +1,93 @@
|
||||
Copyright (c) 2010, 2011 Georg Duffner (http://www.georgduffner.at)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
Binary file not shown.
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 50 KiB |
@ -0,0 +1,20 @@
|
||||
set(MWINIIMPORT
|
||||
main.cpp
|
||||
importer.cpp
|
||||
)
|
||||
|
||||
set(MWINIIMPORT_HEADER
|
||||
importer.hpp
|
||||
)
|
||||
|
||||
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
|
||||
|
||||
add_executable(mwiniimport
|
||||
${MWINIIMPORT}
|
||||
)
|
||||
|
||||
target_link_libraries(mwiniimport
|
||||
${Boost_LIBRARIES}
|
||||
components
|
||||
)
|
||||
|
@ -0,0 +1,216 @@
|
||||
#include "importer.hpp"
|
||||
#include <boost/iostreams/device/file.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
MwIniImporter::MwIniImporter() {
|
||||
const char *map[][2] =
|
||||
{
|
||||
{ "fps", "General:Show FPS" },
|
||||
{ "nosound", "General:Disable Audio" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
const char *fallback[] = {
|
||||
"Weather:Sunrise Time",
|
||||
"Weather:Sunset Time",
|
||||
0
|
||||
};
|
||||
|
||||
for(int i=0; map[i][0]; i++) {
|
||||
mMergeMap.insert(std::make_pair<std::string, std::string>(map[i][0], map[i][1]));
|
||||
}
|
||||
|
||||
for(int i=0; fallback[i]; i++) {
|
||||
mMergeFallback.push_back(fallback[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::setVerbose(bool verbose) {
|
||||
mVerbose = verbose;
|
||||
}
|
||||
|
||||
std::string MwIniImporter::numberToString(int n) {
|
||||
std::stringstream str;
|
||||
str << n;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) {
|
||||
std::cout << "load ini file: " << filename << std::endl;
|
||||
|
||||
std::string section("");
|
||||
MwIniImporter::multistrmap map;
|
||||
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
|
||||
if(line[0] == '[') {
|
||||
if(line.length() > 2) {
|
||||
section = line.substr(1, line.length()-3);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int comment_pos = line.find(";");
|
||||
if(comment_pos > 0) {
|
||||
line = line.substr(0,comment_pos);
|
||||
}
|
||||
|
||||
if(line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int pos = line.find("=");
|
||||
if(pos < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key(section + ":" + line.substr(0,pos));
|
||||
std::string value(line.substr(pos+1));
|
||||
|
||||
multistrmap::iterator it;
|
||||
if((it = map.find(key)) == map.end()) {
|
||||
map.insert( std::make_pair<std::string, std::vector<std::string> > (key, std::vector<std::string>() ) );
|
||||
}
|
||||
map[key].push_back(value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
MwIniImporter::multistrmap MwIniImporter::loadCfgFile(std::string filename) {
|
||||
std::cout << "load cfg file: " << filename << std::endl;
|
||||
|
||||
MwIniImporter::multistrmap map;
|
||||
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
|
||||
// we cant say comment by only looking at first char anymore
|
||||
int comment_pos = line.find("#");
|
||||
if(comment_pos > 0) {
|
||||
line = line.substr(0,comment_pos);
|
||||
}
|
||||
|
||||
if(line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int pos = line.find("=");
|
||||
if(pos < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key(line.substr(0,pos));
|
||||
std::string value(line.substr(pos+1));
|
||||
|
||||
multistrmap::iterator it;
|
||||
if((it = map.find(key)) == map.end()) {
|
||||
map.insert( std::make_pair<std::string, std::vector<std::string> > (key, std::vector<std::string>() ) );
|
||||
}
|
||||
map[key].push_back(value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void MwIniImporter::merge(multistrmap &cfg, multistrmap &ini) {
|
||||
multistrmap::iterator cfgIt;
|
||||
multistrmap::iterator iniIt;
|
||||
for(strmap::iterator it=mMergeMap.begin(); it!=mMergeMap.end(); it++) {
|
||||
if((iniIt = ini.find(it->second)) != ini.end()) {
|
||||
for(std::vector<std::string>::iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); vc++) {
|
||||
cfg.erase(it->first);
|
||||
insertMultistrmap(cfg, it->first, *vc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) {
|
||||
cfg.erase("fallback");
|
||||
|
||||
multistrmap::iterator cfgIt;
|
||||
multistrmap::iterator iniIt;
|
||||
for(std::vector<std::string>::iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); it++) {
|
||||
if((iniIt = ini.find(*it)) != ini.end()) {
|
||||
for(std::vector<std::string>::iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); vc++) {
|
||||
std::string value(*it);
|
||||
std::replace( value.begin(), value.end(), ' ', '_' );
|
||||
std::replace( value.begin(), value.end(), ':', '_' );
|
||||
value.append(",").append(vc->substr(0,vc->length()-1));
|
||||
insertMultistrmap(cfg, "fallback", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void MwIniImporter::insertMultistrmap(multistrmap &cfg, std::string key, std::string value) {
|
||||
multistrmap::iterator it = cfg.find(key);
|
||||
if(it == cfg.end()) {
|
||||
cfg.insert(std::make_pair<std::string, std::vector<std::string> >(key, std::vector<std::string>() ));
|
||||
}
|
||||
cfg[key].push_back(value);
|
||||
}
|
||||
|
||||
void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
|
||||
std::vector<std::string> esmFiles;
|
||||
std::vector<std::string> espFiles;
|
||||
std::string baseGameFile("Game Files:GameFile");
|
||||
std::string gameFile("");
|
||||
|
||||
multistrmap::iterator it = ini.begin();
|
||||
for(int i=0; it != ini.end(); i++) {
|
||||
gameFile = baseGameFile;
|
||||
gameFile.append(this->numberToString(i));
|
||||
|
||||
it = ini.find(gameFile);
|
||||
if(it == ini.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); entry++) {
|
||||
std::string filetype(entry->substr(entry->length()-4, 3));
|
||||
std::transform(filetype.begin(), filetype.end(), filetype.begin(), ::tolower);
|
||||
|
||||
if(filetype.compare("esm") == 0) {
|
||||
esmFiles.push_back(*entry);
|
||||
}
|
||||
else if(filetype.compare("esp") == 0) {
|
||||
espFiles.push_back(*entry);
|
||||
}
|
||||
}
|
||||
|
||||
gameFile = "";
|
||||
}
|
||||
|
||||
cfg.erase("master");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) );
|
||||
|
||||
for(std::vector<std::string>::iterator it=esmFiles.begin(); it!=esmFiles.end(); it++) {
|
||||
cfg["master"].push_back(*it);
|
||||
}
|
||||
|
||||
cfg.erase("plugin");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) );
|
||||
|
||||
for(std::vector<std::string>::iterator it=espFiles.begin(); it!=espFiles.end(); it++) {
|
||||
cfg["plugin"].push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg) {
|
||||
|
||||
for(multistrmap::iterator it=cfg.begin(); it != cfg.end(); it++) {
|
||||
for(std::vector<std::string>::iterator entry=it->second.begin(); entry != it->second.end(); entry++) {
|
||||
out << (it->first) << "=" << (*entry) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef MWINIIMPORTER_IMPORTER
|
||||
#define MWINIIMPORTER_IMPORTER 1
|
||||
|
||||
#include <boost/iostreams/device/file.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
|
||||
class MwIniImporter {
|
||||
public:
|
||||
typedef std::map<std::string, std::string> strmap;
|
||||
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
||||
|
||||
MwIniImporter();
|
||||
void setVerbose(bool verbose);
|
||||
multistrmap loadIniFile(std::string filename);
|
||||
multistrmap loadCfgFile(std::string filename);
|
||||
void merge(multistrmap &cfg, multistrmap &ini);
|
||||
void mergeFallback(multistrmap &cfg, multistrmap &ini);
|
||||
void importGameFiles(multistrmap &cfg, multistrmap &ini);
|
||||
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg);
|
||||
|
||||
private:
|
||||
void insertMultistrmap(multistrmap &cfg, std::string key, std::string value);
|
||||
std::string numberToString(int n);
|
||||
bool mVerbose;
|
||||
strmap mMergeMap;
|
||||
std::vector<std::string> mMergeFallback;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,75 @@
|
||||
#include "importer.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
bpo::options_description desc("Syntax: mwiniimporter <options> inifile configfile\nAllowed options");
|
||||
bpo::positional_options_description p_desc;
|
||||
desc.add_options()
|
||||
("help,h", "produce help message")
|
||||
("verbose,v", "verbose output")
|
||||
("ini,i", bpo::value<std::string>(), "morrowind.ini file")
|
||||
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
|
||||
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
|
||||
("game-files,g", "import esm and esp files")
|
||||
;
|
||||
p_desc.add("ini", 1).add("cfg", 1);
|
||||
|
||||
bpo::variables_map vm;
|
||||
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
|
||||
.options(desc)
|
||||
.positional(p_desc)
|
||||
.run();
|
||||
|
||||
bpo::store(parsed, vm);
|
||||
|
||||
if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) {
|
||||
std::cout << desc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bpo::notify(vm);
|
||||
|
||||
std::string iniFile = vm["ini"].as<std::string>();
|
||||
std::string cfgFile = vm["cfg"].as<std::string>();
|
||||
|
||||
// if no output is given, write back to cfg file
|
||||
std::string outputFile(vm["output"].as<std::string>());
|
||||
if(vm["output"].defaulted()) {
|
||||
outputFile = vm["cfg"].as<std::string>();
|
||||
}
|
||||
|
||||
if(!boost::filesystem::exists(iniFile)) {
|
||||
std::cerr << "ini file does not exist" << std::endl;
|
||||
return -3;
|
||||
}
|
||||
if(!boost::filesystem::exists(cfgFile)) {
|
||||
std::cerr << "cfg file does not exist" << std::endl;
|
||||
return -4;
|
||||
}
|
||||
|
||||
MwIniImporter importer;
|
||||
importer.setVerbose(vm.count("verbose"));
|
||||
|
||||
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
|
||||
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
|
||||
|
||||
importer.merge(cfg, ini);
|
||||
importer.mergeFallback(cfg, ini);
|
||||
|
||||
if(vm.count("game-files")) {
|
||||
importer.importGameFiles(cfg, ini);
|
||||
}
|
||||
|
||||
std::cout << "write to: " << outputFile << std::endl;
|
||||
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
|
||||
importer.writeToFile(file, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#include "cursorreplace.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <openengine/ogre/imagerotate.hpp>
|
||||
|
||||
#include <OgreResourceGroupManager.h>
|
||||
#include <OgreRoot.h>
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
CursorReplace::CursorReplace()
|
||||
{
|
||||
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90);
|
||||
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45);
|
||||
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45);
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#ifndef GAME_CURSORREPLACE_H
|
||||
#define GAME_CURSORREPLACE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
/// \brief MyGUI does not support rotating cursors, so we have to do it manually
|
||||
class CursorReplace
|
||||
{
|
||||
public:
|
||||
CursorReplace();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,106 @@
|
||||
#include "map_window.hpp"
|
||||
#include "window_manager.hpp"
|
||||
/*
|
||||
#include "../mwmechanics/mechanicsmanager.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
*/
|
||||
using namespace MWGui;
|
||||
|
||||
MapWindow::MapWindow(WindowManager& parWindowManager) :
|
||||
MWGui::WindowPinnableBase("openmw_map_window_layout.xml", parWindowManager),
|
||||
mGlobal(false)
|
||||
{
|
||||
setCoord(500,0,320,300);
|
||||
setText("WorldButton", "World");
|
||||
setImage("Compass", "textures\\compass.dds");
|
||||
|
||||
// Obviously you should override this later on
|
||||
setCellName("No Cell Loaded");
|
||||
|
||||
getWidget(mLocalMap, "LocalMap");
|
||||
getWidget(mGlobalMap, "GlobalMap");
|
||||
getWidget(mPlayerArrow, "Compass");
|
||||
|
||||
getWidget(mButton, "WorldButton");
|
||||
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
|
||||
|
||||
MyGUI::Button* eventbox;
|
||||
getWidget(eventbox, "EventBox");
|
||||
eventbox->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
|
||||
eventbox->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
|
||||
LocalMapBase::init(mLocalMap, this);
|
||||
}
|
||||
|
||||
void MapWindow::setCellName(const std::string& cellName)
|
||||
{
|
||||
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(cellName);
|
||||
adjustWindowCaption();
|
||||
}
|
||||
|
||||
void MapWindow::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
if (mGlobal || !mVisible || (x == mLastPositionX && y == mLastPositionY)) return;
|
||||
MyGUI::IntSize size = mLocalMap->getCanvasSize();
|
||||
MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height);
|
||||
MyGUI::IntCoord viewsize = mLocalMap->getCoord();
|
||||
MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top);
|
||||
mLocalMap->setViewOffset(pos);
|
||||
|
||||
mPlayerArrow->setPosition(MyGUI::IntPoint(x*512-16, y*512-16));
|
||||
mLastPositionX = x;
|
||||
mLastPositionY = y;
|
||||
}
|
||||
|
||||
void MapWindow::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
if (!mVisible || (x == mLastDirectionX && y == mLastDirectionY)) return;
|
||||
MyGUI::ISubWidget* main = mPlayerArrow->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(x,y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
|
||||
mLastDirectionX = x;
|
||||
mLastDirectionY = y;
|
||||
}
|
||||
|
||||
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
if (!mGlobal)
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
|
||||
void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
|
||||
if (!mGlobal)
|
||||
{
|
||||
MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos;
|
||||
mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff );
|
||||
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
}
|
||||
|
||||
void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mGlobal = !mGlobal;
|
||||
mGlobalMap->setVisible(mGlobal);
|
||||
mLocalMap->setVisible(!mGlobal);
|
||||
|
||||
mButton->setCaption( mGlobal ? "Local" : "World" );
|
||||
}
|
||||
|
||||
void MapWindow::onPinToggled()
|
||||
{
|
||||
mWindowManager.setMinimapVisibility(!mPinned);
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef MWGUI_MAPWINDOW_H
|
||||
#define MWGUI_MAPWINDOW_H
|
||||
|
||||
#include "layouts.hpp"
|
||||
#include "window_pinnable_base.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase
|
||||
{
|
||||
public:
|
||||
MapWindow(WindowManager& parWindowManager);
|
||||
virtual ~MapWindow(){}
|
||||
|
||||
void setPlayerPos(const float x, const float y);
|
||||
void setPlayerDir(const float x, const float y);
|
||||
void setCellName(const std::string& cellName);
|
||||
|
||||
private:
|
||||
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onWorldButtonClicked(MyGUI::Widget* _sender);
|
||||
|
||||
MyGUI::ScrollView* mGlobalMap;
|
||||
MyGUI::ImageBox* mPlayerArrow;
|
||||
MyGUI::Button* mButton;
|
||||
MyGUI::IntPoint mLastDragPos;
|
||||
bool mGlobal;
|
||||
|
||||
protected:
|
||||
virtual void onPinToggled();
|
||||
};
|
||||
}
|
||||
#endif
|
@ -0,0 +1,33 @@
|
||||
#include "window_pinnable_base.hpp"
|
||||
#include "window_manager.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager)
|
||||
: WindowBase(parLayout, parWindowManager), mPinned(false), mVisible(false)
|
||||
{
|
||||
MyGUI::WindowPtr t = static_cast<MyGUI::WindowPtr>(mMainWidget);
|
||||
t->eventWindowButtonPressed += MyGUI::newDelegate(this, &WindowPinnableBase::onWindowButtonPressed);
|
||||
}
|
||||
|
||||
void WindowPinnableBase::setVisible(bool b)
|
||||
{
|
||||
// Pinned windows can not be hidden
|
||||
if (mPinned && !b)
|
||||
return;
|
||||
|
||||
WindowBase::setVisible(b);
|
||||
mVisible = b;
|
||||
}
|
||||
|
||||
void WindowPinnableBase::onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName)
|
||||
{
|
||||
if ("PinToggle" == eventName)
|
||||
{
|
||||
mPinned = !mPinned;
|
||||
onPinToggled();
|
||||
}
|
||||
|
||||
eventDone(this);
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
#ifndef MWGUI_WINDOW_PINNABLE_BASE_H
|
||||
#define MWGUI_WINDOW_PINNABLE_BASE_H
|
||||
|
||||
#include "window_base.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class WindowManager;
|
||||
|
||||
class WindowPinnableBase: public WindowBase
|
||||
{
|
||||
public:
|
||||
WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager);
|
||||
void setVisible(bool b);
|
||||
|
||||
private:
|
||||
void onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName);
|
||||
|
||||
protected:
|
||||
virtual void onPinToggled() = 0;
|
||||
|
||||
bool mPinned;
|
||||
bool mVisible;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,77 @@
|
||||
|
||||
#include "actors.hpp"
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
void Actors::updateActor (const MWWorld::Ptr& ptr, float duration)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)
|
||||
{
|
||||
if (!paused && ptr.getRefData().getHandle()!="player")
|
||||
MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip (
|
||||
MWWorld::Class::get (ptr).getNpcStats (ptr), mEnvironment);
|
||||
}
|
||||
|
||||
Actors::Actors (MWWorld::Environment& environment) : mEnvironment (environment), mDuration (0) {}
|
||||
|
||||
void Actors::addActor (const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mActors.insert (ptr);
|
||||
}
|
||||
|
||||
void Actors::removeActor (const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mActors.erase (ptr);
|
||||
}
|
||||
|
||||
void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore)
|
||||
{
|
||||
std::set<MWWorld::Ptr>::iterator iter = mActors.begin();
|
||||
|
||||
while (iter!=mActors.end())
|
||||
if (iter->getCell()==cellStore)
|
||||
{
|
||||
mActors.erase (iter++);
|
||||
}
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
|
||||
void Actors::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, float duration,
|
||||
bool paused)
|
||||
{
|
||||
mDuration += duration;
|
||||
|
||||
if (mDuration>=0.25)
|
||||
{
|
||||
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter)
|
||||
{
|
||||
updateActor (*iter, mDuration);
|
||||
|
||||
if (iter->getTypeName()==typeid (ESM::NPC).name())
|
||||
updateNpc (*iter, mDuration, paused);
|
||||
}
|
||||
|
||||
mDuration = 0;
|
||||
}
|
||||
|
||||
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end();
|
||||
++iter)
|
||||
{
|
||||
Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter);
|
||||
|
||||
if (vector!=Ogre::Vector3::ZERO)
|
||||
movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
#ifndef GAME_MWMECHANICS_ACTORS_H
|
||||
#define GAME_MWMECHANICS_ACTORS_H
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Vector3;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Environment;
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
class Actors
|
||||
{
|
||||
MWWorld::Environment& mEnvironment;
|
||||
std::set<MWWorld::Ptr> mActors;
|
||||
float mDuration;
|
||||
|
||||
void updateActor (const MWWorld::Ptr& ptr, float duration);
|
||||
|
||||
void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused);
|
||||
|
||||
public:
|
||||
|
||||
Actors (MWWorld::Environment& environment);
|
||||
|
||||
void addActor (const MWWorld::Ptr& ptr);
|
||||
///< Register an actor for stats management
|
||||
|
||||
void removeActor (const MWWorld::Ptr& ptr);
|
||||
///< Deregister an actor for stats management
|
||||
|
||||
void dropActors (const MWWorld::Ptr::CellStore *cellStore);
|
||||
///< Deregister all actors in the given cell.
|
||||
|
||||
void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
|
||||
float duration, bool paused);
|
||||
///< Update actor stats and store desired velocity vectors in \a movement
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,13 @@
|
||||
#ifndef GAME_MWMECHANICS_DRAWSTATE_H
|
||||
#define GAME_MWMECHANICS_DRAWSTATE_H
|
||||
|
||||
#undef DrawState
|
||||
|
||||
enum DrawState
|
||||
{
|
||||
DrawState_Weapon = 0,
|
||||
DrawState_Spell = 1,
|
||||
DrawState_Nothing = 2,
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,81 @@
|
||||
|
||||
#include "spells.hpp"
|
||||
|
||||
#include <components/esm/loadspel.hpp>
|
||||
|
||||
#include "../mwworld/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
#include "magiceffects.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
void Spells::addSpell (const ESM::Spell *spell, MagicEffects& effects) const
|
||||
{
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->effects.list.begin();
|
||||
iter!=spell->effects.list.end(); ++iter)
|
||||
{
|
||||
EffectParam param;
|
||||
param.mMagnitude = iter->magnMax; /// \todo calculate magnitude
|
||||
effects.add (EffectKey (*iter), param);
|
||||
}
|
||||
}
|
||||
|
||||
Spells::TIterator Spells::begin() const
|
||||
{
|
||||
return mSpells.begin();
|
||||
}
|
||||
|
||||
Spells::TIterator Spells::end() const
|
||||
{
|
||||
return mSpells.end();
|
||||
}
|
||||
|
||||
void Spells::add (const std::string& spellId)
|
||||
{
|
||||
if (std::find (mSpells.begin(), mSpells.end(), spellId)!=mSpells.end())
|
||||
mSpells.push_back (spellId);
|
||||
}
|
||||
|
||||
void Spells::remove (const std::string& spellId)
|
||||
{
|
||||
TContainer::iterator iter = std::find (mSpells.begin(), mSpells.end(), spellId);
|
||||
|
||||
if (iter!=mSpells.end())
|
||||
mSpells.erase (iter);
|
||||
|
||||
if (spellId==mSelectedSpell)
|
||||
mSelectedSpell.clear();
|
||||
}
|
||||
|
||||
MagicEffects Spells::getMagicEffects (const MWWorld::Environment& environment) const
|
||||
{
|
||||
MagicEffects effects;
|
||||
|
||||
for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter)
|
||||
{
|
||||
const ESM::Spell *spell = environment.mWorld->getStore().spells.find (*iter);
|
||||
|
||||
if (spell->data.type==ESM::Spell::ST_Ability || spell->data.type==ESM::Spell::ST_Blight ||
|
||||
spell->data.type==ESM::Spell::ST_Disease || spell->data.type==ESM::Spell::ST_Curse)
|
||||
addSpell (spell, effects);
|
||||
}
|
||||
|
||||
return effects;
|
||||
}
|
||||
|
||||
void Spells::clear()
|
||||
{
|
||||
mSpells.clear();
|
||||
}
|
||||
|
||||
void Spells::setSelectedSpell (const std::string& spellId)
|
||||
{
|
||||
mSelectedSpell = spellId;
|
||||
}
|
||||
|
||||
const std::string Spells::getSelectedSpell() const
|
||||
{
|
||||
return mSelectedSpell;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
#ifndef GAME_MWMECHANICS_SPELLS_H
|
||||
#define GAME_MWMECHANICS_SPELLS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Spell;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
struct Environment;
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
class MagicEffects;
|
||||
|
||||
/// \brief Spell list
|
||||
///
|
||||
/// This class manages known spells as well as abilities, powers and permanent negative effects like
|
||||
/// diseaes.
|
||||
class Spells
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<std::string> TContainer;
|
||||
typedef TContainer::const_iterator TIterator;
|
||||
|
||||
private:
|
||||
|
||||
std::vector<std::string> mSpells;
|
||||
std::string mSelectedSpell;
|
||||
|
||||
void addSpell (const ESM::Spell *, MagicEffects& effects) const;
|
||||
|
||||
public:
|
||||
|
||||
TIterator begin() const;
|
||||
|
||||
TIterator end() const;
|
||||
|
||||
void add (const std::string& spell);
|
||||
///< Adding a spell that is already listed in *this is a no-op.
|
||||
|
||||
void remove (const std::string& spell);
|
||||
///< If the spell to be removed is the selected spell, the selected spell will be changed to
|
||||
/// no spell (empty string).
|
||||
|
||||
MagicEffects getMagicEffects (const MWWorld::Environment& environment) const;
|
||||
///< Return sum of magic effects resulting from abilities, blights, deseases and curses.
|
||||
|
||||
void clear();
|
||||
///< Remove all spells of al types.
|
||||
|
||||
void setSelectedSpell (const std::string& spellId);
|
||||
///< This function does not verify, if the spell is available.
|
||||
|
||||
const std::string getSelectedSpell() const;
|
||||
///< May return an empty string.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,304 @@
|
||||
#include "occlusionquery.hpp"
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#include <OgreRenderSystem.h>
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreBillboardSet.h>
|
||||
#include <OgreHardwareOcclusionQuery.h>
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreSubEntity.h>
|
||||
#include <OgreMaterialManager.h>
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
||||
OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNode* sunNode) :
|
||||
mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mSingleObjectQuery(0), mActiveQuery(0),
|
||||
mDoQuery(0), mSunVisibility(0), mQuerySingleObjectStarted(false), mTestResult(false),
|
||||
mQuerySingleObjectRequested(false), mWasVisible(false), mObjectWasVisible(false), mDoQuery2(false),
|
||||
mBBNode(0)
|
||||
{
|
||||
mRendering = renderer;
|
||||
mSunNode = sunNode;
|
||||
|
||||
try {
|
||||
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
|
||||
|
||||
mSunTotalAreaQuery = renderSystem->createHardwareOcclusionQuery();
|
||||
mSunVisibleAreaQuery = renderSystem->createHardwareOcclusionQuery();
|
||||
mSingleObjectQuery = renderSystem->createHardwareOcclusionQuery();
|
||||
|
||||
mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0) && (mSingleObjectQuery != 0);
|
||||
}
|
||||
catch (Ogre::Exception e)
|
||||
{
|
||||
mSupported = false;
|
||||
}
|
||||
|
||||
if (!mSupported)
|
||||
{
|
||||
std::cout << "Hardware occlusion queries not supported." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialPtr matBase = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting");
|
||||
MaterialPtr matQueryArea = matBase->clone("QueryTotalPixels");
|
||||
matQueryArea->setDepthWriteEnabled(false);
|
||||
matQueryArea->setColourWriteEnabled(false);
|
||||
matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects
|
||||
MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels");
|
||||
matQueryVisible->setDepthWriteEnabled(false);
|
||||
matQueryVisible->setColourWriteEnabled(false); // Uncomment this to visualize the occlusion query
|
||||
matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects
|
||||
matQueryVisible->setCullingMode(CULL_NONE);
|
||||
matQueryVisible->setManualCullingMode(MANUAL_CULL_NONE);
|
||||
|
||||
if (sunNode)
|
||||
mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode();
|
||||
|
||||
mObjectNode = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
|
||||
mBBNodeReal = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
|
||||
|
||||
mBBQueryTotal = mRendering->getScene()->createBillboardSet(1);
|
||||
mBBQueryTotal->setCastShadows(false);
|
||||
mBBQueryTotal->setDefaultDimensions(150, 150);
|
||||
mBBQueryTotal->createBillboard(Vector3::ZERO);
|
||||
mBBQueryTotal->setMaterialName("QueryTotalPixels");
|
||||
mBBQueryTotal->setRenderQueueGroup(RQG_OcclusionQuery+1);
|
||||
mBBNodeReal->attachObject(mBBQueryTotal);
|
||||
|
||||
mBBQueryVisible = mRendering->getScene()->createBillboardSet(1);
|
||||
mBBQueryVisible->setCastShadows(false);
|
||||
mBBQueryVisible->setDefaultDimensions(150, 150);
|
||||
mBBQueryVisible->createBillboard(Vector3::ZERO);
|
||||
mBBQueryVisible->setMaterialName("QueryVisiblePixels");
|
||||
mBBQueryVisible->setRenderQueueGroup(RQG_OcclusionQuery+1);
|
||||
mBBNodeReal->attachObject(mBBQueryVisible);
|
||||
|
||||
mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1);
|
||||
/// \todo ideally this should occupy exactly 1 pixel on the screen
|
||||
mBBQuerySingleObject->setCastShadows(false);
|
||||
mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003);
|
||||
mBBQuerySingleObject->createBillboard(Vector3::ZERO);
|
||||
mBBQuerySingleObject->setMaterialName("QueryVisiblePixels");
|
||||
mBBQuerySingleObject->setRenderQueueGroup(RQG_OcclusionQuery);
|
||||
mObjectNode->attachObject(mBBQuerySingleObject);
|
||||
|
||||
mRendering->getScene()->addRenderObjectListener(this);
|
||||
mRendering->getScene()->addRenderQueueListener(this);
|
||||
mDoQuery = true;
|
||||
}
|
||||
|
||||
OcclusionQuery::~OcclusionQuery()
|
||||
{
|
||||
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
|
||||
if (mSunTotalAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery);
|
||||
if (mSunVisibleAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery);
|
||||
if (mSingleObjectQuery) renderSystem->destroyHardwareOcclusionQuery(mSingleObjectQuery);
|
||||
}
|
||||
|
||||
bool OcclusionQuery::supported()
|
||||
{
|
||||
return mSupported;
|
||||
}
|
||||
|
||||
void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source,
|
||||
const LightList* pLightList, bool suppressRenderStateChanges)
|
||||
{
|
||||
// The following code activates and deactivates the occlusion queries
|
||||
// so that the queries only include the rendering of their intended targets
|
||||
|
||||
// Close the last occlusion query
|
||||
// Each occlusion query should only last a single rendering
|
||||
if (mActiveQuery != NULL)
|
||||
{
|
||||
mActiveQuery->endOcclusionQuery();
|
||||
mActiveQuery = NULL;
|
||||
}
|
||||
|
||||
// Open a new occlusion query
|
||||
if (mDoQuery == true)
|
||||
{
|
||||
if (rend == mBBQueryTotal)
|
||||
{
|
||||
mActiveQuery = mSunTotalAreaQuery;
|
||||
mWasVisible = true;
|
||||
}
|
||||
else if (rend == mBBQueryVisible)
|
||||
{
|
||||
mActiveQuery = mSunVisibleAreaQuery;
|
||||
}
|
||||
}
|
||||
if (mDoQuery == true && rend == mBBQuerySingleObject)
|
||||
{
|
||||
mQuerySingleObjectStarted = true;
|
||||
mQuerySingleObjectRequested = false;
|
||||
mActiveQuery = mSingleObjectQuery;
|
||||
mObjectWasVisible = true;
|
||||
}
|
||||
|
||||
if (mActiveQuery != NULL)
|
||||
mActiveQuery->beginOcclusionQuery();
|
||||
}
|
||||
|
||||
void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocation, bool& repeatThisInvocation)
|
||||
{
|
||||
if (mActiveQuery != NULL)
|
||||
{
|
||||
mActiveQuery->endOcclusionQuery();
|
||||
mActiveQuery = NULL;
|
||||
}
|
||||
/**
|
||||
* for every beginOcclusionQuery(), we want a respective pullOcclusionQuery() and vice versa
|
||||
* this also means that results can be wrong at other places if we pull, but beginOcclusionQuery() was never called
|
||||
* this can happen for example if the object that is tested is outside of the view frustum
|
||||
* to prevent this, check if the queries have been performed after everything has been rendered and if not, start them manually
|
||||
*/
|
||||
if (queueGroupId == RQG_SkiesLate)
|
||||
{
|
||||
if (mWasVisible == false && mDoQuery)
|
||||
{
|
||||
mSunTotalAreaQuery->beginOcclusionQuery();
|
||||
mSunTotalAreaQuery->endOcclusionQuery();
|
||||
mSunVisibleAreaQuery->beginOcclusionQuery();
|
||||
mSunVisibleAreaQuery->endOcclusionQuery();
|
||||
}
|
||||
if (mObjectWasVisible == false && mDoQuery)
|
||||
{
|
||||
mSingleObjectQuery->beginOcclusionQuery();
|
||||
mSingleObjectQuery->endOcclusionQuery();
|
||||
mQuerySingleObjectStarted = true;
|
||||
mQuerySingleObjectRequested = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OcclusionQuery::update(float duration)
|
||||
{
|
||||
if (!mSupported) return;
|
||||
|
||||
mWasVisible = false;
|
||||
mObjectWasVisible = false;
|
||||
|
||||
// Adjust the position of the sun billboards according to camera viewing distance
|
||||
// we need to do this to make sure that _everything_ can occlude the sun
|
||||
float dist = mRendering->getCamera()->getFarClipDistance();
|
||||
if (dist==0) dist = 10000000;
|
||||
dist -= 1000; // bias
|
||||
dist /= 1000.f;
|
||||
if (mBBNode)
|
||||
{
|
||||
mBBNode->setPosition(mSunNode->getPosition() * dist);
|
||||
mBBNode->setScale(dist, dist, dist);
|
||||
mBBNodeReal->setPosition(mBBNode->_getDerivedPosition());
|
||||
mBBNodeReal->setScale(mBBNode->getScale());
|
||||
}
|
||||
|
||||
// Stop occlusion queries until we get their information
|
||||
// (may not happen on the same frame they are requested in)
|
||||
mDoQuery = false;
|
||||
|
||||
if (!mSunTotalAreaQuery->isStillOutstanding()
|
||||
&& !mSunVisibleAreaQuery->isStillOutstanding()
|
||||
&& !mSingleObjectQuery->isStillOutstanding())
|
||||
{
|
||||
unsigned int totalPixels;
|
||||
unsigned int visiblePixels;
|
||||
|
||||
mSunTotalAreaQuery->pullOcclusionQuery(&totalPixels);
|
||||
mSunVisibleAreaQuery->pullOcclusionQuery(&visiblePixels);
|
||||
|
||||
if (totalPixels == 0)
|
||||
{
|
||||
// probably outside of the view frustum
|
||||
mSunVisibility = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSunVisibility = float(visiblePixels) / float(totalPixels);
|
||||
if (mSunVisibility > 1) mSunVisibility = 1;
|
||||
}
|
||||
|
||||
unsigned int result;
|
||||
|
||||
mSingleObjectQuery->pullOcclusionQuery(&result);
|
||||
|
||||
mTestResult = (result != 0);
|
||||
|
||||
mQuerySingleObjectStarted = false;
|
||||
mQuerySingleObjectRequested = false;
|
||||
|
||||
mDoQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object)
|
||||
{
|
||||
assert( !occlusionTestPending()
|
||||
&& "Occlusion test still pending");
|
||||
|
||||
mBBQuerySingleObject->setVisible(true);
|
||||
|
||||
mObjectNode->setPosition(position);
|
||||
// scale proportional to camera distance, in order to always give the billboard the same size in screen-space
|
||||
mObjectNode->setScale( Vector3(1,1,1)*(position - mRendering->getCamera()->getRealPosition()).length() );
|
||||
|
||||
mQuerySingleObjectRequested = true;
|
||||
}
|
||||
|
||||
bool OcclusionQuery::occlusionTestPending()
|
||||
{
|
||||
return (mQuerySingleObjectRequested || mQuerySingleObjectStarted);
|
||||
}
|
||||
|
||||
void OcclusionQuery::setSunNode(Ogre::SceneNode* node)
|
||||
{
|
||||
mSunNode = node;
|
||||
if (!mBBNode)
|
||||
mBBNode = node->getParentSceneNode()->createChildSceneNode();
|
||||
}
|
||||
|
||||
bool OcclusionQuery::getTestResult()
|
||||
{
|
||||
assert( !occlusionTestPending()
|
||||
&& "Occlusion test still pending");
|
||||
|
||||
return mTestResult;
|
||||
}
|
||||
|
||||
bool OcclusionQuery::isPotentialOccluder(Ogre::SceneNode* node)
|
||||
{
|
||||
bool result = false;
|
||||
for (unsigned int i=0; i < node->numAttachedObjects(); ++i)
|
||||
{
|
||||
MovableObject* ob = node->getAttachedObject(i);
|
||||
std::string type = ob->getMovableType();
|
||||
if (type == "Entity")
|
||||
{
|
||||
Entity* ent = static_cast<Entity*>(ob);
|
||||
for (unsigned int j=0; j < ent->getNumSubEntities(); ++j)
|
||||
{
|
||||
// if any sub entity has a material with depth write off,
|
||||
// consider the object as not an occluder
|
||||
MaterialPtr mat = ent->getSubEntity(j)->getMaterial();
|
||||
|
||||
Material::TechniqueIterator techIt = mat->getTechniqueIterator();
|
||||
while (techIt.hasMoreElements())
|
||||
{
|
||||
Technique* tech = techIt.getNext();
|
||||
Technique::PassIterator passIt = tech->getPassIterator();
|
||||
while (passIt.hasMoreElements())
|
||||
{
|
||||
Pass* pass = passIt.getNext();
|
||||
|
||||
if (pass->getDepthWriteEnabled() == false)
|
||||
return false;
|
||||
else
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
#ifndef _GAME_OCCLUSION_QUERY_H
|
||||
#define _GAME_OCCLUSION_QUERY_H
|
||||
|
||||
#include <OgreRenderObjectListener.h>
|
||||
#include <OgreRenderQueueListener.h>
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class HardwareOcclusionQuery;
|
||||
class Entity;
|
||||
class SceneNode;
|
||||
}
|
||||
|
||||
#include <openengine/ogre/renderer.hpp>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
///
|
||||
/// \brief Implements hardware occlusion queries on the GPU
|
||||
///
|
||||
class OcclusionQuery : public Ogre::RenderObjectListener, public Ogre::RenderQueueListener
|
||||
{
|
||||
public:
|
||||
OcclusionQuery(OEngine::Render::OgreRenderer*, Ogre::SceneNode* sunNode);
|
||||
~OcclusionQuery();
|
||||
|
||||
/**
|
||||
* @return true if occlusion queries are supported on the user's hardware
|
||||
*/
|
||||
bool supported();
|
||||
|
||||
/**
|
||||
* per-frame update
|
||||
*/
|
||||
void update(float duration);
|
||||
|
||||
/**
|
||||
* request occlusion test for a billboard at the given position, omitting an entity
|
||||
* @param position of the billboard in ogre coordinates
|
||||
* @param object to exclude from the occluders
|
||||
*/
|
||||
void occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object);
|
||||
|
||||
/**
|
||||
* @return true if a request is still outstanding
|
||||
*/
|
||||
bool occlusionTestPending();
|
||||
|
||||
/**
|
||||
* Checks if the objects held by this scenenode
|
||||
* can be considered as potential occluders
|
||||
* (which might not be the case when transparency is involved)
|
||||
* @param Scene node
|
||||
*/
|
||||
bool isPotentialOccluder(Ogre::SceneNode* node);
|
||||
|
||||
/**
|
||||
* @return true if the object tested in the last request was occluded
|
||||
*/
|
||||
bool getTestResult();
|
||||
|
||||
float getSunVisibility() const {return mSunVisibility;};
|
||||
|
||||
void setSunNode(Ogre::SceneNode* node);
|
||||
|
||||
private:
|
||||
Ogre::HardwareOcclusionQuery* mSunTotalAreaQuery;
|
||||
Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery;
|
||||
Ogre::HardwareOcclusionQuery* mSingleObjectQuery;
|
||||
Ogre::HardwareOcclusionQuery* mActiveQuery;
|
||||
|
||||
Ogre::BillboardSet* mBBQueryVisible;
|
||||
Ogre::BillboardSet* mBBQueryTotal;
|
||||
Ogre::BillboardSet* mBBQuerySingleObject;
|
||||
|
||||
Ogre::SceneNode* mSunNode;
|
||||
Ogre::SceneNode* mBBNode;
|
||||
Ogre::SceneNode* mBBNodeReal;
|
||||
float mSunVisibility;
|
||||
|
||||
Ogre::SceneNode* mObjectNode;
|
||||
|
||||
bool mWasVisible;
|
||||
bool mObjectWasVisible;
|
||||
|
||||
bool mTestResult;
|
||||
|
||||
bool mSupported;
|
||||
bool mDoQuery;
|
||||
bool mDoQuery2;
|
||||
|
||||
bool mQuerySingleObjectRequested;
|
||||
bool mQuerySingleObjectStarted;
|
||||
|
||||
OEngine::Render::OgreRenderer* mRendering;
|
||||
|
||||
protected:
|
||||
virtual void notifyRenderSingleObject(Ogre::Renderable* rend, const Ogre::Pass* pass, const Ogre::AutoParamDataSource* source,
|
||||
const Ogre::LightList* pLightList, bool suppressRenderStateChanges);
|
||||
|
||||
virtual void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,62 @@
|
||||
#ifndef GAME_RENDER_CONST_H
|
||||
#define GAME_RENDER_CONST_H
|
||||
|
||||
#include <OgreRenderQueue.h>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
// Render queue groups
|
||||
enum RenderQueueGroups
|
||||
{
|
||||
// Sky early (atmosphere, clouds, moons)
|
||||
RQG_SkiesEarly = Ogre::RENDER_QUEUE_SKIES_EARLY,
|
||||
|
||||
RQG_Main = Ogre::RENDER_QUEUE_MAIN,
|
||||
|
||||
RQG_Water = Ogre::RENDER_QUEUE_7+1,
|
||||
|
||||
RQG_Alpha = Ogre::RENDER_QUEUE_MAIN,
|
||||
|
||||
RQG_UnderWater = Ogre::RENDER_QUEUE_7+1,
|
||||
|
||||
RQG_OcclusionQuery = Ogre::RENDER_QUEUE_8,
|
||||
|
||||
// Sky late (sun & sun flare)
|
||||
RQG_SkiesLate = Ogre::RENDER_QUEUE_SKIES_LATE
|
||||
};
|
||||
|
||||
// Visibility flags
|
||||
enum VisibilityFlags
|
||||
{
|
||||
// Terrain
|
||||
RV_Terrain = 1,
|
||||
|
||||
// Statics (e.g. trees, houses)
|
||||
RV_Statics = 2,
|
||||
|
||||
// Small statics
|
||||
RV_StaticsSmall = 4,
|
||||
|
||||
// Water
|
||||
RV_Water = 8,
|
||||
|
||||
// Actors (player, npcs, creatures)
|
||||
RV_Actors = 16,
|
||||
|
||||
// Misc objects (containers, dynamic objects)
|
||||
RV_Misc = 32,
|
||||
|
||||
RV_Sky = 64,
|
||||
|
||||
// Sun glare (not visible in reflection)
|
||||
RV_Glare = 128,
|
||||
|
||||
RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water,
|
||||
|
||||
/// \todo markers (normally hidden)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,308 @@
|
||||
#include "shaderhelper.hpp"
|
||||
#include "renderingmanager.hpp"
|
||||
#include "shadows.hpp"
|
||||
|
||||
#include <OgreHighLevelGpuProgramManager.h>
|
||||
#include <OgreHighLevelGpuProgram.h>
|
||||
#include <OgreGpuProgramParams.h>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
using namespace Ogre;
|
||||
using namespace MWRender;
|
||||
|
||||
ShaderHelper::ShaderHelper(RenderingManager* rend)
|
||||
{
|
||||
mRendering = rend;
|
||||
applyShaders();
|
||||
}
|
||||
|
||||
void ShaderHelper::applyShaders()
|
||||
{
|
||||
if (!Settings::Manager::getBool("shaders", "Objects")) return;
|
||||
|
||||
bool mrt = RenderingManager::useMRT();
|
||||
bool shadows = Settings::Manager::getBool("enabled", "Shadows");
|
||||
bool split = Settings::Manager::getBool("split", "Shadows");
|
||||
|
||||
// shader for normal rendering
|
||||
createShader(mrt, shadows, split, "main");
|
||||
|
||||
// fallback shader without mrt and without shadows
|
||||
// (useful for reflection and for minimap)
|
||||
createShader(false, false, false, "main_fallback");
|
||||
}
|
||||
|
||||
void ShaderHelper::createShader(const bool mrt, const bool shadows, const bool split, const std::string& name)
|
||||
{
|
||||
HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
|
||||
|
||||
const int numsplits = 3;
|
||||
|
||||
// the number of lights to support.
|
||||
// when rendering an object, OGRE automatically picks the lights that are
|
||||
// closest to the object being rendered. unfortunately this mechanism does
|
||||
// not work perfectly for objects batched together (they will all use the same
|
||||
// lights). to work around this, we are simply pushing the maximum number
|
||||
// of lights here in order to minimize disappearing lights.
|
||||
int num_lights = Settings::Manager::getInt("num lights", "Objects");
|
||||
|
||||
{
|
||||
// vertex
|
||||
HighLevelGpuProgramPtr vertex;
|
||||
if (!mgr.getByName(name+"_vp").isNull())
|
||||
mgr.remove(name+"_vp");
|
||||
|
||||
vertex = mgr.createProgram(name+"_vp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
"cg", GPT_VERTEX_PROGRAM);
|
||||
vertex->setParameter("profiles", "vs_4_0 vs_2_x vp40 arbvp1");
|
||||
vertex->setParameter("entry_point", "main_vp");
|
||||
StringUtil::StrStreamType outStream;
|
||||
outStream <<
|
||||
"void main_vp( \n"
|
||||
" float4 position : POSITION, \n"
|
||||
" float4 normal : NORMAL, \n"
|
||||
" float4 colour : COLOR, \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float2 oUV : TEXCOORD0, \n"
|
||||
" out float4 oPosition : POSITION, \n"
|
||||
" out float4 oPositionObjSpace : TEXCOORD1, \n"
|
||||
" out float4 oNormal : TEXCOORD2, \n"
|
||||
" out float oDepth : TEXCOORD3, \n"
|
||||
" out float4 oVertexColour : TEXCOORD4, \n";
|
||||
if (shadows && !split) outStream <<
|
||||
" out float4 oLightSpacePos0 : TEXCOORD5, \n"
|
||||
" uniform float4x4 worldMatrix, \n"
|
||||
" uniform float4x4 texViewProjMatrix0, \n";
|
||||
else
|
||||
{
|
||||
for (int i=0; i<numsplits; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" out float4 oLightSpacePos"<<i<<" : TEXCOORD"<<i+5<<", \n"
|
||||
" uniform float4x4 texViewProjMatrix"<<i<<", \n";
|
||||
}
|
||||
outStream <<
|
||||
" uniform float4x4 worldMatrix, \n";
|
||||
}
|
||||
outStream <<
|
||||
" uniform float4x4 worldViewProj \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" oVertexColour = colour; \n"
|
||||
" oUV = uv; \n"
|
||||
" oNormal = normal; \n"
|
||||
" oPosition = mul( worldViewProj, position ); \n"
|
||||
" oDepth = oPosition.z; \n"
|
||||
" oPositionObjSpace = position; \n";
|
||||
if (shadows && !split) outStream <<
|
||||
" oLightSpacePos0 = mul(texViewProjMatrix0, mul(worldMatrix, position)); \n";
|
||||
else
|
||||
{
|
||||
outStream <<
|
||||
" float4 wPos = mul(worldMatrix, position); \n";
|
||||
for (int i=0; i<numsplits; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" oLightSpacePos"<<i<<" = mul(texViewProjMatrix"<<i<<", wPos); \n";
|
||||
}
|
||||
}
|
||||
outStream <<
|
||||
"}";
|
||||
vertex->setSource(outStream.str());
|
||||
vertex->load();
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
|
||||
if (shadows)
|
||||
{
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX);
|
||||
if (!split)
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("texViewProjMatrix0", GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, 0);
|
||||
else
|
||||
{
|
||||
for (int i=0; i<numsplits; ++i)
|
||||
{
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("texViewProjMatrix"+StringConverter::toString(i), GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// fragment
|
||||
HighLevelGpuProgramPtr fragment;
|
||||
if (!mgr.getByName(name+"_fp").isNull())
|
||||
mgr.remove(name+"_fp");
|
||||
|
||||
fragment = mgr.createProgram(name+"_fp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
"cg", GPT_FRAGMENT_PROGRAM);
|
||||
fragment->setParameter("profiles", "ps_4_0 ps_2_x fp40 arbfp1");
|
||||
fragment->setParameter("entry_point", "main_fp");
|
||||
StringUtil::StrStreamType outStream;
|
||||
|
||||
if (shadows) outStream <<
|
||||
"float depthShadow(sampler2D shadowMap, float4 shadowMapPos, float2 offset) \n"
|
||||
"{ \n"
|
||||
" shadowMapPos /= shadowMapPos.w; \n"
|
||||
" float3 o = float3(offset.xy, -offset.x) * 0.3f; \n"
|
||||
" float c = (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left \n"
|
||||
" c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right \n"
|
||||
" c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left \n"
|
||||
" c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right \n"
|
||||
" return c / 4; \n"
|
||||
"} \n";
|
||||
|
||||
outStream <<
|
||||
"void main_fp( \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float4 oColor : COLOR, \n"
|
||||
" uniform sampler2D texture : register(s0), \n"
|
||||
" float4 positionObjSpace : TEXCOORD1, \n"
|
||||
" float4 normal : TEXCOORD2, \n"
|
||||
" float iDepth : TEXCOORD3, \n"
|
||||
" float4 vertexColour : TEXCOORD4, \n"
|
||||
" uniform float4 fogColour, \n"
|
||||
" uniform float4 fogParams, \n";
|
||||
|
||||
if (shadows) outStream <<
|
||||
" uniform float4 shadowFar_fadeStart, \n";
|
||||
|
||||
if (shadows && !split) outStream <<
|
||||
" uniform sampler2D shadowMap : register(s1), \n"
|
||||
" float4 lightSpacePos0 : TEXCOORD5, \n"
|
||||
" uniform float4 invShadowmapSize0, \n";
|
||||
else
|
||||
{
|
||||
outStream <<
|
||||
" uniform float4 pssmSplitPoints, \n";
|
||||
for (int i=0; i<numsplits; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" uniform sampler2D shadowMap"<<i<<" : register(s"<<i+1<<"), \n"
|
||||
" float4 lightSpacePos"<<i<<" : TEXCOORD"<<i+5<<", \n"
|
||||
" uniform float4 invShadowmapSize"<<i<<", \n";
|
||||
}
|
||||
}
|
||||
|
||||
if (mrt) outStream <<
|
||||
" out float4 oColor1 : COLOR1, \n"
|
||||
" uniform float far, \n";
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" uniform float4 lightDiffuse"<<i<<", \n"
|
||||
" uniform float4 lightPositionObjSpace"<<i<<", \n"
|
||||
" uniform float4 lightAttenuation"<<i<<", \n";
|
||||
}
|
||||
outStream <<
|
||||
" uniform float4 lightAmbient, \n"
|
||||
" uniform float4 ambient, \n"
|
||||
" uniform float4 diffuse, \n"
|
||||
" uniform float4 emissive \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" float4 tex = tex2D(texture, uv); \n"
|
||||
" float d; \n"
|
||||
" float attn; \n"
|
||||
" float3 lightDir; \n"
|
||||
" float3 lightColour = float3(0, 0, 0); \n";
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" lightDir = lightPositionObjSpace"<<i<<".xyz - (positionObjSpace.xyz * lightPositionObjSpace"<<i<<".w); \n"
|
||||
|
||||
// pre-multiply light color with attenuation factor
|
||||
" d = length( lightDir ); \n"
|
||||
" attn = ( 1.0 / (( lightAttenuation"<<i<<".y ) + ( lightAttenuation"<<i<<".z * d ) + ( lightAttenuation"<<i<<".w * d * d ))); \n"
|
||||
" lightDiffuse"<<i<<" *= attn; \n";
|
||||
|
||||
if (i == 0 && shadows)
|
||||
{
|
||||
outStream <<
|
||||
" float shadow; \n";
|
||||
if (!split) outStream <<
|
||||
" shadow = depthShadow(shadowMap, lightSpacePos0, invShadowmapSize0.xy); \n";
|
||||
else
|
||||
{
|
||||
for (int j=0; j<numsplits; ++j)
|
||||
{
|
||||
std::string channel;
|
||||
if (j==0) channel = "x";
|
||||
else if (j==1) channel = "y";
|
||||
else if (j==2) channel = "z";
|
||||
|
||||
if (j==0)
|
||||
outStream << " if (iDepth <= pssmSplitPoints." << channel << ") \n";
|
||||
else if (j < numsplits - 1)
|
||||
outStream << " else if (iDepth <= pssmSplitPoints." << channel << ") \n";
|
||||
else
|
||||
outStream << " else \n";
|
||||
|
||||
outStream <<
|
||||
" { \n"
|
||||
" shadow = depthShadow(shadowMap" << j << ", lightSpacePos" << j << ", invShadowmapSize" << j << ".xy); \n"
|
||||
" } \n";
|
||||
}
|
||||
}
|
||||
outStream <<
|
||||
" float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; \n"
|
||||
" float fade = 1-((iDepth - shadowFar_fadeStart.y) / fadeRange); \n"
|
||||
" shadow = (iDepth > shadowFar_fadeStart.x) ? 1 : ((iDepth > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); \n"
|
||||
" lightColour.xyz += shadow * lit(dot(normalize(lightDir), normalize(normal)), 0, 0).y * lightDiffuse"<<i<<".xyz;\n";
|
||||
}
|
||||
else outStream <<
|
||||
" lightColour.xyz += lit(dot(normalize(lightDir), normalize(normal)), 0, 0).y * lightDiffuse"<<i<<".xyz;\n";
|
||||
}
|
||||
|
||||
outStream <<
|
||||
" float3 lightingFinal = lightColour.xyz * diffuse.xyz * vertexColour.xyz + ambient.xyz * lightAmbient.xyz + emissive.xyz; \n"
|
||||
" float fogValue = saturate((iDepth - fogParams.y) * fogParams.w); \n"
|
||||
" oColor.xyz = lerp(lightingFinal * tex.xyz, fogColour.xyz, fogValue); \n"
|
||||
" oColor.a = tex.a * diffuse.a * vertexColour.a; \n";
|
||||
|
||||
if (mrt) outStream <<
|
||||
" oColor1 = float4(iDepth / far, 0, 0, (oColor.a == 1)); \n"; // only write to MRT if alpha is 1
|
||||
|
||||
outStream <<
|
||||
"}";
|
||||
fragment->setSource(outStream.str());
|
||||
fragment->load();
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightPositionObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightDiffuse"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i);
|
||||
}
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_SURFACE_AMBIENT_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightAmbient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS);
|
||||
|
||||
if (shadows)
|
||||
{
|
||||
fragment->getDefaultParameters()->setNamedConstant("shadowFar_fadeStart", Vector4(mRendering->getShadows()->getShadowFar(), mRendering->getShadows()->getFadeStart()*mRendering->getShadows()->getShadowFar(), 0, 0));
|
||||
for (int i=0; i < (split ? numsplits : 1); ++i)
|
||||
{
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("invShadowmapSize" + StringConverter::toString(i), GpuProgramParameters::ACT_INVERSE_TEXTURE_SIZE, i+1);
|
||||
}
|
||||
if (split)
|
||||
{
|
||||
Vector4 splitPoints;
|
||||
const PSSMShadowCameraSetup::SplitPointList& splitPointList = mRendering->getShadows()->getPSSMSetup()->getSplitPoints();
|
||||
// Populate from split point 1, not 0, since split 0 isn't useful (usually 0)
|
||||
for (int i = 1; i < numsplits; ++i)
|
||||
{
|
||||
splitPoints[i-1] = splitPointList[i];
|
||||
}
|
||||
fragment->getDefaultParameters()->setNamedConstant("pssmSplitPoints", splitPoints);
|
||||
}
|
||||
}
|
||||
|
||||
if (mrt)
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("far", GpuProgramParameters::ACT_FAR_CLIP_DISTANCE);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
#ifndef GAME_SHADERHELPER_H
|
||||
#define GAME_SHADERHELPER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class RenderingManager;
|
||||
|
||||
///
|
||||
/// \brief manages the main shader
|
||||
///
|
||||
class ShaderHelper
|
||||
{
|
||||
public:
|
||||
ShaderHelper(RenderingManager* rend);
|
||||
|
||||
void applyShaders();
|
||||
///< apply new settings
|
||||
|
||||
private:
|
||||
RenderingManager* mRendering;
|
||||
|
||||
void createShader(const bool mrt, const bool shadows, const bool split, const std::string& name);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,173 @@
|
||||
#include "shadows.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <openengine/ogre/renderer.hpp>
|
||||
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreColourValue.h>
|
||||
#include <OgreShadowCameraSetupLiSPSM.h>
|
||||
#include <OgreShadowCameraSetupPSSM.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
|
||||
#include <OgreOverlayContainer.h>
|
||||
#include <OgreOverlayManager.h>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
using namespace MWRender;
|
||||
|
||||
Shadows::Shadows(OEngine::Render::OgreRenderer* rend) :
|
||||
mShadowFar(1000), mFadeStart(0.9)
|
||||
{
|
||||
mRendering = rend;
|
||||
mSceneMgr = mRendering->getScene();
|
||||
recreate();
|
||||
}
|
||||
|
||||
void Shadows::recreate()
|
||||
{
|
||||
bool enabled = Settings::Manager::getBool("enabled", "Shadows");
|
||||
|
||||
// Split shadow maps are currently disabled because the terrain cannot cope with them
|
||||
// (Too many texture units) Solution would be a multi-pass terrain material
|
||||
bool split = Settings::Manager::getBool("split", "Shadows");
|
||||
//const bool split = false;
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
int texsize = Settings::Manager::getInt("texture size", "Shadows");
|
||||
mSceneMgr->setShadowTextureSize(texsize);
|
||||
|
||||
mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE_INTEGRATED);
|
||||
|
||||
// no point light shadows, i'm afraid. might revisit this with Deferred Shading
|
||||
mSceneMgr->setShadowTextureCountPerLightType(Light::LT_POINT, 0);
|
||||
|
||||
mSceneMgr->setShadowTextureCountPerLightType(Light::LT_DIRECTIONAL, split ? 3 : 1);
|
||||
mSceneMgr->setShadowTextureCount(split ? 3 : 1);
|
||||
|
||||
mSceneMgr->setShadowTextureSelfShadow(true);
|
||||
mSceneMgr->setShadowCasterRenderBackFaces(true);
|
||||
mSceneMgr->setShadowTextureCasterMaterial("depth_shadow_caster");
|
||||
mSceneMgr->setShadowTexturePixelFormat(PF_FLOAT32_R);
|
||||
mSceneMgr->setShadowDirectionalLightExtrusionDistance(1000000);
|
||||
|
||||
mShadowFar = split ? Settings::Manager::getInt("split shadow distance", "Shadows") : Settings::Manager::getInt("shadow distance", "Shadows");
|
||||
mSceneMgr->setShadowFarDistance(mShadowFar);
|
||||
|
||||
mFadeStart = Settings::Manager::getFloat("fade start", "Shadows");
|
||||
|
||||
ShadowCameraSetupPtr shadowCameraSetup;
|
||||
if (split)
|
||||
{
|
||||
mPSSMSetup = new PSSMShadowCameraSetup();
|
||||
mPSSMSetup->setSplitPadding(5);
|
||||
mPSSMSetup->calculateSplitPoints(3, mRendering->getCamera()->getNearClipDistance(), mShadowFar);
|
||||
|
||||
const Real adjustFactors[3] = {64, 64, 64};
|
||||
for (int i=0; i < 3; ++i)
|
||||
{
|
||||
mPSSMSetup->setOptimalAdjustFactor(i, adjustFactors[i]);
|
||||
/*if (i==0)
|
||||
mSceneMgr->setShadowTextureConfig(i, texsize, texsize, Ogre::PF_FLOAT32_R);
|
||||
else if (i ==1)
|
||||
mSceneMgr->setShadowTextureConfig(i, texsize/2, texsize/2, Ogre::PF_FLOAT32_R);
|
||||
else if (i ==2)
|
||||
mSceneMgr->setShadowTextureConfig(i, texsize/4, texsize/4, Ogre::PF_FLOAT32_R);*/
|
||||
}
|
||||
|
||||
shadowCameraSetup = ShadowCameraSetupPtr(mPSSMSetup);
|
||||
}
|
||||
else
|
||||
{
|
||||
LiSPSMShadowCameraSetup* lispsmSetup = new LiSPSMShadowCameraSetup();
|
||||
lispsmSetup->setOptimalAdjustFactor(2);
|
||||
//lispsmSetup->setCameraLightDirectionThreshold(Degree(0));
|
||||
//lispsmSetup->setUseAggressiveFocusRegion(false);
|
||||
shadowCameraSetup = ShadowCameraSetupPtr(lispsmSetup);
|
||||
}
|
||||
mSceneMgr->setShadowCameraSetup(shadowCameraSetup);
|
||||
|
||||
// Set visibility mask for the shadow render textures
|
||||
int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows")
|
||||
+ (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics shadows", "Shadows")
|
||||
+ RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows");
|
||||
|
||||
for (int i = 0; i < (split ? 3 : 1); ++i)
|
||||
{
|
||||
TexturePtr shadowTexture = mSceneMgr->getShadowTexture(i);
|
||||
Viewport* vp = shadowTexture->getBuffer()->getRenderTarget()->getViewport(0);
|
||||
vp->setVisibilityMask(visibilityMask);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// --------------------------- Debug overlays to display the content of shadow maps -----------------------------------
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
OverlayManager& mgr = OverlayManager::getSingleton();
|
||||
Overlay* overlay;
|
||||
|
||||
// destroy if already exists
|
||||
if (overlay = mgr.getByName("DebugOverlay"))
|
||||
mgr.destroy(overlay);
|
||||
|
||||
overlay = mgr.create("DebugOverlay");
|
||||
for (size_t i = 0; i < (split ? 3 : 1); ++i) {
|
||||
TexturePtr tex = mRendering->getScene()->getShadowTexture(i);
|
||||
|
||||
// Set up a debug panel to display the shadow
|
||||
|
||||
if (MaterialManager::getSingleton().resourceExists("Ogre/DebugTexture" + StringConverter::toString(i)))
|
||||
MaterialManager::getSingleton().remove("Ogre/DebugTexture" + StringConverter::toString(i));
|
||||
MaterialPtr debugMat = MaterialManager::getSingleton().create(
|
||||
"Ogre/DebugTexture" + StringConverter::toString(i),
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
|
||||
debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||||
TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName());
|
||||
t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
|
||||
|
||||
OverlayContainer* debugPanel;
|
||||
|
||||
// destroy container if exists
|
||||
try
|
||||
{
|
||||
if (debugPanel =
|
||||
static_cast<OverlayContainer*>(
|
||||
mgr.getOverlayElement("Ogre/DebugTexPanel" + StringConverter::toString(i)
|
||||
)))
|
||||
mgr.destroyOverlayElement(debugPanel);
|
||||
}
|
||||
catch (Ogre::Exception&) {}
|
||||
|
||||
debugPanel = (OverlayContainer*)
|
||||
(OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/DebugTexPanel" + StringConverter::toString(i)));
|
||||
debugPanel->_setPosition(0.8, i*0.25);
|
||||
debugPanel->_setDimensions(0.2, 0.24);
|
||||
debugPanel->setMaterialName(debugMat->getName());
|
||||
debugPanel->show();
|
||||
overlay->add2D(debugPanel);
|
||||
overlay->show();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
PSSMShadowCameraSetup* Shadows::getPSSMSetup()
|
||||
{
|
||||
return mPSSMSetup;
|
||||
}
|
||||
|
||||
float Shadows::getShadowFar() const
|
||||
{
|
||||
return mShadowFar;
|
||||
}
|
||||
|
||||
float Shadows::getFadeStart() const
|
||||
{
|
||||
return mFadeStart;
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
#ifndef GAME_SHADOWS_H
|
||||
#define GAME_SHADOWS_H
|
||||
|
||||
// forward declares
|
||||
namespace Ogre
|
||||
{
|
||||
class SceneManager;
|
||||
class PSSMShadowCameraSetup;
|
||||
}
|
||||
namespace OEngine{
|
||||
namespace Render{
|
||||
class OgreRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class Shadows
|
||||
{
|
||||
public:
|
||||
Shadows(OEngine::Render::OgreRenderer* rend);
|
||||
|
||||
void recreate();
|
||||
|
||||
Ogre::PSSMShadowCameraSetup* getPSSMSetup();
|
||||
float getShadowFar() const;
|
||||
float getFadeStart() const;
|
||||
|
||||
protected:
|
||||
OEngine::Render::OgreRenderer* mRendering;
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
|
||||
Ogre::PSSMShadowCameraSetup* mPSSMSetup;
|
||||
float mShadowFar;
|
||||
float mFadeStart;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,531 @@
|
||||
#include <OgreTerrain.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
#include "terrainmaterial.hpp"
|
||||
#include "terrain.hpp"
|
||||
#include "renderconst.hpp"
|
||||
#include "shadows.hpp"
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
TerrainManager::TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend, const MWWorld::Environment& evn) :
|
||||
mEnvironment(evn), mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)), mRendering(rend)
|
||||
{
|
||||
|
||||
TerrainMaterialGeneratorPtr matGen;
|
||||
TerrainMaterialGeneratorB* matGenP = new TerrainMaterialGeneratorB();
|
||||
matGen.bind(matGenP);
|
||||
mTerrainGlobals.setDefaultMaterialGenerator(matGen);
|
||||
|
||||
TerrainMaterialGenerator::Profile* const activeProfile =
|
||||
mTerrainGlobals.getDefaultMaterialGenerator()
|
||||
->getActiveProfile();
|
||||
mActiveProfile = static_cast<TerrainMaterialGeneratorB::SM2Profile*>(activeProfile);
|
||||
|
||||
//The pixel error should be as high as possible without it being noticed
|
||||
//as it governs how fast mesh quality decreases.
|
||||
mTerrainGlobals.setMaxPixelError(8);
|
||||
|
||||
mTerrainGlobals.setLayerBlendMapSize(32);
|
||||
mTerrainGlobals.setDefaultGlobalColourMapSize(65);
|
||||
|
||||
//10 (default) didn't seem to be quite enough
|
||||
mTerrainGlobals.setSkirtSize(128);
|
||||
|
||||
//due to the sudden flick between composite and non composite textures,
|
||||
//this seemed the distance where it wasn't too noticeable
|
||||
mTerrainGlobals.setCompositeMapDistance(mWorldSize*2);
|
||||
|
||||
mActiveProfile->setLightmapEnabled(false);
|
||||
mActiveProfile->setLayerSpecularMappingEnabled(false);
|
||||
mActiveProfile->setLayerNormalMappingEnabled(false);
|
||||
mActiveProfile->setLayerParallaxMappingEnabled(false);
|
||||
|
||||
bool shadows = Settings::Manager::getBool("enabled", "Shadows");
|
||||
mActiveProfile->setReceiveDynamicShadowsEnabled(shadows);
|
||||
mActiveProfile->setReceiveDynamicShadowsDepth(shadows);
|
||||
if (Settings::Manager::getBool("split", "Shadows"))
|
||||
mActiveProfile->setReceiveDynamicShadowsPSSM(mRendering->getShadows()->getPSSMSetup());
|
||||
else
|
||||
mActiveProfile->setReceiveDynamicShadowsPSSM(0);
|
||||
|
||||
mActiveProfile->setShadowFar(mRendering->getShadows()->getShadowFar());
|
||||
mActiveProfile->setShadowFadeStart(mRendering->getShadows()->getFadeStart());
|
||||
|
||||
//composite maps lead to a drastic increase in loading time so are
|
||||
//disabled
|
||||
mActiveProfile->setCompositeMapEnabled(false);
|
||||
|
||||
mTerrainGroup.setOrigin(Vector3(mWorldSize/2,
|
||||
0,
|
||||
-mWorldSize/2));
|
||||
|
||||
Terrain::ImportData& importSettings = mTerrainGroup.getDefaultImportSettings();
|
||||
|
||||
importSettings.inputBias = 0;
|
||||
importSettings.terrainSize = mLandSize;
|
||||
importSettings.worldSize = mWorldSize;
|
||||
importSettings.minBatchSize = 9;
|
||||
importSettings.maxBatchSize = mLandSize;
|
||||
|
||||
importSettings.deleteInputData = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
TerrainManager::~TerrainManager()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
void TerrainManager::setDiffuse(const ColourValue& diffuse)
|
||||
{
|
||||
mTerrainGlobals.setCompositeMapDiffuse(diffuse);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
void TerrainManager::setAmbient(const ColourValue& ambient)
|
||||
{
|
||||
mTerrainGlobals.setCompositeMapAmbient(ambient);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
void TerrainManager::cellAdded(MWWorld::Ptr::CellStore *store)
|
||||
{
|
||||
const int cellX = store->cell->getGridX();
|
||||
const int cellY = store->cell->getGridY();
|
||||
|
||||
ESM::Land* land = mEnvironment.mWorld->getStore().lands.search(cellX, cellY);
|
||||
if ( land != NULL )
|
||||
{
|
||||
if (!land->dataLoaded)
|
||||
{
|
||||
land->loadData();
|
||||
}
|
||||
}
|
||||
|
||||
//split the cell terrain into four segments
|
||||
const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2;
|
||||
|
||||
for ( int x = 0; x < 2; x++ )
|
||||
{
|
||||
for ( int y = 0; y < 2; y++ )
|
||||
{
|
||||
Terrain::ImportData terrainData =
|
||||
mTerrainGroup.getDefaultImportSettings();
|
||||
|
||||
const int terrainX = cellX * 2 + x;
|
||||
const int terrainY = cellY * 2 + y;
|
||||
|
||||
//it makes far more sense to reallocate the memory here,
|
||||
//and let Ogre deal with it due to the issues with deleting
|
||||
//it at the wrong time if using threads (Which Terrain does)
|
||||
terrainData.inputFloat = OGRE_ALLOC_T(float,
|
||||
mLandSize*mLandSize,
|
||||
MEMCATEGORY_GEOMETRY);
|
||||
|
||||
if ( land != NULL )
|
||||
{
|
||||
//copy the height data row by row
|
||||
for ( int terrainCopyY = 0; terrainCopyY < mLandSize; terrainCopyY++ )
|
||||
{
|
||||
//the offset of the current segment
|
||||
const size_t yOffset = y * (mLandSize-1) * ESM::Land::LAND_SIZE +
|
||||
//offset of the row
|
||||
terrainCopyY * ESM::Land::LAND_SIZE;
|
||||
const size_t xOffset = x * (mLandSize-1);
|
||||
|
||||
memcpy(&terrainData.inputFloat[terrainCopyY*mLandSize],
|
||||
&land->landData->heights[yOffset + xOffset],
|
||||
mLandSize*sizeof(float));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(terrainData.inputFloat, 0, mLandSize*mLandSize*sizeof(float));
|
||||
}
|
||||
|
||||
std::map<uint16_t, int> indexes;
|
||||
initTerrainTextures(&terrainData, cellX, cellY,
|
||||
x * numTextures, y * numTextures,
|
||||
numTextures, indexes);
|
||||
|
||||
if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL)
|
||||
{
|
||||
mTerrainGroup.defineTerrain(terrainX, terrainY, &terrainData);
|
||||
|
||||
mTerrainGroup.loadTerrain(terrainX, terrainY, true);
|
||||
|
||||
Terrain* terrain = mTerrainGroup.getTerrain(terrainX, terrainY);
|
||||
initTerrainBlendMaps(terrain,
|
||||
cellX, cellY,
|
||||
x * numTextures, y * numTextures,
|
||||
numTextures,
|
||||
indexes);
|
||||
terrain->setVisibilityFlags(RV_Terrain);
|
||||
terrain->setRenderQueueGroup(RQG_Main);
|
||||
|
||||
if ( land && land->landData->usingColours )
|
||||
{
|
||||
// disable or enable global colour map (depends on available vertex colours)
|
||||
mActiveProfile->setGlobalColourMapEnabled(true);
|
||||
TexturePtr vertex = getVertexColours(land,
|
||||
cellX, cellY,
|
||||
x*(mLandSize-1),
|
||||
y*(mLandSize-1),
|
||||
mLandSize);
|
||||
|
||||
//this is a hack to get around the fact that Ogre seems to
|
||||
//corrupt the global colour map leading to rendering errors
|
||||
MaterialPtr mat = terrain->getMaterial();
|
||||
mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() );
|
||||
//mat = terrain->_getCompositeMapMaterial();
|
||||
//mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mActiveProfile->setGlobalColourMapEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mTerrainGroup.freeTemporaryResources();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
void TerrainManager::cellRemoved(MWWorld::Ptr::CellStore *store)
|
||||
{
|
||||
for ( int x = 0; x < 2; x++ )
|
||||
{
|
||||
for ( int y = 0; y < 2; y++ )
|
||||
{
|
||||
mTerrainGroup.unloadTerrain(store->cell->getGridX() * 2 + x,
|
||||
store->cell->getGridY() * 2 + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData,
|
||||
int cellX, int cellY,
|
||||
int fromX, int fromY, int size,
|
||||
std::map<uint16_t, int>& indexes)
|
||||
{
|
||||
assert(terrainData != NULL && "Must have valid terrain data");
|
||||
assert(fromX >= 0 && fromY >= 0 &&
|
||||
"Can't get a terrain texture on terrain outside the current cell");
|
||||
assert(fromX+size <= ESM::Land::LAND_TEXTURE_SIZE &&
|
||||
fromY+size <= ESM::Land::LAND_TEXTURE_SIZE &&
|
||||
"Can't get a terrain texture on terrain outside the current cell");
|
||||
|
||||
//this ensures that the ltex indexes are sorted (or retrived as sorted
|
||||
//which simplifies shading between cells).
|
||||
//
|
||||
//If we don't sort the ltex indexes, the splatting order may differ between
|
||||
//cells which may lead to inconsistent results when shading between cells
|
||||
std::set<uint16_t> ltexIndexes;
|
||||
for ( int y = fromY - 1; y < fromY + size + 1; y++ )
|
||||
{
|
||||
for ( int x = fromX - 1; x < fromX + size + 1; x++ )
|
||||
{
|
||||
ltexIndexes.insert(getLtexIndexAt(cellX, cellY, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
//there is one texture that we want to use as a base (i.e. it won't have
|
||||
//a blend map). This holds the ltex index of that base texture so that
|
||||
//we know not to include it in the output map
|
||||
int baseTexture = -1;
|
||||
for ( std::set<uint16_t>::iterator iter = ltexIndexes.begin();
|
||||
iter != ltexIndexes.end();
|
||||
++iter )
|
||||
{
|
||||
const uint16_t ltexIndex = *iter;
|
||||
//this is the base texture, so we can ignore this at present
|
||||
if ( ltexIndex == baseTexture )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::map<uint16_t, int>::const_iterator it = indexes.find(ltexIndex);
|
||||
|
||||
if ( it == indexes.end() )
|
||||
{
|
||||
//NB: All vtex ids are +1 compared to the ltex ids
|
||||
|
||||
assert( (int)mEnvironment.mWorld->getStore().landTexts.getSize() >= (int)ltexIndex - 1 &&
|
||||
"LAND.VTEX must be within the bounds of the LTEX array");
|
||||
|
||||
std::string texture;
|
||||
if ( ltexIndex == 0 )
|
||||
{
|
||||
texture = "_land_default.dds";
|
||||
}
|
||||
else
|
||||
{
|
||||
texture = mEnvironment.mWorld->getStore().landTexts.search(ltexIndex-1)->texture;
|
||||
//TODO this is needed due to MWs messed up texture handling
|
||||
texture = texture.substr(0, texture.rfind(".")) + ".dds";
|
||||
}
|
||||
|
||||
const size_t position = terrainData->layerList.size();
|
||||
terrainData->layerList.push_back(Terrain::LayerInstance());
|
||||
|
||||
terrainData->layerList[position].worldSize = 256;
|
||||
terrainData->layerList[position].textureNames.push_back("textures\\" + texture);
|
||||
|
||||
if ( baseTexture == -1 )
|
||||
{
|
||||
baseTexture = ltexIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
indexes[ltexIndex] = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
void TerrainManager::initTerrainBlendMaps(Terrain* terrain,
|
||||
int cellX, int cellY,
|
||||
int fromX, int fromY, int size,
|
||||
const std::map<uint16_t, int>& indexes)
|
||||
{
|
||||
assert(terrain != NULL && "Must have valid terrain");
|
||||
assert(fromX >= 0 && fromY >= 0 &&
|
||||
"Can't get a terrain texture on terrain outside the current cell");
|
||||
assert(fromX+size <= ESM::Land::LAND_TEXTURE_SIZE &&
|
||||
fromY+size <= ESM::Land::LAND_TEXTURE_SIZE &&
|
||||
"Can't get a terrain texture on terrain outside the current cell");
|
||||
|
||||
//size must be a power of 2 as we do divisions with a power of 2 number
|
||||
//that need to result in an integer for correct splatting
|
||||
assert( (size & (size - 1)) == 0 && "Size must be a power of 2");
|
||||
|
||||
const int blendMapSize = terrain->getLayerBlendMapSize();
|
||||
const int splatSize = blendMapSize / size;
|
||||
|
||||
//zero out every map
|
||||
std::map<uint16_t, int>::const_iterator iter;
|
||||
for ( iter = indexes.begin(); iter != indexes.end(); ++iter )
|
||||
{
|
||||
float* pBlend = terrain->getLayerBlendMap(iter->second)
|
||||
->getBlendPointer();
|
||||
memset(pBlend, 0, sizeof(float) * blendMapSize * blendMapSize);
|
||||
}
|
||||
|
||||
//covert the ltex data into a set of blend maps
|
||||
for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ )
|
||||
{
|
||||
for ( int texX = fromX - 1; texX < fromX + size + 1; texX++ )
|
||||
{
|
||||
const uint16_t ltexIndex = getLtexIndexAt(cellX, cellY, texX, texY);
|
||||
|
||||
//check if it is the base texture (which isn't in the map) and
|
||||
//if it is don't bother altering the blend map for it
|
||||
if ( indexes.find(ltexIndex) == indexes.end() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//while texX is the splat index relative to the entire cell,
|
||||
//relX is relative to the current segment we are splatting
|
||||
const int relX = texX - fromX;
|
||||
const int relY = texY - fromY;
|
||||
|
||||
const int layerIndex = indexes.find(ltexIndex)->second;
|
||||
|
||||
float* const pBlend = terrain->getLayerBlendMap(layerIndex)
|
||||
->getBlendPointer();
|
||||
|
||||
for ( int y = -1; y < splatSize + 1; y++ )
|
||||
{
|
||||
for ( int x = -1; x < splatSize + 1; x++ )
|
||||
{
|
||||
|
||||
//Note: Y is reversed
|
||||
const int splatY = blendMapSize - 1 - relY * splatSize - y;
|
||||
const int splatX = relX * splatSize + x;
|
||||
|
||||
if ( splatX >= 0 && splatX < blendMapSize &&
|
||||
splatY >= 0 && splatY < blendMapSize )
|
||||
{
|
||||
const int index = (splatY)*blendMapSize + splatX;
|
||||
|
||||
if ( y >= 0 && y < splatSize &&
|
||||
x >= 0 && x < splatSize )
|
||||
{
|
||||
pBlend[index] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//this provides a transition shading but also
|
||||
//rounds off the corners slightly
|
||||
pBlend[index] = std::min(1.0f, pBlend[index] + 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i = 1; i < terrain->getLayerCount(); i++ )
|
||||
{
|
||||
TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(i);
|
||||
blend->dirty();
|
||||
blend->update();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
int TerrainManager::getLtexIndexAt(int cellX, int cellY,
|
||||
int x, int y)
|
||||
{
|
||||
//check texture index falls within the 9 cell bounds
|
||||
//as this function can't cope with anything above that
|
||||
assert(x >= -ESM::Land::LAND_TEXTURE_SIZE &&
|
||||
y >= -ESM::Land::LAND_TEXTURE_SIZE &&
|
||||
"Trying to get land textures that are out of bounds");
|
||||
|
||||
assert(x < 2*ESM::Land::LAND_TEXTURE_SIZE &&
|
||||
y < 2*ESM::Land::LAND_TEXTURE_SIZE &&
|
||||
"Trying to get land textures that are out of bounds");
|
||||
|
||||
if ( x < 0 )
|
||||
{
|
||||
cellX--;
|
||||
x += ESM::Land::LAND_TEXTURE_SIZE;
|
||||
}
|
||||
else if ( x >= ESM::Land::LAND_TEXTURE_SIZE )
|
||||
{
|
||||
cellX++;
|
||||
x -= ESM::Land::LAND_TEXTURE_SIZE;
|
||||
}
|
||||
|
||||
if ( y < 0 )
|
||||
{
|
||||
cellY--;
|
||||
y += ESM::Land::LAND_TEXTURE_SIZE;
|
||||
}
|
||||
else if ( y >= ESM::Land::LAND_TEXTURE_SIZE )
|
||||
{
|
||||
cellY++;
|
||||
y -= ESM::Land::LAND_TEXTURE_SIZE;
|
||||
}
|
||||
|
||||
|
||||
ESM::Land* land = mEnvironment.mWorld->getStore().lands.search(cellX, cellY);
|
||||
if ( land != NULL )
|
||||
{
|
||||
if (!land->dataLoaded)
|
||||
{
|
||||
land->loadData();
|
||||
}
|
||||
|
||||
return land->landData
|
||||
->textures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
TexturePtr TerrainManager::getVertexColours(ESM::Land* land,
|
||||
int cellX, int cellY,
|
||||
int fromX, int fromY, int size)
|
||||
{
|
||||
TextureManager* const texMgr = TextureManager::getSingletonPtr();
|
||||
|
||||
const std::string colourTextureName = "VtexColours_" +
|
||||
boost::lexical_cast<std::string>(cellX) +
|
||||
"_" +
|
||||
boost::lexical_cast<std::string>(cellY) +
|
||||
"_" +
|
||||
boost::lexical_cast<std::string>(fromX) +
|
||||
"_" +
|
||||
boost::lexical_cast<std::string>(fromY);
|
||||
|
||||
TexturePtr tex = texMgr->getByName(colourTextureName);
|
||||
if ( !tex.isNull() )
|
||||
{
|
||||
return tex;
|
||||
}
|
||||
|
||||
tex = texMgr->createManual(colourTextureName,
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
TEX_TYPE_2D, size, size, 0, PF_BYTE_BGR);
|
||||
|
||||
HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer();
|
||||
|
||||
pixelBuffer->lock(HardwareBuffer::HBL_DISCARD);
|
||||
const PixelBox& pixelBox = pixelBuffer->getCurrentLock();
|
||||
|
||||
uint8* pDest = static_cast<uint8*>(pixelBox.data);
|
||||
|
||||
if ( land != NULL )
|
||||
{
|
||||
const char* const colours = land->landData->colours;
|
||||
for ( int y = 0; y < size; y++ )
|
||||
{
|
||||
for ( int x = 0; x < size; x++ )
|
||||
{
|
||||
const size_t colourOffset = (y+fromY)*3*65 + (x+fromX)*3;
|
||||
|
||||
assert( colourOffset < 65*65*3 &&
|
||||
"Colour offset is out of the expected bounds of record" );
|
||||
|
||||
const unsigned char r = colours[colourOffset + 0];
|
||||
const unsigned char g = colours[colourOffset + 1];
|
||||
const unsigned char b = colours[colourOffset + 2];
|
||||
|
||||
//as is the case elsewhere we need to flip the y
|
||||
const size_t imageOffset = (size - 1 - y)*size*4 + x*4;
|
||||
pDest[imageOffset + 0] = b;
|
||||
pDest[imageOffset + 1] = g;
|
||||
pDest[imageOffset + 2] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int y = 0; y < size; y++ )
|
||||
{
|
||||
for ( int x = 0; x < size; x++ )
|
||||
{
|
||||
for ( int k = 0; k < 3; k++ )
|
||||
{
|
||||
*pDest++ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pixelBuffer->unlock();
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
#ifndef _GAME_RENDER_TERRAIN_H
|
||||
#define _GAME_RENDER_TERRAIN_H
|
||||
|
||||
#include <OgreTerrain.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include "terrainmaterial.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace Ogre{
|
||||
class SceneManager;
|
||||
class TerrainGroup;
|
||||
class TerrainGlobalOptions;
|
||||
class Terrain;
|
||||
}
|
||||
|
||||
namespace MWRender{
|
||||
|
||||
/**
|
||||
* Implements the Morrowind terrain using the Ogre Terrain Component
|
||||
*
|
||||
* Each terrain cell is split into four blocks as this leads to an increase
|
||||
* in performance and means we don't hit splat limits quite as much
|
||||
*/
|
||||
class TerrainManager{
|
||||
public:
|
||||
TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend, const MWWorld::Environment& env);
|
||||
virtual ~TerrainManager();
|
||||
|
||||
void setDiffuse(const Ogre::ColourValue& diffuse);
|
||||
void setAmbient(const Ogre::ColourValue& ambient);
|
||||
|
||||
void cellAdded(MWWorld::Ptr::CellStore* store);
|
||||
void cellRemoved(MWWorld::Ptr::CellStore* store);
|
||||
private:
|
||||
Ogre::TerrainGlobalOptions mTerrainGlobals;
|
||||
Ogre::TerrainGroup mTerrainGroup;
|
||||
|
||||
const MWWorld::Environment& mEnvironment;
|
||||
RenderingManager* mRendering;
|
||||
|
||||
Ogre::TerrainMaterialGeneratorB::SM2Profile* mActiveProfile;
|
||||
|
||||
/**
|
||||
* The length in verticies of a single terrain block.
|
||||
*/
|
||||
static const int mLandSize = (ESM::Land::LAND_SIZE - 1)/2 + 1;
|
||||
|
||||
/**
|
||||
* The length in game units of a single terrain block.
|
||||
*/
|
||||
static const int mWorldSize = ESM::Land::REAL_SIZE/2;
|
||||
|
||||
/**
|
||||
* Setups up the list of textures for part of a cell, using indexes as
|
||||
* an output to create a mapping of MW LtexIndex to the relevant terrain
|
||||
* layer
|
||||
*
|
||||
* @param terrainData the terrain data to setup the textures for
|
||||
* @param cellX the coord of the cell
|
||||
* @param cellY the coord of the cell
|
||||
* @param fromX the ltex index in the current cell to start making the texture from
|
||||
* @param fromY the ltex index in the current cell to start making the texture from
|
||||
* @param size the size (number of splats) to get
|
||||
* @param indexes a mapping of ltex index to the terrain texture layer that
|
||||
* can be used by initTerrainBlendMaps
|
||||
*/
|
||||
void initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
|
||||
int cellX, int cellY,
|
||||
int fromX, int fromY, int size,
|
||||
std::map<uint16_t, int>& indexes);
|
||||
|
||||
/**
|
||||
* Creates the blend (splatting maps) for the given terrain from the ltex data.
|
||||
*
|
||||
* @param terrain the terrain object for the current cell
|
||||
* @param cellX the coord of the cell
|
||||
* @param cellY the coord of the cell
|
||||
* @param fromX the ltex index in the current cell to start making the texture from
|
||||
* @param fromY the ltex index in the current cell to start making the texture from
|
||||
* @param size the size (number of splats) to get
|
||||
* @param indexes the mapping of ltex to blend map produced by initTerrainTextures
|
||||
*/
|
||||
void initTerrainBlendMaps(Ogre::Terrain* terrain,
|
||||
int cellX, int cellY,
|
||||
int fromX, int fromY, int size,
|
||||
const std::map<uint16_t, int>& indexes);
|
||||
|
||||
/**
|
||||
* Gets a LTEX index at the given point, assuming the current cell
|
||||
* starts at (0,0). This supports getting values from the surrounding
|
||||
* cells so negative x, y is acceptable
|
||||
*
|
||||
* @param cellX the coord of the cell
|
||||
* @param cellY the coord of the cell
|
||||
* @param x, y the splat position of the ltex index to get relative to the
|
||||
* first splat of the current cell
|
||||
*/
|
||||
int getLtexIndexAt(int cellX, int cellY, int x, int y);
|
||||
|
||||
/**
|
||||
* Due to the fact that Ogre terrain doesn't support vertex colours
|
||||
* we have to generate them manually
|
||||
*
|
||||
* @param cellX the coord of the cell
|
||||
* @param cellY the coord of the cell
|
||||
* @param fromX the *vertex* index in the current cell to start making texture from
|
||||
* @param fromY the *vertex* index in the current cell to start making the texture from
|
||||
* @param size the size (number of vertexes) to get
|
||||
*/
|
||||
Ogre::TexturePtr getVertexColours(ESM::Land* land,
|
||||
int cellX, int cellY,
|
||||
int fromX, int fromY, int size);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _GAME_RENDER_TERRAIN_H
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,271 @@
|
||||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
This source file is part of OGRE
|
||||
(Object-oriented Graphics Rendering Engine)
|
||||
For the latest info, see http://www.ogre3d.org/
|
||||
|
||||
Copyright (c) 2000-2011 Torus Knot Software Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __Ogre_TerrainMaterialGeneratorB_H__
|
||||
#define __Ogre_TerrainMaterialGeneratorB_H__
|
||||
|
||||
#include "OgreTerrainPrerequisites.h"
|
||||
#include "OgreTerrainMaterialGenerator.h"
|
||||
#include "OgreGpuProgramParams.h"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class PSSMShadowCameraSetup;
|
||||
|
||||
/** \addtogroup Optional Components
|
||||
* @{
|
||||
*/
|
||||
/** \addtogroup Terrain
|
||||
* Some details on the terrain component
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/** A TerrainMaterialGenerator which can cope with normal mapped, specular mapped
|
||||
terrain.
|
||||
@note Requires the Cg plugin to render correctly
|
||||
*/
|
||||
class TerrainMaterialGeneratorB : public TerrainMaterialGenerator
|
||||
{
|
||||
public:
|
||||
TerrainMaterialGeneratorB();
|
||||
~TerrainMaterialGeneratorB();
|
||||
|
||||
/** Shader model 2 profile target.
|
||||
*/
|
||||
class SM2Profile : public TerrainMaterialGenerator::Profile
|
||||
{
|
||||
public:
|
||||
SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc);
|
||||
~SM2Profile();
|
||||
|
||||
bool isVertexCompressionSupported() const {return false;}
|
||||
|
||||
MaterialPtr generate(const Terrain* terrain);
|
||||
MaterialPtr generateForCompositeMap(const Terrain* terrain);
|
||||
uint8 getMaxLayers(const Terrain* terrain) const;
|
||||
void updateParams(const MaterialPtr& mat, const Terrain* terrain);
|
||||
void updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain);
|
||||
void requestOptions(Terrain* terrain);
|
||||
|
||||
void setShadowFar(float far);
|
||||
void setShadowFadeStart(float fadestart);
|
||||
|
||||
/** Whether to support normal mapping per layer in the shader (default true).
|
||||
*/
|
||||
bool isLayerNormalMappingEnabled() const { return mLayerNormalMappingEnabled; }
|
||||
/** Whether to support normal mapping per layer in the shader (default true).
|
||||
*/
|
||||
void setLayerNormalMappingEnabled(bool enabled);
|
||||
/** Whether to support parallax mapping per layer in the shader (default true).
|
||||
*/
|
||||
bool isLayerParallaxMappingEnabled() const { return mLayerParallaxMappingEnabled; }
|
||||
/** Whether to support parallax mapping per layer in the shader (default true).
|
||||
*/
|
||||
void setLayerParallaxMappingEnabled(bool enabled);
|
||||
/** Whether to support specular mapping per layer in the shader (default true).
|
||||
*/
|
||||
bool isLayerSpecularMappingEnabled() const { return mLayerSpecularMappingEnabled; }
|
||||
/** Whether to support specular mapping per layer in the shader (default true).
|
||||
*/
|
||||
void setLayerSpecularMappingEnabled(bool enabled);
|
||||
/** Whether to support a global colour map over the terrain in the shader,
|
||||
if it's present (default true).
|
||||
*/
|
||||
bool isGlobalColourMapEnabled() const { return mGlobalColourMapEnabled; }
|
||||
/** Whether to support a global colour map over the terrain in the shader,
|
||||
if it's present (default true).
|
||||
*/
|
||||
void setGlobalColourMapEnabled(bool enabled);
|
||||
/** Whether to support a light map over the terrain in the shader,
|
||||
if it's present (default true).
|
||||
*/
|
||||
bool isLightmapEnabled() const { return mLightmapEnabled; }
|
||||
/** Whether to support a light map over the terrain in the shader,
|
||||
if it's present (default true).
|
||||
*/
|
||||
void setLightmapEnabled(bool enabled);
|
||||
/** Whether to use the composite map to provide a lower LOD technique
|
||||
in the distance (default true).
|
||||
*/
|
||||
bool isCompositeMapEnabled() const { return mCompositeMapEnabled; }
|
||||
/** Whether to use the composite map to provide a lower LOD technique
|
||||
in the distance (default true).
|
||||
*/
|
||||
void setCompositeMapEnabled(bool enabled);
|
||||
/** Whether to support dynamic texture shadows received from other
|
||||
objects, on the terrain (default true).
|
||||
*/
|
||||
bool getReceiveDynamicShadowsEnabled() const { return mReceiveDynamicShadows; }
|
||||
/** Whether to support dynamic texture shadows received from other
|
||||
objects, on the terrain (default true).
|
||||
*/
|
||||
void setReceiveDynamicShadowsEnabled(bool enabled);
|
||||
|
||||
/** Whether to use PSSM support dynamic texture shadows, and if so the
|
||||
settings to use (default 0).
|
||||
*/
|
||||
void setReceiveDynamicShadowsPSSM(PSSMShadowCameraSetup* pssmSettings);
|
||||
/** Whether to use PSSM support dynamic texture shadows, and if so the
|
||||
settings to use (default 0).
|
||||
*/
|
||||
PSSMShadowCameraSetup* getReceiveDynamicShadowsPSSM() const { return mPSSM; }
|
||||
/** Whether to use depth shadows (default false).
|
||||
*/
|
||||
void setReceiveDynamicShadowsDepth(bool enabled);
|
||||
/** Whether to use depth shadows (default false).
|
||||
*/
|
||||
bool getReceiveDynamicShadowsDepth() const { return mDepthShadows; }
|
||||
/** Whether to use shadows on low LOD material rendering (when using composite map) (default false).
|
||||
*/
|
||||
void setReceiveDynamicShadowsLowLod(bool enabled);
|
||||
/** Whether to use shadows on low LOD material rendering (when using composite map) (default false).
|
||||
*/
|
||||
bool getReceiveDynamicShadowsLowLod() const { return mLowLodShadows; }
|
||||
|
||||
int getNumberOfLightsSupported() const;
|
||||
|
||||
/// Internal
|
||||
bool _isSM3Available() const { return mSM3Available; }
|
||||
|
||||
protected:
|
||||
|
||||
enum TechniqueType
|
||||
{
|
||||
HIGH_LOD,
|
||||
LOW_LOD,
|
||||
RENDER_COMPOSITE_MAP
|
||||
};
|
||||
void addTechnique(const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt);
|
||||
|
||||
/// Interface definition for helper class to generate shaders
|
||||
class ShaderHelper : public TerrainAlloc
|
||||
{
|
||||
public:
|
||||
ShaderHelper() {}
|
||||
virtual ~ShaderHelper() {}
|
||||
virtual HighLevelGpuProgramPtr generateVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
virtual HighLevelGpuProgramPtr generateFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
virtual void updateParams(const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap);
|
||||
protected:
|
||||
virtual String getVertexProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
virtual String getFragmentProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
virtual HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0;
|
||||
virtual HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0;
|
||||
virtual void generateVertexProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
virtual void generateFragmentProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
virtual void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0;
|
||||
virtual void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0;
|
||||
virtual void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0;
|
||||
virtual void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0;
|
||||
virtual void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0;
|
||||
virtual void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0;
|
||||
virtual void defaultVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog);
|
||||
virtual void defaultFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog);
|
||||
virtual void updateVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params);
|
||||
virtual void updateFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params);
|
||||
static String getChannel(uint idx);
|
||||
|
||||
size_t mShadowSamplerStartHi;
|
||||
size_t mShadowSamplerStartLo;
|
||||
|
||||
};
|
||||
|
||||
/// Utility class to help with generating shaders for Cg / HLSL.
|
||||
class ShaderHelperCg : public ShaderHelper
|
||||
{
|
||||
protected:
|
||||
HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream);
|
||||
void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream);
|
||||
void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
uint generateVpDynamicShadowsParams(uint texCoordStart, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
void generateVpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
void generateFpDynamicShadowsHelpers(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
void generateFpDynamicShadowsParams(uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
void generateFpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
|
||||
};
|
||||
|
||||
class ShaderHelperHLSL : public ShaderHelperCg
|
||||
{
|
||||
protected:
|
||||
HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
};
|
||||
|
||||
/// Utility class to help with generating shaders for GLSL.
|
||||
class ShaderHelperGLSL : public ShaderHelper
|
||||
{
|
||||
protected:
|
||||
HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
|
||||
void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {}
|
||||
void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {}
|
||||
void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {}
|
||||
void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {}
|
||||
void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {}
|
||||
void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {}
|
||||
};
|
||||
|
||||
ShaderHelper* mShaderGen;
|
||||
bool mLayerNormalMappingEnabled;
|
||||
bool mLayerParallaxMappingEnabled;
|
||||
bool mLayerSpecularMappingEnabled;
|
||||
bool mGlobalColourMapEnabled;
|
||||
bool mLightmapEnabled;
|
||||
bool mCompositeMapEnabled;
|
||||
bool mReceiveDynamicShadows;
|
||||
PSSMShadowCameraSetup* mPSSM;
|
||||
bool mDepthShadows;
|
||||
bool mLowLodShadows;
|
||||
bool mSM3Available;
|
||||
float mShadowFar;
|
||||
float mShadowFadeStart;
|
||||
|
||||
bool isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,293 @@
|
||||
#include "water.hpp"
|
||||
#include <components/settings/settings.hpp>
|
||||
#include "sky.hpp"
|
||||
#include "renderingmanager.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
Water::Water (Ogre::Camera *camera, SkyManager* sky, const ESM::Cell* cell) :
|
||||
mCamera (camera), mViewport (camera->getViewport()), mSceneManager (camera->getSceneManager()),
|
||||
mIsUnderwater(false), mVisibilityFlags(0),
|
||||
mReflectionTarget(0), mActive(1), mToggled(1)
|
||||
{
|
||||
mSky = sky;
|
||||
|
||||
try
|
||||
{
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false);
|
||||
} catch(...) {}
|
||||
|
||||
mTop = cell->water;
|
||||
|
||||
mIsUnderwater = false;
|
||||
|
||||
mWaterPlane = Plane(Vector3::UNIT_Y, 0);
|
||||
|
||||
MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Z);
|
||||
|
||||
mWater = mSceneManager->createEntity("water");
|
||||
mWater->setVisibilityFlags(RV_Water);
|
||||
mWater->setRenderQueueGroup(RQG_Water);
|
||||
mWater->setCastShadows(false);
|
||||
|
||||
mVisibilityFlags = RV_Terrain * Settings::Manager::getBool("reflect terrain", "Water")
|
||||
+ RV_Statics * Settings::Manager::getBool("reflect statics", "Water")
|
||||
+ RV_StaticsSmall * Settings::Manager::getBool("reflect small statics", "Water")
|
||||
+ RV_Actors * Settings::Manager::getBool("reflect actors", "Water")
|
||||
+ RV_Misc * Settings::Manager::getBool("reflect misc", "Water")
|
||||
+ RV_Sky;
|
||||
|
||||
mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
|
||||
mWaterNode->setPosition(0, mTop, 0);
|
||||
|
||||
mReflectionCamera = mSceneManager->createCamera("ReflectionCamera");
|
||||
|
||||
if(!(cell->data.flags & cell->Interior))
|
||||
{
|
||||
mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY));
|
||||
}
|
||||
mWaterNode->attachObject(mWater);
|
||||
|
||||
// Create rendertarget for reflection
|
||||
int rttsize = Settings::Manager::getInt("rtt size", "Water");
|
||||
|
||||
TexturePtr tex;
|
||||
if (Settings::Manager::getBool("shader", "Water"))
|
||||
{
|
||||
tex = TextureManager::getSingleton().createManual("WaterReflection",
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, rttsize, rttsize, 0, PF_FLOAT16_RGBA, TU_RENDERTARGET);
|
||||
|
||||
RenderTarget* rtt = tex->getBuffer()->getRenderTarget();
|
||||
Viewport* vp = rtt->addViewport(mReflectionCamera);
|
||||
vp->setOverlaysEnabled(false);
|
||||
vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f));
|
||||
vp->setShadowsEnabled(false);
|
||||
vp->setVisibilityMask( mVisibilityFlags );
|
||||
// use fallback techniques without shadows and without mrt (currently not implemented for sky and terrain)
|
||||
//vp->setMaterialScheme("Fallback");
|
||||
rtt->addListener(this);
|
||||
rtt->setActive(true);
|
||||
|
||||
mReflectionTarget = rtt;
|
||||
}
|
||||
|
||||
mCompositorName = RenderingManager::useMRT() ? "Underwater" : "UnderwaterNoMRT";
|
||||
|
||||
createMaterial();
|
||||
mWater->setMaterial(mMaterial);
|
||||
|
||||
mUnderwaterEffect = Settings::Manager::getBool("underwater effect", "Water");
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// ---------------------------------- reflection debug overlay ----------------------------------
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
/*
|
||||
if (Settings::Manager::getBool("shader", "Water"))
|
||||
{
|
||||
OverlayManager& mgr = OverlayManager::getSingleton();
|
||||
Overlay* overlay;
|
||||
// destroy if already exists
|
||||
if (overlay = mgr.getByName("ReflectionDebugOverlay"))
|
||||
mgr.destroy(overlay);
|
||||
|
||||
overlay = mgr.create("ReflectionDebugOverlay");
|
||||
|
||||
if (MaterialManager::getSingleton().resourceExists("Ogre/ReflectionDebugTexture"))
|
||||
MaterialManager::getSingleton().remove("Ogre/ReflectionDebugTexture");
|
||||
MaterialPtr debugMat = MaterialManager::getSingleton().create(
|
||||
"Ogre/ReflectionDebugTexture",
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
|
||||
debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||||
TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName());
|
||||
t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
|
||||
|
||||
OverlayContainer* debugPanel;
|
||||
|
||||
// destroy container if exists
|
||||
try
|
||||
{
|
||||
if (debugPanel =
|
||||
static_cast<OverlayContainer*>(
|
||||
mgr.getOverlayElement("Ogre/ReflectionDebugTexPanel"
|
||||
)))
|
||||
mgr.destroyOverlayElement(debugPanel);
|
||||
}
|
||||
catch (Ogre::Exception&) {}
|
||||
|
||||
debugPanel = (OverlayContainer*)
|
||||
(OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/ReflectionDebugTexPanel"));
|
||||
debugPanel->_setPosition(0, 0.55);
|
||||
debugPanel->_setDimensions(0.3, 0.3);
|
||||
debugPanel->setMaterialName(debugMat->getName());
|
||||
debugPanel->show();
|
||||
overlay->add2D(debugPanel);
|
||||
overlay->show();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void Water::setActive(bool active)
|
||||
{
|
||||
mActive = active;
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
Water::~Water()
|
||||
{
|
||||
MeshManager::getSingleton().remove("water");
|
||||
|
||||
mWaterNode->detachObject(mWater);
|
||||
mSceneManager->destroyEntity(mWater);
|
||||
mSceneManager->destroySceneNode(mWaterNode);
|
||||
|
||||
CompositorManager::getSingleton().removeCompositorChain(mViewport);
|
||||
}
|
||||
|
||||
void Water::changeCell(const ESM::Cell* cell)
|
||||
{
|
||||
mTop = cell->water;
|
||||
|
||||
if(!(cell->data.flags & cell->Interior))
|
||||
mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY));
|
||||
else
|
||||
setHeight(mTop);
|
||||
}
|
||||
|
||||
void Water::setHeight(const float height)
|
||||
{
|
||||
mTop = height;
|
||||
mWaterNode->setPosition(0, height, 0);
|
||||
}
|
||||
|
||||
void Water::toggle()
|
||||
{
|
||||
mToggled = !mToggled;
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
void Water::checkUnderwater(float y)
|
||||
{
|
||||
if (!mActive) return;
|
||||
if ((mIsUnderwater && y > mTop) || !mWater->isVisible() || mCamera->getPolygonMode() != Ogre::PM_SOLID)
|
||||
{
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mViewport, mCompositorName, false);
|
||||
|
||||
// tell the shader we are not underwater
|
||||
Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0);
|
||||
if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false))
|
||||
pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(0));
|
||||
|
||||
mWater->setRenderQueueGroup(RQG_Water);
|
||||
|
||||
mIsUnderwater = false;
|
||||
}
|
||||
|
||||
if (!mIsUnderwater && y < mTop && mWater->isVisible() && mCamera->getPolygonMode() == Ogre::PM_SOLID)
|
||||
{
|
||||
if (mUnderwaterEffect)
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mViewport, mCompositorName, true);
|
||||
|
||||
// tell the shader we are underwater
|
||||
Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0);
|
||||
if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false))
|
||||
pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(1));
|
||||
|
||||
mWater->setRenderQueueGroup(RQG_UnderWater);
|
||||
|
||||
mIsUnderwater = true;
|
||||
}
|
||||
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY)
|
||||
{
|
||||
return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2));
|
||||
}
|
||||
|
||||
void Water::preRenderTargetUpdate(const RenderTargetEvent& evt)
|
||||
{
|
||||
if (evt.source == mReflectionTarget)
|
||||
{
|
||||
mReflectionCamera->setOrientation(mCamera->getDerivedOrientation());
|
||||
mReflectionCamera->setPosition(mCamera->getDerivedPosition());
|
||||
mReflectionCamera->setNearClipDistance(mCamera->getNearClipDistance());
|
||||
mReflectionCamera->setFarClipDistance(mCamera->getFarClipDistance());
|
||||
mReflectionCamera->setAspectRatio(mCamera->getAspectRatio());
|
||||
mReflectionCamera->setFOVy(mCamera->getFOVy());
|
||||
|
||||
// Some messy code to get the skybox to show up at all
|
||||
// The problem here is that it gets clipped by the water plane
|
||||
// Therefore scale it up a bit
|
||||
Vector3 pos = mCamera->getRealPosition();
|
||||
pos.y = mTop*2 - pos.y;
|
||||
mSky->setSkyPosition(pos);
|
||||
mSky->scaleSky(mCamera->getFarClipDistance() / 1000.f);
|
||||
|
||||
mReflectionCamera->enableCustomNearClipPlane(Plane(Vector3::UNIT_Y, mTop));
|
||||
mReflectionCamera->enableReflection(Plane(Vector3::UNIT_Y, mTop));
|
||||
}
|
||||
}
|
||||
|
||||
void Water::postRenderTargetUpdate(const RenderTargetEvent& evt)
|
||||
{
|
||||
if (evt.source == mReflectionTarget)
|
||||
{
|
||||
mSky->resetSkyPosition();
|
||||
mSky->scaleSky(1);
|
||||
mReflectionCamera->disableCustomNearClipPlane();
|
||||
mReflectionCamera->disableReflection();
|
||||
}
|
||||
}
|
||||
|
||||
void Water::createMaterial()
|
||||
{
|
||||
mMaterial = MaterialManager::getSingleton().getByName("Water");
|
||||
|
||||
// these have to be set in code
|
||||
std::string textureNames[32];
|
||||
for (int i=0; i<32; ++i)
|
||||
{
|
||||
textureNames[i] = "textures\\water\\water" + StringConverter::toString(i, 2, '0') + ".dds";
|
||||
}
|
||||
mMaterial->getTechnique(1)->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2);
|
||||
|
||||
// use technique without shaders if reflection is disabled
|
||||
if (mReflectionTarget == 0)
|
||||
mMaterial->removeTechnique(0);
|
||||
|
||||
if (Settings::Manager::getBool("shader", "Water"))
|
||||
{
|
||||
CompositorInstance* compositor = CompositorManager::getSingleton().getCompositorChain(mViewport)->getCompositor("gbuffer");
|
||||
|
||||
TexturePtr colorTexture = compositor->getTextureInstance("mrt_output", 0);
|
||||
TextureUnitState* tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("refractionMap");
|
||||
if (tus != 0)
|
||||
tus->setTexture(colorTexture);
|
||||
|
||||
TexturePtr depthTexture = compositor->getTextureInstance("mrt_output", 1);
|
||||
tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("depthMap");
|
||||
if (tus != 0)
|
||||
tus->setTexture(depthTexture);
|
||||
}
|
||||
}
|
||||
|
||||
void Water::setViewportBackground(const ColourValue& bg)
|
||||
{
|
||||
if (mReflectionTarget)
|
||||
mReflectionTarget->getViewport(0)->setBackgroundColour(bg);
|
||||
}
|
||||
|
||||
void Water::updateVisible()
|
||||
{
|
||||
mWater->setVisible(mToggled && mActive);
|
||||
if (mReflectionTarget)
|
||||
mReflectionTarget->setActive(mToggled && mActive && !mIsUnderwater);
|
||||
}
|
||||
|
||||
} // namespace
|
@ -0,0 +1,69 @@
|
||||
#ifndef GAME_MWRENDER_WATER_H
|
||||
#define GAME_MWRENDER_WATER_H
|
||||
|
||||
#include <Ogre.h>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
namespace MWRender {
|
||||
|
||||
class SkyManager;
|
||||
|
||||
/// Water rendering
|
||||
class Water : public Ogre::RenderTargetListener
|
||||
{
|
||||
static const int CELL_SIZE = 8192;
|
||||
Ogre::Camera *mCamera;
|
||||
Ogre::SceneManager *mSceneManager;
|
||||
Ogre::Viewport *mViewport;
|
||||
|
||||
Ogre::Plane mWaterPlane;
|
||||
Ogre::SceneNode *mWaterNode;
|
||||
Ogre::Entity *mWater;
|
||||
|
||||
bool mIsUnderwater;
|
||||
bool mActive;
|
||||
bool mToggled;
|
||||
int mTop;
|
||||
|
||||
Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY);
|
||||
|
||||
protected:
|
||||
void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
|
||||
void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
|
||||
void updateVisible();
|
||||
|
||||
SkyManager* mSky;
|
||||
|
||||
std::string mCompositorName;
|
||||
|
||||
void createMaterial();
|
||||
Ogre::MaterialPtr mMaterial;
|
||||
|
||||
Ogre::Camera* mReflectionCamera;
|
||||
|
||||
Ogre::RenderTarget* mReflectionTarget;
|
||||
|
||||
bool mUnderwaterEffect;
|
||||
int mVisibilityFlags;
|
||||
|
||||
public:
|
||||
Water (Ogre::Camera *camera, SkyManager* sky, const ESM::Cell* cell);
|
||||
~Water();
|
||||
|
||||
void setActive(bool active);
|
||||
|
||||
void toggle();
|
||||
|
||||
void setViewportBackground(const Ogre::ColourValue& bg);
|
||||
|
||||
void checkUnderwater(float y);
|
||||
void changeCell(const ESM::Cell* cell);
|
||||
void setHeight(const float height);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue