Merge branch 'master' of https://github.com/zinnschlag/openmw.git into Factions2

Conflicts:
	apps/openmw/mwscript/docs/vmformat.txt
actorid
gugus 13 years ago
commit 07ea63c10c

@ -4,9 +4,6 @@ if (APPLE)
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
# using 10.6 sdk
set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk")
endif (APPLE)
# Macros
@ -146,6 +143,7 @@ endif (USE_MPG123)
# Platform specific
if (WIN32)
set(Boost_USE_STATIC_LIBS ON)
set(PLATFORM_INCLUDE_DIR "platform")
add_definitions(-DBOOST_ALL_NO_LIB)
else (WIN32)
@ -163,7 +161,7 @@ endif (APPLE)
# Dependencies
# Fix for not visible pthreads functions for linker with glibc 2.15
# Fix for not visible pthreads functions for linker with glibc 2.15
if (UNIX AND NOT APPLE)
find_package (Threads)
endif()
@ -184,6 +182,7 @@ ENDIF(WIN32)
ENDIF(OGRE_STATIC)
include_directories("."
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS}
${OGRE_Terrain_INCLUDE_DIR}
${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR}
${PLATFORM_INCLUDE_DIR}
${MYGUI_INCLUDE_DIRS}
@ -203,6 +202,7 @@ if(APPLE)
"Plugin_ParticleFX")
endif(APPLE)
add_subdirectory( files/)
add_subdirectory( files/mygui )
# Specify build paths
@ -258,7 +258,16 @@ endif (APPLE)
# Compiler settings
if (CMAKE_COMPILER_IS_GNUCC)
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-reorder)
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder)
# Silence warnings in OGRE headers. Remove once OGRE got fixed!
add_definitions (-Wno-ignored-qualifiers)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
add_definitions (-Wno-unused-but-set-parameter)
endif("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
endif (CMAKE_COMPILER_IS_GNUCC)
if(DPKG_PROGRAM)
@ -297,7 +306,7 @@ if(DPKG_PROGRAM)
Data files from the original game is required to run it.")
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "nvidia-cg-toolkit (>= 2.1), libboost-filesystem1.46.1 (>= 1.46.1), libboost-program-options1.46.1 (>= 1.46.1), libboost-system1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
@ -318,6 +327,7 @@ if(WIN32)
FILE(GLOB files "${OpenMW_BINARY_DIR}/Release/*.*")
INSTALL(FILES ${files} DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/readme.txt" DESTINATION ".")
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
SET(CPACK_GENERATOR "NSIS")
@ -328,6 +338,7 @@ if(WIN32)
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO})
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;esmtool;Esmtool;omwlauncher;OpenMW Launcher")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
SET(CPACK_RESOURCE_FILE_LICENSE "${OpenMW_SOURCE_DIR}/GPL3.txt")
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
@ -381,6 +392,11 @@ if (BUILD_LAUNCHER)
add_subdirectory( apps/launcher )
endif()
option(BUILD_MWINIIMPORTER "build MWiniImporter inspector" ON)
if (BUILD_MWINIIMPORTER)
add_subdirectory( apps/mwiniimporter )
endif()
if (WIN32)
if (MSVC)
if (USE_DEBUG_CONSOLE)
@ -470,6 +486,7 @@ if (APPLE)
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})

@ -95,5 +95,5 @@ else()
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}launcher.cfg")
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg")
endif()

@ -225,7 +225,7 @@ void DataFilesPage::setupDataFiles()
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setText(tr("<br><b>Could not find the Data Files location</b><br><br> \
The directory containing the Data Files was not found.<br><br> \
The directory containing the data files was not found.<br><br> \
Press \"Browse...\" to specify the location manually.<br>"));
QAbstractButton *dirSelectButton =
@ -1057,16 +1057,8 @@ void DataFilesPage::writeConfig(QString profile)
return;
}
// Prepare the OpenMW config
QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "openmw.cfg").string());
QFile file(config);
if (!file.exists()) {
config = QString::fromStdString((mCfgMgr.getUserPath() / "openmw.cfg").string());
}
// Open the config as a QFile
file.setFileName(config);
// Open the OpenMW config as a QFile
QFile file(QString::fromStdString((mCfgMgr.getUserPath() / "openmw.cfg").string()));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
// File cannot be opened or created

@ -194,6 +194,7 @@ void MainDialog::play()
QDir dir(QCoreApplication::applicationDirPath());
QString game = dir.absoluteFilePath("openmw");
QFile file(game);
game = "\"" + game + "\"";
#else
QString game = "./openmw";
QFile file(game);

@ -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,184 @@
#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" },
{ 0, 0 }
};
for(int i=0; map[i][0]; i++) {
mMergeMap.insert(std::make_pair<std::string, std::string>(map[i][0], map[i][1]));
}
}
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()) {
cfg.erase(it->first);
if(!this->specialMerge(it->first, it->second, cfg, ini)) {
cfg.insert(std::make_pair<std::string, std::vector<std::string> >(it->first, iniIt->second));
}
}
}
}
bool MwIniImporter::specialMerge(std::string cfgKey, std::string iniKey, multistrmap &cfg, multistrmap &ini) {
return false;
}
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,32 @@
#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 importGameFiles(multistrmap &cfg, multistrmap &ini);
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg);
private:
bool specialMerge(std::string cfgKey, std::string iniKey, multistrmap &cfg, multistrmap &ini);
std::string numberToString(int n);
bool mVerbose;
strmap mMergeMap;
};
#endif

@ -0,0 +1,79 @@
#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>\nAllowed options");
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")
;
bpo::variables_map vm;
try {
bpo::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
// parse help before calling notify because we dont want it to throw an error if help is set
if(vm.count("help")) {
std::cout << desc;
return 0;
}
bpo::notify(vm);
}
catch(std::exception& e) {
std::cerr << "Error:" << e.what() << std::endl;
return -1;
}
catch(...) {
std::cerr << "Error" << std::endl;
return -2;
}
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"));
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
importer.merge(cfg, ini);
if(vm.count("game-files")) {
importer.importGameFiles(cfg, ini);
}
std::cout << "write to: " << outputFile << std::endl;
importer.writeToFile(file, cfg);
return 0;
}

@ -15,7 +15,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
renderinginterface localmap
renderinginterface localmap occlusionquery terrain terrainmaterial water
)
add_openmw_dir (mwinput
@ -82,6 +82,7 @@ add_definitions(${SOUND_DEFINE})
target_link_libraries(openmw
${OGRE_LIBRARIES}
${OGRE_Terrain_LIBRARY}
${OGRE_STATIC_PLUGINS}
${OIS_LIBRARIES}
${Boost_LIBRARIES}

@ -204,13 +204,18 @@ OMW::Engine::~Engine()
void OMW::Engine::loadBSA()
{
const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa");
std::string dataDirectory;
for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter)
{
std::cout << "Adding " << iter->second.string() << std::endl;
Bsa::addBSA(iter->second.string());
}
dataDirectory = iter->second.parent_path().string();
const Files::PathContainer& dataDirs = mFileCollections.getPaths();
std::string dataDirectory;
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
dataDirectory = iter->string();
std::cout << "Data dir " << dataDirectory << std::endl;
Bsa::addDir(dataDirectory, mFSStrict);
}
@ -315,7 +320,11 @@ void OMW::Engine::go()
// This has to be added BEFORE MyGUI is initialized, as it needs
// to find core.xml here.
//addResourcesDirectory(mResDir);
addResourcesDirectory(mResDir / "mygui");
addResourcesDirectory(mResDir / "water");
// Create the window
mOgre->createWindow("OpenMW");

@ -56,7 +56,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -60,7 +60,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -58,7 +58,7 @@ namespace MWClass
{
// TODO implement reading
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -57,7 +57,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -85,7 +85,7 @@ namespace MWClass
{
// TODO check for key
std::cout << "Locked container" << std::endl;
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
else
@ -100,7 +100,7 @@ namespace MWClass
{
// Trap activation goes here
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0);
ptr.getCellRef().trap = "";
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}

@ -73,7 +73,7 @@ namespace MWClass
// TODO check for key
// TODO report failure to player (message, sound?). Look up behaviour of original MW.
std::cout << "Locked!" << std::endl;
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
@ -81,7 +81,7 @@ namespace MWClass
{
// Trap activation
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0);
ptr.getCellRef().trap = "";
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
@ -110,7 +110,7 @@ namespace MWClass
// TODO return action for rotating the door
// This is a little pointless, but helps with testing
environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
}

@ -54,7 +54,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Ingredient::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -59,7 +59,7 @@ namespace MWClass
if (!ref->base->sound.empty())
{
environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, true);
environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop);
}
}
@ -83,7 +83,7 @@ namespace MWClass
if (!(ref->base->data.flags & ESM::Light::Carry))
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -58,7 +58,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -56,7 +56,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -56,7 +56,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -57,7 +57,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -56,7 +56,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -57,7 +57,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -43,9 +43,6 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager,MWWorld::Environm
// Centre dialog
center();
//WindowManager *wm = environment.mWindowManager;
setText("NpcName", "Name of character");
//History view
getWidget(history, "History");
history->setOverflowToTheLeft(true);
@ -116,7 +113,8 @@ void DialogueWindow::onSelectTopic(MyGUI::ListBox* _sender, size_t _index)
void DialogueWindow::startDialogue(std::string npcName)
{
setText("NpcName", npcName);
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(npcName);
adjustWindowCaption();
}
void DialogueWindow::setKeywords(std::list<std::string> keyWords)

@ -182,7 +182,9 @@ void HUD::setPlayerPos(const float x, const float y)
}
MapWindow::MapWindow()
: Layout("openmw_map_window_layout.xml"), mGlobal(false)
: Layout("openmw_map_window_layout.xml")
, mGlobal(false)
, mVisible(false)
{
setCoord(500,0,320,300);
setText("WorldButton", "World");
@ -272,6 +274,17 @@ void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
mButton->setCaption( mGlobal ? "Local" : "World" );
}
LocalMapBase::LocalMapBase()
: mCurX(0)
, mCurY(0)
, mInterior(false)
, mLocalMap(NULL)
, mPrefix()
, mChanged(true)
, mLayout(NULL)
{
}
void LocalMapBase::init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout)
{
mLocalMap = widget;

@ -34,6 +34,7 @@ namespace MWGui
class LocalMapBase
{
public:
LocalMapBase();
void init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout);
void setCellPrefix(const std::string& prefix);
@ -85,6 +86,7 @@ namespace MWGui
{
public:
MapWindow();
virtual ~MapWindow(){}
void setVisible(bool b);
void setPlayerPos(const float x, const float y);

@ -180,71 +180,58 @@ void WindowManager::updateVisible()
// Mouse is visible whenever we're not in game mode
MyGUI::PointerManager::getInstance().setVisible(isGuiMode());
// If in game mode, don't show anything.
if(mode == GM_Game) //Use a switch/case structure
{
return;
}
if(mode == GM_MainMenu)
{
// Enable the main menu
menu->setVisible(true);
return;
}
if(mode == GM_Console)
{
console->enable();
return;
}
//There must be a more elegant solution
if (mode == GM_Name || mode == GM_Race || mode == GM_Class || mode == GM_ClassPick || mode == GM_ClassCreate || mode == GM_Birth || mode == GM_ClassGenerate || mode == GM_Review)
{
mCharGen->spawnDialog(mode);
return;
}
if(mode == GM_Inventory)
{
// Ah, inventory mode. First, compute the effective set of
// windows to show. This is controlled both by what windows the
// user has opened/closed (the 'shown' variable) and by what
// windows we are allowed to show (the 'allowed' var.)
int eff = shown & allowed;
// Show the windows we want
map -> setVisible( (eff & GW_Map) != 0 );
stats -> setVisible( (eff & GW_Stats) != 0 );
return;
}
if (mode == GM_Dialogue)
{
dialogueWindow->open();
return;
}
if(mode == GM_InterMessageBox)
{
if(!mMessageBoxManager->isInteractiveMessageBox()) {
setGuiMode(GM_Game);
switch(mode) {
case GM_Game:
// If in game mode, don't show anything.
break;
case GM_MainMenu:
menu->setVisible(true);
break;
case GM_Console:
console->enable();
break;
case GM_Name:
case GM_Race:
case GM_Class:
case GM_ClassPick:
case GM_ClassCreate:
case GM_Birth:
case GM_ClassGenerate:
case GM_Review:
mCharGen->spawnDialog(mode);
break;
case GM_Inventory:
{
// First, compute the effective set of windows to show.
// This is controlled both by what windows the
// user has opened/closed (the 'shown' variable) and by what
// windows we are allowed to show (the 'allowed' var.)
int eff = shown & allowed;
// Show the windows we want
map -> setVisible( (eff & GW_Map) != 0 );
stats -> setVisible( (eff & GW_Stats) != 0 );
break;
}
return;
}
if(mode == GM_Journal)
{
mJournal->setVisible(true);
mJournal->open();
return;
case GM_Dialogue:
dialogueWindow->open();
break;
case GM_InterMessageBox:
if(!mMessageBoxManager->isInteractiveMessageBox()) {
setGuiMode(GM_Game);
}
break;
case GM_Journal:
mJournal->setVisible(true);
mJournal->open();
break;
default:
// Unsupported mode, switch back to game
// Note: The call will eventually end up this method again but
// will stop at the check if mode is GM_Game.
setGuiMode(GM_Game);
break;
}
// Unsupported mode, switch back to game
// Note: The call will eventually end up this method again but
// will stop at the check if(mode == GM_Game) above.
setGuiMode(GM_Game);
}
void WindowManager::setValue (const std::string& id, const MWMechanics::Stat<int>& value)
@ -371,7 +358,6 @@ void WindowManager::updateSkillArea()
void WindowManager::removeDialog(OEngine::GUI::Layout*dialog)
{
std::cout << "dialogue a la poubelle";
assert(dialog);
if (!dialog)
return;

@ -126,6 +126,11 @@ namespace MWRender{
void Animation::handleShapes(std::vector<Nif::NiTriShapeCopy>* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){
shapeNumber = 0;
if (allshapes == NULL || creaturemodel == NULL || skel == NULL)
{
return;
}
std::vector<Nif::NiTriShapeCopy>::iterator allshapesiter;
for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++)

@ -27,11 +27,7 @@ bool Debugging::toggleRenderMode (int mode){
switch (mode)
{
case MWWorld::World::Render_CollisionDebug:
// TODO use a proper function instead of accessing the member variable
// directly.
eng->setDebugRenderingMode (!eng->isDebugCreated);
return eng->isDebugCreated;
return eng->toggleDebugRendering();
}
return false;

@ -88,38 +88,76 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
NifOgre::NIFLoader::load(mesh);
Ogre::Entity *ent = mRenderer.getScene()->createEntity(mesh);
/*
Ogre::Vector3 extents = ent->getBoundingBox().getSize();
extents *= insert->getScale();
// float size = std::max(std::max(extents.x, extents.y), extents.z);
bool small = (size < 250); /// \todo config value
// do not fade out doors. that will cause holes and look stupid
if (ptr.getTypeName().find("Door") != std::string::npos)
small = false;
*/
const bool small = false;
if (mBounds.find(ptr.getCell()) == mBounds.end())
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
Ogre::AxisAlignedBox bounds = ent->getBoundingBox();
bounds = Ogre::AxisAlignedBox(
insert->_getDerivedPosition() + bounds.getMinimum(),
insert->_getDerivedPosition() + bounds.getMaximum()
);
bounds.scale(insert->getScale());
mBounds[ptr.getCell()].merge(bounds);
if(!mIsStatic)
{
insert->attachObject(ent);
ent->setRenderingDistance(small ? 2500 : 0); /// \todo config value
}
else
{
Ogre::StaticGeometry* sg = 0;
if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
if (small)
{
uniqueID = uniqueID +1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
//Create the scenenode and put it in the map
mStaticGeometry[ptr.getCell()] = sg;
// This specifies the size of a single batch region.
// If it is set too high:
// - there will be problems choosing the correct lights
// - the culling will be more inefficient
// If it is set too low:
// - there will be too many batches.
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
mBounds[ptr.getCell()].merge(ent->getBoundingBox());
if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end())
{
uniqueID = uniqueID +1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
mStaticGeometrySmall[ptr.getCell()] = sg;
sg->setRenderingDistance(2500); /// \todo config value
}
else
sg = mStaticGeometrySmall[ptr.getCell()];
}
else
{
sg = mStaticGeometry[ptr.getCell()];
if( mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
{
uniqueID = uniqueID +1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
mStaticGeometry[ptr.getCell()] = sg;
}
else
sg = mStaticGeometry[ptr.getCell()];
}
// This specifies the size of a single batch region.
// If it is set too high:
// - there will be problems choosing the correct lights
// - the culling will be more inefficient
// If it is set too low:
// - there will be too many batches.
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
mBounds[ptr.getCell()].merge(insert->_getDerivedPosition());
mRenderer.getScene()->destroyEntity(ent);
}
@ -206,7 +244,14 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store)
mRenderer.getScene()->destroyStaticGeometry (sg);
sg = 0;
}
if(mStaticGeometrySmall.find(store) != mStaticGeometrySmall.end())
{
Ogre::StaticGeometry* sg = mStaticGeometrySmall[store];
mStaticGeometrySmall.erase(store);
mRenderer.getScene()->destroyStaticGeometry (sg);
sg = 0;
}
if(mBounds.find(store) != mBounds.end())
mBounds.erase(store);
}
@ -218,6 +263,11 @@ void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
Ogre::StaticGeometry* sg = mStaticGeometry[&cell];
sg->build();
}
if(mStaticGeometrySmall.find(&cell) != mStaticGeometrySmall.end())
{
Ogre::StaticGeometry* sg = mStaticGeometrySmall[&cell];
sg->build();
}
}
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)

