| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547 |
- //-----------------------------------------------------------------------------
- // 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 "interior/interiorInstance.h"
- #include "interior/interior.h"
- #include "console/consoleTypes.h"
- #include "scene/sceneManager.h"
- #include "scene/sceneRenderState.h"
- #include "scene/zones/sceneTraversalState.h"
- #include "scene/zones/sceneRootZone.h"
- #include "core/stream/bitStream.h"
- #include "core/stream/fileStream.h"
- #include "gfx/bitmap/gBitmap.h"
- #include "math/mathIO.h"
- #include "gui/worldEditor/editor.h"
- #include "interior/interiorResObjects.h"
- #include "scene/simPath.h"
- #include "interior/forceField.h"
- #include "lighting/lightManager.h"
- #include "collision/convex.h"
- #include "sfx/sfxProfile.h"
- #include "sfx/sfxEnvironment.h"
- #include "core/frameAllocator.h"
- #include "sim/netConnection.h"
- #include "platform/profiler.h"
- #include "gui/3d/guiTSControl.h"
- #include "math/mathUtils.h"
- #include "renderInstance/renderPassManager.h"
- #include "core/resourceManager.h"
- #include "materials/materialManager.h"
- #include "materials/materialFeatureTypes.h"
- #include "materials/matInstance.h"
- #include "collision/concretePolyList.h"
- #include "T3D/physics/physicsPlugin.h"
- #include "T3D/physics/physicsBody.h"
- #include "T3D/physics/physicsCollision.h"
- #include "console/engineAPI.h"
- #ifdef TORQUE_COLLADA
- #include "ts/collada/colladaUtils.h"
- #endif
- IMPLEMENT_CO_NETOBJECT_V1(InteriorInstance);
- ConsoleDocClass( InteriorInstance,
- "@brief Object used to represent buildings and other architectural structures (legacy).\n\n"
-
- "Interiors are made up entirely from convex hulls or, as they are more commonly known as by game "
- "artists, brushes. So what you see is what you collide against. There is no difference between the "
- "visible meshes and the collision meshes.\n\n"
- "Unlike a DTS or COLLADA mesh, interiors do not support any animation. They also do not support "
- "transparent textures. If you need animation or transparency then you are forced to use other model objects.\n\n"
- "It is important to note that interiors are no longer the preferred format for large structures. It is an "
- "old format, which does not have much to offer above DTS or COLLADA. They are still included in Torque 3D "
- "for the sake of backwards compatibility for developers porting older TGE or TGEA projects. It will be "
- "deprecated soon.\n\n"
-
- "@ingroup gameObjects"
- );
- static const U32 csgMaxZoneSize = 256;
- static bool sgScopeBoolArray[256];
- bool InteriorInstance::smDontRestrictOutside = false;
- F32 InteriorInstance::smDetailModification = 1.0f;
- //-----------------------------------------------------------------------------
- InteriorInstance::InteriorInstance()
- {
- mAlarmState = false;
- mInteriorFileName = NULL;
- mTypeMask |= InteriorObjectType | StaticObjectType | StaticShapeObjectType;
- mZoneFlags.clear( ZoneFlag_IsClosedOffSpace ); // Interiors are open spaces.
- mForcedDetailLevel = -1;
- mConvexList = new Convex;
- mCRC = 0;
- mPhysicsRep = NULL;
- }
- //-----------------------------------------------------------------------------
- InteriorInstance::~InteriorInstance()
- {
- delete mConvexList;
- mConvexList = NULL;
- // GFX2_RENDER_MERGE
- //for (U32 i = 0; i < mReflectPlanes.size(); i++)
- // mReflectPlanes[i].clearTextures();
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::inspectPostApply()
- {
- // Apply any transformations set in the editor
- Parent::inspectPostApply();
- // Update the Transform on Editor Apply.
- setMaskBits(TransformMask);
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::initPersistFields()
- {
- addGroup("Media");
- addProtectedField( "interiorFile", TypeFilename, Offset( mInteriorFileName, InteriorInstance ),
- &_setInteriorFile, &defaultProtectedGetFn,
- "Path and filename of the Interior file (.DIF) to load for this InteriorInstance.");
- endGroup("Media");
- Parent::initPersistFields();
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::consoleInit()
- {
- //-------------------------------------- Class level variables
- Con::addVariable( "pref::Interior::VertexLighting", TypeBool, &Interior::smUseVertexLighting,
- "Forces all InteriorInstances to not render their lightmaps.\n"
- "@ingroup Interior" );
- Con::addVariable( "pref::Interior::detailAdjust", TypeF32, &InteriorInstance::smDetailModification,
- "Forces all InteriorInstance rendering to a particular detail level.\n"
- "@ingroup Interior" );
- // DEBUG ONLY!!!
- #ifndef TORQUE_SHIPPING
- Con::addVariable( "Interior::DontRestrictOutside", TypeBool, &smDontRestrictOutside,
- "Render only the outside zone of all InteriorInstances.\n"
- "@ingroup Interior" );
- #endif
- }
- //-----------------------------------------------------------------------------
- #ifdef TORQUE_COLLADA
- void InteriorInstance::exportToCollada(bool bakeTransform)
- {
- if (mInteriorRes->getNumDetailLevels() == 0)
- {
- Con::errorf("InteriorInstance::exportToCollada() called an InteriorInstance with no Interior");
- return;
- }
- // For now I am only worrying about the highest lod
- Interior* pInterior = mInteriorRes->getDetailLevel(0);
- if (!pInterior)
- {
- Con::errorf("InteriorInstance::exportToCollada() called an InteriorInstance with an invalid Interior");
- return;
- }
- // Get an optimized version of our mesh
- OptimizedPolyList interiorMesh;
- if (bakeTransform)
- {
- MatrixF mat = getTransform();
- Point3F scale = getScale();
- pInterior->buildExportPolyList(interiorMesh, &mat, &scale);
- }
- else
- pInterior->buildExportPolyList(interiorMesh);
- // Get our export path
- Torque::Path colladaFile = mInteriorRes.getPath();
- // Make sure to set our Collada extension
- colladaFile.setExtension("dae");
- // Use the InteriorInstance name if possible
- String meshName = getName();
- // Otherwise use the DIF's file name
- if (meshName.isEmpty())
- meshName = colladaFile.getFileName();
- // If we are baking the transform then append
- // a CRC version of the transform to the mesh/file name
- if (bakeTransform)
- {
- F32 trans[19];
- const MatrixF& mat = getTransform();
- const Point3F& scale = getScale();
- // Copy in the transform
- for (U32 i = 0; i < 4; i++)
- {
- for (U32 j = 0; j < 4; j++)
- {
- trans[i * 4 + j] = mat(i, j);
- }
- }
- // Copy in the scale
- trans[16] = scale.x;
- trans[17] = scale.y;
- trans[18] = scale.z;
- U32 crc = CRC::calculateCRC(trans, sizeof(F32) * 19);
- meshName += String::ToString("_%x", crc);
- }
- // Set the file name as the meshName
- colladaFile.setFileName(meshName);
- // Use a ColladaUtils function to do the actual export to a Collada file
- ColladaUtils::exportToCollada(colladaFile, interiorMesh, meshName);
- }
- #endif
- //-----------------------------------------------------------------------------
- bool InteriorInstance::onAdd()
- {
- if(! _loadInterior())
- return false;
- if(!Parent::onAdd())
- return false;
- addToScene();
- if ( PHYSICSMGR && mInteriorRes && mInteriorRes->getNumDetailLevels() > 0 )
- {
- // TODO: We need to cache the collision by resource name
- // and reuse it across multiple instances.
- // Get the interior collision geometry.
- ConcretePolyList polylist;
- mInteriorRes->getDetailLevel(0)->buildPolyList( &polylist, Box3F(999999.0f), MatrixF::Identity, getScale() );
- polylist.triangulate();
- // Look out... this could possibly happen!
- if ( !polylist.isEmpty() )
- {
- // Use a triangle mesh for collision.
- PhysicsCollision *colShape = PHYSICSMGR->createCollision();
- colShape->addTriangleMesh( polylist.mVertexList.address(),
- polylist.mVertexList.size(),
- polylist.mIndexList.address(),
- polylist.mIndexList.size() / 3,
- MatrixF::Identity );
- PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
- mPhysicsRep = PHYSICSMGR->createBody();
- mPhysicsRep->init( colShape, 0, 0, this, world );
- mPhysicsRep->setTransform( getTransform() );
- }
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::onRemove()
- {
- SAFE_DELETE( mPhysicsRep );
- _unloadInterior();
- removeFromScene();
- Parent::onRemove();
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::_loadInterior()
- {
- U32 i;
- // Load resource
- mInteriorRes = ResourceManager::get().load(mInteriorFileName);
- if (bool(mInteriorRes) == false) {
- Con::errorf(ConsoleLogEntry::General, "Unable to load interior: %s", mInteriorFileName);
- NetConnection::setLastError("Unable to load interior: %s", mInteriorFileName);
- return false;
- }
- if(isClientObject())
- {
- if(mCRC != mInteriorRes.getChecksum())
- {
- NetConnection::setLastError("Local interior file '%s' does not match version on server.", mInteriorFileName);
- return false;
- }
- for (i = 0; i < mInteriorRes->getNumDetailLevels(); i++) {
- // ok, if the material list load failed...
- // if this is a local connection, we'll assume that's ok
- // and just have white textures...
- // otherwise we want to return false.
- Interior* pInterior = mInteriorRes->getDetailLevel(i);
- if(!pInterior->prepForRendering(mInteriorRes.getPath().getFullPath().c_str()) )
- {
- if(!bool(mServerObject))
- {
- return false;
- }
- }
- }
- // copy planar reflect list from top detail level - for now
- Interior* pInterior = mInteriorRes->getDetailLevel(0);
- if( pInterior->mReflectPlanes.size() )
- {
- for ( i = 0; i < pInterior->mReflectPlanes.size(); i++ )
- {
- mPlaneReflectors.increment();
- PlaneReflector &plane = mPlaneReflectors.last();
- plane.refplane = pInterior->mReflectPlanes[i];
- plane.objectSpace = true;
- plane.registerReflector( this, &mReflectorDesc );
- }
- }
- }
- else
- mCRC = mInteriorRes.getChecksum();
- // Ok, everything's groovy! Let's cache our hashed filename for renderimage sorting...
- mInteriorFileHash = _StringTable::hashString(mInteriorFileName);
- // Setup bounding information
- mObjBox = mInteriorRes->getDetailLevel(0)->getBoundingBox();
- resetWorldBox();
- setRenderTransform(mObjToWorld);
- // Do any handle loading, etc. required.
- if (isClientObject()) {
- for (i = 0; i < mInteriorRes->getNumDetailLevels(); i++) {
- Interior* pInterior = mInteriorRes->getDetailLevel(i);
- // Force the lightmap manager to download textures if we're
- // running the mission editor. Normally they are only
- // downloaded after the whole scene is lit.
- gInteriorLMManager.addInstance(pInterior->getLMHandle(), mLMHandle, this);
- if (gEditingMission) {
- gInteriorLMManager.useBaseTextures(pInterior->getLMHandle(), mLMHandle);
- gInteriorLMManager.downloadGLTextures(pInterior->getLMHandle());
- }
- // Install material list
- // mMaterialMaps.push_back(new MaterialList(pInterior->mMaterialList));
- }
- } else {
- }
- setMaskBits(0xffffffff);
- return true;
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::_unloadInterior()
- {
- mConvexList->nukeList();
- delete mConvexList;
- mConvexList = new Convex;
- if(isClientObject())
- {
- if(bool(mInteriorRes) && mLMHandle != 0xFFFFFFFF)
- {
- for(U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++)
- {
- Interior * pInterior = mInteriorRes->getDetailLevel(i);
- if (pInterior->getLMHandle() != 0xFFFFFFFF)
- gInteriorLMManager.removeInstance(pInterior->getLMHandle(), mLMHandle);
- }
- }
-
- if( mPlaneReflectors.size() )
- {
- for ( U32 i = 0; i < mPlaneReflectors.size(); i++ )
- {
- mPlaneReflectors[i].unregisterReflector();
- }
- mPlaneReflectors.clear();
- }
- }
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::onSceneAdd()
- {
- AssertFatal(mInteriorRes, "Error, should not have been added to the scene if there's no interior!");
- if (Parent::onSceneAdd() == false)
- return false;
- U32 maxNumZones = 0;
- for (U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++)
- {
- if (mInteriorRes->getDetailLevel(i)->mZones.size() > maxNumZones)
- maxNumZones = mInteriorRes->getDetailLevel(i)->mZones.size();
- }
- if( maxNumZones > 1 )
- {
- SceneZoneSpaceManager* zoneManager = getSceneManager()->getZoneManager();
- if( zoneManager )
- {
- zoneManager->registerZones(this, (maxNumZones - 1));
- // Connect to outdoor zone.
- zoneManager->getRootZone()->connectZoneSpace( this );
- connectZoneSpace( zoneManager->getRootZone() );
- }
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::onSceneRemove()
- {
- // Disconnect from root zone in case we have connected.
- SceneZoneSpaceManager* zoneManager = getSceneManager()->getZoneManager();
- if( zoneManager )
- zoneManager->getRootZone()->disconnectZoneSpace( this );
- Parent::onSceneRemove();
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::_getOverlappingZones( const Box3F& aabb, const MatrixF& transform, const Point3F& scale, U32* outZones, U32& outNumZones )
- {
- MatrixF xForm(true);
- Point3F invScale(1.0f / getScale().x,
- 1.0f / getScale().y,
- 1.0f / getScale().z);
- xForm.scale(invScale);
- xForm.mul(getWorldTransform());
- xForm.mul(transform);
- xForm.scale(scale);
- U32 waterMark = FrameAllocator::getWaterMark();
- U16* zoneVector = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mZones.size() * sizeof(U16));
- U32 numRetZones = 0;
- bool outsideToo = mInteriorRes->getDetailLevel(0)->scanZones(aabb,
- xForm,
- zoneVector,
- &numRetZones
- );
- if (numRetZones > SceneObject::MaxObjectZones)
- {
- Con::warnf(ConsoleLogEntry::General, "Too many zones returned for query on %s. Returning first %d",
- mInteriorFileName, SceneObject::MaxObjectZones);
- }
- for (U32 i = 0; i < getMin(numRetZones, U32(SceneObject::MaxObjectZones)); i++)
- outZones[i] = zoneVector[i] + mZoneRangeStart - 1;
- outNumZones = numRetZones;
- FrameAllocator::setWaterMark(waterMark);
- return outsideToo;
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones )
- {
- return _getOverlappingZones( aabb, MatrixF::Identity, Point3F( 1.f, 1.f, 1.f ), outZones, outNumZones );
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones )
- {
- return _getOverlappingZones( obj->getObjBox(), obj->getTransform(), obj->getScale(), outZones, outNumZones );
- }
- //-----------------------------------------------------------------------------
- U32 InteriorInstance::getPointZone(const Point3F& p)
- {
- AssertFatal(mInteriorRes, "Error, no interior!");
- Point3F osPoint = p;
- mWorldToObj.mulP(osPoint);
- osPoint.convolveInverse(mObjScale);
- S32 zone = mInteriorRes->getDetailLevel(0)->getZoneForPoint(osPoint);
- // If we're in solid (-1) or outside, we need to return 0
- if (zone == -1 || zone == 0)
- return SceneZoneSpaceManager::InvalidZoneId;
- return (zone-1) + mZoneRangeStart;
- }
- //-----------------------------------------------------------------------------
- // does a hack check to determine how much a point is 'inside'.. should have
- // portals prebuilt with the transfer energy to each other portal in the zone
- // from the neighboring zone.. these values can be used to determine the factor
- // from within an individual zone.. also, each zone could be marked with
- // average material property for eax environment audio
- // ~0: outside -> 1: inside
- bool InteriorInstance::getPointInsideScale(const Point3F & pos, F32 * pScale)
- {
- AssertFatal(mInteriorRes, "InteriorInstance::getPointInsideScale: no interior");
- Interior * interior = mInteriorRes->getDetailLevel(0);
- Point3F p = pos;
- mWorldToObj.mulP(p);
- p.convolveInverse(mObjScale);
- U32 zoneIndex = interior->getZoneForPoint(p);
- if(zoneIndex == -1) // solid?
- {
- *pScale = 1.f;
- return(true);
- }
- else if(zoneIndex == 0) // outside?
- {
- *pScale = 0.f;
- return(true);
- }
- U32 waterMark = FrameAllocator::getWaterMark();
- const Interior::Portal** portals = (const Interior::Portal**)FrameAllocator::alloc(256 * sizeof(const Interior::Portal*));
- U32 numPortals = 0;
- Interior::Zone & zone = interior->mZones[zoneIndex];
- U32 i;
- for(i = 0; i < zone.portalCount; i++)
- {
- const Interior::Portal & portal = interior->mPortals[interior->mZonePortalList[zone.portalStart + i]];
- if(portal.zoneBack == 0 || portal.zoneFront == 0) {
- AssertFatal(numPortals < 256, "Error, overflow in temporary portal buffer!");
- portals[numPortals++] = &portal;
- }
- }
- // inside?
- if(numPortals == 0)
- {
- *pScale = 1.f;
- FrameAllocator::setWaterMark(waterMark);
- return(true);
- }
- Point3F* portalCenters = (Point3F*)FrameAllocator::alloc(numPortals * sizeof(Point3F));
- U32 numPortalCenters = 0;
- // scale using the distances to the portals in this zone...
- for(i = 0; i < numPortals; i++)
- {
- const Interior::Portal * portal = portals[i];
- if(!portal->triFanCount)
- continue;
- Point3F center(0, 0, 0);
- for(U32 j = 0; j < portal->triFanCount; j++)
- {
- const Interior::TriFan & fan = interior->mWindingIndices[portal->triFanStart + j];
- U32 numPoints = fan.windingCount;
- if(!numPoints)
- continue;
- for(U32 k = 0; k < numPoints; k++)
- {
- const Point3F & a = interior->mPoints[interior->mWindings[fan.windingStart + k]].point;
- center += a;
- }
- center /= (F32)numPoints;
- portalCenters[numPortalCenters++] = center;
- }
- }
- // 'magic' check here...
- F32 magic = Con::getFloatVariable("Interior::insideDistanceFalloff", 10.f);
- F32 val = 0.f;
- for(i = 0; i < numPortalCenters; i++)
- val += 1.f - mClampF(Point3F(portalCenters[i] - p).len() / magic, 0.f, 1.f);
- *pScale = 1.f - mClampF(val, 0.f, 1.f);
- FrameAllocator::setWaterMark(waterMark);
- return(true);
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::_renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat )
- {
- #ifndef TORQUE_SHIPPING
- if (Interior::smRenderMode == 0)
- return;
- if (overrideMat)
- return;
- if(gEditingMission && isHidden())
- return;
- U32 detailLevel = 0;
- detailLevel = _calcDetailLevel(state, state->getCameraPosition());
- Interior* pInterior = mInteriorRes->getDetailLevel( detailLevel );
- if (!pInterior)
- return;
- PROFILE_START( IRO_DebugRender );
- GFX->pushWorldMatrix();
- // setup world matrix - for fixed function
- MatrixF world = GFX->getWorldMatrix();
- world.mul( getRenderTransform() );
- world.scale( getScale() );
- GFX->setWorldMatrix( world );
- // setup world matrix - for shaders
- MatrixF proj = GFX->getProjectionMatrix();
- proj.mul(world);
- SceneData sgData;
- sgData = pInterior->setupSceneGraphInfo( this, state );
- ZoneVisDeterminer zoneVis = pInterior->setupZoneVis( this, state );
- pInterior->debugRender( zoneVis, sgData, this, proj );
- GFX->popWorldMatrix();
- PROFILE_END();
- #endif
- }
- //-----------------------------------------------------------------------------
- U32 InteriorInstance::_calcDetailLevel(SceneRenderState* state, const Point3F& wsPoint)
- {
- AssertFatal(mInteriorRes, "Error, should not try to calculate the deatil level without a resource to work with!");
- AssertFatal(_getNumCurrZones() > 0, "Error, must belong to a zone for this to work");
- if (smDetailModification < 0.3f)
- smDetailModification = 0.3f;
- if (smDetailModification > 1.0f)
- smDetailModification = 1.0f;
- // Early out for simple interiors
- if (mInteriorRes->getNumDetailLevels() == 1)
- return 0;
- if((mForcedDetailLevel >= 0) && (mForcedDetailLevel < mInteriorRes->getNumDetailLevels()))
- return(mForcedDetailLevel);
- Point3F osPoint = wsPoint;
- mRenderWorldToObj.mulP(osPoint);
- osPoint.convolveInverse(mObjScale);
- // First, see if the point is in the object space bounding box of the highest detail
- // If it is, then the detail level is zero.
- if (mObjBox.isContained(osPoint))
- return 0;
- // Otherwise, we're going to have to do some ugly trickery to get the projection.
- // I've stolen the worldToScreenScale from dglMatrix, we'll have to calculate the
- // projection of the bounding sphere of the lowest detail level.
- // worldToScreenScale = (near * view.extent.x) / (right - left)
- F32 worldToScreenScale = state->getWorldToScreenScale().x;
- const SphereF& lowSphere = mInteriorRes->getDetailLevel(mInteriorRes->getNumDetailLevels() - 1)->mBoundingSphere;
- F32 dist = (lowSphere.center - osPoint).len();
- F32 projRadius = (lowSphere.radius / dist) * worldToScreenScale;
- // Scale the projRadius based on the objects maximum scale axis
- projRadius *= getMax(mFabs(mObjScale.x), getMax(mFabs(mObjScale.y), mFabs(mObjScale.z)));
- // Multiply based on detail preference...
- projRadius *= smDetailModification;
- // Ok, now we have the projected radius, we need to search through the interiors to
- // find the largest interior that will support this projection.
- U32 final = mInteriorRes->getNumDetailLevels() - 1;
- for (U32 i = 0; i< mInteriorRes->getNumDetailLevels() - 1; i++) {
- Interior* pDetail = mInteriorRes->getDetailLevel(i);
- if (pDetail->mMinPixels < projRadius) {
- final = i;
- break;
- }
- }
- // Ok, that's it.
- return final;
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::traverseZones( SceneTraversalState* state )
- {
- U32 startZone = getPointZone( state->getCullingState()->getCameraState().getViewPosition() );
- if( startZone != SceneZoneSpaceManager::InvalidZoneId )
- startZone = startZone - mZoneRangeStart + 1;
- else
- startZone = SceneZoneSpaceManager::RootZoneId;
- traverseZones( state, startZone );
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::traverseZones( SceneTraversalState* state, U32 startZoneId )
- {
- PROFILE_SCOPE( InteriorInstance_traverseZones );
- SceneCullingState* cullingState = state->getCullingState();
- // [rene, 23-Mar-11] This is a really gross hack. It effectively renders all zoning in interiors
- // ineffective and just lets them render with the root frustum. It's just that after untangling
- // DMM's mess in the sceneGraph system, I just don't have the energy anymore to also dig through
- // the ungodly mess that is the interior code and since this is all quasi-deprecated-and-soon-to-die
- // anyway it would just be wasted effort.
- for( U32 i = getZoneRangeStart(); i < ( getZoneRangeStart() + getZoneRange() ); ++ i )
- cullingState->addCullingVolumeToZone( i, state->getCurrentCullingVolume() );
- #if 0
- U32 baseZoneForPrep = getCurrZone( 0 );
- bool multipleZones = ( getNumCurrZones() > 1 );
- Frustum outFrustum;
- bool continueOut = mInteriorRes->getDetailLevel( 0 )->traverseZones(
- renderState,
- frustum,
- baseZoneForPrep,
- startZoneId,
- mZoneRangeStart,
- mRenderObjToWorld,
- mObjScale,
- smDontRestrictOutside | multipleZones,
- renderState->isInvertedWorld(),
- outFrustum
- );
- if( smDontRestrictOutside )
- continueOut = true;
- #endif
- if( true )// continueOut )
- {
- state->pushZone( startZoneId );
- _traverseConnectedZoneSpaces( state );
- state->popZone();
- }
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::prepRenderImage( SceneRenderState* state )
- {
- PROFILE_SCOPE( InteriorInstance_prepRenderImage );
- U32 detailLevel = _calcDetailLevel( state, state->getCameraPosition() );
- Interior* pInterior = getResource()->getDetailLevel( detailLevel );
- pInterior->prepBatchRender( this, state );
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::castRay(const Point3F& s, const Point3F& e, RayInfo* info)
- {
- info->object = this;
- return mInteriorRes->getDetailLevel(0)->castRay(s, e, info);
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::buildPolyList(PolyListContext context, AbstractPolyList* list, const Box3F& wsBox, const SphereF&)
- {
- if (bool(mInteriorRes) == false)
- return false;
- // Setup collision state data
- list->setTransform(&getTransform(), getScale());
- list->setObject(this);
- return mInteriorRes->getDetailLevel(0)->buildPolyList(list, wsBox, mWorldToObj, getScale());
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::buildConvex(const Box3F& box, Convex* convex)
- {
- if (bool(mInteriorRes) == false)
- return;
- mConvexList->collectGarbage();
- Box3F realBox = box;
- mWorldToObj.mul(realBox);
- realBox.minExtents.convolveInverse(mObjScale);
- realBox.maxExtents.convolveInverse(mObjScale);
- if (realBox.isOverlapped(getObjBox()) == false)
- return;
- U32 waterMark = FrameAllocator::getWaterMark();
- if ((convex->getObject()->getTypeMask() & VehicleObjectType) &&
- mInteriorRes->getDetailLevel(0)->mVehicleConvexHulls.size() > 0)
- {
- // Can never have more hulls than there are hulls in the interior...
- U16* hulls = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mVehicleConvexHulls.size() * sizeof(U16));
- U32 numHulls = 0;
- Interior* pInterior = mInteriorRes->getDetailLevel(0);
- if (pInterior->getIntersectingVehicleHulls(realBox, hulls, &numHulls) == false) {
- FrameAllocator::setWaterMark(waterMark);
- return;
- }
- for (U32 i = 0; i < numHulls; i++) {
- // See if this hull exists in the working set already...
- Convex* cc = 0;
- CollisionWorkingList& wl = convex->getWorkingList();
- for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
- if (itr->mConvex->getType() == InteriorConvexType &&
- (static_cast<InteriorConvex*>(itr->mConvex)->getObject() == this &&
- static_cast<InteriorConvex*>(itr->mConvex)->hullId == -S32(hulls[i] + 1))) {
- cc = itr->mConvex;
- break;
- }
- }
- if (cc)
- continue;
- // Create a new convex.
- InteriorConvex* cp = new InteriorConvex;
- mConvexList->registerObject(cp);
- convex->addToWorkingList(cp);
- cp->mObject = this;
- cp->pInterior = pInterior;
- cp->hullId = -S32(hulls[i] + 1);
- cp->box.minExtents.x = pInterior->mVehicleConvexHulls[hulls[i]].minX;
- cp->box.minExtents.y = pInterior->mVehicleConvexHulls[hulls[i]].minY;
- cp->box.minExtents.z = pInterior->mVehicleConvexHulls[hulls[i]].minZ;
- cp->box.maxExtents.x = pInterior->mVehicleConvexHulls[hulls[i]].maxX;
- cp->box.maxExtents.y = pInterior->mVehicleConvexHulls[hulls[i]].maxY;
- cp->box.maxExtents.z = pInterior->mVehicleConvexHulls[hulls[i]].maxZ;
- }
- }
- else
- {
- // Can never have more hulls than there are hulls in the interior...
- U16* hulls = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mConvexHulls.size() * sizeof(U16));
- U32 numHulls = 0;
- Interior* pInterior = mInteriorRes->getDetailLevel(0);
- if (pInterior->getIntersectingHulls(realBox, hulls, &numHulls) == false) {
- FrameAllocator::setWaterMark(waterMark);
- return;
- }
- for (U32 i = 0; i < numHulls; i++) {
- // See if this hull exists in the working set already...
- Convex* cc = 0;
- CollisionWorkingList& wl = convex->getWorkingList();
- for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
- if (itr->mConvex->getType() == InteriorConvexType &&
- (static_cast<InteriorConvex*>(itr->mConvex)->getObject() == this &&
- static_cast<InteriorConvex*>(itr->mConvex)->hullId == hulls[i])) {
- cc = itr->mConvex;
- break;
- }
- }
- if (cc)
- continue;
- // Create a new convex.
- InteriorConvex* cp = new InteriorConvex;
- mConvexList->registerObject(cp);
- convex->addToWorkingList(cp);
- cp->mObject = this;
- cp->pInterior = pInterior;
- cp->hullId = hulls[i];
- cp->box.minExtents.x = pInterior->mConvexHulls[hulls[i]].minX;
- cp->box.minExtents.y = pInterior->mConvexHulls[hulls[i]].minY;
- cp->box.minExtents.z = pInterior->mConvexHulls[hulls[i]].minZ;
- cp->box.maxExtents.x = pInterior->mConvexHulls[hulls[i]].maxX;
- cp->box.maxExtents.y = pInterior->mConvexHulls[hulls[i]].maxY;
- cp->box.maxExtents.z = pInterior->mConvexHulls[hulls[i]].maxZ;
- }
- }
- FrameAllocator::setWaterMark(waterMark);
- }
- //-----------------------------------------------------------------------------
- U32 InteriorInstance::packUpdate(NetConnection* c, U32 mask, BitStream* stream)
- {
- U32 retMask = Parent::packUpdate(c, mask, stream);
- if (stream->writeFlag((mask & InitMask) != 0)) {
- // Initial update, write the whole kit and kaboodle
- stream->write(mCRC);
- stream->writeString(mInteriorFileName);
- // Write the alarm state
- stream->writeFlag(mAlarmState);
- }
- else
- {
- stream->writeFlag(mAlarmState);
- }
- return retMask;
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::unpackUpdate(NetConnection* c, BitStream* stream)
- {
- Parent::unpackUpdate(c, stream);
- MatrixF temp;
- Point3F tempScale;
- if (stream->readFlag()) {
- bool isNewUpdate(mInteriorRes);
- if(isNewUpdate)
- _unloadInterior();
- // Initial Update
- // CRC
- stream->read(&mCRC);
- // File
- mInteriorFileName = stream->readSTString();
- // Alarm state: Note that we handle this ourselves on the initial update
- // so that the state is always full on or full off...
- mAlarmState = stream->readFlag();
- if(isNewUpdate)
- {
- if(! _loadInterior())
- Con::errorf("InteriorInstance::unpackUpdate - Unable to load new interior");
- }
- }
- else
- {
- setAlarmMode(stream->readFlag());
- }
- }
- //-----------------------------------------------------------------------------
- Interior* InteriorInstance::getDetailLevel(const U32 level)
- {
- return mInteriorRes->getDetailLevel(level);
- }
- //-----------------------------------------------------------------------------
- U32 InteriorInstance::getNumDetailLevels()
- {
- return mInteriorRes->getNumDetailLevels();
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::setAlarmMode(const bool alarm)
- {
- if (mInteriorRes->getDetailLevel(0)->mHasAlarmState == false)
- return;
- if (mAlarmState == alarm)
- return;
- mAlarmState = alarm;
- if (isServerObject())
- {
- setMaskBits(AlarmMask);
- }
- else
- {
- // DMMTODO: Invalidate current light state
- }
- }
- //-----------------------------------------------------------------------------
- void InteriorInstance::_createTriggerTransform(const InteriorResTrigger* trigger, MatrixF* transform)
- {
- Point3F offset;
- MatrixF xform = getTransform();
- xform.getColumn(3, &offset);
- Point3F triggerOffset = trigger->mOffset;
- triggerOffset.convolve(mObjScale);
- getTransform().mulV(triggerOffset);
- offset += triggerOffset;
- xform.setColumn(3, offset);
- *transform = xform;
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::readLightmaps(GBitmap**** lightmaps)
- {
- AssertFatal(mInteriorRes, "Error, no interior loaded!");
- AssertFatal(lightmaps, "Error, no lightmaps or numdetails result pointers");
- AssertFatal(*lightmaps == NULL, "Error, already have a pointer in the lightmaps result field!");
- // Load resource
- FileStream* pStream;
- if((pStream = FileStream::createAndOpen( mInteriorFileName, Torque::FS::File::Read )) == NULL)
- {
- Con::errorf(ConsoleLogEntry::General, "Unable to load interior: %s", mInteriorFileName);
- return false;
- }
- InteriorResource* pResource = new InteriorResource;
- bool success = pResource->read(*pStream);
- delete pStream;
- if (success == false)
- {
- delete pResource;
- return false;
- }
- AssertFatal(pResource->getNumDetailLevels() == mInteriorRes->getNumDetailLevels(),
- "Mismatched detail levels!");
- *lightmaps = new GBitmap**[mInteriorRes->getNumDetailLevels()];
- for (U32 i = 0; i < pResource->getNumDetailLevels(); i++)
- {
- Interior* pInterior = pResource->getDetailLevel(i);
- (*lightmaps)[i] = new GBitmap*[pInterior->mLightmaps.size()];
- for (U32 j = 0; j < pInterior->mLightmaps.size(); j++)
- {
- ((*lightmaps)[i])[j] = pInterior->mLightmaps[j];
- pInterior->mLightmaps[j] = NULL;
- }
- pInterior->mLightmaps.clear();
- }
- delete pResource;
- return true;
- }
- //-----------------------------------------------------------------------------
- S32 InteriorInstance::getSurfaceZone(U32 surfaceindex, Interior *detail)
- {
- AssertFatal(((surfaceindex >= 0) && (surfaceindex < detail->surfaceZones.size())), "Bad surface index!");
- S32 zone = detail->surfaceZones[surfaceindex];
- if(zone > -1)
- return zone + mZoneRangeStart;
- return _getCurrZone(0);
- }
- //-----------------------------------------------------------------------------
- bool InteriorInstance::_setInteriorFile( void *object, const char *, const char *data )
- {
- if(data == NULL)
- return true;
- InteriorInstance *inst = static_cast<InteriorInstance *>(object);
- if(inst->isProperlyAdded())
- inst->_unloadInterior();
- inst->mInteriorFileName = StringTable->insert(data);
- if(inst->isProperlyAdded())
- {
- if(! inst->_loadInterior())
- Con::errorf("InteriorInstance::setInteriorFile - Unable to load new interior");
- }
- return false;
- }
- //=============================================================================
- // Console API.
- //=============================================================================
- // MARK: ---- Console API ----
- ConsoleFunctionGroupBegin(Interiors, "");
- //-----------------------------------------------------------------------------
- #ifndef TORQUE_SHIPPING
- DefineEngineFunction( setInteriorRenderMode, void, ( S32 mode ),,
- "Globally changes how InteriorInstances are rendered. Useful for debugging geometry and rendering artifacts\n"
-
- "@note This does not work in shipping mode\n\n"
- "@param mode The render mode can be one of the following numbers:\n\n"
- "NormalRender = 0,\n\n"
- "NormalRenderLines = 1,\n\n"
- "ShowDetail = 2,\n\n"
- "ShowAmbiguous = 3,\n\n"
- "ShowOrphan = 4,\n\n"
- "ShowLightmaps = 5,\n\n"
- "ShowTexturesOnly = 6,\n\n"
- "ShowPortalZones = 7,\n\n"
- "ShowOutsideVisible = 8,\n\n"
- "ShowCollisionFans = 9,\n\n"
- "ShowStrips = 10,\n\n"
- "ShowNullSurfaces = 11,\n\n"
- "ShowLargeTextures = 12,\n\n"
- "ShowHullSurfaces = 13,\n\n"
- "ShowVehicleHullSurfaces = 14,\n\n"
- "ShowVertexColors = 15,\n\n"
- "ShowDetailLevel = 16\n\n"
-
- "@ingroup Game" )
- {
- if (mode < 0 || mode > Interior::ShowDetailLevel)
- mode = 0;
- Interior::smRenderMode = mode;
- }
- //-----------------------------------------------------------------------------
- ConsoleFunction( setInteriorFocusedDebug, void, 2, 2, "(bool enable)"
- "@brief No longer properly supported\n\n"
- "@internal")
- {
- if (dAtob(argv[1])) {
- Interior::smFocusedDebug = true;
- } else {
- Interior::smFocusedDebug = false;
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- ConsoleDocFragment _isPointInside1(
- "@brief Check to see if a point in world space is inside of an interior.\n\n"
- "@param position The position to check in world space.\n\n"
- "@tsexample\n"
- "// Check to see if a point is inside any interior\n"
- "%point = \"100 100 100\";\n"
- "%isInside = isPointInside(%point);\n"
- "@endtsexample\n\n"
- "@ingroup Game",
- NULL,
- "bool isPointInside( Point3F position );"
- );
- ConsoleDocFragment _isPointInside2(
- "Check to see if a set of coordinates in world space are inside of an interior.\n\n"
- "@param x X-coordinate for position in world space.\n"
- "@param y Y-coordinate for position in world space.\n"
- "@param z Z-coordinate for position in world space.\n"
- "@tsexample\n\n"
- "// Check to see if a point is inside any interior\n"
- "%isInside = isPointInside(100, 100, 100);\n"
- "@endtsexample\n\n"
- "@ingroup Game",
- NULL,
- "bool isPointInside( F32 x, F32 y, F32 z );"
- );
- ConsoleFunction( isPointInside, bool, 2, 4, "Check to see if a point in world space is inside of an interior."
- "@hide")
- {
- static bool lastValue = false;
- if(!(argc == 2 || argc == 4))
- {
- Con::errorf(ConsoleLogEntry::General, "cIsPointInside: invalid parameters");
- return(lastValue);
- }
- Point3F pos;
- if(argc == 2)
- dSscanf(argv[1], "%g %g %g", &pos.x, &pos.y, &pos.z);
- else
- {
- pos.x = dAtof(argv[1]);
- pos.y = dAtof(argv[2]);
- pos.z = dAtof(argv[3]);
- }
- RayInfo collision;
- if(gClientContainer.castRay(pos, Point3F(pos.x, pos.y, pos.z - 2000.f), InteriorObjectType, &collision))
- {
- if(collision.face == -1)
- Con::errorf(ConsoleLogEntry::General, "cIsPointInside: failed to find hit face on interior");
- else
- {
- InteriorInstance * interior = dynamic_cast<InteriorInstance *>(collision.object);
- if(interior)
- lastValue = !interior->getDetailLevel(0)->isSurfaceOutsideVisible(collision.face);
- else
- Con::errorf(ConsoleLogEntry::General, "cIsPointInside: invalid interior on collision");
- }
- }
- return(lastValue);
- }
- ConsoleFunctionGroupEnd(Interiors);
- //-----------------------------------------------------------------------------
- #ifdef TORQUE_COLLADA
- DefineEngineMethod( InteriorInstance, exportToCollada, void, ( bool bakeTransform ),,
- "@brief Exports the Interior to a Collada file\n\n"
- "@param bakeTransform Bakes the InteriorInstance's transform into the vertex positions\n\n"
-
- "@tsexample\n"
- "// Export to COLLADA, do not bakeTransform\n"
- "%interiorObject.exportToCollada(0);\n"
- "@endtsexample\n\n")
- {
- object->exportToCollada(bakeTransform);
- }
- #endif
- //-----------------------------------------------------------------------------
- DefineEngineMethod( InteriorInstance, setAlarmMode, void, ( const char* alarmMode),,
- "@brief This sets the alarm mode of the interior\n\n"
- "The alarm mode is used when debugging bad geometry for an interior. When on, the the bad verties "
- "will be rendered a different color.\n\n"
- "@param alarmMode If true the interior will be in an alarm state next frame. Options are \'On\' or \'Off\'.\n\n"
-
- "@tsexample\n"
- "// Turn on alarm mode debugging for interior\n"
- "%interiorObject.setAlarmMode(\"On\");\n"
- "@endtsexample\n\n")
- {
- AssertFatal(dynamic_cast<InteriorInstance*>(object) != NULL,
- "Error, how did a non-interior get here?");
- bool alarm;
- if (dStricmp(alarmMode, "On") == 0)
- alarm = true;
- else
- alarm = false;
- InteriorInstance* interior = static_cast<InteriorInstance*>(object);
- if (interior->isClientObject()) {
- Con::errorf(ConsoleLogEntry::General, "InteriorInstance: client objects may not receive console commands. Ignored");
- return;
- }
- interior->setAlarmMode(alarm);
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( InteriorInstance, getNumDetailLevels, S32, (),,
- "@brief Get the number of detail levels interior was created with\n\n"
-
- "@tsexample\n"
- "%numLODs = %interiorObject.getNumDetailLevels();\n"
- "echo(%numLODs);\n"
- "@endtsexample\n\n")
- {
- InteriorInstance * instance = static_cast<InteriorInstance*>(object);
- return(instance->getNumDetailLevels());
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( InteriorInstance, setDetailLevel, void, (S32 level),,
- "@brief Manually changes the current detail level, rather than automatically via view distance\n\n"
-
- "@param level Detail level to force.\n\n"
- "@tsexample\n"
- "%interiorObject.setDetailLevel(2);\n"
- "@endtsexample\n\n")
- {
- InteriorInstance * instance = static_cast<InteriorInstance*>(object);
- if(instance->isServerObject())
- {
- NetConnection * toServer = NetConnection::getConnectionToServer();
- NetConnection * toClient = NetConnection::getLocalClientConnection();
- if(!toClient || !toServer)
- return;
- S32 index = toClient->getGhostIndex(instance);
- if(index == -1)
- return;
- InteriorInstance * clientInstance = dynamic_cast<InteriorInstance*>(toServer->resolveGhost(index));
- if(clientInstance)
- clientInstance->setDetailLevel(level);
- }
- else
- instance->setDetailLevel(level);
- }
- //-----------------------------------------------------------------------------
- //These functions are duplicated in tsStatic, shapeBase, and interiorInstance.
- //They each function a little differently; but achieve the same purpose of gathering
- //target names/counts without polluting simObject.
- DefineEngineMethod( InteriorInstance, getTargetName, const char*, (S32 detailLevel, S32 targetNum),,
- "@brief Get the name of the indexed shape material\n\n"
-
- "@param detailLevel Target LOD\n"
- "@param targetNum Index mapped to the target\n\n"
- "@return The name of the target (material) at the specified detail level and index\n\n"
- "@tsexample\n"
- "// First level of detail, top of the index map\n"
- "%targetName = %interiorObject.getTargetName(1, 0);\n"
- "echo(%targetName);\n"
- "@endtsexample\n\n")
- {
- Interior* obj = object->getDetailLevel(detailLevel);
- if(obj)
- return obj->getTargetName(targetNum);
- return "";
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( InteriorInstance, getTargetCount, S32, (U32 detailLevel),,
- "@brief Get the number of materials used by interior\n\n"
-
- "@param detailLevel Interior level of detail to scan\n"
- "@return The number of materials used by the interior at a specified detail level\n\n"
- "@tsexample\n"
- "// Find materials used at first level of detail\n"
- "%targetCount = %interiorObject.getTargetCount(1);\n"
- "echo(%targetCount);\n"
- "@endtsexample\n\n")
- {
- Interior* obj = object->getDetailLevel(detailLevel);
- if(obj)
- return obj->getTargetCount();
- return -1;
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( InteriorInstance, changeMaterial, void, (const char* mapTo, Material* oldMat, Material* newMat),,
- "@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\n"
- "@note Warning, right now this only sort of works. It doesn't do a live "
- "update like it should.\n\b"
- "@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 = %interiorObject.getTargetName( 0 );\n"
- "%interiorObject.changeMaterial( %mapTo, 0, MyMaterial );\n"
- "@endtsexample\n" )
- {
- // if no valid new material, theres no reason for doing this
- if( !newMat )
- {
- Con::errorf("InteriorInstance::changeMaterial failed: New material does not exist!");
- return;
- }
- // simple parsing through the interiors detail levels looking for the correct mapto.
- // break when we find the correct detail level to depend on.
- U32 level;
- S32 matIndex = -1;
- for( level = 0; level < object->getNumDetailLevels(); level++ )
- {
- matIndex = object->getDetailLevel(level)->mMaterialList->getMaterialNameList().find_next(String(mapTo));
- if (matIndex >= 0)
- break;
- }
- if (matIndex == -1)
- {
- Con::errorf("InteriorInstance::changeMaterial failed: Invalid mapTo name '%s'", mapTo);
- return;
- }
- // initilize server/client versions
- Interior *serverObj = object->getDetailLevel( level );
- InteriorInstance * instanceClientObj = dynamic_cast< InteriorInstance* > ( object->getClientObject() );
- Interior *clientObj = instanceClientObj ? instanceClientObj->getDetailLevel( level ) : NULL;
- if(serverObj)
- {
- // 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 in the in the matmgr
- MATMGR->mapMaterial( mapTo, newMat->mMapTo );
- // Replace instances with the new material being traded in. Lets make sure that we only
- // target the specific targets per inst. This technically is only done here for interiors for
- // safe keeping. The remapping that truly matters most (for on the fly changes) are done in the node lists
- delete serverObj->mMaterialList->mMatInstList[matIndex];
- serverObj->mMaterialList->mMatInstList[matIndex] = newMat->createMatInstance();
- // Finishing the safekeeping
- const GFXVertexFormat *flags = getGFXVertexFormat<GFXVertexPNTTB>();
- FeatureSet features = MATMGR->getDefaultFeatures();
- serverObj->mMaterialList->getMaterialInst(matIndex)->init( features, flags );
- if (clientObj)
- {
- delete clientObj->mMaterialList->mMatInstList[matIndex];
- clientObj->mMaterialList->mMatInstList[matIndex] = newMat->createMatInstance();
- clientObj->mMaterialList->getMaterialInst(matIndex)->init( features, flags );
- // These loops are referenced in interior.cpp's initMatInstances
- // Made a couple of alterations to tailor specifically towards one changing one instance
- for( U32 i=0; i<clientObj->getNumZones(); i++ )
- {
- for( U32 j=0; j<clientObj->mZoneRNList[i].renderNodeList.size(); j++ )
- {
- BaseMatInstance *matInst = clientObj->mZoneRNList[i].renderNodeList[j].matInst;
- Material* refMat = dynamic_cast<Material*>(matInst->getMaterial());
- if(refMat == oldMat)
- {
- clientObj->mZoneRNList[i].renderNodeList[j].matInst = newMat->createMatInstance();
- clientObj->mZoneRNList[i].renderNodeList[j].matInst->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTTB>());
- //if ( pMat )
- //mHasTranslucentMaterials |= pMat->mTranslucent && !pMat->mTranslucentZWrite;
- }
- }
- }
- // Lets reset the clientObj settings in order to accomodate the new material
- clientObj->fillSurfaceTexMats();
- clientObj->createZoneVBs();
- clientObj->cloneMatInstances();
- clientObj->createReflectPlanes();
- clientObj->initMatInstances();
- }
- }
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( InteriorInstance, getModelFile, const char*, (),,
- "@brief Get the interior file name\n\n"
-
- "@return The name of the interior's model file in .DIF.\n\n"
- "@tsexample\n"
- "%interiorObject.getModelFile();\n"
- "@endtsexample\n\n")
- {
- return object->getInteriorFileName();
- }
|