Merge remote-tracking branch 'upstream/master'

actorid
Roman Melnik 13 years ago
commit 35f787c4bf

@ -182,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}
@ -201,6 +202,7 @@ if(APPLE)
"Plugin_ParticleFX")
endif(APPLE)
add_subdirectory( files/)
add_subdirectory( files/mygui )
# Specify build paths

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

@ -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 water terrain terrainmaterial
)
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)

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

@ -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);
@ -53,6 +59,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
//mSkyManager = 0;
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
mWater = 0;
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mSun = 0;
@ -64,6 +74,7 @@ RenderingManager::~RenderingManager ()
//TODO: destroy mSun?
delete mPlayer;
delete mSkyManager;
delete mTerrainManager;
delete mLocalMap;
}
@ -88,14 +99,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){
@ -142,6 +172,27 @@ void RenderingManager::update (float duration){
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 ()
@ -236,17 +287,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 +337,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 +357,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,6 +25,7 @@
#include "objects.hpp"
#include "actors.hpp"
#include "player.hpp"
#include "water.hpp"
#include "localmap.hpp"
namespace Ogre
@ -59,6 +61,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 +71,7 @@ class RenderingManager: private RenderingInterface {
void toggleLight();
bool toggleRenderMode(int mode);
OEngine::Render::Fader* getFader();
void removeCell (MWWorld::Ptr::CellStore *store);
@ -75,6 +79,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 +93,21 @@ 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();
void setGlare(bool glare);
void skyEnable ();
void skyDisable ();
@ -109,13 +120,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 +142,13 @@ class RenderingManager: private RenderingInterface {
private:
void setAmbientMode();
SkyManager* mSkyManager;
MWRender::Water *mWater;
TerrainManager* mTerrainManager;
OEngine::Render::OgreRenderer &mRendering;
MWRender::Objects mObjects;

@ -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,4 +123,8 @@ op 0x200013d: FadeOut
op 0x200013e: FadeTo
op 0x200013f: GetCurrentWeather
op 0x2000140: ChangeWeather
opcodes 0x2000141-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,8 +95,7 @@ public:
virtual void stop();
virtual bool isPlaying();
virtual void setVolume(float volume);
virtual void update(const float *pos);
virtual void update();
void play();
bool process();
@ -187,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;
@ -255,16 +259,10 @@ bool OpenAL_SoundStream::isPlaying()
return !mIsFinished;
}
void OpenAL_SoundStream::setVolume(float volume)
{
alSourcef(mSource, AL_GAIN, volume*mBaseVolume);
throwALerror();
mVolume = volume;
}
void OpenAL_SoundStream::update(const float *pos)
void OpenAL_SoundStream::update()
{
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume);
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();
@ -321,15 +319,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);
@ -339,8 +339,23 @@ public:
virtual void stop();
virtual bool isPlaying();
virtual void setVolume(float volume);
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)
@ -372,16 +387,22 @@ bool OpenAL_Sound::isPlaying()
return state==AL_PLAYING;
}
void OpenAL_Sound::setVolume(float volume)
void OpenAL_Sound::update()
{
alSourcef(mSource, AL_GAIN, volume*mBaseVolume);
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume);
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();
mVolume = volume;
}
void OpenAL_Sound::update(const float *pos)
void OpenAL_Sound3D::update()
{
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance)
alSourcef(mSource, AL_GAIN, 0.0f);
else
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume);
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();
@ -410,8 +431,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)
@ -428,7 +448,12 @@ 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();
@ -441,6 +466,8 @@ void OpenAL_Output::init(const std::string &devname)
try
{
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++)
{
ALuint src = 0;
@ -580,10 +607,8 @@ void OpenAL_Output::bufferFinished(ALuint buf)
}
SoundPtr 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();
boost::shared_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0;
@ -618,7 +643,7 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float
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);
@ -628,11 +653,9 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float
return sound;
}
SoundPtr 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();
boost::shared_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0;
@ -644,7 +667,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos,
try
{
buf = getBuffer(fname);
sound.reset(new OpenAL_Sound(*this, src, buf));
sound.reset(new OpenAL_Sound3D(*this, src, buf));
}
catch(std::exception &e)
{
@ -655,7 +678,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos,
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);
@ -663,11 +686,12 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos,
alSourcef(src, AL_MAX_DISTANCE, max);
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
alSourcef(src, AL_GAIN, volume);
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);
@ -680,8 +704,6 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos,
SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch)
{
throwALerror();
boost::shared_ptr<OpenAL_SoundStream> sound;
ALuint src;
@ -721,61 +743,21 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa
return sound;
}
SoundPtr OpenAL_Output::streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max)
{
throwALerror();
boost::shared_ptr<OpenAL_SoundStream> sound;
ALuint src;
if(mFreeSources.empty())
fail("No free sources");
src = mFreeSources.front();
mFreeSources.pop_front();
void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir)
{
mPos = pos;
try
{
DecoderPtr decoder = mManager.getDecoder();
decoder->open(fname);
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
}
catch(std::exception &e)
if(mContext)
{
mFreeSources.push_back(src);
throw;
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();
}
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_LOOPING, AL_FALSE);
throwALerror();
sound->play();
return sound;
}
void OpenAL_Output::updateListener(const float *pos, const float *atdir, const float *updir)
{
float orient[6] = {
atdir[0], atdir[2], -atdir[1],
updir[0], updir[2], -updir[1]
};
alListener3f(AL_POSITION, pos[0], pos[2], -pos[1]);
alListenerfv(AL_ORIENTATION, orient);
throwALerror();
}