@ -14,6 +14,7 @@ class Objects{
OEngine::Render::OgreRenderer &mRenderer;
std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometry;
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometrySmall;
std::map<MWWorld::Ptr::CellStore *, Ogre::AxisAlignedBox> mBounds;
Ogre::SceneNode* mMwRoot;
bool mIsStatic;

@ -0,0 +1,266 @@
#include "occlusionquery.hpp"
#include <OgreRenderSystem.h>
#include <OgreRoot.h>
#include <OgreBillboardSet.h>
#include <OgreHardwareOcclusionQuery.h>
#include <OgreEntity.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;
}
// This means that everything up to RENDER_QUEUE_MAIN can occlude the objects that are tested
const int queue = RENDER_QUEUE_MAIN+1;
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->setDefaultDimensions(150, 150);
mBBQueryTotal->createBillboard(Vector3::ZERO);
mBBQueryTotal->setMaterialName("QueryTotalPixels");
mBBQueryTotal->setRenderQueueGroup(queue+1);
mBBNodeReal->attachObject(mBBQueryTotal);
mBBQueryVisible = mRendering->getScene()->createBillboardSet(1);
mBBQueryVisible->setDefaultDimensions(150, 150);
mBBQueryVisible->createBillboard(Vector3::ZERO);
mBBQueryVisible->setMaterialName("QueryVisiblePixels");
mBBQueryVisible->setRenderQueueGroup(queue+1);
mBBNodeReal->attachObject(mBBQueryVisible);
mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1);
/// \todo ideally this should occupy exactly 1 pixel on the screen
mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003);
mBBQuerySingleObject->createBillboard(Vector3::ZERO);
mBBQuerySingleObject->setMaterialName("QueryVisiblePixels");
mBBQuerySingleObject->setRenderQueueGroup(queue);
mObjectNode->attachObject(mBBQuerySingleObject);
mRendering->getScene()->addRenderObjectListener(this);
mRendering->getScene()->addRenderQueueListener(this);
mDoQuery = true;
mDoQuery2 = 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 == RENDER_QUEUE_SKIES_LATE)
{
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;
mDoQuery2 = 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;
}

@ -0,0 +1,97 @@
#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();
/**
* @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

@ -23,6 +23,12 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
:mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0), mDebugging(engine)
{
mRendering.createScene("PlayerCam", 55, 5);
mTerrainManager = new TerrainManager(mRendering.getScene(),
environment);
//The fog type must be set before any terrain objects are created as if the
//fog type is set to FOG_NONE then the initially created terrain won't have any fog
configureFog(1, ColourValue(1,1,1));
// Set default mipmap level (NB some APIs ignore this)
TextureManager::getSingleton().setDefaultNumMipmaps(5);
@ -40,9 +46,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
mMwRoot->pitch(Degree(-90));
mObjects.setMwRoot(mMwRoot);
mActors.setMwRoot(mMwRoot);
//used to obtain ingame information of ogre objects (which are faced or selected)
mRaySceneQuery = mRendering.getScene()->createRayQuery(Ray());
Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player");
playerNode->pitch(Degree(90));
@ -53,6 +56,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
//mSkyManager = 0;
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
mWater = 0;
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mSun = 0;
@ -64,7 +71,9 @@ RenderingManager::~RenderingManager ()
//TODO: destroy mSun?
delete mPlayer;
delete mSkyManager;
delete mTerrainManager;
delete mLocalMap;
delete mOcclusionQuery;
}
MWRender::SkyManager* RenderingManager::getSkyManager()
@ -88,14 +97,33 @@ OEngine::Render::Fader* RenderingManager::getFader()
return mRendering.getFader();
}
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store){
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store)
{
mObjects.removeCell(store);
mActors.removeCell(store);
if (store->cell->isExterior())
mTerrainManager->cellRemoved(store);
}
void RenderingManager::removeWater ()
{
if(mWater){
delete mWater;
mWater = 0;
}
}
void RenderingManager::toggleWater()
{
if (mWater)
mWater->toggle();
}
void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
{
mObjects.buildStaticGeometry (*store);
if (store->cell->isExterior())
mTerrainManager->cellAdded(store);
}
void RenderingManager::addObject (const MWWorld::Ptr& ptr){
@ -136,18 +164,45 @@ void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Ve
void RenderingManager::update (float duration){
mActors.update (duration);
mOcclusionQuery->update(duration);
mSkyManager->update(duration);
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
mRendering.update(duration);
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );
checkUnderwater();
}
void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){
if(store->cell->data.flags & store->cell->HasWater){
if(mWater == 0)
mWater = new MWRender::Water(mRendering.getCamera(), store->cell);
else
mWater->changeCell(store->cell);
//else
}
else
removeWater();
}
void RenderingManager::setWaterHeight(const float height)
{
if (mWater)
mWater->setHeight(height);
}
void RenderingManager::skyEnable ()
{
if(mSkyManager)
mSkyManager->enable();
mOcclusionQuery->setSunNode(mSkyManager->getSunNode());
}
void RenderingManager::skyDisable ()
@ -236,17 +291,17 @@ void RenderingManager::setAmbientMode()
{
case 0:
mRendering.getScene()->setAmbientLight(mAmbientColor);
setAmbientColour(mAmbientColor);
break;
case 1:
mRendering.getScene()->setAmbientLight(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1));
setAmbientColour(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1));
break;
case 2:
mRendering.getScene()->setAmbientLight(ColourValue(1,1,1));
setAmbientColour(ColourValue(1,1,1));
break;
}
}
@ -286,6 +341,11 @@ void RenderingManager::toggleLight()
setAmbientMode();
}
void RenderingManager::checkUnderwater(){
if(mWater){
mWater->checkUnderwater( mRendering.getCamera()->getRealPosition().y );
}
}
void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
int mode, int number)
@ -301,11 +361,13 @@ void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr)
void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
{
mSun->setDiffuseColour(colour);
mTerrainManager->setDiffuse(colour);
}
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
{
mRendering.getScene()->setAmbientLight(colour);
mTerrainManager->setAmbient(colour);
}
void RenderingManager::sunEnable()

@ -3,6 +3,7 @@
#include "sky.hpp"
#include "terrain.hpp"
#include "debugging.hpp"
#include "../mwworld/class.hpp"
@ -24,7 +25,9 @@
#include "objects.hpp"
#include "actors.hpp"
#include "player.hpp"
#include "water.hpp"
#include "localmap.hpp"
#include "occlusionquery.hpp"
namespace Ogre
{
@ -59,6 +62,8 @@ class RenderingManager: private RenderingInterface {
RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment);
virtual ~RenderingManager();
virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as
/// MWWorld::Player has been rewritten to not need access
/// to internal details of the rendering system anymore
@ -67,7 +72,7 @@ class RenderingManager: private RenderingInterface {
void toggleLight();
bool toggleRenderMode(int mode);
OEngine::Render::Fader* getFader();
void removeCell (MWWorld::Ptr::CellStore *store);
@ -75,6 +80,9 @@ class RenderingManager: private RenderingInterface {
/// \todo this function should be removed later. Instead the rendering subsystems should track
/// when rebatching is needed and update automatically at the end of each frame.
void cellAdded (MWWorld::Ptr::CellStore *store);
void waterAdded(MWWorld::Ptr::CellStore *store);
void removeWater();
void preCellChange (MWWorld::Ptr::CellStore* store);
///< this event is fired immediately before changing cell
@ -86,17 +94,24 @@ class RenderingManager: private RenderingInterface {
void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale);
void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation);
void checkUnderwater();
void setWaterHeight(const float height);
void toggleWater();
/// \param store Cell the object was in previously (\a ptr has already been updated to the new cell).
void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store);
void update (float duration);
void setAmbientColour(const Ogre::ColourValue& colour);
void setSunColour(const Ogre::ColourValue& colour);
void setSunDirection(const Ogre::Vector3& direction);
void sunEnable();
void sunDisable();
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); };
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; };
void setGlare(bool glare);
void skyEnable ();
void skyDisable ();
@ -109,13 +124,13 @@ class RenderingManager: private RenderingInterface {
void requestMap (MWWorld::Ptr::CellStore* cell);
///< request the local map for a cell
/// configure fog according to cell
void configureFog(ESMS::CellStore<MWWorld::RefData> &mCell);
/// configure fog manually
void configureFog(const float density, const Ogre::ColourValue& colour);
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
int number = 1);
///< Run animation for a MW-reference. Calls to this function for references that are currently not
@ -131,9 +146,15 @@ class RenderingManager: private RenderingInterface {
private:
void setAmbientMode();
SkyManager* mSkyManager;
OcclusionQuery* mOcclusionQuery;
TerrainManager* mTerrainManager;
MWRender::Water *mWater;
OEngine::Render::OgreRenderer &mRendering;
MWRender::Objects mObjects;
@ -149,7 +170,6 @@ class RenderingManager: private RenderingInterface {
/// that the OGRE coordinate system matches that used internally in
/// Morrowind.
Ogre::SceneNode *mMwRoot;
Ogre::RaySceneQuery *mRaySceneQuery;
OEngine::Physic::PhysicEngine* mPhysicsEngine;

@ -12,6 +12,7 @@
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
#include "occlusionquery.hpp"
using namespace MWRender;
using namespace Ogre;
@ -30,7 +31,7 @@ BillboardObject::BillboardObject()
void BillboardObject::setVisible(const bool visible)
{
mNode->setVisible(visible);
mBBSet->setVisible(visible);
}
void BillboardObject::setSize(const float size)
@ -88,7 +89,7 @@ void BillboardObject::init(const String& textureName,
/// \todo These billboards are not 100% correct, might want to revisit them later
mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1);
mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize);
mBBSet->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+2);
mBBSet->setRenderQueueGroup(RENDER_QUEUE_MAIN+2);
mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON);
mBBSet->setCommonDirection( -position.normalisedCopy() );
mNode = rootNode->createChildSceneNode();
@ -319,19 +320,22 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
, mThunderTextureUnit(NULL)
, mRemainingTransitionTime(0.0f)
, mGlareFade(0.0f)
, mGlare(0.0f)
, mEnabled(true)
, mGlareEnabled(true)
, mSunEnabled(true)
, mMasserEnabled(true)
, mSecundaEnabled(true)
, mCreated(false)
{
mViewport = pCamera->getViewport();
mSceneMgr = pMwRoot->getCreator();
mRootNode = pCamera->getParentSceneNode()->createChildSceneNode();
mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates
mRootNode->setInheritOrientation(false);
}
void SkyManager::create()
{
/// \todo preload all the textures and meshes that are used for sky rendering
// Create overlay used for thunderstorm
@ -532,7 +536,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
" uniform float4 emissive \n"
") \n"
"{ \n"
" uv += float2(1,0) * time * speed * 0.003; \n" // Scroll in x direction
" uv += float2(0,1) * time * speed * 0.003; \n" // Scroll in y direction
" float4 tex = lerp(tex2D(texture, uv), tex2D(secondTexture, uv), transitionFactor); \n"
" oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n"
"}";
@ -561,6 +565,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA);
mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("");
mCreated = true;
}
SkyManager::~SkyManager()
@ -573,11 +579,13 @@ SkyManager::~SkyManager()
int SkyManager::getMasserPhase() const
{
if (!mCreated) return 0;
return mMasser->getPhaseInt();
}
int SkyManager::getSecundaPhase() const
{
if (!mCreated) return 0;
return mSecunda->getPhaseInt();
}
@ -592,10 +600,23 @@ void SkyManager::update(float duration)
mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
mSecunda->setPhase ( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
if (mSunEnabled)
{
// take 1/5 sec for fading the glare effect from invisible to full
if (mGlareFade > mGlare)
{
mGlareFade -= duration*5;
if (mGlareFade < mGlare) mGlareFade = mGlare;
}
else if (mGlareFade < mGlare)
{
mGlareFade += duration*5;
if (mGlareFade > mGlare) mGlareFade = mGlare;
}
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
Vector3 sun = mSunGlare->getPosition();
sun = Vector3(sun.x, sun.z, -sun.y);
Vector3 cam = mViewport->getCamera()->getRealDirection();
@ -603,21 +624,10 @@ void SkyManager::update(float duration)
float val = 1- (angle.valueDegrees() / 180.f);
val = (val*val*val*val)*2;
if (mGlareEnabled)
{
mGlareFade += duration*3;
if (mGlareFade > 1) mGlareFade = 1;
}
else
{
mGlareFade -= duration*3;
if (mGlareFade < 0.3) mGlareFade = 0;
}
mSunGlare->setSize(val * (mGlareFade));
mSunGlare->setSize(val * mGlareFade);
}
mSunGlare->setVisible(mGlareFade>0 && mSunEnabled);
mSunGlare->setVisible(mSunEnabled);
mSun->setVisible(mSunEnabled);
mMasser->setVisible(mMasserEnabled);
mSecunda->setVisible(mSecundaEnabled);
@ -628,6 +638,9 @@ void SkyManager::update(float duration)
void SkyManager::enable()
{
if (!mCreated)
create();
mRootNode->setVisible(true);
mEnabled = true;
}
@ -651,6 +664,7 @@ void SkyManager::setCloudsOpacity(float opacity)
void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
{
if (!mCreated) return;
if (mClouds != weather.mCloudTexture)
{
mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("textures\\"+weather.mCloudTexture);
@ -719,15 +733,15 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
else
strength = 1.f;
mSunGlare->setVisibility(weather.mGlareView * strength);
mSun->setVisibility(strength);
mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength);
mSun->setVisibility(mGlareFade >= 0.5 ? weather.mGlareView * mGlareFade * strength : 0);
mAtmosphereNight->setVisible(weather.mNight && mEnabled);
}
void SkyManager::setGlare(bool glare)
void SkyManager::setGlare(const float glare)
{
mGlareEnabled = glare;
mGlare = glare;
}
Vector3 SkyManager::getRealSunPos()
@ -747,17 +761,20 @@ void SkyManager::sunDisable()
void SkyManager::setSunDirection(const Vector3& direction)
{
if (!mCreated) return;
mSun->setPosition(direction);
mSunGlare->setPosition(direction);
}
void SkyManager::setMasserDirection(const Vector3& direction)
{
if (!mCreated) return;
mMasser->setPosition(direction);
}
void SkyManager::setSecundaDirection(const Vector3& direction)
{
if (!mCreated) return;
mSecunda->setPosition(direction);
}
@ -783,6 +800,7 @@ void SkyManager::secundaDisable()
void SkyManager::setThunder(const float factor)
{
if (!mCreated) return;
if (factor > 0.f)
{
mThunderOverlay->show();
@ -812,3 +830,9 @@ void SkyManager::setDate(int day, int month)
mDay = day;
mMonth = month;
}
Ogre::SceneNode* SkyManager::getSunNode()
{
if (!mCreated) return 0;
return mSun->getNode();
}

@ -109,61 +109,68 @@ namespace MWRender
public:
SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env);
~SkyManager();
void update(float duration);
void create();
///< no need to call this, automatically done on first enable()
void enable();
void disable();
void setHour (double hour);
///< will be called even when sky is disabled.
void setDate (int day, int month);
///< will be called even when sky is disabled.
int getMasserPhase() const;
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
/// 3 waxing or waning gibbous, 4 full moon
int getSecundaPhase() const;
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
/// 3 waxing or waning gibbous, 4 full moon
void setMoonColour (bool red);
///< change Secunda colour to red
void setCloudsOpacity(float opacity);
///< change opacity of the clouds
void setWeather(const MWWorld::WeatherResult& weather);
Ogre::SceneNode* getSunNode();
void sunEnable();
void sunDisable();
void setSunDirection(const Ogre::Vector3& direction);
void setMasserDirection(const Ogre::Vector3& direction);
void setSecundaDirection(const Ogre::Vector3& direction);
void setMasserFade(const float fade);
void setSecundaFade(const float fade);
void masserEnable();
void masserDisable();
void secundaEnable();
void secundaDisable();
void setThunder(const float factor);
void setGlare(bool glare);
void setGlare(const float glare);
Ogre::Vector3 getRealSunPos();
private:
bool mCreated;
MWWorld::Environment* mEnvironment;
float mHour;
int mDay;
@ -203,12 +210,12 @@ namespace MWRender
float mRemainingTransitionTime;
float mGlareFade;
float mGlare; // target
float mGlareFade; // actual
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
bool mEnabled;
bool mGlareEnabled;
bool mSunEnabled;
bool mMasserEnabled;
bool mSecundaEnabled;

@ -0,0 +1,510 @@
#include <OgreTerrain.h>
#include <OgreTerrainGroup.h>
#include <boost/lexical_cast.hpp>
#include "../mwworld/world.hpp"
#include "terrainmaterial.hpp"
#include "terrain.hpp"
using namespace Ogre;
namespace MWRender
{
//----------------------------------------------------------------------------------------------
TerrainManager::TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& evn) :
mEnvironment(evn), mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize))
{
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);
mActiveProfile->setReceiveDynamicShadowsEnabled(false);
//composite maps lead to a drastic reduction 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 )
{
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);
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 )
{
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,117 @@
#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, 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;
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,266 @@
/*
-----------------------------------------------------------------------------
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);
/** 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;
bool isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const;
};
};
/** @} */
/** @} */
}
#endif

