1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 19:19:56 +00:00
openmw-tes3mp/apps/openmw/mwrender/objects.cpp

446 lines
14 KiB
C++
Raw Normal View History

2011-11-01 03:59:16 +00:00
#include "objects.hpp"
2012-02-06 09:41:13 +00:00
2011-11-02 04:13:33 +00:00
#include <OgreSceneNode.h>
2012-02-06 09:41:13 +00:00
#include <components/nifogre/ogre_nif_loader.hpp>
#include <components/settings/settings.hpp>
2012-04-03 13:13:47 +00:00
#include "renderconst.hpp"
2011-11-01 03:59:16 +00:00
using namespace MWRender;
2012-04-19 18:59:57 +00:00
// These are the Morrowind.ini defaults
float Objects::lightLinearValue = 3;
float Objects::lightLinearRadiusMult = 1;
float Objects::lightQuadraticValue = 16;
float Objects::lightQuadraticRadiusMult = 1;
2012-04-19 18:59:57 +00:00
bool Objects::lightOutQuadInLin = true;
bool Objects::lightQuadratic = false;
int Objects::uniqueID = 0;
2011-11-01 03:59:16 +00:00
void Objects::clearSceneNode (Ogre::SceneNode *node)
{
/// \todo This should probably be moved into OpenEngine at some point.
for (int i=node->numAttachedObjects()-1; i>=0; --i)
{
Ogre::MovableObject *object = node->getAttachedObject (i);
node->detachObject (object);
2012-02-06 09:41:13 +00:00
mRenderer.getScene()->destroyMovableObject (object);
}
}
2012-02-06 09:41:13 +00:00
void Objects::setMwRoot(Ogre::SceneNode* root)
{
2011-11-22 07:39:28 +00:00
mMwRoot = root;
2011-11-17 22:10:27 +00:00
}
2012-02-06 09:41:13 +00:00
void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
{
2011-11-22 07:39:28 +00:00
Ogre::SceneNode* root = mMwRoot;
2011-11-05 01:57:39 +00:00
Ogre::SceneNode* cellnode;
if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end())
{
//Create the scenenode and put it in the map
cellnode = root->createChildSceneNode();
2011-11-05 01:57:39 +00:00
mCellSceneNodes[ptr.getCell()] = cellnode;
}
else
2011-11-05 01:57:39 +00:00
{
cellnode = mCellSceneNodes[ptr.getCell()];
}
2011-11-05 01:48:52 +00:00
2011-11-05 01:57:39 +00:00
Ogre::SceneNode* insert = cellnode->createChildSceneNode();
2011-11-09 18:53:29 +00:00
const float *f = ptr.getRefData().getPosition().pos;
2012-03-25 02:03:08 +00:00
2011-11-05 01:57:39 +00:00
insert->setPosition(f[0], f[1], f[2]);
insert->setScale(ptr.getCellRef().scale, ptr.getCellRef().scale, ptr.getCellRef().scale);
2012-03-25 02:03:08 +00:00
2011-11-05 01:57:39 +00:00
// Convert MW rotation to a quaternion:
f = ptr.getCellRef().pos.rot;
2011-11-05 01:57:39 +00:00
// Rotate around X axis
2012-02-06 09:41:13 +00:00
Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X);
2011-11-05 01:57:39 +00:00
// Rotate around Y axis
2012-02-06 09:41:13 +00:00
Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y);
2011-11-05 01:57:39 +00:00
// Rotate around Z axis
2012-02-06 09:41:13 +00:00
Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z);
2012-02-06 09:41:13 +00:00
// Rotates first around z, then y, then x
2011-11-05 01:57:39 +00:00
insert->setOrientation(xr*yr*zr);
2011-11-05 01:57:39 +00:00
if (!enabled)
insert->setVisible (false);
2011-11-03 02:41:48 +00:00
ptr.getRefData().setBaseNode(insert);
2012-02-06 09:41:13 +00:00
mIsStatic = static_;
2011-11-01 03:59:16 +00:00
}
2012-02-06 09:41:13 +00:00
void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
{
2011-11-11 19:37:42 +00:00
Ogre::SceneNode* insert = ptr.getRefData().getBaseNode();
2011-11-05 01:57:39 +00:00
assert(insert);
2011-11-05 01:57:39 +00:00
NifOgre::NIFLoader::load(mesh);
2012-02-06 09:41:13 +00:00
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);
2012-04-02 14:14:39 +00:00
bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && Settings::Manager::getBool("limit small object distance", "Objects");
// do not fade out doors. that will cause holes and look stupid
if (ptr.getTypeName().find("Door") != std::string::npos)
small = false;
2012-03-29 14:47:59 +00:00
if (mBounds.find(ptr.getCell()) == mBounds.end())
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
Ogre::AxisAlignedBox bounds = ent->getBoundingBox();
2012-03-29 15:30:13 +00:00
bounds = Ogre::AxisAlignedBox(
insert->_getDerivedPosition() + bounds.getMinimum(),
insert->_getDerivedPosition() + bounds.getMaximum()
);
2012-03-29 14:47:59 +00:00
bounds.scale(insert->getScale());
mBounds[ptr.getCell()].merge(bounds);
2012-04-04 16:53:40 +00:00
bool transparent = false;
for (unsigned int i=0; i<ent->getNumSubEntities(); ++i)
{
Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial();
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
while (techIt.hasMoreElements())
{
Ogre::Technique* tech = techIt.getNext();
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
while (passIt.hasMoreElements())
{
Ogre::Pass* pass = passIt.getNext();
if (pass->getDepthWriteEnabled() == false)
transparent = true;
}
}
}
2012-04-19 18:59:57 +00:00
if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || transparent)
2011-11-05 01:57:39 +00:00
{
insert->attachObject(ent);
2012-04-03 13:13:47 +00:00
ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0);
ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc);
ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
2011-11-05 01:57:39 +00:00
}
else
{
Ogre::StaticGeometry* sg = 0;
2012-04-19 18:59:57 +00:00
if (small)
2011-11-05 01:57:39 +00:00
{
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(Settings::Manager::getInt("small object distance", "Viewing distance"));
}
else
sg = mStaticGeometrySmall[ptr.getCell()];
2011-11-05 01:57:39 +00:00
}
else
{
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()];
2011-11-05 01:57:39 +00:00
}
// 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));
2011-11-05 01:57:39 +00:00
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
2012-04-03 13:13:47 +00:00
sg->setVisibilityFlags(small ? RV_StaticsSmall : RV_Statics);
2012-04-11 16:53:13 +00:00
sg->setCastShadows(true);
sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
2012-02-06 09:41:13 +00:00
mRenderer.getScene()->destroyEntity(ent);
2011-11-05 01:57:39 +00:00
}
2011-11-01 03:59:16 +00:00
}
2012-02-06 09:41:13 +00:00
void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius)
{
Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle());
2011-11-05 01:57:39 +00:00
assert(insert);
2012-02-06 09:41:13 +00:00
Ogre::Light *light = mRenderer.getScene()->createLight();
2011-11-05 01:57:39 +00:00
light->setDiffuseColour (r, g, b);
2012-04-28 18:42:53 +00:00
ESMS::LiveCellRef<ESM::Light, MWWorld::RefData> *ref =
ptr.get<ESM::Light>();
2012-04-19 18:59:57 +00:00
LightInfo info;
info.name = light->getName();
info.radius = radius;
info.colour = Ogre::ColourValue(r, g, b);
2012-04-28 18:42:53 +00:00
if (ref->base->data.flags & ESM::Light::Negative)
info.colour *= -1;
2012-04-28 18:42:53 +00:00
info.interior = (ptr.getCell()->cell->data.flags & ESM::Cell::Interior);
2012-02-06 09:41:13 +00:00
2012-04-28 18:42:53 +00:00
if (ref->base->data.flags & ESM::Light::Flicker)
info.type = LT_Flicker;
else if (ref->base->data.flags & ESM::Light::FlickerSlow)
info.type = LT_FlickerSlow;
else if (ref->base->data.flags & ESM::Light::Pulse)
info.type = LT_Pulse;
else if (ref->base->data.flags & ESM::Light::PulseSlow)
info.type = LT_PulseSlow;
else
info.type = LT_Normal;
// random starting phase for the animation
info.time = Ogre::Math::RangeRandom(0, 2 * M_PI);
// adjust the lights depending if we're in an interior or exterior cell
// quadratic means the light intensity falls off quite fast, resulting in a
// dark, atmospheric environment (perfect for exteriors)
// for interiors, we want more "warm" lights, so use linear attenuation.
2012-04-19 18:59:57 +00:00
bool quadratic = false;
if (!lightOutQuadInLin)
quadratic = lightQuadratic;
else
{
2012-04-28 18:42:53 +00:00
quadratic = !info.interior;
2012-04-19 18:59:57 +00:00
}
2012-02-06 09:41:13 +00:00
2012-04-19 18:59:57 +00:00
if (!quadratic)
2012-02-06 09:41:13 +00:00
{
2012-04-19 18:59:57 +00:00
float r = radius * lightLinearRadiusMult;
float attenuation = lightLinearValue / r;
light->setAttenuation(r*10, 0, attenuation, 0);
2012-02-06 09:41:13 +00:00
}
else
{
2012-04-19 18:59:57 +00:00
float r = radius * lightQuadraticRadiusMult;
float attenuation = lightQuadraticValue / pow(r, 2);
light->setAttenuation(r*10, 0, 0, attenuation);
2012-02-06 09:41:13 +00:00
}
insert->attachObject(light);
2012-04-28 18:42:53 +00:00
mLights.push_back(info);
2011-11-01 03:59:16 +00:00
}
2011-11-02 04:13:33 +00:00
bool Objects::deleteObject (const MWWorld::Ptr& ptr)
2011-11-05 01:48:52 +00:00
{
if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode())
2011-11-05 01:48:52 +00:00
{
Ogre::SceneNode *parent = base->getParentSceneNode();
for (std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *>::const_iterator iter (
mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter)
if (iter->second==parent)
{
clearSceneNode (base);
base->removeAndDestroyAllChildren();
2012-02-06 09:41:13 +00:00
mRenderer.getScene()->destroySceneNode (base);
ptr.getRefData().setBaseNode (0);
return true;
}
return false;
2011-11-05 01:48:52 +00:00
}
return true;
2011-11-05 01:48:52 +00:00
}
2012-02-06 09:41:13 +00:00
void Objects::removeCell(MWWorld::Ptr::CellStore* store)
{
if(mCellSceneNodes.find(store) != mCellSceneNodes.end())
2011-11-05 01:48:52 +00:00
{
Ogre::SceneNode* base = mCellSceneNodes[store];
for (int i=0; i<base->numChildren(); ++i)
clearSceneNode (static_cast<Ogre::SceneNode *> (base->getChild (i)));
2011-11-05 01:57:39 +00:00
base->removeAndDestroyAllChildren();
2011-11-20 01:22:56 +00:00
mCellSceneNodes.erase(store);
2012-02-06 09:41:13 +00:00
mRenderer.getScene()->destroySceneNode(base);
base = 0;
2011-11-05 01:48:52 +00:00
}
2012-02-06 09:41:13 +00:00
if(mStaticGeometry.find(store) != mStaticGeometry.end())
2011-11-05 01:48:52 +00:00
{
2012-02-06 09:41:13 +00:00
Ogre::StaticGeometry* sg = mStaticGeometry[store];
mStaticGeometry.erase(store);
mRenderer.getScene()->destroyStaticGeometry (sg);
2011-11-05 01:48:52 +00:00
sg = 0;
}
if(mStaticGeometrySmall.find(store) != mStaticGeometrySmall.end())
{
Ogre::StaticGeometry* sg = mStaticGeometrySmall[store];
mStaticGeometrySmall.erase(store);
mRenderer.getScene()->destroyStaticGeometry (sg);
sg = 0;
}
2012-03-29 16:04:52 +00:00
if(mBounds.find(store) != mBounds.end())
mBounds.erase(store);
2011-11-05 01:48:52 +00:00
}
2012-02-06 09:41:13 +00:00
void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
{
if(mStaticGeometry.find(&cell) != mStaticGeometry.end())
2011-11-05 18:57:33 +00:00
{
2012-02-06 09:41:13 +00:00
Ogre::StaticGeometry* sg = mStaticGeometry[&cell];
sg->build();
2011-11-05 18:57:33 +00:00
}
if(mStaticGeometrySmall.find(&cell) != mStaticGeometrySmall.end())
{
Ogre::StaticGeometry* sg = mStaticGeometrySmall[&cell];
sg->build();
}
2011-11-05 18:57:33 +00:00
}
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
{
return mBounds[cell];
}
2012-04-02 17:37:24 +00:00
void Objects::enableLights()
{
2012-04-19 18:59:57 +00:00
std::vector<LightInfo>::iterator it = mLights.begin();
2012-04-02 17:37:24 +00:00
while (it != mLights.end())
{
2012-04-19 18:59:57 +00:00
if (mMwRoot->getCreator()->hasLight(it->name))
2012-04-02 17:37:24 +00:00
{
2012-04-19 18:59:57 +00:00
mMwRoot->getCreator()->getLight(it->name)->setVisible(true);
2012-04-02 17:37:24 +00:00
++it;
}
else
it = mLights.erase(it);
}
2011-11-05 18:57:33 +00:00
}
2012-04-02 17:37:24 +00:00
void Objects::disableLights()
{
2012-04-19 18:59:57 +00:00
std::vector<LightInfo>::iterator it = mLights.begin();
2012-04-02 17:37:24 +00:00
while (it != mLights.end())
{
2012-04-19 18:59:57 +00:00
if (mMwRoot->getCreator()->hasLight(it->name))
2012-04-02 17:37:24 +00:00
{
2012-04-19 18:59:57 +00:00
mMwRoot->getCreator()->getLight(it->name)->setVisible(false);
2012-04-02 17:37:24 +00:00
++it;
}
else
it = mLights.erase(it);
}
}
2012-04-19 18:59:57 +00:00
void Objects::update(const float dt)
{
std::vector<LightInfo>::iterator it = mLights.begin();
while (it != mLights.end())
{
if (mMwRoot->getCreator()->hasLight(it->name))
{
Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name);
2012-04-28 18:42:53 +00:00
// Light animation (pulse & flicker)
it->time += dt;
2012-04-28 19:21:40 +00:00
const float phase = std::fmod(static_cast<double> (it->time), (32 * 2 * M_PI)) * 20;
2012-04-28 18:42:53 +00:00
float pulseConstant;
// These formulas are just guesswork, but they work pretty well
if (it->type == LT_Normal)
2012-04-19 18:59:57 +00:00
{
2012-04-28 18:42:53 +00:00
// Less than 1/255 light modifier for a constant light:
pulseConstant = (const float)(1.0 + sin(phase) / 255.0 );
2012-04-19 18:59:57 +00:00
}
2012-04-28 18:42:53 +00:00
else if (it->type == LT_Flicker)
2012-04-19 18:59:57 +00:00
{
2012-04-28 18:42:53 +00:00
// Let's do a 50% -> 100% sine wave pulse over 1 second:
// This is 75% +/- 25%
pulseConstant = (const float)(0.75 + sin(phase) * 0.25);
// Then add a 25% flicker variation:
it->resetTime -= dt;
if (it->resetTime < 0)
{
it->flickerVariation = (rand() % 1000) / 1000 * 0.25;
it->resetTime = 0.5;
}
if (it->resetTime > 0.25)
{
pulseConstant = (pulseConstant+it->flickerVariation) * (1-it->resetTime * 2.0f) + pulseConstant * it->resetTime * 2.0f;
}
else
{
pulseConstant = (pulseConstant+it->flickerVariation) * (it->resetTime * 2.0f) + pulseConstant * (1-it->resetTime * 2.0f);
}
2012-04-19 18:59:57 +00:00
}
2012-04-28 18:42:53 +00:00
else if (it->type == LT_FlickerSlow)
{
// Let's do a 50% -> 100% sine wave pulse over 1 second:
// This is 75% +/- 25%
pulseConstant = (const float)(0.75 + sin(phase / 4.0) * 0.25);
// Then add a 25% flicker variation:
it->resetTime -= dt;
if (it->resetTime < 0)
{
it->flickerVariation = (rand() % 1000) / 1000 * 0.25;
it->resetTime = 0.5;
}
if (it->resetTime > 0.5)
{
pulseConstant = (pulseConstant+it->flickerVariation) * (1-it->resetTime) + pulseConstant * it->resetTime;
}
else
{
pulseConstant = (pulseConstant+it->flickerVariation) * (it->resetTime) + pulseConstant * (1-it->resetTime);
}
}
else if (it->type == LT_Pulse)
2012-04-19 18:59:57 +00:00
{
2012-04-28 18:42:53 +00:00
// Let's do a 75% -> 125% sine wave pulse over 1 second:
// This is 100% +/- 25%
pulseConstant = (const float)(1.0 + sin(phase) * 0.25);
2012-04-19 18:59:57 +00:00
}
2012-04-28 18:42:53 +00:00
else if (it->type == LT_PulseSlow)
{
// Let's do a 75% -> 125% sine wave pulse over 1 second:
// This is 100% +/- 25%
pulseConstant = (const float)(1.0 + sin(phase / 4.0) * 0.25);
}
else
assert(0 && "Invalid light type");
light->setDiffuseColour( it->colour * pulseConstant );
2012-04-19 18:59:57 +00:00
++it;
}
else
it = mLights.erase(it);
}
}