diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a29beb35c8..15df907b2f 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -17,6 +17,8 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" +#include + #include "../mwmechanics/character.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwworld/class.hpp" @@ -24,6 +26,7 @@ #include "renderconst.hpp" + namespace MWRender { @@ -166,8 +169,37 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) } } +struct AddGlow +{ + Ogre::Vector3* mColor; + AddGlow(Ogre::Vector3* col) : mColor(col) {} + + // TODO: integrate this with material controllers? + void operator()(Ogre::Entity* entity) const + { + unsigned int numsubs = entity->getNumSubEntities(); + for(unsigned int i = 0;i < numsubs;++i) + { + unsigned int numsubs = entity->getNumSubEntities(); + for(unsigned int i = 0;i < numsubs;++i) + { + Ogre::SubEntity* subEnt = entity->getSubEntity(i); + std::string newName = subEnt->getMaterialName() + "@fx"; + if (sh::Factory::getInstance().searchInstance(newName) == NULL) + { + sh::MaterialInstance* instance = + sh::Factory::getInstance().createMaterialInstance(newName, subEnt->getMaterialName()); + instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true))); + instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z))); + } + subEnt->setMaterialName(newName); + } + } + } +}; -class VisQueueSet { +class VisQueueSet +{ Ogre::uint32 mVisFlags; Ogre::uint8 mSolidQueue, mTransQueue; Ogre::Real mDist; @@ -201,12 +233,16 @@ public: } }; -void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist) +void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist, bool enchantedGlow, Ogre::Vector3* glowColor) { std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(), VisQueueSet(visflags, solidqueue, transqueue, dist)); std::for_each(objlist.mParticles.begin(), objlist.mParticles.end(), VisQueueSet(visflags, solidqueue, transqueue, dist)); + + if (enchantedGlow) + std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(), + AddGlow(glowColor)); } @@ -1116,6 +1152,23 @@ void Animation::updateEffects(float duration) } } +// TODO: Should not be here +Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item) +{ + Ogre::Vector3 result(1,1,1); + std::string enchantmentName = item.getClass().getEnchantment(item); + if (enchantmentName.empty()) + return result; + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(enchantmentName); + assert (enchantment->mEffects.mList.size()); + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( + enchantment->mEffects.mList.front().mEffectID); + result.x = magicEffect->mData.mRed / 255.f; + result.y = magicEffect->mData.mGreen / 255.f; + result.z = magicEffect->mData.mBlue / 255.f; + return result; +} + ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model) : Animation(ptr, ptr.getRefData().getBaseNode()) @@ -1132,9 +1185,10 @@ ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &mod small = false; float dist = small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0.0f; + Ogre::Vector3 col = getEnchantmentColor(ptr); setRenderProperties(mObjectRoot, (mPtr.getTypeName() == typeid(ESM::Static).name()) ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc, - RQG_Main, RQG_Alpha, dist); + RQG_Main, RQG_Alpha, dist, !ptr.getClass().getEnchantment(ptr).empty(), &col); } void ObjectAnimation::addLight(const ESM::Light *light) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 7d1fd010ca..38e7742ef1 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -191,10 +191,15 @@ protected: static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); - static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist=0.0f); + static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, + Ogre::uint8 transqueue, Ogre::Real dist=0.0f, + bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); void clearAnimSources(); + // TODO: Should not be here + Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item); + public: Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); virtual ~Animation(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index bc7e48af1b..c714289416 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -208,17 +208,19 @@ void NpcAnimation::updateParts() removeIndividualPart(ESM::PRT_Hair); int prio = 1; + bool enchantedGlow = !store->getClass().getEnchantment(*store).empty(); + Ogre::Vector3 glowColor = getEnchantmentColor(*store); if(store->getTypeName() == typeid(ESM::Clothing).name()) { prio = ((slotlist[i].mBasePriority+1)<<1) + 0; const ESM::Clothing *clothes = store->get()->mBase; - addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts); + addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts, enchantedGlow, &glowColor); } else if(store->getTypeName() == typeid(ESM::Armor).name()) { prio = ((slotlist[i].mBasePriority+1)<<1) + 1; const ESM::Armor *armor = store->get()->mBase; - addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts); + addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts, enchantedGlow, &glowColor); } if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe) @@ -389,10 +391,11 @@ public: } }; -NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) +NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename, bool enchantedGlow, Ogre::Vector3* glowColor) { NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); - setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha); + setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, + enchantedGlow, glowColor); std::for_each(objects.mEntities.begin(), objects.mEntities.end(), SetObjectGroup(group)); std::for_each(objects.mParticles.begin(), objects.mParticles.end(), SetObjectGroup(group)); @@ -475,7 +478,7 @@ void NpcAnimation::removePartGroup(int group) } } -bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh) +bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh, bool enchantedGlow, Ogre::Vector3* glowColor) { if(priority <= mPartPriorities[type]) return false; @@ -484,7 +487,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g mPartslots[type] = group; mPartPriorities[type] = priority; - mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type)); + mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type), enchantedGlow, glowColor); if(mObjectParts[type].mSkelBase) { Ogre::SkeletonInstance *skel = mObjectParts[type].mSkelBase->getSkeleton(); @@ -521,7 +524,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g return true; } -void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts) +void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts, bool enchantedGlow, Ogre::Vector3* glowColor) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); @@ -559,7 +562,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectormPart, group, priority, "meshes\\"+bodypart->mModel); + addOrReplaceIndividualPart((ESM::PartReferenceType)part->mPart, group, priority, "meshes\\"+bodypart->mModel, enchantedGlow, glowColor); else reserveIndividualPart((ESM::PartReferenceType)part->mPart, group, priority); } @@ -574,8 +577,10 @@ void NpcAnimation::showWeapons(bool showWeapon) MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(weapon != inv.end()) // special case for weapons { + Ogre::Vector3 glowColor = getEnchantmentColor(*weapon); std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon); - addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh); + addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, + mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); } } else diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index becd014370..7116b6ef1c 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -51,14 +51,17 @@ private: void updateNpcBase(); - NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename); + NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename, + bool enchantedGlow, Ogre::Vector3* glowColor=NULL); void removeIndividualPart(ESM::PartReferenceType type); void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority); - bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh); + bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh, + bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); void removePartGroup(int group); - void addPartGroup(int group, int priority, const std::vector &parts); + void addPartGroup(int group, int priority, const std::vector &parts, + bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); public: /** diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 165f6551d3..022752fae9 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -33,6 +33,8 @@ class Objects{ void insertBegin(const MWWorld::Ptr& ptr); + + public: Objects(OEngine::Render::OgreRenderer &renderer) : mRenderer(renderer) diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index cc9cc180ee..77056b9ec6 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -49,8 +49,9 @@ struct MagicEffect int mSchool; // SpellSchool, see defs.hpp float mBaseCost; int mFlags; - // Properties of the fired magic 'ball' I think - int mRed, mBlue, mGreen; + // Glow color for enchanted items with this effect + int mRed, mGreen, mBlue; + // Properties of the fired magic 'ball' float mSpeed, mSize, mSizeCap; }; // 36 bytes diff --git a/extern/shiny/Main/Factory.hpp b/extern/shiny/Main/Factory.hpp index 97984609ea..7d52266b55 100644 --- a/extern/shiny/Main/Factory.hpp +++ b/extern/shiny/Main/Factory.hpp @@ -259,8 +259,9 @@ namespace sh Platform* mPlatform; MaterialInstance* findInstance (const std::string& name); + public: MaterialInstance* searchInstance (const std::string& name); - + private: /// @return was anything removed? bool removeCache (const std::string& pattern); diff --git a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp index 54dda3523f..f215f4ab7d 100644 --- a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp +++ b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp @@ -1,5 +1,8 @@ #include "OgreTextureUnitState.hpp" +#include +#include + #include "OgrePass.hpp" #include "OgrePlatform.hpp" #include "OgreMaterialSerializer.hpp" @@ -28,6 +31,32 @@ namespace sh setTextureName (retrieveValue(value, context).get()); return true; } + else if (name == "anim_texture2") + { + std::string val = retrieveValue(value, context).get(); + std::vector tokens; + boost::split(tokens, val, boost::is_any_of(" ")); + assert(tokens.size() == 3); + std::string texture = tokens[0]; + int frames = boost::lexical_cast(tokens[1]); + float duration = boost::lexical_cast(tokens[2]); + + std::vector frameTextures; + for (int i=0; isetAnimatedTextureName(&frameTextures[0], frames, duration); + return true; + } else if (name == "create_in_ffp") return true; // handled elsewhere diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 8f8734d629..10fb72740f 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -19,6 +19,8 @@ material openmw_objects_base alpha_rejection default transparent_sorting default polygon_mode default + env_map false + env_map_color 1 1 1 pass { @@ -33,6 +35,8 @@ material openmw_objects_base detailMapUVSet $detailMapUVSet emissiveMap $emissiveMap detailMap $detailMap + env_map $env_map + env_map_color $env_map_color } diffuse $diffuse @@ -75,6 +79,14 @@ material openmw_objects_base direct_texture $detailMap tex_coord_set $detailMapUVSet } + + texture_unit envMap + { + create_in_ffp $env_map + env_map spherical + anim_texture2 textures\magicitem\caust.dds 32 2 + colour_op add + } texture_unit shadowMap0 { diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 36f92bfd91..9fc9fceaf0 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -30,6 +30,8 @@ #define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix) +#define ENV_MAP @shPropertyBool(env_map) + #ifdef SH_VERTEX_SHADER // ------------------------------------- VERTEX --------------------------------------- @@ -61,7 +63,7 @@ shOutput(float3, tangentPassthrough) #endif -#if !VERTEX_LIGHTING +#if !VERTEX_LIGHTING || ENV_MAP shOutput(float3, normalPassthrough) #endif @@ -79,12 +81,15 @@ shOutput(float4, colourPassthrough) #endif +#if ENV_MAP || VERTEX_LIGHTING + shUniform(float4x4, worldView) @shAutoConstant(worldView, worldview_matrix) +#endif + #if VERTEX_LIGHTING shUniform(float4, lightPosition[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightPosition, light_position_view_space_array, @shGlobalSettingString(num_lights)) shUniform(float4, lightDiffuse[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightDiffuse, light_diffuse_colour_array, @shGlobalSettingString(num_lights)) shUniform(float4, lightAttenuation[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightAttenuation, light_attenuation_array, @shGlobalSettingString(num_lights)) shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - shUniform(float4x4, worldView) @shAutoConstant(worldView, worldview_matrix) #if VERTEXCOLOR_MODE != 2 shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) #endif @@ -125,10 +130,23 @@ UV.zw = uv1; #endif +#if ENV_MAP || VERTEX_LIGHTING + float3 viewNormal = normalize(shMatrixMult(worldView, float4(normal.xyz, 0)).xyz); +#endif + +#if ENV_MAP + float3 viewVec = normalize( shMatrixMult(worldView, shInputPosition).xyz); + + float3 r = reflect( viewVec, viewNormal ); + float m = 2.0 * sqrt( r.x*r.x + r.y*r.y + (r.z+1.0)*(r.z+1.0) ); + UV.z = r.x/m + 0.5; + UV.w = r.y/m + 0.5; +#endif + #if NORMAL_MAP tangentPassthrough = tangent.xyz; #endif -#if !VERTEX_LIGHTING +#if !VERTEX_LIGHTING || ENV_MAP normalPassthrough = normal.xyz; #endif #if VERTEXCOLOR_MODE != 0 && !VERTEX_LIGHTING @@ -173,7 +191,6 @@ #if VERTEX_LIGHTING float3 viewPos = shMatrixMult(worldView, shInputPosition).xyz; - float3 viewNormal = normalize(shMatrixMult(worldView, float4(normal.xyz, 0)).xyz); float3 lightDir; float d; @@ -242,12 +259,18 @@ shSampler2D(detailMap) #endif +#if ENV_MAP + shSampler2D(envMap) + shUniform(float3, env_map_color) @shUniformProperty3f(env_map_color, env_map_color) + shUniform(float3, cameraPosObjSpace) @shAutoConstant(cameraPosObjSpace, camera_position_object_space) +#endif + shInput(float4, UV) #if NORMAL_MAP shInput(float3, tangentPassthrough) #endif -#if !VERTEX_LIGHTING +#if !VERTEX_LIGHTING || ENV_MAP shInput(float3, normalPassthrough) #endif @@ -327,8 +350,11 @@ #endif #endif -#if NORMAL_MAP +#if !VERTEX_LIGHTING || ENV_MAP float3 normal = normalPassthrough; +#endif + +#if NORMAL_MAP float3 binormal = cross(tangentPassthrough.xyz, normal.xyz); float3x3 tbn = float3x3(tangentPassthrough.xyz, binormal, normal.xyz); @@ -420,6 +446,23 @@ shOutputColour(0) *= lightResult; #endif +#if EMISSIVE_MAP + #if @shPropertyString(emissiveMapUVSet) + shOutputColour(0).xyz += shSample(emissiveMap, UV.zw).xyz; + #else + shOutputColour(0).xyz += shSample(emissiveMap, UV.xy).xyz; + #endif +#endif + +#if ENV_MAP + // Everything looks better with fresnel + float3 eyeDir = normalize(cameraPosObjSpace.xyz - objSpacePositionPassthrough.xyz); + float facing = 1.0 - max(abs(dot(-eyeDir, normal)), 0); + float envFactor = shSaturate(0.25 + 0.75 * pow(facing, 1)); + + shOutputColour(0).xyz += shSample(envMap, UV.zw).xyz * envFactor * env_map_color; +#endif + #if FOG float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); @@ -430,14 +473,6 @@ shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); #endif -#endif - -#if EMISSIVE_MAP - #if @shPropertyString(emissiveMapUVSet) - shOutputColour(0).xyz += shSample(emissiveMap, UV.zw).xyz; - #else - shOutputColour(0).xyz += shSample(emissiveMap, UV.xy).xyz; - #endif #endif // prevent negative colour output (for example with negative lights)