@ -0,0 +1,95 @@
#include "water.hpp"
namespace MWRender
{
Water::Water (Ogre::Camera *camera, const ESM::Cell* cell) :
mCamera (camera), mViewport (camera->getViewport()), mSceneManager (camera->getSceneManager()),
mIsUnderwater(false)
{
try
{
Ogre::CompositorManager::getSingleton().addCompositor(mViewport, "Water", -1);
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false);
} catch(...) {}
mTop = cell->water;
mIsUnderwater = false;
mWaterPlane = Ogre::Plane(Ogre::Vector3::UNIT_Y, 0);
Ogre::MeshManager::getSingleton().createPlane("water", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,5, Ogre::Vector3::UNIT_Z);
mWater = mSceneManager->createEntity("water");
mWater->setMaterialName("Examples/Water0");
mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
mWaterNode->setPosition(0, mTop, 0);
if(!(cell->data.flags & cell->Interior))
{
mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY));
}
mWaterNode->attachObject(mWater);
}
Water::~Water()
{
Ogre::MeshManager::getSingleton().remove("water");
mWaterNode->detachObject(mWater);
mSceneManager->destroyEntity(mWater);
mSceneManager->destroySceneNode(mWaterNode);
Ogre::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()
{
mWater->setVisible(!mWater->getVisible());
}
void Water::checkUnderwater(float y)
{
if ((mIsUnderwater && y > mTop) || !mWater->isVisible())
{
try {
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false);
} catch(...) {}
mIsUnderwater = false;
}
if (!mIsUnderwater && y < mTop && mWater->isVisible())
{
try {
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", true);
} catch(...) {}
mIsUnderwater = true;
}
}
Ogre::Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY)
{
return Ogre::Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2));
}
} // namespace

@ -0,0 +1,40 @@
#ifndef GAME_MWRENDER_WATER_H
#define GAME_MWRENDER_WATER_H
#include <Ogre.h>
#include <components/esm/loadcell.hpp>
namespace MWRender {
/// Water rendering
class Water : Ogre::RenderTargetListener, Ogre::Camera::Listener
{
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;
int mTop;
Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY);
public:
Water (Ogre::Camera *camera, const ESM::Cell* cell);
~Water();
void toggle();
void checkUnderwater(float y);
void changeCell(const ESM::Cell* cell);
void setHeight(const float height);
};
}
#endif

@ -133,11 +133,70 @@ namespace MWScript
}
};
class OpGetWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
runtime.push (cell->mWaterLevel);
}
};
class OpSetWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
Interpreter::Type_Float level = runtime[0].mFloat;
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
if (!(cell->cell->data.flags & ESM::Cell::Interior))
throw std::runtime_error("Can't set water level in exterior cell");
cell->mWaterLevel = level;
context.getEnvironment().mWorld->setWaterHeight(cell->mWaterLevel);
}
};
class OpModWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
Interpreter::Type_Float level = runtime[0].mFloat;
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
if (!(cell->cell->data.flags & ESM::Cell::Interior))
throw std::runtime_error("Can't set water level in exterior cell");
cell->mWaterLevel +=level;
context.getEnvironment().mWorld->setWaterHeight(cell->mWaterLevel);
}
};
const int opcodeCellChanged = 0x2000000;
const int opcodeCOC = 0x2000026;
const int opcodeCOE = 0x200008e;
const int opcodeGetInterior = 0x2000131;
const int opcodeGetPCCell = 0x2000136;
const int opcodeGetWaterLevel = 0x2000141;
const int opcodeSetWaterLevel = 0x2000142;
const int opcodeModWaterLevel = 0x2000143;
void registerExtensions (Compiler::Extensions& extensions)
{
@ -146,8 +205,11 @@ namespace MWScript
extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
extensions.registerInstruction ("coe", "ll", opcodeCOE);
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel);
extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel);
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
@ -157,6 +219,9 @@ namespace MWScript
interpreter.installSegment5 (opcodeCOE, new OpCOE);
interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior);
interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell);
interpreter.installSegment5 (opcodeGetWaterLevel, new OpGetWaterLevel);
interpreter.installSegment5 (opcodeSetWaterLevel, new OpSetWaterLevel);
interpreter.installSegment5 (opcodeModWaterLevel, new OpModWaterLevel);
}
}
}

@ -123,5 +123,8 @@ op 0x200013d: FadeOut
op 0x200013e: FadeTo
op 0x200013f: GetCurrentWeather
op 0x2000140: ChangeWeather
op 0x2000141: OpPCJoinFaction
opcodes 0x2000142-0x3ffffff unused
op 0x2000141: GetWaterLevel
op 0x2000142: SetWaterLevel
op 0x2000143: ModWaterLevel
op 0x2000144: ToggleWater, twa
opcodes 0x2000145-0x3ffffff unused

@ -175,6 +175,19 @@ namespace MWScript
}
};
class OpToggleWater : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().toggleWater();
}
};
const int opcodeXBox = 0x200000c;
const int opcodeOnActivate = 0x200000d;
const int opcodeActivate = 0x2000075;
@ -187,6 +200,7 @@ namespace MWScript
const int opcodeFadeIn = 0x200013c;
const int opcodeFadeOut = 0x200013d;
const int opcodeFadeTo = 0x200013e;
const int opcodeToggleWater = 0x2000144;
void registerExtensions (Compiler::Extensions& extensions)
{
@ -204,6 +218,8 @@ namespace MWScript
extensions.registerInstruction ("fadein", "f", opcodeFadeIn);
extensions.registerInstruction ("fadeout", "f", opcodeFadeOut);
extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo);
extensions.registerInstruction ("togglewater", "", opcodeToggleWater);
extensions.registerInstruction ("twa", "", opcodeToggleWater);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
@ -220,6 +236,7 @@ namespace MWScript
interpreter.installSegment5 (opcodeFadeIn, new OpFadeIn);
interpreter.installSegment5 (opcodeFadeOut, new OpFadeOut);
interpreter.installSegment5 (opcodeFadeTo, new OpFadeTo);
interpreter.installSegment5 (opcodeToggleWater, new OpToggleWater);
}
}
}

@ -130,7 +130,7 @@ namespace MWScript
std::string sound = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
context.getSoundManager().playSound3D (ptr, sound, 1.0, 1.0, mLoop);
context.getSoundManager().playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWSound::Play_Loop : 0);
}
};
@ -159,7 +159,7 @@ namespace MWScript
Interpreter::Type_Float pitch = runtime[0].mFloat;
runtime.pop();
context.getSoundManager().playSound3D (ptr, sound, volume, pitch, mLoop);
context.getSoundManager().playSound3D (ptr, sound, volume, pitch, mLoop ? MWSound::Play_Loop : 0);
}
};

