1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 03:35:27 +00:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Roman Melnik 2012-04-01 17:53:12 +03:00
commit ed58e9e553
24 changed files with 998 additions and 89 deletions

View File

@ -306,7 +306,7 @@ if(DPKG_PROGRAM)
Data files from the original game is required to run it.") Data files from the original game is required to run it.")
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") 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_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") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
@ -392,6 +392,11 @@ if (BUILD_LAUNCHER)
add_subdirectory( apps/launcher ) add_subdirectory( apps/launcher )
endif() endif()
option(BUILD_MWINIIMPORTER "build MWiniImporter inspector" ON)
if (BUILD_MWINIIMPORTER)
add_subdirectory( apps/mwiniimporter )
endif()
if (WIN32) if (WIN32)
if (MSVC) if (MSVC)
if (USE_DEBUG_CONSOLE) if (USE_DEBUG_CONSOLE)

View File

@ -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
)

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -15,7 +15,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
renderinginterface localmap water terrain terrainmaterial renderinginterface localmap occlusionquery terrain terrainmaterial water
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

View File

@ -0,0 +1,254 @@
#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)
{
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);
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;
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);
}
bool OcclusionQuery::getTestResult()
{
assert( !occlusionTestPending()
&& "Occlusion test still pending");
return mTestResult;
}

View File

@ -0,0 +1,95 @@
#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;};
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

View File

@ -47,9 +47,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
mObjects.setMwRoot(mMwRoot); mObjects.setMwRoot(mMwRoot);
mActors.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"); Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player");
playerNode->pitch(Degree(90)); playerNode->pitch(Degree(90));
Ogre::SceneNode *cameraYawNode = playerNode->createChildSceneNode(); Ogre::SceneNode *cameraYawNode = playerNode->createChildSceneNode();
@ -59,10 +56,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
//mSkyManager = 0; //mSkyManager = 0;
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment); mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
mWater = 0; mWater = 0;
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mSun = 0; mSun = 0;
@ -76,6 +73,7 @@ RenderingManager::~RenderingManager ()
delete mSkyManager; delete mSkyManager;
delete mTerrainManager; delete mTerrainManager;
delete mLocalMap; delete mLocalMap;
delete mOcclusionQuery;
} }
MWRender::SkyManager* RenderingManager::getSkyManager() MWRender::SkyManager* RenderingManager::getSkyManager()
@ -167,8 +165,12 @@ void RenderingManager::update (float duration){
mActors.update (duration); mActors.update (duration);
mOcclusionQuery->update(duration);
mSkyManager->update(duration); mSkyManager->update(duration);
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
mRendering.update(duration); mRendering.update(duration);
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() ); mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );

View File

