diff --git a/Engine/source/T3D/tsStatic.cpp.orig b/Engine/source/T3D/tsStatic.cpp.orig new file mode 100644 index 000000000..11848499e --- /dev/null +++ b/Engine/source/T3D/tsStatic.cpp.orig @@ -0,0 +1,1330 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/tsStatic.h" + +#include "core/resourceManager.h" +#include "core/stream/bitStream.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "scene/sceneObjectLightingPlugin.h" +#include "lighting/lightManager.h" +#include "math/mathIO.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsMaterialList.h" +#include "console/consoleTypes.h" +#include "T3D/shapeBase.h" +#include "sim/netConnection.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTransformSaver.h" +#include "ts/tsRenderState.h" +#include "collision/boxConvex.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "materials/materialDefinition.h" +#include "materials/materialManager.h" +#include "materials/matInstance.h" +#include "materials/materialFeatureData.h" +#include "materials/materialFeatureTypes.h" +#include "console/engineAPI.h" +#include "T3D/accumulationVolume.h" + +using namespace Torque; + +extern bool gEditingMission; + +IMPLEMENT_CO_NETOBJECT_V1(TSStatic); + +ConsoleDocClass( TSStatic, + "@brief A static object derived from a 3D model file and placed within the game world.\n\n" + + "TSStatic is the most basic 3D shape in Torque. Unlike StaticShape it doesn't make use of " + "a datablock. It derrives directly from SceneObject. This makes TSStatic extremely light " + "weight, which is why the Tools use this class when you want to drop in a DTS or DAE object.\n\n" + + "While a TSStatic doesn't provide any motion -- it stays were you initally put it -- it does allow for " + "a single ambient animation sequence to play when the object is first added to the scene.\n\n" + + "@tsexample\n" + "new TSStatic(Team1Base) {\n" + " shapeName = \"art/shapes/desertStructures/station01.dts\";\n" + " playAmbient = \"1\";\n" + " receiveSunLight = \"1\";\n" + " receiveLMLighting = \"1\";\n" + " useCustomAmbientLighting = \"0\";\n" + " customAmbientLighting = \"0 0 0 1\";\n" + " collisionType = \"Visible Mesh\";\n" + " decalType = \"Collision Mesh\";\n" + " allowPlayerStep = \"1\";\n" + " renderNormals = \"0\";\n" + " forceDetail = \"-1\";\n" + " position = \"315.18 -180.418 244.313\";\n" + " rotation = \"0 0 1 195.952\";\n" + " scale = \"1 1 1\";\n" + " isRenderEnabled = \"true\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n" + "@endtsexample\n" + + "@ingroup gameObjects\n" +); + +TSStatic::TSStatic() +: + cubeDescId( 0 ), + reflectorDesc( NULL ) +{ + mNetFlags.set(Ghostable | ScopeAlways); + + mTypeMask |= StaticObjectType | StaticShapeObjectType; + + mShapeName = ""; + mShapeInstance = NULL; + + mPlayAmbient = true; + mAmbientThread = NULL; + + mAllowPlayerStep = false; + + mConvexList = new Convex; + + mRenderNormalScalar = 0; + mForceDetail = -1; + + mMeshCulling = false; + mUseOriginSort = false; + + mUseAlphaFade = false; + mAlphaFadeStart = 100.0f; + mAlphaFadeEnd = 150.0f; + mInvertAlphaFade = false; + mAlphaFade = 1.0f; + mPhysicsRep = NULL; + + mCollisionType = CollisionMesh; + mDecalType = CollisionMesh; +} + +TSStatic::~TSStatic() +{ + delete mConvexList; + mConvexList = NULL; +} + +ImplementEnumType( TSMeshType, + "Type of mesh data available in a shape.\n" + "@ingroup gameObjects" ) + { TSStatic::None, "None", "No mesh data." }, + { TSStatic::Bounds, "Bounds", "Bounding box of the shape." }, + { TSStatic::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." }, + { TSStatic::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." }, +EndImplementEnumType; + + +void TSStatic::initPersistFields() +{ + addGroup("Media"); + + addField("shapeName", TypeShapeFilename, Offset( mShapeName, TSStatic ), + "%Path and filename of the model file (.DTS, .DAE) to use for this TSStatic." ); + + addProtectedField( "skin", TypeRealString, Offset( mAppliedSkinName, TSStatic ), &_setFieldSkin, &_getFieldSkin, + "@brief The skin applied to the shape.\n\n" + + "'Skinning' the shape effectively renames the material targets, allowing " + "different materials to be used on different instances of the same model.\n\n" + + "Any material targets that start with the old skin name have that part " + "of the name replaced with the new skin name. The initial old skin name is " + "\"base\". For example, if a new skin of \"blue\" was applied to a model " + "that had material targets base_body and face, the new targets " + "would be blue_body and face. Note that face was not " + "renamed since it did not start with the old skin name of \"base\".\n\n" + + "To support models that do not use the default \"base\" naming convention, " + "you can also specify the part of the name to replace in the skin field " + "itself. For example, if a model had a material target called shapemat, " + "we could apply a new skin \"shape=blue\", and the material target would be " + "renamed to bluemat (note \"shape\" has been replaced with \"blue\").\n\n" + + "Multiple skin updates can also be applied at the same time by separating " + "them with a semicolon. For example: \"base=blue;face=happy_face\".\n\n" + + "Material targets are only renamed if an existing Material maps to that " + "name, or if there is a diffuse texture in the model folder with the same " + "name as the new target.\n\n" ); + + endGroup("Media"); + + addGroup("Rendering"); + + addField( "playAmbient", TypeBool, Offset( mPlayAmbient, TSStatic ), + "Enables automatic playing of the animation sequence named \"ambient\" (if it exists) when the TSStatic is loaded."); + addField( "meshCulling", TypeBool, Offset( mMeshCulling, TSStatic ), + "Enables detailed culling of meshes within the TSStatic. Should only be used " + "with large complex shapes like buildings which contain many submeshes." ); + addField( "originSort", TypeBool, Offset( mUseOriginSort, TSStatic ), + "Enables translucent sorting of the TSStatic by its origin instead of the bounds." ); + + endGroup("Rendering"); + + addGroup( "Reflection" ); + addField( "cubeReflectorDesc", TypeRealString, Offset( cubeDescName, TSStatic ), + "References a ReflectorDesc datablock that defines performance and quality properties for dynamic reflections.\n"); + endGroup( "Reflection" ); + + addGroup("Collision"); + + addField( "collisionType", TypeTSMeshType, Offset( mCollisionType, TSStatic ), + "The type of mesh data to use for collision queries." ); + addField( "decalType", TypeTSMeshType, Offset( mDecalType, TSStatic ), + "The type of mesh data used to clip decal polygons against." ); + addField( "allowPlayerStep", TypeBool, Offset( mAllowPlayerStep, TSStatic ), + "@brief Allow a Player to walk up sloping polygons in the TSStatic (based on the collisionType).\n\n" + "When set to false, the slightest bump will stop the player from walking on top of the object.\n"); + + endGroup("Collision"); + + addGroup( "AlphaFade" ); + addField( "alphaFadeEnable", TypeBool, Offset(mUseAlphaFade, TSStatic), "Turn on/off Alpha Fade" ); + addField( "alphaFadeStart", TypeF32, Offset(mAlphaFadeStart, TSStatic), "Distance of start Alpha Fade" ); + addField( "alphaFadeEnd", TypeF32, Offset(mAlphaFadeEnd, TSStatic), "Distance of end Alpha Fade" ); + addField( "alphaFadeInverse", TypeBool, Offset(mInvertAlphaFade, TSStatic), "Invert Alpha Fade's Start & End Distance" ); + endGroup( "AlphaFade" ); + + addGroup("Debug"); + + addField( "renderNormals", TypeF32, Offset( mRenderNormalScalar, TSStatic ), + "Debug rendering mode shows the normals for each point in the TSStatic's mesh." ); + addField( "forceDetail", TypeS32, Offset( mForceDetail, TSStatic ), + "Forces rendering to a particular detail level." ); + + endGroup("Debug"); + + Parent::initPersistFields(); +} + +bool TSStatic::_setFieldSkin( void *object, const char *index, const char *data ) +{ + TSStatic *ts = static_cast( object ); + if ( ts ) + ts->setSkinName( data ); + return false; +} + +const char *TSStatic::_getFieldSkin( void *object, const char *data ) +{ + TSStatic *ts = static_cast( object ); + return ts ? ts->mSkinNameHandle.getString() : ""; +} + +void TSStatic::inspectPostApply() +{ + // Apply any transformations set in the editor + Parent::inspectPostApply(); + + if(isServerObject()) + { + setMaskBits(AdvancedStaticOptionsMask); + prepCollision(); + } + + _updateShouldTick(); +} + +bool TSStatic::onAdd() +{ + PROFILE_SCOPE(TSStatic_onAdd); + + if ( isServerObject() ) + { + // Handle the old "usePolysoup" field + SimFieldDictionary* fieldDict = getFieldDictionary(); + + if ( fieldDict ) + { + StringTableEntry slotName = StringTable->insert( "usePolysoup" ); + + SimFieldDictionary::Entry * entry = fieldDict->findDynamicField( slotName ); + + if ( entry ) + { + // Was "usePolysoup" set? + bool usePolysoup = dAtob( entry->value ); + + // "usePolysoup" maps to the new VisibleMesh type + if ( usePolysoup ) + mCollisionType = VisibleMesh; + + // Remove the field in favor on the new "collisionType" field + fieldDict->setFieldValue( slotName, "" ); + } + } + } + + if ( !Parent::onAdd() ) + return false; + + // Setup the shape. + if ( !_createShape() ) + { + Con::errorf( "TSStatic::onAdd() - Shape creation failed!" ); + return false; + } + + setRenderTransform(mObjToWorld); + + // Register for the resource change signal. + ResourceManager::get().getChangedSignal().notify( this, &TSStatic::_onResourceChanged ); + + addToScene(); + + if ( isClientObject() ) + { + mCubeReflector.unregisterReflector(); + + if ( reflectorDesc ) + mCubeReflector.registerReflector( this, reflectorDesc ); + } + + _updateShouldTick(); + + // Accumulation and environment mapping + if (isClientObject() && mShapeInstance) + { + AccumulationVolume::addObject(this); + } + + return true; +} + +bool TSStatic::_createShape() +{ + // Cleanup before we create. + mCollisionDetails.clear(); + mLOSDetails.clear(); + SAFE_DELETE( mPhysicsRep ); + SAFE_DELETE( mShapeInstance ); + mAmbientThread = NULL; + mShape = NULL; + + if (!mShapeName || mShapeName[0] == '\0') + { + Con::errorf( "TSStatic::_createShape() - No shape name!" ); + return false; + } + + mShapeHash = _StringTable::hashString(mShapeName); + + mShape = ResourceManager::get().load(mShapeName); + if ( bool(mShape) == false ) + { + Con::errorf( "TSStatic::_createShape() - Unable to load shape: %s", mShapeName ); + return false; + } + + if ( isClientObject() && + !mShape->preloadMaterialList(mShape.getPath()) && + NetConnection::filesWereDownloaded() ) + return false; + + mObjBox = mShape->bounds; + resetWorldBox(); + + mShapeInstance = new TSShapeInstance( mShape, isClientObject() ); + + if( isGhost() ) + { + // Reapply the current skin + mAppliedSkinName = ""; + reSkin(); + } + + prepCollision(); + + // Find the "ambient" animation if it exists + S32 ambientSeq = mShape->findSequence("ambient"); + + if ( ambientSeq > -1 && !mAmbientThread ) + mAmbientThread = mShapeInstance->addThread(); + + if ( mAmbientThread ) + mShapeInstance->setSequence( mAmbientThread, ambientSeq, 0); + + // Resolve CubeReflectorDesc. + if ( cubeDescName.isNotEmpty() ) + { + Sim::findObject( cubeDescName, reflectorDesc ); + } + else if( cubeDescId > 0 ) + { + Sim::findObject( cubeDescId, reflectorDesc ); + } + + return true; +} + +void TSStatic::prepCollision() +{ + // Let the client know that the collision was updated + setMaskBits( UpdateCollisionMask ); + + // Allow the ShapeInstance to prep its collision if it hasn't already + if ( mShapeInstance ) + mShapeInstance->prepCollision(); + + // Cleanup any old collision data + mCollisionDetails.clear(); + mLOSDetails.clear(); + mConvexList->nukeList(); + + if ( mCollisionType == CollisionMesh || mCollisionType == VisibleMesh ) + mShape->findColDetails( mCollisionType == VisibleMesh, &mCollisionDetails, &mLOSDetails ); + + _updatePhysics(); +} + +void TSStatic::_updatePhysics() +{ + SAFE_DELETE( mPhysicsRep ); + + if ( !PHYSICSMGR || mCollisionType == None ) + return; + + PhysicsCollision *colShape = NULL; + if ( mCollisionType == Bounds ) + { + MatrixF offset( true ); + offset.setPosition( mShape->center ); + colShape = PHYSICSMGR->createCollision(); + colShape->addBox( getObjBox().getExtents() * 0.5f * mObjScale, offset ); + } + else + colShape = mShape->buildColShape( mCollisionType == VisibleMesh, getScale() ); + + if ( colShape ) + { + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, 0, this, world ); + mPhysicsRep->setTransform( getTransform() ); + } +} + +void TSStatic::onRemove() +{ + SAFE_DELETE( mPhysicsRep ); + + // Accumulation + if ( isClientObject() && mShapeInstance ) + { + if ( mShapeInstance->hasAccumulation() ) + AccumulationVolume::removeObject(this); + } + + mConvexList->nukeList(); + + removeFromScene(); + + // Remove the resource change signal. + ResourceManager::get().getChangedSignal().remove( this, &TSStatic::_onResourceChanged ); + + delete mShapeInstance; + mShapeInstance = NULL; + + mAmbientThread = NULL; + if ( isClientObject() ) + mCubeReflector.unregisterReflector(); + + Parent::onRemove(); +} + +void TSStatic::_onResourceChanged( const Torque::Path &path ) +{ + if ( path != Path( mShapeName ) ) + return; + + _createShape(); + _updateShouldTick(); +} + +void TSStatic::setSkinName( const char *name ) +{ + if ( !isGhost() ) + { + if ( name[0] != '\0' ) + { + // Use tags for better network performance + // Should be a tag, but we'll convert to one if it isn't. + if ( name[0] == StringTagPrefixByte ) + mSkinNameHandle = NetStringHandle( U32(dAtoi(name + 1)) ); + else + mSkinNameHandle = NetStringHandle( name ); + } + else + mSkinNameHandle = NetStringHandle(); + + setMaskBits( SkinMask ); + } +} + +void TSStatic::reSkin() +{ + if ( isGhost() && mShapeInstance && mSkinNameHandle.isValidString() ) + { + Vector skins; + String(mSkinNameHandle.getString()).split( ";", skins ); + + for (S32 i = 0; i < skins.size(); i++) + { + String oldSkin( mAppliedSkinName.c_str() ); + String newSkin( skins[i] ); + + // Check if the skin handle contains an explicit "old" base string. This + // allows all models to support skinning, even if they don't follow the + // "base_xxx" material naming convention. + S32 split = newSkin.find( '=' ); // "old=new" format skin? + if ( split != String::NPos ) + { + oldSkin = newSkin.substr( 0, split ); + newSkin = newSkin.erase( 0, split+1 ); + } + + mShapeInstance->reSkin( newSkin, oldSkin ); + mAppliedSkinName = newSkin; + } + } +} + +void TSStatic::processTick( const Move *move ) +{ + if ( isServerObject() && mPlayAmbient && mAmbientThread ) + mShapeInstance->advanceTime( TickSec, mAmbientThread ); + + if ( isMounted() ) + { + MatrixF mat( true ); + mMount.object->getMountTransform(mMount.node, mMount.xfm, &mat ); + setTransform( mat ); + } +} + +void TSStatic::interpolateTick( F32 delta ) +{ +} + +void TSStatic::advanceTime( F32 dt ) +{ + if ( mPlayAmbient && mAmbientThread ) + mShapeInstance->advanceTime( dt, mAmbientThread ); + + if ( isMounted() ) + { + MatrixF mat( true ); + mMount.object->getRenderMountTransform( dt, mMount.node, mMount.xfm, &mat ); + setRenderTransform( mat ); + } +} + +void TSStatic::_updateShouldTick() +{ + bool shouldTick = (mPlayAmbient && mAmbientThread) || isMounted(); + + if ( isTicking() != shouldTick ) + setProcessTick( shouldTick ); +} + +void TSStatic::prepRenderImage( SceneRenderState* state ) +{ + if( !mShapeInstance ) + return; + + Point3F cameraOffset; + getRenderTransform().getColumn(3,&cameraOffset); + cameraOffset -= state->getDiffuseCameraPosition(); + F32 dist = cameraOffset.len(); + if (dist < 0.01f) + dist = 0.01f; + + if (mUseAlphaFade) + { + mAlphaFade = 1.0f; + if ((mAlphaFadeStart < mAlphaFadeEnd) && mAlphaFadeStart > 0.1f) + { + if (mInvertAlphaFade) + { + if (dist <= mAlphaFadeStart) + { + return; + } + if (dist < mAlphaFadeEnd) + { + mAlphaFade = ((dist - mAlphaFadeStart) / (mAlphaFadeEnd - mAlphaFadeStart)); + } + } + else + { + if (dist >= mAlphaFadeEnd) + { + return; + } + if (dist > mAlphaFadeStart) + { + mAlphaFade -= ((dist - mAlphaFadeStart) / (mAlphaFadeEnd - mAlphaFadeStart)); + } + } + } + } + + F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); + + // If we're currently rendering our own reflection we + // don't want to render ourselves into it. + if ( mCubeReflector.isRendering() ) + return; + + + if ( mForceDetail == -1 ) + mShapeInstance->setDetailFromDistance( state, dist * invScale ); + else + mShapeInstance->setCurrentDetail( mForceDetail ); + + if ( mShapeInstance->getCurrentDetail() < 0 ) + return; + + GFXTransformSaver saver; + + // Set up our TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + rdata.setFadeOverride( 1.0f ); + rdata.setOriginSort( mUseOriginSort ); + + if ( mCubeReflector.isEnabled() ) + rdata.setCubemap( mCubeReflector.getCubemap() ); + + // Acculumation + rdata.setAccuTex(mAccuTex); + + // If we have submesh culling enabled then prepare + // the object space frustum to pass to the shape. + Frustum culler; + if ( mMeshCulling ) + { + culler = state->getCullingFrustum(); + MatrixF xfm( true ); + xfm.scale( Point3F::One / getScale() ); + xfm.mul( getRenderWorldTransform() ); + xfm.mul( culler.getTransform() ); + culler.setTransform( xfm ); + rdata.setCuller( &culler ); + } + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->setWorldMatrix( mat ); + + if ( state->isDiffusePass() && mCubeReflector.isEnabled() && mCubeReflector.getOcclusionQuery() ) + { + RenderPassManager *pass = state->getRenderPass(); + OccluderRenderInst *ri = pass->allocInst(); + + ri->type = RenderPassManager::RIT_Occluder; + ri->query = mCubeReflector.getOcclusionQuery(); + mObjToWorld.mulP( mObjBox.getCenter(), &ri->position ); + ri->scale.set( mObjBox.getExtents() ); + ri->orientation = pass->allocUniqueXform( mObjToWorld ); + ri->isSphere = false; + state->getRenderPass()->addInst( ri ); + } + + mShapeInstance->animate(); + if(mShapeInstance) + { + if (mUseAlphaFade) + { + mShapeInstance->setAlphaAlways(mAlphaFade); + S32 s = mShapeInstance->mMeshObjects.size(); + + for(S32 x = 0; x < s; x++) + { + mShapeInstance->mMeshObjects[x].visible = mAlphaFade; + } + } + } + mShapeInstance->render( rdata ); + + if ( mRenderNormalScalar > 0 ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &TSStatic::_renderNormals ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } +} + +void TSStatic::_renderNormals( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + PROFILE_SCOPE( TSStatic_RenderNormals ); + + GFXTransformSaver saver; + + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->multWorld( mat ); + + S32 dl = mShapeInstance->getCurrentDetail(); + mShapeInstance->renderDebugNormals( mRenderNormalScalar, dl ); +} + +void TSStatic::onScaleChanged() +{ + Parent::onScaleChanged(); + + if ( mPhysicsRep ) + { + // If the editor is enabled delay the scale operation + // by a few milliseconds so that we're not rebuilding + // during an active scale drag operation. + if ( gEditingMission ) + mPhysicsRep->queueCallback( 500, Delegate( this, &TSStatic::_updatePhysics ) ); + else + _updatePhysics(); + } + + setMaskBits( ScaleMask ); +} + +void TSStatic::setTransform(const MatrixF & mat) +{ + Parent::setTransform(mat); + if ( !isMounted() ) + setMaskBits( TransformMask ); + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( mat ); + + // Accumulation + if ( isClientObject() && mShapeInstance ) + { + if ( mShapeInstance->hasAccumulation() ) + AccumulationVolume::updateObject(this); + } + + // Since this is a static it's render transform changes 1 + // to 1 with it's collision transform... no interpolation. + setRenderTransform(mat); +} + +U32 TSStatic::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if ( stream->writeFlag( mask & TransformMask ) ) + mathWrite( *stream, getTransform() ); + + if ( stream->writeFlag( mask & ScaleMask ) ) + { + // Only write one bit if the scale is one. + if ( stream->writeFlag( mObjScale != Point3F::One ) ) + mathWrite( *stream, mObjScale ); + } + + if ( stream->writeFlag( mask & UpdateCollisionMask ) ) + stream->write( (U32)mCollisionType ); + + if ( stream->writeFlag( mask & SkinMask ) ) + con->packNetStringHandleU( stream, mSkinNameHandle ); + + if (stream->writeFlag(mask & AdvancedStaticOptionsMask)) + { + stream->writeString(mShapeName); + stream->write((U32)mDecalType); + + stream->writeFlag(mAllowPlayerStep); + stream->writeFlag(mMeshCulling); + stream->writeFlag(mUseOriginSort); + + stream->write(mRenderNormalScalar); + + stream->write(mForceDetail); + + stream->writeFlag(mPlayAmbient); + } + + if ( stream->writeFlag(mUseAlphaFade) ) + { + stream->write(mAlphaFadeStart); + stream->write(mAlphaFadeEnd); + stream->write(mInvertAlphaFade); + } + + if ( mLightPlugin ) + retMask |= mLightPlugin->packUpdate(this, AdvancedStaticOptionsMask, con, mask, stream); + + if( stream->writeFlag( reflectorDesc != NULL ) ) + { + stream->writeRangedU32( reflectorDesc->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + return retMask; +} + +void TSStatic::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con, stream); + + if ( stream->readFlag() ) // TransformMask + { + MatrixF mat; + mathRead( *stream, &mat ); + setTransform(mat); + setRenderTransform(mat); + } + + if ( stream->readFlag() ) // ScaleMask + { + if ( stream->readFlag() ) + { + VectorF scale; + mathRead( *stream, &scale ); + setScale( scale ); + } + else + setScale( Point3F::One ); + } + + if ( stream->readFlag() ) // UpdateCollisionMask + { + U32 collisionType = CollisionMesh; + + stream->read( &collisionType ); + + // Handle it if we have changed CollisionType's + if ( (MeshType)collisionType != mCollisionType ) + { + mCollisionType = (MeshType)collisionType; + + if ( isProperlyAdded() && mShapeInstance ) + prepCollision(); + } + } + + if (stream->readFlag()) // SkinMask + { + NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream);; + if (mSkinNameHandle != skinDesiredNameHandle) + { + mSkinNameHandle = skinDesiredNameHandle; + reSkin(); + } + } + + if (stream->readFlag()) // AdvancedStaticOptionsMask + { + mShapeName = stream->readSTString(); + + stream->read((U32*)&mDecalType); + + mAllowPlayerStep = stream->readFlag(); + mMeshCulling = stream->readFlag(); + mUseOriginSort = stream->readFlag(); + + stream->read(&mRenderNormalScalar); + + stream->read(&mForceDetail); + mPlayAmbient = stream->readFlag(); + } + + mUseAlphaFade = stream->readFlag(); + if (mUseAlphaFade) + { + stream->read(&mAlphaFadeStart); + stream->read(&mAlphaFadeEnd); + stream->read(&mInvertAlphaFade); + } + + if ( mLightPlugin ) + { + mLightPlugin->unpackUpdate(this, con, stream); + } + + if( stream->readFlag() ) + { + cubeDescId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + if ( isProperlyAdded() ) + _updateShouldTick(); +} + +//---------------------------------------------------------------------------- +bool TSStatic::castRay(const Point3F &start, const Point3F &end, RayInfo* info) +{ + if ( mCollisionType == None ) + return false; + + if ( !mShapeInstance ) + return false; + + if ( mCollisionType == Bounds ) + { + F32 fst; + if (!mObjBox.collideLine(start, end, &fst, &info->normal)) + return false; + + info->t = fst; + info->object = this; + info->point.interpolate( start, end, fst ); + info->material = NULL; + return true; + } + else + { + RayInfo shortest = *info; + RayInfo localInfo; + shortest.t = 1e8f; + localInfo.generateTexCoord = info->generateTexCoord; + + for ( U32 i = 0; i < mLOSDetails.size(); i++ ) + { + mShapeInstance->animate( mLOSDetails[i] ); + + if ( mShapeInstance->castRayOpcode( mLOSDetails[i], start, end, &localInfo ) ) + { + localInfo.object = this; + + if (localInfo.t < shortest.t) + shortest = localInfo; + } + } + + if (shortest.object == this) + { + // Copy out the shortest time... + *info = shortest; + return true; + } + } + + return false; +} + +bool TSStatic::castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info) +{ + if ( !mShapeInstance ) + return false; + + // Cast the ray against the currently visible detail + RayInfo localInfo; + bool res = mShapeInstance->castRayOpcode( mShapeInstance->getCurrentDetail(), start, end, &localInfo ); + + if ( res ) + { + *info = localInfo; + info->object = this; + return true; + } + + return false; +} + +bool TSStatic::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &) +{ + if ( !mShapeInstance ) + return false; + + // This is safe to set even if we're not outputing + polyList->setTransform( &mObjToWorld, mObjScale ); + polyList->setObject( this ); + + if ( context == PLC_Export ) + { + // Use highest detail level + S32 dl = 0; + + // Try to call on the client so we can export materials + if ( isServerObject() && getClientObject() ) + dynamic_cast(getClientObject())->mShapeInstance->buildPolyList( polyList, dl ); + else + mShapeInstance->buildPolyList( polyList, dl ); + } + else if ( context == PLC_Selection ) + { + // Use the last rendered detail level + S32 dl = mShapeInstance->getCurrentDetail(); + mShapeInstance->buildPolyListOpcode( dl, polyList, box ); + } + else + { + // Figure out the mesh type we're looking for. + MeshType meshType = ( context == PLC_Decal ) ? mDecalType : mCollisionType; + + if ( meshType == None ) + return false; + else if ( meshType == Bounds ) + polyList->addBox( mObjBox ); + else if ( meshType == VisibleMesh ) + mShapeInstance->buildPolyList( polyList, 0 ); + else + { + // Everything else is done from the collision meshes + // which may be built from either the visual mesh or + // special collision geometry. + for ( U32 i = 0; i < mCollisionDetails.size(); i++ ) + mShapeInstance->buildPolyListOpcode( mCollisionDetails[i], polyList, box ); + } + } + + return true; +} + +void TSStatic::buildConvex(const Box3F& box, Convex* convex) +{ + if ( mCollisionType == None ) + return; + + if ( mShapeInstance == NULL ) + return; + + // These should really come out of a pool + mConvexList->collectGarbage(); + + if ( mCollisionType == Bounds ) + { + // Just return a box convex for the entire shape... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) + { + if (itr->mConvex->getType() == BoxConvexType && + itr->mConvex->getObject() == this) + { + cc = itr->mConvex; + break; + } + } + if (cc) + return; + + // Create a new convex. + BoxConvex* cp = new BoxConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->init(this); + + mObjBox.getCenter(&cp->mCenter); + cp->mSize.x = mObjBox.len_x() / 2.0f; + cp->mSize.y = mObjBox.len_y() / 2.0f; + cp->mSize.z = mObjBox.len_z() / 2.0f; + } + else // CollisionMesh || VisibleMesh + { + TSStaticPolysoupConvex::smCurObject = this; + + for (U32 i = 0; i < mCollisionDetails.size(); i++) + mShapeInstance->buildConvexOpcode( mObjToWorld, mObjScale, mCollisionDetails[i], box, convex, mConvexList ); + + TSStaticPolysoupConvex::smCurObject = NULL; + } +} + +SceneObject* TSStaticPolysoupConvex::smCurObject = NULL; + +TSStaticPolysoupConvex::TSStaticPolysoupConvex() +: box( 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f ), + normal( 0.0f, 0.0f, 0.0f, 0.0f ), + idx( 0 ), + mesh( NULL ) +{ + mType = TSPolysoupConvexType; + + for ( U32 i = 0; i < 4; ++i ) + { + verts[i].set( 0.0f, 0.0f, 0.0f ); + } +} + +Point3F TSStaticPolysoupConvex::support(const VectorF& vec) const +{ + F32 bestDot = mDot( verts[0], vec ); + + const Point3F *bestP = &verts[0]; + for(S32 i=1; i<4; i++) + { + F32 newD = mDot(verts[i], vec); + if(newD > bestDot) + { + bestDot = newD; + bestP = &verts[i]; + } + } + + return *bestP; +} + +Box3F TSStaticPolysoupConvex::getBoundingBox() const +{ + Box3F wbox = box; + wbox.minExtents.convolve( mObject->getScale() ); + wbox.maxExtents.convolve( mObject->getScale() ); + mObject->getTransform().mul(wbox); + return wbox; +} + +Box3F TSStaticPolysoupConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const +{ + AssertISV(false, "TSStaticPolysoupConvex::getBoundingBox(m,p) - Not implemented. -- XEA"); + return box; +} + +void TSStaticPolysoupConvex::getPolyList(AbstractPolyList *list) +{ + // Transform the list into object space and set the pointer to the object + MatrixF i( mObject->getTransform() ); + Point3F iS( mObject->getScale() ); + list->setTransform(&i, iS); + list->setObject(mObject); + + // Add only the original collision triangle + S32 base = list->addPoint(verts[0]); + list->addPoint(verts[2]); + list->addPoint(verts[1]); + + list->begin(0, (U32)idx ^ (uintptr_t)mesh); + list->vertex(base + 2); + list->vertex(base + 1); + list->vertex(base + 0); + list->plane(base + 0, base + 1, base + 2); + list->end(); +} + +void TSStaticPolysoupConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf) +{ + cf->material = 0; + cf->object = mObject; + + // For a tetrahedron this is pretty easy... first + // convert everything into world space. + Point3F tverts[4]; + mat.mulP(verts[0], &tverts[0]); + mat.mulP(verts[1], &tverts[1]); + mat.mulP(verts[2], &tverts[2]); + mat.mulP(verts[3], &tverts[3]); + + // points... + S32 firstVert = cf->mVertexList.size(); + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3]; + + // edges... + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+0; + cf->mEdgeList.last().vertex[1] = firstVert+1; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+1; + cf->mEdgeList.last().vertex[1] = firstVert+2; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+2; + cf->mEdgeList.last().vertex[1] = firstVert+0; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+0; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+1; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+2; + + // triangles... + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]); + cf->mFaceList.last().vertex[0] = firstVert+2; + cf->mFaceList.last().vertex[1] = firstVert+1; + cf->mFaceList.last().vertex[2] = firstVert+0; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+1; + cf->mFaceList.last().vertex[1] = firstVert+0; + cf->mFaceList.last().vertex[2] = firstVert+3; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+2; + cf->mFaceList.last().vertex[1] = firstVert+1; + cf->mFaceList.last().vertex[2] = firstVert+3; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+0; + cf->mFaceList.last().vertex[1] = firstVert+2; + cf->mFaceList.last().vertex[2] = firstVert+3; + + // All done! +} + +void TSStatic::onMount( SceneObject *obj, S32 node ) +{ + Parent::onMount(obj, node); + _updateShouldTick(); +} + +void TSStatic::onUnmount( SceneObject *obj, S32 node ) +{ + Parent::onUnmount( obj, node ); + setMaskBits( TransformMask ); + _updateShouldTick(); +} + +//------------------------------------------------------------------------ +//These functions are duplicated in tsStatic and shapeBase. +//They each function a little differently; but achieve the same purpose of gathering +//target names/counts without polluting simObject. + +DefineEngineMethod( TSStatic, getTargetName, const char*, ( S32 index ),(0), + "Get the name of the indexed shape material.\n" + "@param index index of the material to get (valid range is 0 - getTargetCount()-1).\n" + "@return the name of the indexed material.\n" + "@see getTargetCount()\n") +{ + TSStatic *obj = dynamic_cast< TSStatic* > ( object ); + if(obj) + { + // Try to use the client object (so we get the reskinned targets in the Material Editor) + if ((TSStatic*)obj->getClientObject()) + obj = (TSStatic*)obj->getClientObject(); + + return obj->getShapeInstance()->getTargetName(index); + } + + return ""; +} + +DefineEngineMethod( TSStatic, getTargetCount, S32,(),, + "Get the number of materials in the shape.\n" + "@return the number of materials in the shape.\n" + "@see getTargetName()\n") +{ + TSStatic *obj = dynamic_cast< TSStatic* > ( object ); + if(obj) + { + // Try to use the client object (so we get the reskinned targets in the Material Editor) + if ((TSStatic*)obj->getClientObject()) + obj = (TSStatic*)obj->getClientObject(); + + return obj->getShapeInstance()->getTargetCount(); + } + + return -1; +} + +// This method is able to change materials per map to with others. The material that is being replaced is being mapped to +// unmapped_mat as a part of this transition + +DefineEngineMethod( TSStatic, changeMaterial, void, ( const char* mapTo, Material* oldMat, Material* newMat ),("",nullAsType(),nullAsType()), + "@brief Change one of the materials on the shape.\n\n" + + "This method changes materials per mapTo with others. The material that " + "is being replaced is mapped to unmapped_mat as a part of this transition.\n" + + "@note Warning, right now this only sort of works. It doesn't do a live " + "update like it should.\n" + + "@param mapTo the name of the material target to remap (from getTargetName)\n" + "@param oldMat the old Material that was mapped \n" + "@param newMat the new Material to map\n\n" + + "@tsexample\n" + "// remap the first material in the shape\n" + "%mapTo = %obj.getTargetName( 0 );\n" + "%obj.changeMaterial( %mapTo, 0, MyMaterial );\n" + "@endtsexample\n" ) +{ + // if no valid new material, theres no reason for doing this + if( !newMat ) + { + Con::errorf("TSShape::changeMaterial failed: New material does not exist!"); + return; + } + + TSMaterialList* shapeMaterialList = object->getShape()->materialList; + + // Check the mapTo name exists for this shape + S32 matIndex = shapeMaterialList->getMaterialNameList().find_next(String(mapTo)); + if (matIndex < 0) + { + Con::errorf("TSShape::changeMaterial failed: Invalid mapTo name '%s'", mapTo); + return; + } + + // Lets remap the old material off, so as to let room for our current material room to claim its spot + if( oldMat ) + oldMat->mMapTo = String("unmapped_mat"); + + newMat->mMapTo = mapTo; + + // Map the material by name in the matmgr + MATMGR->mapMaterial( mapTo, newMat->getName() ); + + // Replace instances with the new material being traded in. Lets make sure that we only + // target the specific targets per inst, this is actually doing more than we thought + delete shapeMaterialList->mMatInstList[matIndex]; + shapeMaterialList->mMatInstList[matIndex] = newMat->createMatInstance(); + + // Finish up preparing the material instances for rendering + const GFXVertexFormat *flags = getGFXVertexFormat(); + FeatureSet features = MATMGR->getDefaultFeatures(); + shapeMaterialList->getMaterialInst(matIndex)->init(features, flags); +} + +DefineEngineMethod( TSStatic, getModelFile, const char *, (),, + "@brief Get the model filename used by this shape.\n\n" + + "@return the shape filename\n\n" + "@tsexample\n" + "// Acquire the model filename used on this shape.\n" + "%modelFilename = %obj.getModelFile();\n" + "@endtsexample\n" + ) +{ +<<<<<<< HEAD + return object->getShapeFileName(); +======= + return object->getShapeFileName(); +>>>>>>> garagegames/development +}