@ -25,14 +25,20 @@ static void throwALCerror(ALCdevice *device)
{
ALCenum err = alcGetError(device);
if(err != ALC_NO_ERROR)
fail(alcGetString(device, err));
{
const ALCchar *errstring = alcGetString(device, err);
fail(errstring ? errstring : "");
}
}
static void throwALerror()
{
ALenum err = alGetError();
if(err != AL_NO_ERROR)
fail(alGetString(err));
{
const ALchar *errstring = alGetString(err);
fail(errstring ? errstring : "");
}
}
@ -89,7 +95,7 @@ public:
virtual void stop();
virtual bool isPlaying();
virtual void update(const float *pos);
virtual void update();
void play();
bool process();
@ -186,7 +192,6 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode
}
catch(std::exception &e)
{
mOutput.mFreeSources.push_back(mSource);
alDeleteBuffers(sNumBuffers, mBuffers);
alGetError();
throw;
@ -254,9 +259,19 @@ bool OpenAL_SoundStream::isPlaying()
return !mIsFinished;
}
void OpenAL_SoundStream::update(const float *pos)
void OpenAL_SoundStream::update()
{
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
ALfloat gain = mVolume*mBaseVolume;
ALfloat pitch = mPitch;
if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater)
{
gain *= 0.9f;
pitch *= 0.7f;
}
alSourcef(mSource, AL_GAIN, gain);
alSourcef(mSource, AL_PITCH, pitch);
alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
throwALerror();
@ -313,15 +328,17 @@ bool OpenAL_SoundStream::process()
}
//
// A regular OpenAL sound
// A regular 2D OpenAL sound
//
class OpenAL_Sound : public Sound
{
protected:
OpenAL_Output &mOutput;
ALuint mSource;
ALuint mBuffer;
private:
OpenAL_Sound(const OpenAL_Sound &rhs);
OpenAL_Sound& operator=(const OpenAL_Sound &rhs);
@ -331,7 +348,23 @@ public:
virtual void stop();
virtual bool isPlaying();
virtual void update(const float *pos);
virtual void update();
};
//
// A regular 3D OpenAL sound
//
class OpenAL_Sound3D : public OpenAL_Sound
{
OpenAL_Sound3D(const OpenAL_Sound &rhs);
OpenAL_Sound3D& operator=(const OpenAL_Sound &rhs);
public:
OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf)
: OpenAL_Sound(output, src, buf)
{ }
virtual void update();
};
OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf)
@ -363,9 +396,39 @@ bool OpenAL_Sound::isPlaying()
return state==AL_PLAYING;
}
void OpenAL_Sound::update(const float *pos)
void OpenAL_Sound::update()
{
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
ALfloat gain = mVolume*mBaseVolume;
ALfloat pitch = mPitch;
if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater)
{
gain *= 0.9f;
pitch *= 0.7f;
}
alSourcef(mSource, AL_GAIN, gain);
alSourcef(mSource, AL_PITCH, pitch);
alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
throwALerror();
}
void OpenAL_Sound3D::update()
{
ALfloat gain = mVolume*mBaseVolume;
ALfloat pitch = mPitch;
if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance)
gain = 0.0f;
else if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater)
{
gain *= 0.9f;
pitch *= 0.7f;
}
alSourcef(mSource, AL_GAIN, gain);
alSourcef(mSource, AL_PITCH, pitch);
alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
throwALerror();
@ -394,8 +457,7 @@ std::vector<std::string> OpenAL_Output::enumerate()
void OpenAL_Output::init(const std::string &devname)
{
if(mDevice || mContext)
fail("Device already open");
deinit();
mDevice = alcOpenDevice(devname.c_str());
if(!mDevice)
@ -412,29 +474,39 @@ void OpenAL_Output::init(const std::string &devname)
mContext = alcCreateContext(mDevice, NULL);
if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE)
{
if(mContext)
alcDestroyContext(mContext);
mContext = 0;
fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice)));
}
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
throwALerror();
ALCint maxmono, maxstereo;
ALCint maxmono=0, maxstereo=0;
alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono);
alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo);
throwALCerror(mDevice);
mFreeSources.resize(std::min(maxmono+maxstereo, 256));
for(size_t i = 0;i < mFreeSources.size();i++)
try
{
ALuint src;
alGenSources(1, &src);
if(alGetError() != AL_NO_ERROR)
ALCuint maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256);
if (maxtotal == 0) // workaround for broken implementations
maxtotal = 256;
for(size_t i = 0;i < maxtotal;i++)
{
mFreeSources.resize(i);
break;
ALuint src = 0;
alGenSources(1, &src);
throwALerror();
mFreeSources.push_back(src);
}
mFreeSources[i] = src;
}
if(mFreeSources.size() == 0)
catch(std::exception &e)
{
std::cout <<"Error: "<<e.what()<<", trying to continue"<< std::endl;
}
if(mFreeSources.empty())
fail("Could not allocate any sources");
}
@ -442,10 +514,10 @@ void OpenAL_Output::deinit()
{
mStreamThread->removeAll();
if(!mFreeSources.empty())
while(!mFreeSources.empty())
{
alDeleteSources(mFreeSources.size(), mFreeSources.data());
mFreeSources.clear();
alDeleteSources(1, &mFreeSources.front());
mFreeSources.pop_front();
}
mBufferRefs.clear();
@ -561,17 +633,15 @@ void OpenAL_Output::bufferFinished(ALuint buf)
}
Sound* OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, bool loop)
SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags)
{
throwALerror();
std::auto_ptr<OpenAL_Sound> sound;
boost::shared_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.back();
mFreeSources.pop_back();
src = mFreeSources.front();
mFreeSources.pop_front();
try
{
@ -595,37 +665,40 @@ Sound* OpenAL_Output::playSound(const std::string &fname, float volume, float pi
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater)
{
volume *= 0.9f;
pitch *= 0.7f;
}
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE));
alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE);
throwALerror();
alSourcei(src, AL_BUFFER, buf);
alSourcePlay(src);
throwALerror();
return sound.release();
return sound;
}
Sound* OpenAL_Output::playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max, bool loop)
SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch,
float min, float max, int flags)
{
throwALerror();
std::auto_ptr<OpenAL_Sound> sound;
boost::shared_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.back();
mFreeSources.pop_back();
src = mFreeSources.front();
mFreeSources.pop_front();
try
{
buf = getBuffer(fname);
sound.reset(new OpenAL_Sound(*this, src, buf));
sound.reset(new OpenAL_Sound3D(*this, src, buf));
}
catch(std::exception &e)
{
@ -636,7 +709,7 @@ Sound* OpenAL_Output::playSound3D(const std::string &fname, const float *pos, fl
throw;
}
alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]);
alSource3f(src, AL_POSITION, pos.x, pos.z, -pos.y);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
@ -644,35 +717,41 @@ Sound* OpenAL_Output::playSound3D(const std::string &fname, const float *pos, fl
alSourcef(src, AL_MAX_DISTANCE, max);
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
alSourcef(src, AL_GAIN, volume);
if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater)
{
volume *= 0.9f;
pitch *= 0.7f;
}
alSourcef(src, AL_GAIN, (pos.squaredDistance(mPos) > max*max) ?
0.0f : volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE));
alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE);
throwALerror();
alSourcei(src, AL_BUFFER, buf);
alSourcePlay(src);
throwALerror();
return sound.release();
return sound;
}
Sound* OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch)
SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags)
{
throwALerror();
std::auto_ptr<OpenAL_SoundStream> sound;
boost::shared_ptr<OpenAL_SoundStream> sound;
ALuint src;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.back();
mFreeSources.pop_back();
src = mFreeSources.front();
mFreeSources.pop_front();
try
{
if((flags&Play_Loop))
std::cout <<"Warning: cannot loop stream "<<fname<< std::endl;
DecoderPtr decoder = mManager.getDecoder();
decoder->open(fname);
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
@ -691,78 +770,44 @@ Sound* OpenAL_Output::streamSound(const std::string &fname, float volume, float
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(src, AL_LOOPING, AL_FALSE);
throwALerror();
sound->play();
return sound.release();
}
Sound* OpenAL_Output::streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max)
{
throwALerror();
std::auto_ptr<OpenAL_SoundStream> sound;
ALuint src;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.back();
mFreeSources.pop_back();
try
{
DecoderPtr decoder = mManager.getDecoder();
decoder->open(fname);
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
}
catch(std::exception &e)
if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater)
{
mFreeSources.push_back(src);
throw;
volume *= 0.9f;
pitch *= 0.7f;
}
alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcef(src, AL_REFERENCE_DISTANCE, min);
alSourcef(src, AL_MAX_DISTANCE, max);
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(src, AL_LOOPING, AL_FALSE);
throwALerror();
sound->play();
return sound.release();
return sound;
}
void OpenAL_Output::updateListener(const float *pos, const float *atdir, const float *updir)
void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env)
{
float orient[6] = {
atdir[0], atdir[2], -atdir[1],
updir[0], updir[2], -updir[1]
};
mPos = pos;
mLastEnvironment = env;
alListener3f(AL_POSITION, pos[0], pos[2], -pos[1]);
alListenerfv(AL_ORIENTATION, orient);
throwALerror();
if(mContext)
{
ALfloat orient[6] = {
atdir.x, atdir.z, -atdir.y,
updir.x, updir.z, -updir.y
};
alListener3f(AL_POSITION, mPos.x, mPos.z, -mPos.y);
alListenerfv(AL_ORIENTATION, orient);
throwALerror();
}
}
OpenAL_Output::OpenAL_Output(SoundManager &mgr)
: Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0),
mStreamThread(new StreamThread)
mLastEnvironment(Env_Normal), mStreamThread(new StreamThread)
{
}

@ -21,8 +21,9 @@ namespace MWSound
ALCdevice *mDevice;
ALCcontext *mContext;
typedef std::vector<ALuint> IDVec;
IDVec mFreeSources;
typedef std::deque<ALuint> IDDq;
IDDq mFreeSources;
IDDq mUnusedBuffers;
typedef std::map<std::string,ALuint> NameMap;
NameMap mBufferCache;
@ -30,27 +31,23 @@ namespace MWSound
typedef std::map<ALuint,ALuint> IDRefMap;
IDRefMap mBufferRefs;
typedef std::deque<ALuint> IDDq;
IDDq mUnusedBuffers;
uint64_t mBufferCacheMemSize;
ALuint getBuffer(const std::string &fname);
void bufferFinished(ALuint buffer);
Environment mLastEnvironment;
virtual std::vector<std::string> enumerate();
virtual void init(const std::string &devname="");
virtual void deinit();
virtual Sound *playSound(const std::string &fname, float volume, float pitch, bool loop);
virtual Sound *playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max, bool loop);
virtual Sound *streamSound(const std::string &fname, float volume, float pitch);
virtual Sound *streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max);
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags);
virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
float volume, float pitch, float min, float max, int flags);
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags);
virtual void updateListener(const float *pos, const float *atdir, const float *updir);
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env);
OpenAL_Output& operator=(const OpenAL_Output &rhs);
OpenAL_Output(const OpenAL_Output &rhs);
@ -62,6 +59,7 @@ namespace MWSound
std::auto_ptr<StreamThread> mStreamThread;
friend class OpenAL_Sound;
friend class OpenAL_Sound3D;
friend class OpenAL_SoundStream;
friend class SoundManager;
};

@ -1,19 +1,40 @@
#ifndef GAME_SOUND_SOUND_H
#define GAME_SOUND_SOUND_H
#include <OgreVector3.h>
namespace MWSound
{
class Sound
{
virtual void stop() = 0;
virtual bool isPlaying() = 0;
virtual void update(const float *pos) = 0;
virtual void update() = 0;
Sound& operator=(const Sound &rhs);
Sound(const Sound &rhs);
protected:
Ogre::Vector3 mPos;
float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */
float mBaseVolume;
float mPitch;
float mMinDistance;
float mMaxDistance;
int mFlags;
public:
Sound() { }
virtual void stop() = 0;
virtual bool isPlaying() = 0;
void setPosition(const Ogre::Vector3 &pos) { mPos = pos; }
void setVolume(float volume) { mVolume = volume; }
Sound() : mPos(0.0f, 0.0f, 0.0f)
, mVolume(1.0f)
, mBaseVolume(1.0f)
, mPitch(1.0f)
, mMinDistance(20.0f) /* 1 * min_range_scale */
, mMaxDistance(12750.0f) /* 255 * max_range_scale */
, mFlags(Play_Normal)
{ }
virtual ~Sound() { }
friend class OpenAL_Output;

@ -4,6 +4,10 @@
#include <string>
#include <memory>
#include <OgreVector3.h>
#include "soundmanager.hpp"
#include "../mwworld/ptr.hpp"
namespace MWSound
@ -20,19 +24,23 @@ namespace MWSound
virtual void init(const std::string &devname="") = 0;
virtual void deinit() = 0;
virtual Sound *playSound(const std::string &fname, float volume, float pitch, bool loop) = 0;
virtual Sound *playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max, bool loop) = 0;
virtual Sound *streamSound(const std::string &fname, float volume, float pitch) = 0;
virtual Sound *streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max) = 0;
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0;
virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
float volume, float pitch, float min, float max, int flags) = 0;
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0;
virtual void updateListener(const float *pos, const float *atdir, const float *updir) = 0;
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0;
Sound_Output& operator=(const Sound_Output &rhs);
Sound_Output(const Sound_Output &rhs);
Sound_Output(SoundManager &mgr) : mManager(mgr) { }
protected:
Ogre::Vector3 mPos;
Sound_Output(SoundManager &mgr)
: mManager(mgr)
, mPos(0.0f, 0.0f, 0.0f)
{ }
public:
virtual ~Sound_Output() { }