@ -27,6 +27,7 @@
#include "player.hpp" #include "player.hpp"
#include "water.hpp" #include "water.hpp"
#include "localmap.hpp" #include "localmap.hpp"
#include "occlusionquery.hpp"
namespace Ogre namespace Ogre
{ {
@ -108,6 +109,9 @@ class RenderingManager: private RenderingInterface {
void sunEnable(); void sunEnable();
void sunDisable(); void sunDisable();
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); };
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; };
void setGlare(bool glare); void setGlare(bool glare);
void skyEnable (); void skyEnable ();
void skyDisable (); void skyDisable ();
@ -145,10 +149,12 @@ class RenderingManager: private RenderingInterface {
SkyManager* mSkyManager; SkyManager* mSkyManager;
MWRender::Water *mWater; OcclusionQuery* mOcclusionQuery;
TerrainManager* mTerrainManager; TerrainManager* mTerrainManager;
MWRender::Water *mWater;
OEngine::Render::OgreRenderer &mRendering; OEngine::Render::OgreRenderer &mRendering;
MWRender::Objects mObjects; MWRender::Objects mObjects;
@ -164,7 +170,6 @@ class RenderingManager: private RenderingInterface {
/// that the OGRE coordinate system matches that used internally in /// that the OGRE coordinate system matches that used internally in
/// Morrowind. /// Morrowind.
Ogre::SceneNode *mMwRoot; Ogre::SceneNode *mMwRoot;
Ogre::RaySceneQuery *mRaySceneQuery;
OEngine::Physic::PhysicEngine* mPhysicsEngine; OEngine::Physic::PhysicEngine* mPhysicsEngine;

View File

@ -12,6 +12,7 @@
#include "../mwworld/environment.hpp" #include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp" #include "../mwworld/world.hpp"
#include "occlusionquery.hpp"
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
@ -30,7 +31,7 @@ BillboardObject::BillboardObject()
void BillboardObject::setVisible(const bool visible) void BillboardObject::setVisible(const bool visible)
{ {
mNode->setVisible(visible); mBBSet->setVisible(visible);
} }
void BillboardObject::setSize(const float size) 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 /// \todo These billboards are not 100% correct, might want to revisit them later
mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1); mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1);
mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize); 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->setBillboardType(BBT_PERPENDICULAR_COMMON);
mBBSet->setCommonDirection( -position.normalisedCopy() ); mBBSet->setCommonDirection( -position.normalisedCopy() );
mNode = rootNode->createChildSceneNode(); mNode = rootNode->createChildSceneNode();
@ -319,8 +320,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
, mThunderTextureUnit(NULL) , mThunderTextureUnit(NULL)
, mRemainingTransitionTime(0.0f) , mRemainingTransitionTime(0.0f)
, mGlareFade(0.0f) , mGlareFade(0.0f)
, mGlare(0.0f)
, mEnabled(true) , mEnabled(true)
, mGlareEnabled(true)
, mSunEnabled(true) , mSunEnabled(true)
, mMasserEnabled(true) , mMasserEnabled(true)
, mSecundaEnabled(true) , mSecundaEnabled(true)
@ -592,10 +593,23 @@ void SkyManager::update(float duration)
mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) ); mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
mSecunda->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) 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(); Vector3 sun = mSunGlare->getPosition();
sun = Vector3(sun.x, sun.z, -sun.y); sun = Vector3(sun.x, sun.z, -sun.y);
Vector3 cam = mViewport->getCamera()->getRealDirection(); Vector3 cam = mViewport->getCamera()->getRealDirection();
@ -603,21 +617,10 @@ void SkyManager::update(float duration)
float val = 1- (angle.valueDegrees() / 180.f); float val = 1- (angle.valueDegrees() / 180.f);
val = (val*val*val*val)*2; val = (val*val*val*val)*2;
if (mGlareEnabled) mSunGlare->setSize(val * mGlareFade);
{
mGlareFade += duration*3;
if (mGlareFade > 1) mGlareFade = 1;
}
else
{
mGlareFade -= duration*3;
if (mGlareFade < 0.3) mGlareFade = 0;
} }
mSunGlare->setSize(val * (mGlareFade)); mSunGlare->setVisible(mSunEnabled);
}
mSunGlare->setVisible(mGlareFade>0 && mSunEnabled);
mSun->setVisible(mSunEnabled); mSun->setVisible(mSunEnabled);
mMasser->setVisible(mMasserEnabled); mMasser->setVisible(mMasserEnabled);
mSecunda->setVisible(mSecundaEnabled); mSecunda->setVisible(mSecundaEnabled);
@ -719,15 +722,15 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
else else
strength = 1.f; strength = 1.f;
mSunGlare->setVisibility(weather.mGlareView * strength); mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength);
mSun->setVisibility(strength); mSun->setVisibility(mGlareFade >= 0.5 ? weather.mGlareView * mGlareFade * strength : 0);
mAtmosphereNight->setVisible(weather.mNight && mEnabled); mAtmosphereNight->setVisible(weather.mNight && mEnabled);
} }
void SkyManager::setGlare(bool glare) void SkyManager::setGlare(const float glare)
{ {
mGlareEnabled = glare; mGlare = glare;
} }
Vector3 SkyManager::getRealSunPos() Vector3 SkyManager::getRealSunPos()
@ -812,3 +815,8 @@ void SkyManager::setDate(int day, int month)
mDay = day; mDay = day;
mMonth = month; mMonth = month;
} }
Ogre::SceneNode* SkyManager::getSunNode()
{
return mSun->getNode();
}

View File

@ -138,6 +138,8 @@ namespace MWRender
void setWeather(const MWWorld::WeatherResult& weather); void setWeather(const MWWorld::WeatherResult& weather);
Ogre::SceneNode* getSunNode();
void sunEnable(); void sunEnable();
void sunDisable(); void sunDisable();
@ -160,7 +162,7 @@ namespace MWRender
void setThunder(const float factor); void setThunder(const float factor);
void setGlare(bool glare); void setGlare(const float glare);
Ogre::Vector3 getRealSunPos(); Ogre::Vector3 getRealSunPos();
private: private:
@ -203,12 +205,12 @@ namespace MWRender
float mRemainingTransitionTime; float mRemainingTransitionTime;
float mGlareFade; float mGlare; // target
float mGlareFade; // actual
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType); void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
bool mEnabled; bool mEnabled;
bool mGlareEnabled;
bool mSunEnabled; bool mSunEnabled;
bool mMasserEnabled; bool mMasserEnabled;
bool mSecundaEnabled; bool mSecundaEnabled;