@ -40,15 +40,12 @@ namespace MWSound
virtual void init(const std::string &devname="");
virtual void deinit();
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, bool loop);
virtual SoundPtr playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max, bool loop);
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);
virtual SoundPtr streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max);
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);
OpenAL_Output& operator=(const OpenAL_Output &rhs);
OpenAL_Output(const OpenAL_Output &rhs);
@ -60,6 +57,7 @@ namespace MWSound
std::auto_ptr<StreamThread> mStreamThread;
friend class OpenAL_Sound;
friend class OpenAL_Sound3D;
friend class OpenAL_SoundStream;
friend class SoundManager;
};

@ -1,30 +1,37 @@
#ifndef GAME_SOUND_SOUND_H
#define GAME_SOUND_SOUND_H
#include <OgreVector3.h>
namespace MWSound
{
class Sound
{
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 mMinDistance;
float mMaxDistance;
int mFlags;
public:
virtual void stop() = 0;
virtual bool isPlaying() = 0;
virtual void setVolume(float volume) = 0;
void setPosition(const Ogre::Vector3 &pos) { mPos = pos; }
void setVolume(float volume) { mVolume = volume; }
Sound() : mVolume(1.0f)
Sound() : mPos(0.0f, 0.0f, 0.0f)
, mVolume(1.0f)
, mBaseVolume(1.0f)
, mMinDistance(20.0f) /* 1 * min_range_scale */
, mMaxDistance(12750.0f) /* 255 * max_range_scale */
, mFlags(Play_Normal)
{ }
virtual ~Sound() { }

@ -4,6 +4,8 @@
#include <string>
#include <memory>
#include <OgreVector3.h>
#include "soundmanager.hpp"
#include "../mwworld/ptr.hpp"
@ -22,19 +24,23 @@ namespace MWSound
virtual void init(const std::string &devname="") = 0;
virtual void deinit() = 0;
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, bool loop) = 0;
virtual SoundPtr playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max, bool loop) = 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) = 0;
virtual SoundPtr streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
float min, float max) = 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) = 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,8 +62,6 @@ namespace MWSound
catch(std::exception &e)
{
std::cout <<"Sound init failed: "<<e.what()<< std::endl;
mOutput.reset();
return;
}
}
@ -108,7 +106,7 @@ namespace MWSound
max = std::max(min, max);
}
return std::string("Sound/")+snd->sound;
return "Sound/"+snd->sound;
}
@ -182,11 +180,13 @@ namespace MWSound
{
// The range values are not tested
float basevol = 1.0f; /* TODO: volume settings */
std::string filePath = std::string("Sound/")+filename;
std::string filePath = "Sound/"+filename;
const ESM::Position &pos = ptr.getCellRef().pos;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
SoundPtr sound = mOutput->playSound3D(filePath, pos.pos, basevol, 1.0f,
20.0f, 12750.0f, false);
SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f,
20.0f, 12750.0f, Play_Normal);
sound->mPos = objpos;
sound->mBaseVolume = basevol;
mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
@ -203,7 +203,7 @@ namespace MWSound
}
SoundPtr 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)
{
SoundPtr sound;
try
@ -212,11 +212,12 @@ namespace MWSound
float min, max;
std::string file = lookup(soundId, basevol, min, max);
sound = mOutput->playSound(file, volume*basevol, pitch, loop);
sound = mOutput->playSound(file, volume*basevol, pitch, mode);
sound->mVolume = volume;
sound->mBaseVolume = basevol;
sound->mMinDistance = min;
sound->mMaxDistance = max;
sound->mFlags = mode;
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
}
@ -228,8 +229,7 @@ namespace MWSound
}
SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId,
float volume, float pitch, bool loop,
bool untracked)
float volume, float pitch, int mode)
{
SoundPtr sound;
try
@ -239,15 +239,20 @@ namespace MWSound
float min, max;
std::string file = lookup(soundId, basevol, min, max);
const ESM::Position &pos = ptr.getCellRef().pos;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
sound = mOutput->playSound3D(file, pos.pos, volume*basevol, pitch, min, max, loop);
sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode);
sound->mPos = objpos;
sound->mVolume = volume;
sound->mBaseVolume = basevol;
sound->mMinDistance = min;
sound->mMaxDistance = max;
sound->mFlags = mode;
mActiveSounds[sound] = (!untracked ? std::make_pair(ptr, soundId) :
std::make_pair(MWWorld::Ptr(), soundId));
if((mode&Play_NoTrack))
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
else
mActiveSounds[sound] = std::make_pair(ptr, soundId);
}
catch(std::exception &e)
{
@ -326,11 +331,12 @@ namespace MWSound
void SoundManager::updateObject(MWWorld::Ptr ptr)
{
const ESM::Position &pos = ptr.getCellRef().pos;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
if(snditer->second.first == ptr)
snditer->first->update(pos.pos);
snditer->first->setPosition(objpos);
snditer++;
}
}
@ -411,9 +417,9 @@ namespace MWSound
// 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] };
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);
// Check if any sounds are finished playing, and trash them
@ -423,7 +429,10 @@ namespace MWSound
if(!snditer->first->isPlaying())
mActiveSounds.erase(snditer++);
else
{
snditer->first->update();
snditer++;
}
}
}