@ -41,6 +41,8 @@ namespace MWSound
SoundManager::SoundManager(bool useSound, MWWorld::Environment& environment)
: mResourceMgr(Ogre::ResourceGroupManager::getSingleton())
, mEnvironment(environment)
, mOutput(new DEFAULT_OUTPUT(*this))
{
if(!useSound)
return;
@ -50,8 +52,6 @@ namespace MWSound
try
{
mOutput.reset(new DEFAULT_OUTPUT(*this));
std::vector<std::string> names = mOutput->enumerate();
std::cout <<"Enumerated output devices:"<< std::endl;
for(size_t i = 0;i < names.size();i++)
@ -62,14 +62,11 @@ namespace MWSound
catch(std::exception &e)
{
std::cout <<"Sound init failed: "<<e.what()<< std::endl;
mOutput.reset();
return;
}
}
SoundManager::~SoundManager()
{
mLooseSounds.clear();
mActiveSounds.clear();
mMusic.reset();
mOutput.reset();
@ -91,10 +88,7 @@ namespace MWSound
if(snd == NULL)
throw std::runtime_error(std::string("Failed to lookup sound ")+soundId);
if(snd->data.volume == 0)
volume = 0.0f;
else
volume *= pow(10.0, (snd->data.volume/255.0f*3348.0 - 3348.0) / 2000.0);
volume *= pow(10.0, (snd->data.volume/255.0*3348.0 - 3348.0) / 2000.0);
if(snd->data.minRange == 0 && snd->data.maxRange == 0)
{
@ -109,21 +103,20 @@ namespace MWSound
max = std::max(min, max);
}
return std::string("Sound/")+snd->sound;
return "Sound/"+snd->sound;
}
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
{
SoundMap::const_iterator snditer = mActiveSounds.find(ptr);
if(snditer == mActiveSounds.end())
return false;
IDMap::const_iterator iditer = snditer->second.find(id);
if(iditer == snditer->second.end())
return false;
return true;
SoundMap::const_iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
if(snditer->second.first == ptr && snditer->second.second == id)
return snditer->first->isPlaying();
snditer++;
}
return false;
}
@ -139,9 +132,10 @@ namespace MWSound
std::cout <<"Playing "<<filename<< std::endl;
try
{
if(mMusic)
mMusic->stop();
mMusic.reset(mOutput->streamSound(filename, 0.4f, 1.0f));
stopMusic();
mMusic = mOutput->streamSound(filename, 0.4f, 1.0f, Play_NoEnv);
mMusic->mBaseVolume = 0.4f;
mMusic->mFlags = Play_NoEnv;
}
catch(std::exception &e)
{
@ -182,11 +176,17 @@ namespace MWSound
try
{
// The range values are not tested
float basevol = 1.0f; /* TODO: volume settings */
std::string filePath = "Sound/"+filename;
const ESM::Position &pos = ptr.getCellRef().pos;
std::string filePath = std::string("Sound/")+filename;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f,
20.0f, 12750.0f, Play_Normal);
sound->mPos = objpos;
sound->mBaseVolume = basevol;
SoundPtr sound(mOutput->playSound3D(filePath, pos.pos, 1.0f, 1.0f, 100.0f, 20000.0f, false));
mActiveSounds[ptr]["_say_sound"] = sound;
mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
}
catch(std::exception &e)
{
@ -200,86 +200,105 @@ namespace MWSound
}
void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop)
SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode)
{
float min, max;
SoundPtr sound;
try
{
std::string file = lookup(soundId, volume, min, max);
Sound *sound = mOutput->playSound(file, volume, pitch, loop);
mLooseSounds[soundId] = SoundPtr(sound);
float basevol = 1.0f; /* TODO: volume settings */
float min, max;
std::string file = lookup(soundId, basevol, min, max);
sound = mOutput->playSound(file, volume*basevol, pitch, mode);
sound->mVolume = volume;
sound->mBaseVolume = basevol;
sound->mPitch = pitch;
sound->mMinDistance = min;
sound->mMaxDistance = max;
sound->mFlags = mode;
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
}
catch(std::exception &e)
{
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
return sound;
}
void SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId,
float volume, float pitch, bool loop, bool untracked)
SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId,
float volume, float pitch, int mode)
{
float min, max;
SoundPtr sound;
try
{
// Look up the sound in the ESM data
float basevol = 1.0f; /* TODO: volume settings */
float min, max;
std::string file = lookup(soundId, basevol, min, max);
const ESM::Position &pos = ptr.getCellRef().pos;
std::string file = lookup(soundId, volume, min, max);
SoundPtr sound(mOutput->playSound3D(file, pos.pos, volume, pitch, min, max, loop));
if(untracked) mLooseSounds[soundId] = sound;
else mActiveSounds[ptr][soundId] = sound;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode);
sound->mPos = objpos;
sound->mVolume = volume;
sound->mBaseVolume = basevol;
sound->mPitch = pitch;
sound->mMinDistance = min;
sound->mMaxDistance = max;
sound->mFlags = mode;
if((mode&Play_NoTrack))
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
else
mActiveSounds[sound] = std::make_pair(ptr, soundId);
}
catch(std::exception &e)
{
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
return sound;
}
void SoundManager::stopSound3D(MWWorld::Ptr ptr, const std::string& soundId)
{
// Stop a sound and remove it from the list. If soundId="" then
// stop all its sounds.
SoundMap::iterator snditer = mActiveSounds.find(ptr);
if(snditer == mActiveSounds.end())
return;
if(!soundId.empty())
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
IDMap::iterator iditer = snditer->second.find(soundId);
if(iditer != snditer->second.end())
if(snditer->second.first == ptr && snditer->second.second == soundId)
{
iditer->second->stop();
snditer->second.erase(iditer);
if(snditer->second.empty())
mActiveSounds.erase(snditer);
snditer->first->stop();
mActiveSounds.erase(snditer++);
}
else
snditer++;
}
else
}
void SoundManager::stopSound3D(MWWorld::Ptr ptr)
{
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
IDMap::iterator iditer = snditer->second.begin();
while(iditer != snditer->second.end())
if(snditer->second.first == ptr)
{
iditer->second->stop();
iditer++;
snditer->first->stop();
mActiveSounds.erase(snditer++);
}
mActiveSounds.erase(snditer);
else
snditer++;
}
}
void SoundManager::stopSound(MWWorld::Ptr::CellStore *cell)
void SoundManager::stopSound(const MWWorld::Ptr::CellStore *cell)
{
// Remove all references to objects belonging to a given cell
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
if(snditer->first.getCell() == cell)
if(snditer->second.first != MWWorld::Ptr() &&
snditer->second.first.getCell() == cell)
{
IDMap::iterator iditer = snditer->second.begin();
while(iditer != snditer->second.end())
{
iditer->second->stop();
iditer++;
}
snditer->first->stop();
mActiveSounds.erase(snditer++);
}
else
@ -289,11 +308,17 @@ namespace MWSound
void SoundManager::stopSound(const std::string& soundId)
{
IDMap::iterator iditer = mLooseSounds.find(soundId);
if(iditer != mLooseSounds.end())
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
iditer->second->stop();
mLooseSounds.erase(iditer);
if(snditer->second.first == MWWorld::Ptr() &&
snditer->second.second == soundId)
{
snditer->first->stop();
mActiveSounds.erase(snditer++);
}
else
snditer++;
}
}
@ -304,16 +329,14 @@ namespace MWSound
void SoundManager::updateObject(MWWorld::Ptr ptr)
{
SoundMap::iterator snditer = mActiveSounds.find(ptr);
if(snditer == mActiveSounds.end())
return;
const ESM::Position &pos = ptr.getCellRef().pos;
IDMap::iterator iditer = snditer->second.begin();
while(iditer != snditer->second.end())
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
iditer->second->update(pos.pos);
iditer++;
if(snditer->second.first == ptr)
snditer->first->setPosition(objpos);
snditer++;
}
}
@ -384,45 +407,37 @@ namespace MWSound
if(!isMusicPlaying())
startRandomTitle();
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
Ogre::Camera *cam = mEnvironment.mWorld->getPlayer().getRenderer()->getCamera();
Ogre::Vector3 nPos, nDir, nUp;
nPos = cam->getRealPosition();
nDir = cam->getRealDirection();
nUp = cam->getRealUp();
Environment env = Env_Normal;
if(nPos.y < current->cell->water)
env = Env_Underwater;
// The output handler is expecting vectors oriented like the game
// (that is, -Z goes down, +Y goes forward), but that's not what we
// get from Ogre's camera, so we have to convert.
float pos[3] = { nPos[0], -nPos[2], nPos[1] };
float at[3] = { nDir[0], -nDir[2], nDir[1] };
float up[3] = { nUp[0], -nUp[2], nUp[1] };
mOutput->updateListener(pos, at, up);
const Ogre::Vector3 pos(nPos[0], -nPos[2], nPos[1]);
const Ogre::Vector3 at(nDir[0], -nDir[2], nDir[1]);
const Ogre::Vector3 up(nUp[0], -nUp[2], nUp[1]);
mOutput->updateListener(pos, at, up, env);
// Check if any sounds are finished playing, and trash them
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
IDMap::iterator iditer = snditer->second.begin();
while(iditer != snditer->second.end())
{
if(!iditer->second->isPlaying())
snditer->second.erase(iditer++);
else
iditer++;
}
if(snditer->second.empty())
if(!snditer->first->isPlaying())
mActiveSounds.erase(snditer++);
else
{
snditer->first->update();
snditer++;
}
IDMap::iterator iditer = mLooseSounds.begin();
while(iditer != mLooseSounds.end())
{
if(!iditer->second->isPlaying())
mLooseSounds.erase(iditer++);
else
iditer++;
}
}
}

@ -2,11 +2,11 @@
#define GAME_SOUND_SOUNDMANAGER_H
#include <string>
#include <utility>
#include <map>
#include <OgreResourceGroupManager.h>
#include <components/files/filelibrary.hpp>
#include "../mwworld/ptr.hpp"
@ -28,6 +28,25 @@ namespace MWSound
class Sound;
typedef boost::shared_ptr<Sound_Decoder> DecoderPtr;
typedef boost::shared_ptr<Sound> SoundPtr;
enum PlayMode {
Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */
Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */
Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */
Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position
* but do not keep it updated (the sound will not move with
* the object and will not stop when the object is deleted. */
};
static inline int operator|(const PlayMode &a, const PlayMode &b)
{ return (int)a | (int)b; }
static inline int operator&(const PlayMode &a, const PlayMode &b)
{ return (int)a & (int)b; }
enum Environment {
Env_Normal,
Env_Underwater,
};
class SoundManager
{
@ -40,11 +59,9 @@ namespace MWSound
boost::shared_ptr<Sound> mMusic;
std::string mCurrentPlaylist;
typedef boost::shared_ptr<Sound> SoundPtr;
typedef std::map<std::string,SoundPtr> IDMap;
typedef std::map<MWWorld::Ptr,IDMap> SoundMap;
typedef std::pair<MWWorld::Ptr,std::string> PtrIDPair;
typedef std::map<SoundPtr,PtrIDPair> SoundMap;
SoundMap mActiveSounds;
IDMap mLooseSounds;
std::string lookup(const std::string &soundId,
float &volume, float &min, float &max);
@ -88,19 +105,20 @@ namespace MWSound
bool sayDone(MWWorld::Ptr reference) const;
///< Is actor not speaking?
void playSound(const std::string& soundId, float volume, float pitch, bool loop=false);
SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal);
///< Play a sound, independently of 3D-position
void playSound3D(MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop,
bool untracked=false);
SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, int mode=Play_Normal);
///< Play a sound from an object
void stopSound3D(MWWorld::Ptr reference, const std::string& soundId="");
///< Stop the given object from playing the given sound, If no soundId is given,
/// all sounds for this reference will stop.
void stopSound3D(MWWorld::Ptr reference, const std::string& soundId);
///< Stop the given object from playing the given sound,
void stopSound3D(MWWorld::Ptr reference);
///< Stop the given object from playing all sounds.
void stopSound(MWWorld::Ptr::CellStore *cell);
void stopSound(const MWWorld::Ptr::CellStore *cell);
///< Stop all sounds for the given cell.
void stopSound(const std::string& soundId);

@ -50,6 +50,28 @@ namespace MWWorld
return mEngine->rayTest(from,to);
}
std::vector < std::pair <float, std::string> > PhysicsSystem::getFacedObjects ()
{
//get a ray pointing to the center of the viewport
Ray centerRay = mRender.getCamera()->getCameraToViewportRay(
mRender.getViewport()->getWidth()/2,
mRender.getViewport()->getHeight()/2);
btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y);
btVector3 to(centerRay.getPoint(500).x,-centerRay.getPoint(500).z,centerRay.getPoint(500).y);
return mEngine->rayTest2(from,to);
}
btVector3 PhysicsSystem::getRayPoint(float extent)
{
//get a ray pointing to the center of the viewport
Ray centerRay = mRender.getCamera()->getCameraToViewportRay(
mRender.getViewport()->getWidth()/2,
mRender.getViewport()->getHeight()/2);
btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y);
return result;
}
bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to)
{

@ -35,7 +35,11 @@ namespace MWWorld
bool toggleCollisionMode();
std::pair<std::string, float> getFacedHandle (MWWorld::World& world);
btVector3 getRayPoint(float extent);
std::vector < std::pair <float, std::string> > getFacedObjects ();
// cast ray, return true if it hit something
bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to);

@ -54,9 +54,11 @@ void insertCellRefList(MWRender::RenderingManager& rendering, MWWorld::Environme
namespace MWWorld
{
void Scene::update (float duration){
mRendering.update (duration);
}
void Scene::unloadCell (CellStoreCollection::iterator iter)
{
std::cout << "Unloading cell\n";
@ -79,6 +81,7 @@ namespace MWWorld
mPhysics->removeObject (node->getName());
}
}
mRendering.removeCell(*iter);
//mPhysics->removeObject("Unnamed_43");
@ -87,6 +90,7 @@ namespace MWWorld
mEnvironment.mSoundManager->stopSound (*iter);
mActiveCells.erase(*iter);
}
@ -101,7 +105,7 @@ namespace MWWorld
mActiveCells.insert(cell);
if(result.second){
insertCell(*cell, mEnvironment);
mRendering.cellAdded (cell);
mRendering.cellAdded(cell);
mRendering.configureAmbient(*cell);
mRendering.requestMap(cell);
mRendering.configureAmbient(*cell);
@ -192,6 +196,7 @@ namespace MWWorld
mCurrentCell = *iter;
// adjust player
playerCellChange (mWorld->getExterior(X, Y), position, adjustPlayerPos);
@ -199,6 +204,7 @@ namespace MWWorld
mWorld->adjustSky();
mCellChanged = true;
mRendering.waterAdded(mCurrentCell);
}
//We need the ogre renderer and a scene node.
@ -238,6 +244,7 @@ namespace MWWorld
Ptr::CellStore *cell = mWorld->getInterior(cellName);
loadCell (cell);
// adjust player
mCurrentCell = cell;
@ -250,6 +257,8 @@ namespace MWWorld
mWorld->adjustSky();
mCellChanged = true;
mRendering.waterAdded(cell);
}
void Scene::changeToExteriorCell (const ESM::Position& position)