View File

@ -261,7 +261,16 @@ bool OpenAL_SoundStream::isPlaying()
void OpenAL_SoundStream::update() void OpenAL_SoundStream::update()
{ {
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume); 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_POSITION, mPos[0], mPos[2], -mPos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
@ -389,7 +398,16 @@ bool OpenAL_Sound::isPlaying()
void OpenAL_Sound::update() void OpenAL_Sound::update()
{ {
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume); 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_POSITION, mPos[0], mPos[2], -mPos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
@ -398,10 +416,18 @@ void OpenAL_Sound::update()
void OpenAL_Sound3D::update() void OpenAL_Sound3D::update()
{ {
ALfloat gain = mVolume*mBaseVolume;
ALfloat pitch = mPitch;
if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance) if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance)
alSourcef(mSource, AL_GAIN, 0.0f); gain = 0.0f;
else else if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater)
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume); {
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_POSITION, mPos[0], mPos[2], -mPos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
@ -639,6 +665,11 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float
alSourcef(src, AL_MAX_DISTANCE, 1000.0f); alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
alSourcef(src, AL_ROLLOFF_FACTOR, 0.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_GAIN, volume);
alSourcef(src, AL_PITCH, pitch); alSourcef(src, AL_PITCH, pitch);
@ -686,6 +717,11 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector
alSourcef(src, AL_MAX_DISTANCE, max); alSourcef(src, AL_MAX_DISTANCE, max);
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f); alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater)
{
volume *= 0.9f;
pitch *= 0.7f;
}
alSourcef(src, AL_GAIN, (pos.squaredDistance(mPos) > max*max) ? alSourcef(src, AL_GAIN, (pos.squaredDistance(mPos) > max*max) ?
0.0f : volume); 0.0f : volume);
alSourcef(src, AL_PITCH, pitch); alSourcef(src, AL_PITCH, pitch);
@ -702,7 +738,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector
} }
SoundPtr 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)
{ {
boost::shared_ptr<OpenAL_SoundStream> sound; boost::shared_ptr<OpenAL_SoundStream> sound;
ALuint src; ALuint src;
@ -714,6 +750,8 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa
try try
{ {
if((flags&Play_Loop))
std::cout <<"Warning: cannot loop stream "<<fname<< std::endl;
DecoderPtr decoder = mManager.getDecoder(); DecoderPtr decoder = mManager.getDecoder();
decoder->open(fname); decoder->open(fname);
sound.reset(new OpenAL_SoundStream(*this, src, decoder)); sound.reset(new OpenAL_SoundStream(*this, src, decoder));
@ -732,6 +770,11 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa
alSourcef(src, AL_MAX_DISTANCE, 1000.0f); alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
alSourcef(src, AL_ROLLOFF_FACTOR, 0.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_GAIN, volume);
alSourcef(src, AL_PITCH, pitch); alSourcef(src, AL_PITCH, pitch);
@ -744,9 +787,10 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa
} }
void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir) void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env)
{ {
mPos = pos; mPos = pos;
mLastEnvironment = env;
if(mContext) if(mContext)
{ {
@ -763,7 +807,7 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3
OpenAL_Output::OpenAL_Output(SoundManager &mgr) OpenAL_Output::OpenAL_Output(SoundManager &mgr)
: Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0), : Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0),
mStreamThread(new StreamThread) mLastEnvironment(Env_Normal), mStreamThread(new StreamThread)
{ {
} }

View File

@ -36,6 +36,8 @@ namespace MWSound
ALuint getBuffer(const std::string &fname); ALuint getBuffer(const std::string &fname);
void bufferFinished(ALuint buffer); void bufferFinished(ALuint buffer);
Environment mLastEnvironment;
virtual std::vector<std::string> enumerate(); virtual std::vector<std::string> enumerate();
virtual void init(const std::string &devname=""); virtual void init(const std::string &devname="");
virtual void deinit(); virtual void deinit();
@ -43,9 +45,9 @@ namespace MWSound
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags);
virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
float volume, float pitch, float min, float max, int flags); float volume, float pitch, float min, float max, int flags);
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch); virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags);
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &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& operator=(const OpenAL_Output &rhs);
OpenAL_Output(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs);