@ -30,6 +30,19 @@ namespace MWSound
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; }
class SoundManager
{
Ogre::ResourceGroupManager& mResourceMgr;
@ -87,12 +100,11 @@ namespace MWSound
bool sayDone(MWWorld::Ptr reference) const;
///< Is actor not speaking?
SoundPtr 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
SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, bool loop,
bool untracked=false);
float volume, float pitch, int mode=Play_Normal);
///< Play a sound from an object
void stopSound3D(MWWorld::Ptr reference, const std::string& soundId);

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

@ -531,9 +531,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)
{
@ -754,4 +755,15 @@ namespace MWWorld
{
return mRendering->getFader();
}
void World::setWaterHeight(const float height)
{
mRendering->setWaterHeight(height);
}
void World::toggleWater()
{
mRendering->toggleWater();
}
}

@ -112,6 +112,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();

@ -45,6 +45,20 @@ struct ciLessBoost : std::binary_function<std::string, std::string, bool>
}
};
struct pathComparer
{
private:
int m_start, m_size;
public:
pathComparer(int start, int size) : m_start(start), m_size(size) { }
bool operator() (const std::string& first, const std::string& other)
{
return lexicographical_compare(first.substr(m_start,m_size), other.substr(m_start,m_size), boost::algorithm::is_iless());
}
};
static bool fsstrict = false;
/// An OGRE Archive wrapping a BSAFile archive
@ -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++)
if (filename.find(".tga") != std::string::npos)
return false;
{
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;
}
return true;
pathComparer comp(delimiter, copy.size() - delimiter-1);
std::vector<std::string>::iterator find = std::lower_bound(current.begin(), current.end(), copy, comp);
if (find != current.end() && !comp(copy, current.front()))
{
copy = *find;
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;
std::string copy;
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);
}
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;
}
@ -261,7 +246,7 @@ bool exists(const String& filename) {
}
// Check if the file exists.
bool cexists(const String& filename) const {
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) == ':'
@ -272,7 +257,7 @@ bool exists(const String& filename) {
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; }

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

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

@ -199,6 +199,8 @@ 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).
*/
@ -230,6 +232,7 @@ namespace Physic
//debug rendering
BtOgre::DebugDrawer* mDebugDrawer;
bool isDebugCreated;
bool mDebugActive;
};
}}

Loading…
Cancel
Save