@ -146,10 +146,10 @@ namespace MWWorld
mRendering->skySetDate (mGlobalVariables->getInt ("day"),
mGlobalVariables->getInt ("month"));
mRendering->getSkyManager()->enable();
mRendering->skyEnable();
}
else
mRendering->getSkyManager()->disable();
mRendering->skyDisable();
}
World::World (OEngine::Render::OgreRenderer& renderer,
@ -157,7 +157,8 @@ namespace MWWorld
const std::string& master, const boost::filesystem::path& resDir,
bool newGame, Environment& environment, const std::string& encoding)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this)
mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this),
mNumFacing(0)
{
mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine();
@ -498,13 +499,21 @@ namespace MWWorld
std::string World::getFacedHandle()
{
std::pair<std::string, float> result = mPhysics->getFacedHandle (*this);
if (!mRendering->occlusionQuerySupported())
{
std::pair<std::string, float> result = mPhysics->getFacedHandle (*this);
if (result.first.empty() ||
result.second>getStore().gameSettings.find ("iMaxActivateDist")->i)
return "";
if (result.first.empty() ||
result.second>getStore().gameSettings.find ("iMaxActivateDist")->i)
return "";
return result.first;
return result.first;
}
else
{
// updated every few frames in update()
return mFacedHandle;
}
}
void World::deleteObject (Ptr ptr)
@ -531,9 +540,10 @@ namespace MWWorld
ptr.getRefData().getPosition().pos[0] = x;
ptr.getRefData().getPosition().pos[1] = y;
ptr.getRefData().getPosition().pos[2] = z;
if (ptr==mPlayer->getPlayer())
{
//std::cout << "X:" << ptr.getRefData().getPosition().pos[0] << " Z: " << ptr.getRefData().getPosition().pos[1] << "\n";
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
if (currentCell)
{
@ -705,13 +715,82 @@ namespace MWWorld
mWeatherManager->update (duration);
// cast a ray from player to sun to detect if the sun is visible
// this is temporary until we find a better place to put this code
// currently its here because we need to access the physics system
float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
Vector3 sun = mRendering->getSkyManager()->getRealSunPos();
sun = Vector3(sun.x, -sun.z, sun.y);
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
if (!mRendering->occlusionQuerySupported())
{
// cast a ray from player to sun to detect if the sun is visible
// this is temporary until we find a better place to put this code
// currently its here because we need to access the physics system
float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
Vector3 sun = mRendering->getSkyManager()->getRealSunPos();
sun = Vector3(sun.x, -sun.z, sun.y);
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
}
// update faced handle (object the player is looking at)
// this uses a mixture of raycasts and occlusion queries.
else // if (mRendering->occlusionQuerySupported())
{
MWRender::OcclusionQuery* query = mRendering->getOcclusionQuery();
if (!query->occlusionTestPending())
{
// get result of last query
if (mNumFacing == 0) mFacedHandle = "";
else if (mNumFacing == 1)
{
bool result = query->getTestResult();
mFacedHandle = result ? mFaced1Name : "";
}
else if (mNumFacing == 2)
{
bool result = query->getTestResult();
mFacedHandle = result ? mFaced2Name : mFaced1Name;
}
// send new query
// figure out which object we want to test against
std::vector < std::pair < float, std::string > > results = mPhysics->getFacedObjects();
// ignore the player
for (std::vector < std::pair < float, std::string > >::iterator it = results.begin();
it != results.end(); ++it)
{
if ( (*it).second == mPlayer->getPlayer().getRefData().getHandle() )
{
results.erase(it);
break;
}
}
if (results.size() == 0)
{
mNumFacing = 0;
}
else if (results.size() == 1)
{
mFaced1 = getPtrViaHandle(results.front().second);
mFaced1Name = results.front().second;
mNumFacing = 1;
btVector3 p = mPhysics->getRayPoint(results.front().first);
Ogre::Vector3 pos(p.x(), p.z(), -p.y());
Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode();
query->occlusionTest(pos, node);
}
else
{
mFaced1Name = results.front().second;
mFaced2Name = results[1].second;
mFaced1 = getPtrViaHandle(results.front().second);
mFaced2 = getPtrViaHandle(results[1].second);
mNumFacing = 2;
btVector3 p = mPhysics->getRayPoint(results[1].first);
Ogre::Vector3 pos(p.x(), p.z(), -p.y());
Ogre::SceneNode* node = mFaced2.getRefData().getBaseNode();
query->occlusionTest(pos, node);
}
}
}
}
bool World::isCellExterior() const
@ -754,4 +833,15 @@ namespace MWWorld
{
return mRendering->getFader();
}
void World::setWaterHeight(const float height)
{
mRendering->setWaterHeight(height);
}
void World::toggleWater()
{
mRendering->toggleWater();
}
}

@ -93,6 +93,12 @@ namespace MWWorld
Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore);
std::string mFacedHandle;
Ptr mFaced1;
Ptr mFaced2;
std::string mFaced1Name;
std::string mFaced2Name;
int mNumFacing;
int getDaysPerMonth (int month) const;
@ -112,6 +118,9 @@ namespace MWWorld
Ptr::CellStore *getExterior (int x, int y);
Ptr::CellStore *getInterior (const std::string& name);
void setWaterHeight(const float height);
void toggleWater();
void adjustSky();

@ -51,13 +51,13 @@ find_path(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h
# Find the libraries
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics)
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_d)
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_Debug BulletDynamics_d)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_d)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY LinearMath BulletMath)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG LinearMath_d BulletMath_d)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_Debug BulletCollision_d)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY BulletMath LinearMath)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG BulletMath_Debug BulletMath_d LinearMath_debug LinearMath_d)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_d)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletSoftBody_d)
# handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if

@ -41,7 +41,21 @@ struct ciLessBoost : std::binary_function<std::string, std::string, bool>
{
bool operator() (const std::string & s1, const std::string & s2) const {
//case insensitive version of is_less
return lexicographical_compare(s1, s2, boost::algorithm::is_iless());
return boost::ilexicographical_compare(s1, s2);
}
};
struct pathComparer
{
private:
std::string find;
public:
pathComparer(const std::string& toFind) : find(toFind) { }
bool operator() (const std::string& other)
{
return boost::iequals(find, other);
}
};
@ -55,16 +69,62 @@ class DirArchive: public Ogre::FileSystemArchive
std::map<std::string, std::vector<std::string>, ciLessBoost> m;
unsigned int cutoff;
bool comparePortion(std::string file1, std::string file2, int start, int size) const
bool findFile(const String& filename, std::string& copy) const
{
for(int i = start; i < start+size; i++)
{
char one = file1.at(i);
char two = file2.at(i);
if(tolower(one) != tolower(two) )
String passed = filename;
if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<'
|| filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':'
|| filename.at(filename.length() - 1) == '|')
{
passed = filename.substr(0, filename.length() - 2);
}
if(filename.at(filename.length() - 2) == '>')
passed = filename.substr(0, filename.length() - 6);
copy = passed;
}
std::replace(copy.begin(), copy.end(), '\\', '/');
if(copy.at(0) == '/')
copy.erase(0, 1);
if(fsstrict == true)
return true;
std::string folder;
int delimiter = 0;
size_t lastSlash = copy.rfind('/');
if (lastSlash != std::string::npos)
{
folder = copy.substr(0, lastSlash);
delimiter = lastSlash+1;
}
std::vector<std::string> current;
{
std::map<std::string,std::vector<std::string>,ciLessBoost>::const_iterator found = m.find(folder);
if (found == m.end())
{
return false;
}
else
current = found->second;
}
std::vector<std::string>::iterator find = std::lower_bound(current.begin(), current.end(), copy, ciLessBoost());
if (find != current.end() && !ciLessBoost()(copy, current.front()))
{
if (!boost::iequals(copy, *find))
if ((find = std::find_if(current.begin(), current.end(), pathComparer(copy))) == current.end()) //\todo Check if this line is actually needed
return false;
copy = *find;
return true;
}
return true;
return false;
}
public:
@ -83,16 +143,14 @@ class DirArchive: public Ogre::FileSystemArchive
//need to cut off first
boost::filesystem::directory_iterator dir_iter(d), dir_end;
std::vector<std::string> filesind;
boost::filesystem::path f;
for(;dir_iter != dir_end; dir_iter++)
{
if(boost::filesystem::is_directory(*dir_iter))
populateMap(*dir_iter);
else
{
f = *dir_iter;
std::string s = f.string();
std::string s = dir_iter->path().string();
std::replace(s.begin(), s.end(), '\\', '/');
std::string small;
if(cutoff < s.size())
@ -103,14 +161,17 @@ class DirArchive: public Ogre::FileSystemArchive
filesind.push_back(small);
}
}
std::sort(filesind.begin(), filesind.end(), ciLessBoost());
std::string small;
std::string original = d.string();
std::replace(original.begin(), original.end(), '\\', '/');
if(cutoff < original.size())
small = original.substr(cutoff, original.size() - cutoff);
else
small = original.substr(cutoff - 1, original.size() - cutoff);
m[small] = filesind;
m[small] = filesind;
}
bool isCaseSensitive() const { return fsstrict; }
@ -120,97 +181,21 @@ class DirArchive: public Ogre::FileSystemArchive
void unload() {}
bool exists(const String& filename) {
std::string copy = filename;
for (unsigned int i = 0; i < filename.size(); i++)
{
if(copy.at(i) == '\\' ){
copy.replace(i, 1, "/");
}
}
std::string copy;
if(copy.at(0) == '\\' || copy.at(0) == '/')
{
copy.erase(0, 1);
}
if(fsstrict == true)
{
//std::cout << "fsstrict " << copy << "\n";
if (findFile(filename, copy))
return FileSystemArchive::exists(copy);
}
int last = copy.size() - 1;
int i = last;
for (;last >= 0; i--)
{
if(copy.at(i) == '/' || copy.at(i) == '\\')
break;
}
std::string folder = copy.substr(0, i); //folder with no slash
std::vector<std::string>& current = m[folder];
for(std::vector<std::string>::iterator iter = current.begin(); iter != current.end(); iter++)
{
if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){
return FileSystemArchive::exists(*iter);
}
}
return false;
}
DataStreamPtr open(const String& filename, bool readonly = true) const
{
std::map<std::string, std::vector<std::string>, ciLessBoost> mlocal = m;
std::string copy = filename;
for (unsigned int i = 0; i < filename.size(); i++)
{
if(copy.at(i) == '\\' ){
copy.replace(i, 1, "/");
}
}
if(copy.at(0) == '\\' || copy.at(0) == '/')
{
copy.erase(0, 1);
}
std::string copy;
if(fsstrict == true)
{
if (findFile(filename, copy))
return FileSystemArchive::open(copy, readonly);
}
int last = copy.size() - 1;
int i = last;
for (;last >= 0; i--)
{
if(copy.at(i) == '/' || copy.at(i) == '\\')
break;
}
std::string folder = copy.substr(0, i); //folder with no slash
std::vector<std::string> current = mlocal[folder];
for(std::vector<std::string>::iterator iter = current.begin(); iter != current.end(); iter++)
{
if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){
return FileSystemArchive::open(*iter, readonly);
}
}
DataStreamPtr p;
return p;
}
@ -256,8 +241,12 @@ public:
return DataStreamPtr(new Mangle2OgreStream(strm));
}
bool exists(const String& filename) {
return cexists(filename);
}
// Check if the file exists.
bool exists(const String& filename) {
bool cexists(const String& filename) const {
String passed = filename;
if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<'
|| filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':'
@ -268,7 +257,7 @@ public:
if(filename.at(filename.length() - 2) == '>')
passed = filename.substr(0, filename.length() - 6);
return arc.exists(passed.c_str());
return arc.exists(passed.c_str());
}
time_t getModifiedTime(const String&) { return 0; }
@ -308,6 +297,29 @@ return arc.exists(passed.c_str());
located in BSAs. So instead we channel it through exists() and
set up a single-element result list if the file is found.
*/
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
bool dirs = false) const
{
FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList());
// Check if the file exists (only works for single files - wild
// cards and recursive search isn't implemented.)
if(cexists(pattern))
{
FileInfo fi;
fi.archive = this;
fi.filename = pattern;
// It apparently doesn't matter that we return bogus
// information
fi.path = "";
fi.compressedSize = fi.uncompressedSize = 0;
ptr->push_back(fi);
}
return ptr;
}
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
bool dirs = false)
{
@ -315,7 +327,7 @@ return arc.exists(passed.c_str());
// Check if the file exists (only works for single files - wild
// cards and recursive search isn't implemented.)
if(exists(pattern))
if(cexists(pattern))
{
FileInfo fi;
fi.archive = this;

@ -148,9 +148,9 @@ void BSAFile::readHeader()
}
/// Get the index of a given file name, or -1 if not found
int BSAFile::getIndex(const char *str)
int BSAFile::getIndex(const char *str) const
{
Lookup::iterator it;
Lookup::const_iterator it;
it = lookup.find(str);
if(it == lookup.end()) return -1;

@ -93,7 +93,7 @@ class BSAFile
void readHeader();
/// Get the index of a given file name, or -1 if not found
int getIndex(const char *str);
int getIndex(const char *str) const;
public:
@ -119,7 +119,7 @@ class BSAFile
*/
/// Check if a file exists
bool exists(const char *file) { return getIndex(file) != -1; }
bool exists(const char *file) const { return getIndex(file) != -1; }
/** Open a file contained in the archive. Throws an exception if the
file doesn't exist.

@ -21,8 +21,13 @@ void Cell::load(ESMReader &esm)
if (data.flags & Interior)
{
// Interior cells
if (esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
if (esm.isNextSub("INTV"))
{
int waterl;
esm.getHT(waterl);
water = (float) waterl;
}
else if (esm.isNextSub("WHGT"))
esm.getHT(water);
// Quasi-exterior cells have a region (which determines the

@ -114,11 +114,26 @@ struct Cell
ESM_Context context; // File position
DATAstruct data;
AMBIstruct ambi;
int water; // Water level
float water; // Water level
int mapColor;
void load(ESMReader &esm);
bool isExterior() const
{
return !(data.flags & Interior);
}
int getGridX() const
{
return data.gridX;
}
int getGridY() const
{
return data.gridY;
}
// Restore the given reader to the stored position. Will try to open
// the file matching the stored file name. If you want to read from
// somewhere other than the file system, you need to pre-open the

@ -4,6 +4,8 @@ namespace ESM
{
void Land::load(ESMReader &esm)
{
mEsm = &esm;
// Get the grid location
esm.getSubNameIs("INTV");
esm.getSubHeaderIs(8);
@ -19,14 +21,117 @@ void Land::load(ESMReader &esm)
int cnt = 0;
// Skip these here. Load the actual data when the cell is loaded.
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
if (esm.isNextSub("VNML"))
{
esm.skipHSubSize(12675);
cnt++;
}
if (esm.isNextSub("VHGT"))
{
esm.skipHSubSize(4232);
cnt++;
}
if (esm.isNextSub("WNAM"))
{
esm.skipHSubSize(81);
}
if (esm.isNextSub("VCLR"))
{
esm.skipHSubSize(12675);
}
if (esm.isNextSub("VTEX"))
{
esm.skipHSubSize(512);
cnt++;
}
// We need all three of VNML, VHGT and VTEX in order to use the
// landscape.
hasData = (cnt == 3);
dataLoaded = false;
landData = NULL;
}
void Land::loadData()
{
if (dataLoaded)
{
return;
}
landData = new LandData;
if (hasData)
{
mEsm->restoreContext(context);
//esm.getHNExact(landData->normals, sizeof(VNML), "VNML");
if (mEsm->isNextSub("VNML"))
{
mEsm->skipHSubSize(12675);
}
VHGT rawHeights;
mEsm->getHNExact(&rawHeights, sizeof(VHGT), "VHGT");
int currentHeightOffset = rawHeights.heightOffset;
for (int y = 0; y < LAND_SIZE; y++)
{
currentHeightOffset += rawHeights.heightData[y * LAND_SIZE];
landData->heights[y * LAND_SIZE] = currentHeightOffset * HEIGHT_SCALE;
int tempOffset = currentHeightOffset;
for (int x = 1; x < LAND_SIZE; x++)
{
tempOffset += rawHeights.heightData[y * LAND_SIZE + x];
landData->heights[x + y * LAND_SIZE] = tempOffset * HEIGHT_SCALE;
}
}
if (mEsm->isNextSub("WNAM"))
{
mEsm->skipHSubSize(81);
}
if (mEsm->isNextSub("VCLR"))
{
landData->usingColours = true;
mEsm->getHExact(&landData->colours, 3*LAND_NUM_VERTS);
}else{
landData->usingColours = false;
}
//TODO fix magic numbers
uint16_t vtex[512];
mEsm->getHNExact(&vtex, 512, "VTEX");
int readPos = 0; //bit ugly, but it works
for ( int y1 = 0; y1 < 4; y1++ )
for ( int x1 = 0; x1 < 4; x1++ )
for ( int y2 = 0; y2 < 4; y2++)
for ( int x2 = 0; x2 < 4; x2++ )
landData->textures[(y1*4+y2)*16+(x1*4+x2)] = vtex[readPos++];
}
else
{
landData->usingColours = false;
memset(&landData->textures, 0, 512 * sizeof(uint16_t));
for (int i = 0; i < LAND_NUM_VERTS; i++)
{
landData->heights[i] = -256.0f * HEIGHT_SCALE;
}
}
dataLoaded = true;
}
void Land::unloadData()
{
if (dataLoaded)
{
delete landData;
landData = NULL;
dataLoaded = false;
}
}
}

@ -17,11 +17,66 @@ struct Land
// File context. This allows the ESM reader to be 'reset' to this
// location later when we are ready to load the full data set.
ESMReader* mEsm;
ESM_Context context;
bool hasData;
bool dataLoaded;
// number of vertices per side
static const int LAND_SIZE = 65;
// cell terrain size in world coords
static const int REAL_SIZE = 8192;
// total number of vertices
static const int LAND_NUM_VERTS = LAND_SIZE * LAND_SIZE;
static const int HEIGHT_SCALE = 8;
//number of textures per side of land
static const int LAND_TEXTURE_SIZE = 16;
//total number of textures per land
static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE;
#pragma pack(push,1)
struct VHGT
{
float heightOffset;
int8_t heightData[LAND_NUM_VERTS];
short unknown1;
char unknown2;
};
#pragma pack(pop)
typedef uint8_t VNML[LAND_NUM_VERTS * 3];
struct LandData
{
float heightOffset;
float heights[LAND_NUM_VERTS];
//float normals[LAND_NUM_VERTS * 3];
uint16_t textures[LAND_NUM_TEXTURES];
bool usingColours;
char colours[3 * LAND_NUM_VERTS];
};
LandData *landData;
void load(ESMReader &esm);
/**
* Actually loads data
*/
void loadData();
/**
* Frees memory allocated for land data
*/
void unloadData();
};
}
#endif