View File

@ -16,6 +16,7 @@ namespace MWSound
Ogre::Vector3 mPos; Ogre::Vector3 mPos;
float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */ float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */
float mBaseVolume; float mBaseVolume;
float mPitch;
float mMinDistance; float mMinDistance;
float mMaxDistance; float mMaxDistance;
int mFlags; int mFlags;
@ -29,6 +30,7 @@ namespace MWSound
Sound() : mPos(0.0f, 0.0f, 0.0f) Sound() : mPos(0.0f, 0.0f, 0.0f)
, mVolume(1.0f) , mVolume(1.0f)
, mBaseVolume(1.0f) , mBaseVolume(1.0f)
, mPitch(1.0f)
, mMinDistance(20.0f) /* 1 * min_range_scale */ , mMinDistance(20.0f) /* 1 * min_range_scale */
, mMaxDistance(12750.0f) /* 255 * max_range_scale */ , mMaxDistance(12750.0f) /* 255 * max_range_scale */
, mFlags(Play_Normal) , mFlags(Play_Normal)

View File

@ -27,9 +27,9 @@ namespace MWSound
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 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, virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
float volume, float pitch, float min, float max, int flags) = 0; float volume, float pitch, float min, float max, int flags) = 0;
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch) = 0; virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0;
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &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& operator=(const Sound_Output &rhs);
Sound_Output(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs);

View File