@ -95,12 +95,17 @@ namespace ESMS
State_Unloaded, State_Preloaded, State_Loaded
};
CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) {}
CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded)
{
mWaterLevel = cell->water;
}
const ESM::Cell *cell;
State mState;
std::vector<std::string> mIds;
float mWaterLevel;
// Lists for each individual object type
CellRefList<Activator, D> activators;
CellRefList<Potion, D> potions;

@ -201,15 +201,21 @@ namespace ESMS
// TODO: For multiple ESM/ESP files we need one list per file.
std::vector<LandTexture> ltex;
int count;
LTexList() : count(0)
LTexList()
{
// More than enough to hold Morrowind.esm.
ltex.reserve(128);
}
int getSize() { return count; }
const LandTexture* search(size_t index) const
{
assert(index < ltex.size());
return &ltex.at(index);
}
int getSize() { return ltex.size(); }
int getSize() const { return ltex.size(); }
virtual void listIdentifier (std::vector<std::string>& identifier) const {}
@ -233,12 +239,18 @@ namespace ESMS
*/
struct LandList : RecList
{
virtual ~LandList() {}
virtual ~LandList()
{
for ( LandMap::iterator itr = lands.begin(); itr != lands.end(); ++itr )
{
delete itr->second;
}
}
// Map containing all landscapes
typedef std::map<int, Land*> LandsCol;
typedef std::map<int, LandsCol> Lands;
Lands lands;
typedef std::pair<int, int> LandCoord;
typedef std::map<LandCoord, Land*> LandMap;
LandMap lands;
int count;
LandList() : count(0) {}
@ -247,17 +259,15 @@ namespace ESMS
virtual void listIdentifier (std::vector<std::string>& identifier) const {}
// Find land for the given coordinates. Return null if no data.
const Land *search(int x, int y) const
Land *search(int x, int y) const
{
Lands::const_iterator it = lands.find(x);
if(it==lands.end())
return NULL;
LandsCol::const_iterator it2 = it->second.find(y);
if(it2 == it->second.end())
LandMap::const_iterator itr = lands.find(std::make_pair<int, int>(x, y));
if ( itr == lands.end() )
{
return NULL;
}
return it2->second;
return itr->second;
}
void load(ESMReader &esm, const std::string &id)
@ -266,11 +276,11 @@ namespace ESMS
// Create the structure and load it. This actually skips the
// landscape data and remembers the file position for later.
Land *land = new Land;
Land *land = new Land();
land->load(esm);
// Store the structure
lands[land->X][land->Y] = land;
lands[std::make_pair<int, int>(land->X, land->Y)] = land;
}
};

@ -116,7 +116,7 @@ namespace ESMS
recLists[REC_GLOB] = &globals;
recLists[REC_GMST] = &gameSettings;
recLists[REC_INGR] = &ingreds;
//recLists[REC_LAND] = &lands;
recLists[REC_LAND] = &lands;
recLists[REC_LEVC] = &creatureLists;
recLists[REC_LEVI] = &itemLists;
recLists[REC_LIGH] = &lights;

@ -30,4 +30,9 @@ namespace Files
return iter->second;
}
const Files::PathContainer& Collections::getPaths() const
{
return mDirectories;
}
}

@ -21,6 +21,8 @@ namespace Files
/// leading dot and must be all lower-case.
const MultiDirCollection& getCollection(const std::string& extension) const;
const Files::PathContainer& getPaths() const;
private:
typedef std::map<std::string, MultiDirCollection> MultiDirCollectionContainer;
Files::PathContainer mDirectories;

@ -1368,7 +1368,7 @@ void NIFLoader::loadResource(Resource *resource)
if (!vfs->isFile(resourceName))
{
warn("File not found.");
warn("File "+resourceName+" not found.");
return;
}

@ -0,0 +1,13 @@
project(resources)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/caustic_0.png "${OpenMW_BINARY_DIR}/resources/water/caustic_0.png" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Example_Fresnel.cg "${OpenMW_BINARY_DIR}/resources/water/Example_Fresnel.cg" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Example_FresnelPS.asm "${OpenMW_BINARY_DIR}/resources/water/Example_FresnelPS.asm" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/GlassFP.cg "${OpenMW_BINARY_DIR}/resources/water/GlassFP.cg" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/GlassVP.cg "${OpenMW_BINARY_DIR}/resources/water/GlassVP.cg" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/perlinvolume.dds "${OpenMW_BINARY_DIR}/resources/water/perlinvolume.dds" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Water02.jpg "${OpenMW_BINARY_DIR}/resources/water/Water02.jpg" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.compositor "${OpenMW_BINARY_DIR}/resources/water/water.compositor" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/waves2.dds "${OpenMW_BINARY_DIR}/resources/water/waves2.dds" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Examples-Water.material "${OpenMW_BINARY_DIR}/resources/water/Examples-Water.material" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/WaterNormal1.tga "${OpenMW_BINARY_DIR}/resources/water/WaterNormal1.tga" COPYONLY)

Binary file not shown.

@ -1,34 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout">
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 588 433" name="_Main">
<!-- HEADER -->
<Widget type="TextBox" skin="HeaderText" position="0 0 588 18" name="NpcName" align="ALIGN_LEFT ALIGN_TOP">
<Property key="Caption" value="Name"/>
<Property key="TextAlign" value="ALIGN_CENTER"/>
</Widget>
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 588 433" name="_Main">
<!-- The Dialogue history -->
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="8 39 400 375" name="History" align="ALIGN_LEFT ALIGN_TOP STRETCH">
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="8 8 415 381" name="History" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
<Property key="Static" value="true"/>
<Property key="WordWrap" value="true"/>
<Property key="MultiLine" value="1" />
<Property key="VisibleVScroll" value="1" />
<!-- invisible box for receiving mouse events -->
<Widget type="Widget" skin="" position="0 0 400 375" name="EventBox" align="ALIGN_LEFT ALIGN_TOP STRETCH"/>
<Widget type="Widget" skin="" position="0 0 400 375" name="EventBox" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH"/>
</Widget>
<!-- The disposition bar-->
<Widget type="ProgressBar" skin="MW_EnergyBar_Blue" position="432 39 132 18"
<Widget type="ProgressBar" skin="MW_EnergyBar_Blue" position="432 8 132 18"
align="Right Top" name="Disposition">
<Widget type="EditBox" skin="MW_DispositionEdit" position_real = "0.25 0 0.5 1" name = "DispositionText"/>
</Widget>
<!-- The list of topics -->
<Widget type="ListBox" skin="MW_List" position="432 62 132 318" name="TopicsList">
<Widget type="ListBox" skin="MW_List" position="432 31 132 328" name="TopicsList" align="Right VStretch">
</Widget>
<!-- The Goodbye button -->
<Widget type="Button" skin="MW_Button" position="432 387 132 23" name="ByeButton">
<Widget type="Button" skin="MW_Button" position="432 366 132 23" name="ByeButton" align="Right Bottom">
<Property key="Caption" value="Goodbye"/>
</Widget>
</Widget>

@ -294,7 +294,7 @@
<Property key="TextAlign" value = "ALIGN_CENTER" />
<Property key="TextColour" value = "0.8 0.8 0.8" />
<Child type="Widget" skin="DialogBG" offset = "4 4 248 46" align = "ALIGN_STRETCH" name = "Client"/>
<Child type="Widget" skin="BlackBG" offset = "4 4 248 46" align = "ALIGN_STRETCH" name = "Client"/>
<!-- Outer borders -->
<Child type="Widget" skin="DB_T" offset="4 0 248 4" align="ALIGN_TOP ALIGN_HSTRETCH" name="Border">

@ -0,0 +1,116 @@
// Vertex program for fresnel reflections / refractions
void main_vp(
float4 pos : POSITION,
float4 normal : NORMAL,
float2 tex : TEXCOORD0,
out float4 oPos : POSITION,
out float3 noiseCoord : TEXCOORD0,
out float4 projectionCoord : TEXCOORD1,
out float3 oEyeDir : TEXCOORD2,
out float3 oNormal : TEXCOORD3,
uniform float4x4 worldViewProjMatrix,
uniform float3 eyePosition, // object space
uniform float timeVal,
uniform float scale, // the amount to scale the noise texture by
uniform float scroll, // the amount by which to scroll the noise
uniform float noise // the noise perturb as a factor of the time
)
{
oPos = mul(worldViewProjMatrix, pos);
// Projective texture coordinates, adjust for mapping
float4x4 scalemat = float4x4(0.5, 0, 0, 0.5,
0,-0.5, 0, 0.5,
0, 0, 0.5, 0.5,
0, 0, 0, 1);
projectionCoord = mul(scalemat, oPos);
// Noise map coords
noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
noiseCoord.z = noise * timeVal;
oEyeDir = normalize(pos.xyz - eyePosition);
oNormal = normal.rgb;
}
// Fragment program for distorting a texture using a 3D noise texture
void main_fp(
float3 noiseCoord : TEXCOORD0,
float4 projectionCoord : TEXCOORD1,
float3 eyeDir : TEXCOORD2,
float3 normal : TEXCOORD3,
out float4 col : COLOR,
uniform float4 tintColour,
uniform float noiseScale,
uniform float fresnelBias,
uniform float fresnelScale,
uniform float fresnelPower,
uniform sampler2D waterTex : register(s0),
uniform sampler2D noiseMap : register(s1),
uniform sampler2D reflectMap : register(s2),
uniform sampler2D refractMap : register(s3)
)
{
// Do the tex projection manually so we can distort _after_
float2 final = projectionCoord.xy / projectionCoord.w;
// Noise
float3 noiseNormal = (tex2D(noiseMap, (noiseCoord.xy / 5)).rgb - 0.5).rbg * noiseScale;
final += noiseNormal.xz;
// Fresnel
//normal = normalize(normal + noiseNormal.xz);
float fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);
// Reflection / refraction
float4 reflectionColour = tex2D(reflectMap, final);
float4 refractionColour = tex2D(refractMap, final) + tintColour;
// Final colour
col = lerp(refractionColour, reflectionColour, fresnel) * tex2D(waterTex, noiseNormal) / 3 ;
}
// Old version to match ATI PS 1.3 implementation
void main_vp_old(
float4 pos : POSITION,
float4 normal : NORMAL,
float2 tex : TEXCOORD0,
out float4 oPos : POSITION,
out float fresnel : COLOR,
out float3 noiseCoord : TEXCOORD0,
out float4 projectionCoord : TEXCOORD1,
uniform float4x4 worldViewProjMatrix,
uniform float3 eyePosition, // object space
uniform float fresnelBias,
uniform float fresnelScale,
uniform float fresnelPower,
uniform float timeVal,
uniform float scale, // the amount to scale the noise texture by
uniform float scroll, // the amount by which to scroll the noise
uniform float noise // the noise perturb as a factor of the time
)
{
oPos = mul(worldViewProjMatrix, pos);
// Projective texture coordinates, adjust for mapping
float4x4 scalemat = float4x4(0.5, 0, 0, 0.5,
0,-0.5, 0, 0.5,
0, 0, 0.5, 0.5,
0, 0, 0, 1);
projectionCoord = mul(scalemat, oPos);
// Noise map coords
noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
noiseCoord.z = noise * timeVal;
// calc fresnel factor (reflection coefficient)
float3 eyeDir = normalize(pos.xyz - eyePosition);
fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);
}

@ -0,0 +1,72 @@
ps.1.4
// conversion from Cg generated ARB_fragment_program to ps.1.4 by NFZ
// command line args: -profile arbfp1 -entry main_fp
// program main_fp
// c0 : distortionRange
// c1 : tintColour
// testure 0 : noiseMap
// texture 1 : reflectMap
// texture 2 : refractMap
// v0.x : fresnel
// t0.xyz : noiseCoord
// t1.xyw : projectionCoord
def c2, 2, 1, 0, 0
// Cg: distort.x = tex3D(noiseMap, noiseCoord).x;
// arbfp1: TEX R0.x, fragment.texcoord[0], texture[0], 3D;
// sample noise map using noiseCoord in TEX unit 0
texld r0, t0.xyz
// get projected texture coordinates from TEX coord 1
// will be used in phase 2
texcrd r1.xy, t1_dw.xyw
mov r1.z, c2.y
// Cg: distort.y = tex3D(noiseMap, noiseCoord + yoffset).x;
// arbfp1: ADD R1.xyz, fragment.texcoord[0], c1;
// arbfp1: TEX R1.x, R1, texture[0], 3D;
// arbfp1: MOV R0.y, R1.x;
// Cg: distort = (distort * 2 - 1) * distortionRange;
// arbfp1: MAD R0.xy, R0, c0.x, -c0.y;
// arbfp1: MUL R0.xy, R0, u0.x;
// (distort * 2 - 1) same as 2*(distort -.5) so use _bx2
// Cg: final = projectionCoord.xy / projectionCoord.w;
// Cg: final += distort;
// arbfp1: RCP R0.w, fragment.texcoord[1].w;
// arbfp1: MAD R0.xy, fragment.texcoord[1], R0.w, R0;
// final = (distort * projectionCoord.w) + projectionCoord.xy
// for ps.1.4 have to re-arrange things a bit to perturb projected texture coordinates
mad r0.xyz, r0_bx2, c0.x, r1
phase
// do dependant texture reads
// Cg: reflectionColour = tex2D(reflectMap, final);
// arbfp1: TEX R0, R0, texture[1], 2D;
// sampe reflectMap using dependant read : texunit 1
texld r1, r0.xyz
// Cg: refractionColour = tex2D(refractMap, final) + tintColour;
// arbfp1: TEX R1, R0, texture[2], 2D;
// sample refractMap : texunit 2
texld r2, r0.xyz
// adding tintColour that is in global c1
// arbfp1: ADD R1, R1, u1;
add r2, r2, c1
// Cg: col = lerp(refractionColour, reflectionColour, fresnel);
// arbfp1: ADD R0, R0, -R1;
// arbfp1: MAD result.color, fragment.color.primary.x, R0, R1;
lrp r0, v0.x, r1, r2

@ -0,0 +1,149 @@
vertex_program Water/GlassVP cg
{
source GlassVP.cg
entry_point glass_vp
profiles vs_1_1 arbvp1
default_params
{
param_named_auto worldViewProj worldviewproj_matrix
}
}
fragment_program Water/GlassFP cg
{
source GlassFP.cg
entry_point main_ps
profiles ps_2_0 arbfp1
}
material Water/Compositor
{
technique
{
pass
{
depth_check off
vertex_program_ref Water/GlassVP
{
param_named_auto timeVal time 0.25
param_named scale float 0.1
}
fragment_program_ref Water/GlassFP
{
param_named tintColour float4 0 0.35 0.35 1
}
texture_unit RT
{
tex_coord_set 0
tex_address_mode clamp
filtering linear linear linear
}
texture_unit
{
texture WaterNormal1.tga 2d
tex_coord_set 1
//tex_address_mode clamp
filtering linear linear linear
}
texture_unit
{
texture caustic_0.png 2d
tex_coord_set 2
//tex_address_mode clamp
filtering linear linear linear
}
}
}
}
vertex_program Water/RefractReflectVP cg
{
source Example_Fresnel.cg
entry_point main_vp
profiles vs_1_1 arbvp1
}
vertex_program Water/RefractReflectVPold cg
{
source Example_Fresnel.cg
entry_point main_vp_old
profiles vs_1_1 arbvp1
}
fragment_program Water/RefractReflectFP cg
{
source Example_Fresnel.cg
entry_point main_fp
// sorry, ps_1_1 and fp20 can't do this
profiles ps_2_0 arbfp1
}
fragment_program Water/RefractReflectPS asm
{
source Example_FresnelPS.asm
// sorry, only for ps_1_4 :)
syntax ps_1_4
}
material Examples/Water0
{
technique
{
pass
{
//
depth_write off
vertex_program_ref Water/RefractReflectVP
{
param_named_auto worldViewProjMatrix worldviewproj_matrix
param_named_auto eyePosition camera_position_object_space
param_named_auto timeVal time 0.15
param_named scroll float 1
param_named scale float 1
param_named noise float 1
// scroll and noisePos will need updating per frame
}
fragment_program_ref Water/RefractReflectFP
{
param_named fresnelBias float -0.1
param_named fresnelScale float 0.8
param_named fresnelPower float 20
param_named tintColour float4 1 1 1 1
param_named noiseScale float 0.05
}
// Water
scene_blend alpha_blend
texture_unit
{
// Water texture
texture Water02.jpg
// min / mag filtering, no mip
filtering linear linear none
alpha_op_ex source1 src_manual src_current 0.9
}
// Noise
texture_unit
{
alpha_op_ex source1 src_manual src_current 0.9
// Perlin noise volume
texture waves2.dds
// min / mag filtering, no mip
filtering linear linear none
}
}
}
}

@ -0,0 +1,15 @@
sampler RT : register(s0);
sampler NormalMap : register(s1);
sampler CausticMap : register(s2);
float4 main_ps(float2 iTexCoord : TEXCOORD0,
float3 noiseCoord : TEXCOORD1,
uniform float4 tintColour) : COLOR
{
float4 normal = tex2D(NormalMap, noiseCoord);
return tex2D(RT, iTexCoord + normal.xy * 0.05) +
(tex2D(CausticMap, noiseCoord) / 5) +
tintColour ;
}

@ -0,0 +1,24 @@
void glass_vp
(
in float4 inPos : POSITION,
out float4 pos : POSITION,
out float2 uv0 : TEXCOORD0,
out float4 noiseCoord : TEXCOORD1,
uniform float4x4 worldViewProj,
uniform float timeVal,
uniform float scale
)
{
// Use standardise transform, so work accord with render system specific (RS depth, requires texture flipping, etc)
pos = mul(worldViewProj, inPos);
// The input positions adjusted by texel offsets, so clean up inaccuracies
inPos.xy = sign(inPos.xy);
// Convert to image-space
uv0 = (float2(inPos.x, -inPos.y) + 1.0f) * 0.5f;
noiseCoord = (pos + timeVal) * scale;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

@ -0,0 +1,21 @@
compositor Water
{
technique
{
texture rt0 target_width target_height PF_R8G8B8
target rt0 { input previous }
target_output
{
// Start with clear output
input none
pass render_quad
{
material Water/Compositor
input 0 rt0
}
}
}
}

Binary file not shown.

@ -63,17 +63,17 @@ size_t BulletShape::calculateSize() const
//=============================================================================================================
template<> BulletShapeManager *Ogre::Singleton<BulletShapeManager>::ms_Singleton = 0;
template<> BulletShapeManager *Ogre::Singleton<BulletShapeManager>::msSingleton = 0;
BulletShapeManager *BulletShapeManager::getSingletonPtr()
{
return ms_Singleton;
return msSingleton;
}
BulletShapeManager &BulletShapeManager::getSingleton()
{
assert(ms_Singleton);
return(*ms_Singleton);
assert(msSingleton);
return(*msSingleton);
}
BulletShapeManager::BulletShapeManager()

@ -151,7 +151,8 @@ namespace Physic
PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader)
PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) :
mDebugActive(0)
{
// Set up the collision configuration and dispatcher
collisionConfiguration = new btDefaultCollisionConfiguration();
@ -203,6 +204,13 @@ namespace Physic
createDebugRendering();
}
mDebugDrawer->setDebugMode(mode);
mDebugActive = mode;
}
bool PhysicEngine::toggleDebugRendering()
{
setDebugRenderingMode(!mDebugActive);
return mDebugActive;
}
PhysicEngine::~PhysicEngine()
@ -418,4 +426,35 @@ namespace Physic
return std::pair<std::string,float>(name,d);
}
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(btVector3& from, btVector3& to)
{
MyRayResultCallback resultCallback1;
resultCallback1.m_collisionFilterMask = COL_WORLD;
dynamicsWorld->rayTest(from, to, resultCallback1);
std::vector< std::pair<float, btCollisionObject*> > results = resultCallback1.results;
MyRayResultCallback resultCallback2;
resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL;
dynamicsWorld->rayTest(from, to, resultCallback2);
std::vector< std::pair<float, btCollisionObject*> > actorResults = resultCallback2.results;
std::vector< std::pair<float, std::string> > results2;
for (std::vector< std::pair<float, btCollisionObject*> >::iterator it=results.begin();
it != results.end(); ++it)
{
results2.push_back( std::make_pair( (*it).first, static_cast<RigidBody&>(*(*it).second).mName ) );
}
for (std::vector< std::pair<float, btCollisionObject*> >::iterator it=actorResults.begin();
it != actorResults.end(); ++it)
{
results2.push_back( std::make_pair( (*it).first, static_cast<PairCachingGhostObject&>(*(*it).second).mName ) );
}
std::sort(results2.begin(), results2.end(), MyRayResultCallback::cmp);
return results2;
}
}};

@ -199,11 +199,18 @@ namespace Physic
*/
void setDebugRenderingMode(int mode);
bool toggleDebugRendering();
/**
* Return the closest object hit by a ray. If there are no objects, it will return ("",-1).
*/
std::pair<std::string,float> rayTest(btVector3& from,btVector3& to);
/**
* Return all objects hit by a ray.
*/
std::vector< std::pair<float, std::string> > rayTest2(btVector3& from, btVector3& to);
//event list of non player object
std::list<PhysicEvent> NPEventList;
@ -230,6 +237,26 @@ namespace Physic
//debug rendering
BtOgre::DebugDrawer* mDebugDrawer;
bool isDebugCreated;
bool mDebugActive;
};
struct MyRayResultCallback : public btCollisionWorld::RayResultCallback
{
virtual btScalar addSingleResult( btCollisionWorld::LocalRayResult& rayResult, bool bNormalInWorldSpace)
{
results.push_back( std::make_pair(rayResult.m_hitFraction, rayResult.m_collisionObject) );
return rayResult.m_hitFraction;
}
static bool cmp( const std::pair<float, std::string>& i, const std::pair<float, std::string>& j )
{
if( i.first > j.first ) return false;
if( j.first > i.first ) return true;
return false;
}
std::vector < std::pair<float, btCollisionObject*> > results;
};
}}

@ -148,6 +148,7 @@ Bug #207: Ogre.log not written
Bug #209: Sounds do not play
Bug #210: Ogre crash at Dren plantation
Bug #214: Unsupported file format version
Bug #222: Launcher is writing openmw.cfg file to wrong location
Feature #9: NPC Dialogue Window
Feature #16/42: New sky/weather implementation
Feature #40: Fading

Loading…
Cancel
Save