@ -88,10 +88,7 @@ namespace MWSound
if(snd == NULL) if(snd == NULL)
throw std::runtime_error(std::string("Failed to lookup sound ")+soundId); throw std::runtime_error(std::string("Failed to lookup sound ")+soundId);
if(snd->data.volume == 0) volume *= pow(10.0, (snd->data.volume/255.0*3348.0 - 3348.0) / 2000.0);
volume = 0.0f;
else
volume *= pow(10.0, (snd->data.volume/255.0f*3348.0 - 3348.0) / 2000.0);
if(snd->data.minRange == 0 && snd->data.maxRange == 0) if(snd->data.minRange == 0 && snd->data.maxRange == 0)
{ {
@ -135,10 +132,10 @@ namespace MWSound
std::cout <<"Playing "<<filename<< std::endl; std::cout <<"Playing "<<filename<< std::endl;
try try
{ {
if(mMusic) stopMusic();
mMusic->stop(); mMusic = mOutput->streamSound(filename, 0.4f, 1.0f, Play_NoEnv);
mMusic = mOutput->streamSound(filename, 0.4f, 1.0f);
mMusic->mBaseVolume = 0.4f; mMusic->mBaseVolume = 0.4f;
mMusic->mFlags = Play_NoEnv;
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -215,6 +212,7 @@ namespace MWSound
sound = mOutput->playSound(file, volume*basevol, pitch, mode); sound = mOutput->playSound(file, volume*basevol, pitch, mode);
sound->mVolume = volume; sound->mVolume = volume;
sound->mBaseVolume = basevol; sound->mBaseVolume = basevol;
sound->mPitch = pitch;
sound->mMinDistance = min; sound->mMinDistance = min;
sound->mMaxDistance = max; sound->mMaxDistance = max;
sound->mFlags = mode; sound->mFlags = mode;
@ -245,6 +243,7 @@ namespace MWSound
sound->mPos = objpos; sound->mPos = objpos;
sound->mVolume = volume; sound->mVolume = volume;
sound->mBaseVolume = basevol; sound->mBaseVolume = basevol;
sound->mPitch = pitch;
sound->mMinDistance = min; sound->mMinDistance = min;
sound->mMaxDistance = max; sound->mMaxDistance = max;
sound->mFlags = mode; sound->mFlags = mode;
@ -408,19 +407,25 @@ namespace MWSound
if(!isMusicPlaying()) if(!isMusicPlaying())
startRandomTitle(); startRandomTitle();
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
Ogre::Camera *cam = mEnvironment.mWorld->getPlayer().getRenderer()->getCamera(); Ogre::Camera *cam = mEnvironment.mWorld->getPlayer().getRenderer()->getCamera();
Ogre::Vector3 nPos, nDir, nUp; Ogre::Vector3 nPos, nDir, nUp;
nPos = cam->getRealPosition(); nPos = cam->getRealPosition();
nDir = cam->getRealDirection(); nDir = cam->getRealDirection();
nUp = cam->getRealUp(); 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 // The output handler is expecting vectors oriented like the game
// (that is, -Z goes down, +Y goes forward), but that's not what we // (that is, -Z goes down, +Y goes forward), but that's not what we
// get from Ogre's camera, so we have to convert. // get from Ogre's camera, so we have to convert.
const Ogre::Vector3 pos(nPos[0], -nPos[2], nPos[1]); const Ogre::Vector3 pos(nPos[0], -nPos[2], nPos[1]);
const Ogre::Vector3 at(nDir[0], -nDir[2], nDir[1]); const Ogre::Vector3 at(nDir[0], -nDir[2], nDir[1]);
const Ogre::Vector3 up(nUp[0], -nUp[2], nUp[1]); const Ogre::Vector3 up(nUp[0], -nUp[2], nUp[1]);
mOutput->updateListener(pos, at, up);
mOutput->updateListener(pos, at, up, env);
// Check if any sounds are finished playing, and trash them // Check if any sounds are finished playing, and trash them
SoundMap::iterator snditer = mActiveSounds.begin(); SoundMap::iterator snditer = mActiveSounds.begin();

View File

@ -43,6 +43,11 @@ namespace MWSound
static inline int operator&(const PlayMode &a, const PlayMode &b) static inline int operator&(const PlayMode &a, const PlayMode &b)
{ return (int)a & (int)b; } { return (int)a & (int)b; }
enum Environment {
Env_Normal,
Env_Underwater,
};
class SoundManager class SoundManager
{ {
Ogre::ResourceGroupManager& mResourceMgr; Ogre::ResourceGroupManager& mResourceMgr;

View File

@ -51,6 +51,28 @@ namespace MWWorld
return mEngine->rayTest(from,to); 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) bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to)
{ {
btVector3 _from, _to; btVector3 _from, _to;

View File

@ -36,6 +36,10 @@ namespace MWWorld
std::pair<std::string, float> getFacedHandle (MWWorld::World& world); 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 // cast ray, return true if it hit something
bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to);

View File

@ -157,7 +157,8 @@ namespace MWWorld
const std::string& master, const boost::filesystem::path& resDir, const std::string& master, const boost::filesystem::path& resDir,
bool newGame, Environment& environment, const std::string& encoding) bool newGame, Environment& environment, const std::string& encoding)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), : 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); mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine(); mPhysEngine = mPhysics->getEngine();
@ -497,6 +498,8 @@ namespace MWWorld
} }
std::string World::getFacedHandle() std::string World::getFacedHandle()
{
if (!mRendering->occlusionQuerySupported())
{ {
std::pair<std::string, float> result = mPhysics->getFacedHandle (*this); std::pair<std::string, float> result = mPhysics->getFacedHandle (*this);
@ -506,6 +509,12 @@ namespace MWWorld
return result.first; return result.first;
} }
else
{
// updated every few frames in update()
return mFacedHandle;
}
}
void World::deleteObject (Ptr ptr) void World::deleteObject (Ptr ptr)
{ {
@ -706,6 +715,8 @@ namespace MWWorld
mWeatherManager->update (duration); mWeatherManager->update (duration);
if (!mRendering->occlusionQuerySupported())
{
// cast a ray from player to sun to detect if the sun is visible // 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 // this is temporary until we find a better place to put this code
// currently its here because we need to access the physics system // currently its here because we need to access the physics system
@ -715,6 +726,73 @@ namespace MWWorld
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); 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 bool World::isCellExterior() const
{ {
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();

View File

@ -93,6 +93,12 @@ namespace MWWorld
Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); 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; int getDaysPerMonth (int month) const;

View File

@ -426,4 +426,35 @@ namespace Physic
return std::pair<std::string,float>(name,d); 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;
}
}}; }};

View File

@ -206,6 +206,11 @@ namespace Physic
*/ */
std::pair<std::string,float> rayTest(btVector3& from,btVector3& to); 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 //event list of non player object
std::list<PhysicEvent> NPEventList; std::list<PhysicEvent> NPEventList;
@ -235,6 +240,25 @@ namespace Physic
bool mDebugActive; 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;
};
}} }}
#endif #endif