interiorInstance.cpp 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "interior/interiorInstance.h"
  24. #include "interior/interior.h"
  25. #include "console/consoleTypes.h"
  26. #include "scene/sceneManager.h"
  27. #include "scene/sceneRenderState.h"
  28. #include "scene/zones/sceneTraversalState.h"
  29. #include "scene/zones/sceneRootZone.h"
  30. #include "core/stream/bitStream.h"
  31. #include "core/stream/fileStream.h"
  32. #include "gfx/bitmap/gBitmap.h"
  33. #include "math/mathIO.h"
  34. #include "gui/worldEditor/editor.h"
  35. #include "interior/interiorResObjects.h"
  36. #include "scene/simPath.h"
  37. #include "interior/forceField.h"
  38. #include "lighting/lightManager.h"
  39. #include "collision/convex.h"
  40. #include "sfx/sfxProfile.h"
  41. #include "sfx/sfxEnvironment.h"
  42. #include "core/frameAllocator.h"
  43. #include "sim/netConnection.h"
  44. #include "platform/profiler.h"
  45. #include "gui/3d/guiTSControl.h"
  46. #include "math/mathUtils.h"
  47. #include "renderInstance/renderPassManager.h"
  48. #include "core/resourceManager.h"
  49. #include "materials/materialManager.h"
  50. #include "materials/materialFeatureTypes.h"
  51. #include "materials/matInstance.h"
  52. #include "collision/concretePolyList.h"
  53. #include "T3D/physics/physicsPlugin.h"
  54. #include "T3D/physics/physicsBody.h"
  55. #include "T3D/physics/physicsCollision.h"
  56. #include "console/engineAPI.h"
  57. #ifdef TORQUE_COLLADA
  58. #include "ts/collada/colladaUtils.h"
  59. #endif
  60. IMPLEMENT_CO_NETOBJECT_V1(InteriorInstance);
  61. ConsoleDocClass( InteriorInstance,
  62. "@brief Object used to represent buildings and other architectural structures (legacy).\n\n"
  63. "Interiors are made up entirely from convex hulls or, as they are more commonly known as by game "
  64. "artists, brushes. So what you see is what you collide against. There is no difference between the "
  65. "visible meshes and the collision meshes.\n\n"
  66. "Unlike a DTS or COLLADA mesh, interiors do not support any animation. They also do not support "
  67. "transparent textures. If you need animation or transparency then you are forced to use other model objects.\n\n"
  68. "It is important to note that interiors are no longer the preferred format for large structures. It is an "
  69. "old format, which does not have much to offer above DTS or COLLADA. They are still included in Torque 3D "
  70. "for the sake of backwards compatibility for developers porting older TGE or TGEA projects. It will be "
  71. "deprecated soon.\n\n"
  72. "@ingroup gameObjects"
  73. );
  74. static const U32 csgMaxZoneSize = 256;
  75. static bool sgScopeBoolArray[256];
  76. bool InteriorInstance::smDontRestrictOutside = false;
  77. F32 InteriorInstance::smDetailModification = 1.0f;
  78. //-----------------------------------------------------------------------------
  79. InteriorInstance::InteriorInstance()
  80. {
  81. mAlarmState = false;
  82. mInteriorFileName = NULL;
  83. mTypeMask |= InteriorObjectType | StaticObjectType | StaticShapeObjectType;
  84. mZoneFlags.clear( ZoneFlag_IsClosedOffSpace ); // Interiors are open spaces.
  85. mForcedDetailLevel = -1;
  86. mConvexList = new Convex;
  87. mCRC = 0;
  88. mPhysicsRep = NULL;
  89. }
  90. //-----------------------------------------------------------------------------
  91. InteriorInstance::~InteriorInstance()
  92. {
  93. delete mConvexList;
  94. mConvexList = NULL;
  95. // GFX2_RENDER_MERGE
  96. //for (U32 i = 0; i < mReflectPlanes.size(); i++)
  97. // mReflectPlanes[i].clearTextures();
  98. }
  99. //-----------------------------------------------------------------------------
  100. void InteriorInstance::inspectPostApply()
  101. {
  102. // Apply any transformations set in the editor
  103. Parent::inspectPostApply();
  104. // Update the Transform on Editor Apply.
  105. setMaskBits(TransformMask);
  106. }
  107. //-----------------------------------------------------------------------------
  108. void InteriorInstance::initPersistFields()
  109. {
  110. addGroup("Media");
  111. addProtectedField( "interiorFile", TypeFilename, Offset( mInteriorFileName, InteriorInstance ),
  112. &_setInteriorFile, &defaultProtectedGetFn,
  113. "Path and filename of the Interior file (.DIF) to load for this InteriorInstance.");
  114. endGroup("Media");
  115. Parent::initPersistFields();
  116. }
  117. //-----------------------------------------------------------------------------
  118. void InteriorInstance::consoleInit()
  119. {
  120. //-------------------------------------- Class level variables
  121. Con::addVariable( "pref::Interior::VertexLighting", TypeBool, &Interior::smUseVertexLighting,
  122. "Forces all InteriorInstances to not render their lightmaps.\n"
  123. "@ingroup Interior" );
  124. Con::addVariable( "pref::Interior::detailAdjust", TypeF32, &InteriorInstance::smDetailModification,
  125. "Forces all InteriorInstance rendering to a particular detail level.\n"
  126. "@ingroup Interior" );
  127. // DEBUG ONLY!!!
  128. #ifndef TORQUE_SHIPPING
  129. Con::addVariable( "Interior::DontRestrictOutside", TypeBool, &smDontRestrictOutside,
  130. "Render only the outside zone of all InteriorInstances.\n"
  131. "@ingroup Interior" );
  132. #endif
  133. }
  134. //-----------------------------------------------------------------------------
  135. #ifdef TORQUE_COLLADA
  136. void InteriorInstance::exportToCollada(bool bakeTransform)
  137. {
  138. if (mInteriorRes->getNumDetailLevels() == 0)
  139. {
  140. Con::errorf("InteriorInstance::exportToCollada() called an InteriorInstance with no Interior");
  141. return;
  142. }
  143. // For now I am only worrying about the highest lod
  144. Interior* pInterior = mInteriorRes->getDetailLevel(0);
  145. if (!pInterior)
  146. {
  147. Con::errorf("InteriorInstance::exportToCollada() called an InteriorInstance with an invalid Interior");
  148. return;
  149. }
  150. // Get an optimized version of our mesh
  151. OptimizedPolyList interiorMesh;
  152. if (bakeTransform)
  153. {
  154. MatrixF mat = getTransform();
  155. Point3F scale = getScale();
  156. pInterior->buildExportPolyList(interiorMesh, &mat, &scale);
  157. }
  158. else
  159. pInterior->buildExportPolyList(interiorMesh);
  160. // Get our export path
  161. Torque::Path colladaFile = mInteriorRes.getPath();
  162. // Make sure to set our Collada extension
  163. colladaFile.setExtension("dae");
  164. // Use the InteriorInstance name if possible
  165. String meshName = getName();
  166. // Otherwise use the DIF's file name
  167. if (meshName.isEmpty())
  168. meshName = colladaFile.getFileName();
  169. // If we are baking the transform then append
  170. // a CRC version of the transform to the mesh/file name
  171. if (bakeTransform)
  172. {
  173. F32 trans[19];
  174. const MatrixF& mat = getTransform();
  175. const Point3F& scale = getScale();
  176. // Copy in the transform
  177. for (U32 i = 0; i < 4; i++)
  178. {
  179. for (U32 j = 0; j < 4; j++)
  180. {
  181. trans[i * 4 + j] = mat(i, j);
  182. }
  183. }
  184. // Copy in the scale
  185. trans[16] = scale.x;
  186. trans[17] = scale.y;
  187. trans[18] = scale.z;
  188. U32 crc = CRC::calculateCRC(trans, sizeof(F32) * 19);
  189. meshName += String::ToString("_%x", crc);
  190. }
  191. // Set the file name as the meshName
  192. colladaFile.setFileName(meshName);
  193. // Use a ColladaUtils function to do the actual export to a Collada file
  194. ColladaUtils::exportToCollada(colladaFile, interiorMesh, meshName);
  195. }
  196. #endif
  197. //-----------------------------------------------------------------------------
  198. bool InteriorInstance::onAdd()
  199. {
  200. if(! _loadInterior())
  201. return false;
  202. if(!Parent::onAdd())
  203. return false;
  204. addToScene();
  205. if ( PHYSICSMGR && mInteriorRes && mInteriorRes->getNumDetailLevels() > 0 )
  206. {
  207. // TODO: We need to cache the collision by resource name
  208. // and reuse it across multiple instances.
  209. // Get the interior collision geometry.
  210. ConcretePolyList polylist;
  211. mInteriorRes->getDetailLevel(0)->buildPolyList( &polylist, Box3F(999999.0f), MatrixF::Identity, getScale() );
  212. polylist.triangulate();
  213. // Look out... this could possibly happen!
  214. if ( !polylist.isEmpty() )
  215. {
  216. // Use a triangle mesh for collision.
  217. PhysicsCollision *colShape = PHYSICSMGR->createCollision();
  218. colShape->addTriangleMesh( polylist.mVertexList.address(),
  219. polylist.mVertexList.size(),
  220. polylist.mIndexList.address(),
  221. polylist.mIndexList.size() / 3,
  222. MatrixF::Identity );
  223. PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
  224. mPhysicsRep = PHYSICSMGR->createBody();
  225. mPhysicsRep->init( colShape, 0, 0, this, world );
  226. mPhysicsRep->setTransform( getTransform() );
  227. }
  228. }
  229. return true;
  230. }
  231. //-----------------------------------------------------------------------------
  232. void InteriorInstance::onRemove()
  233. {
  234. SAFE_DELETE( mPhysicsRep );
  235. _unloadInterior();
  236. removeFromScene();
  237. Parent::onRemove();
  238. }
  239. //-----------------------------------------------------------------------------
  240. bool InteriorInstance::_loadInterior()
  241. {
  242. U32 i;
  243. // Load resource
  244. mInteriorRes = ResourceManager::get().load(mInteriorFileName);
  245. if (bool(mInteriorRes) == false) {
  246. Con::errorf(ConsoleLogEntry::General, "Unable to load interior: %s", mInteriorFileName);
  247. NetConnection::setLastError("Unable to load interior: %s", mInteriorFileName);
  248. return false;
  249. }
  250. if(isClientObject())
  251. {
  252. if(mCRC != mInteriorRes.getChecksum())
  253. {
  254. NetConnection::setLastError("Local interior file '%s' does not match version on server.", mInteriorFileName);
  255. return false;
  256. }
  257. for (i = 0; i < mInteriorRes->getNumDetailLevels(); i++) {
  258. // ok, if the material list load failed...
  259. // if this is a local connection, we'll assume that's ok
  260. // and just have white textures...
  261. // otherwise we want to return false.
  262. Interior* pInterior = mInteriorRes->getDetailLevel(i);
  263. if(!pInterior->prepForRendering(mInteriorRes.getPath().getFullPath().c_str()) )
  264. {
  265. if(!bool(mServerObject))
  266. {
  267. return false;
  268. }
  269. }
  270. }
  271. // copy planar reflect list from top detail level - for now
  272. Interior* pInterior = mInteriorRes->getDetailLevel(0);
  273. if( pInterior->mReflectPlanes.size() )
  274. {
  275. for ( i = 0; i < pInterior->mReflectPlanes.size(); i++ )
  276. {
  277. mPlaneReflectors.increment();
  278. PlaneReflector &plane = mPlaneReflectors.last();
  279. plane.refplane = pInterior->mReflectPlanes[i];
  280. plane.objectSpace = true;
  281. plane.registerReflector( this, &mReflectorDesc );
  282. }
  283. }
  284. }
  285. else
  286. mCRC = mInteriorRes.getChecksum();
  287. // Ok, everything's groovy! Let's cache our hashed filename for renderimage sorting...
  288. mInteriorFileHash = _StringTable::hashString(mInteriorFileName);
  289. // Setup bounding information
  290. mObjBox = mInteriorRes->getDetailLevel(0)->getBoundingBox();
  291. resetWorldBox();
  292. setRenderTransform(mObjToWorld);
  293. // Do any handle loading, etc. required.
  294. if (isClientObject()) {
  295. for (i = 0; i < mInteriorRes->getNumDetailLevels(); i++) {
  296. Interior* pInterior = mInteriorRes->getDetailLevel(i);
  297. // Force the lightmap manager to download textures if we're
  298. // running the mission editor. Normally they are only
  299. // downloaded after the whole scene is lit.
  300. gInteriorLMManager.addInstance(pInterior->getLMHandle(), mLMHandle, this);
  301. if (gEditingMission) {
  302. gInteriorLMManager.useBaseTextures(pInterior->getLMHandle(), mLMHandle);
  303. gInteriorLMManager.downloadGLTextures(pInterior->getLMHandle());
  304. }
  305. // Install material list
  306. // mMaterialMaps.push_back(new MaterialList(pInterior->mMaterialList));
  307. }
  308. } else {
  309. }
  310. setMaskBits(0xffffffff);
  311. return true;
  312. }
  313. //-----------------------------------------------------------------------------
  314. void InteriorInstance::_unloadInterior()
  315. {
  316. mConvexList->nukeList();
  317. delete mConvexList;
  318. mConvexList = new Convex;
  319. if(isClientObject())
  320. {
  321. if(bool(mInteriorRes) && mLMHandle != 0xFFFFFFFF)
  322. {
  323. for(U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++)
  324. {
  325. Interior * pInterior = mInteriorRes->getDetailLevel(i);
  326. if (pInterior->getLMHandle() != 0xFFFFFFFF)
  327. gInteriorLMManager.removeInstance(pInterior->getLMHandle(), mLMHandle);
  328. }
  329. }
  330. if( mPlaneReflectors.size() )
  331. {
  332. for ( U32 i = 0; i < mPlaneReflectors.size(); i++ )
  333. {
  334. mPlaneReflectors[i].unregisterReflector();
  335. }
  336. mPlaneReflectors.clear();
  337. }
  338. }
  339. }
  340. //-----------------------------------------------------------------------------
  341. bool InteriorInstance::onSceneAdd()
  342. {
  343. AssertFatal(mInteriorRes, "Error, should not have been added to the scene if there's no interior!");
  344. if (Parent::onSceneAdd() == false)
  345. return false;
  346. U32 maxNumZones = 0;
  347. for (U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++)
  348. {
  349. if (mInteriorRes->getDetailLevel(i)->mZones.size() > maxNumZones)
  350. maxNumZones = mInteriorRes->getDetailLevel(i)->mZones.size();
  351. }
  352. if( maxNumZones > 1 )
  353. {
  354. SceneZoneSpaceManager* zoneManager = getSceneManager()->getZoneManager();
  355. if( zoneManager )
  356. {
  357. zoneManager->registerZones(this, (maxNumZones - 1));
  358. // Connect to outdoor zone.
  359. zoneManager->getRootZone()->connectZoneSpace( this );
  360. connectZoneSpace( zoneManager->getRootZone() );
  361. }
  362. }
  363. return true;
  364. }
  365. //-----------------------------------------------------------------------------
  366. void InteriorInstance::onSceneRemove()
  367. {
  368. // Disconnect from root zone in case we have connected.
  369. SceneZoneSpaceManager* zoneManager = getSceneManager()->getZoneManager();
  370. if( zoneManager )
  371. zoneManager->getRootZone()->disconnectZoneSpace( this );
  372. Parent::onSceneRemove();
  373. }
  374. //-----------------------------------------------------------------------------
  375. bool InteriorInstance::_getOverlappingZones( const Box3F& aabb, const MatrixF& transform, const Point3F& scale, U32* outZones, U32& outNumZones )
  376. {
  377. MatrixF xForm(true);
  378. Point3F invScale(1.0f / getScale().x,
  379. 1.0f / getScale().y,
  380. 1.0f / getScale().z);
  381. xForm.scale(invScale);
  382. xForm.mul(getWorldTransform());
  383. xForm.mul(transform);
  384. xForm.scale(scale);
  385. U32 waterMark = FrameAllocator::getWaterMark();
  386. U16* zoneVector = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mZones.size() * sizeof(U16));
  387. U32 numRetZones = 0;
  388. bool outsideToo = mInteriorRes->getDetailLevel(0)->scanZones(aabb,
  389. xForm,
  390. zoneVector,
  391. &numRetZones
  392. );
  393. if (numRetZones > SceneObject::MaxObjectZones)
  394. {
  395. Con::warnf(ConsoleLogEntry::General, "Too many zones returned for query on %s. Returning first %d",
  396. mInteriorFileName, SceneObject::MaxObjectZones);
  397. }
  398. for (U32 i = 0; i < getMin(numRetZones, U32(SceneObject::MaxObjectZones)); i++)
  399. outZones[i] = zoneVector[i] + mZoneRangeStart - 1;
  400. outNumZones = numRetZones;
  401. FrameAllocator::setWaterMark(waterMark);
  402. return outsideToo;
  403. }
  404. //-----------------------------------------------------------------------------
  405. bool InteriorInstance::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones )
  406. {
  407. return _getOverlappingZones( aabb, MatrixF::Identity, Point3F( 1.f, 1.f, 1.f ), outZones, outNumZones );
  408. }
  409. //-----------------------------------------------------------------------------
  410. bool InteriorInstance::getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones )
  411. {
  412. return _getOverlappingZones( obj->getObjBox(), obj->getTransform(), obj->getScale(), outZones, outNumZones );
  413. }
  414. //-----------------------------------------------------------------------------
  415. U32 InteriorInstance::getPointZone(const Point3F& p)
  416. {
  417. AssertFatal(mInteriorRes, "Error, no interior!");
  418. Point3F osPoint = p;
  419. mWorldToObj.mulP(osPoint);
  420. osPoint.convolveInverse(mObjScale);
  421. S32 zone = mInteriorRes->getDetailLevel(0)->getZoneForPoint(osPoint);
  422. // If we're in solid (-1) or outside, we need to return 0
  423. if (zone == -1 || zone == 0)
  424. return SceneZoneSpaceManager::InvalidZoneId;
  425. return (zone-1) + mZoneRangeStart;
  426. }
  427. //-----------------------------------------------------------------------------
  428. // does a hack check to determine how much a point is 'inside'.. should have
  429. // portals prebuilt with the transfer energy to each other portal in the zone
  430. // from the neighboring zone.. these values can be used to determine the factor
  431. // from within an individual zone.. also, each zone could be marked with
  432. // average material property for eax environment audio
  433. // ~0: outside -> 1: inside
  434. bool InteriorInstance::getPointInsideScale(const Point3F & pos, F32 * pScale)
  435. {
  436. AssertFatal(mInteriorRes, "InteriorInstance::getPointInsideScale: no interior");
  437. Interior * interior = mInteriorRes->getDetailLevel(0);
  438. Point3F p = pos;
  439. mWorldToObj.mulP(p);
  440. p.convolveInverse(mObjScale);
  441. U32 zoneIndex = interior->getZoneForPoint(p);
  442. if(zoneIndex == -1) // solid?
  443. {
  444. *pScale = 1.f;
  445. return(true);
  446. }
  447. else if(zoneIndex == 0) // outside?
  448. {
  449. *pScale = 0.f;
  450. return(true);
  451. }
  452. U32 waterMark = FrameAllocator::getWaterMark();
  453. const Interior::Portal** portals = (const Interior::Portal**)FrameAllocator::alloc(256 * sizeof(const Interior::Portal*));
  454. U32 numPortals = 0;
  455. Interior::Zone & zone = interior->mZones[zoneIndex];
  456. U32 i;
  457. for(i = 0; i < zone.portalCount; i++)
  458. {
  459. const Interior::Portal & portal = interior->mPortals[interior->mZonePortalList[zone.portalStart + i]];
  460. if(portal.zoneBack == 0 || portal.zoneFront == 0) {
  461. AssertFatal(numPortals < 256, "Error, overflow in temporary portal buffer!");
  462. portals[numPortals++] = &portal;
  463. }
  464. }
  465. // inside?
  466. if(numPortals == 0)
  467. {
  468. *pScale = 1.f;
  469. FrameAllocator::setWaterMark(waterMark);
  470. return(true);
  471. }
  472. Point3F* portalCenters = (Point3F*)FrameAllocator::alloc(numPortals * sizeof(Point3F));
  473. U32 numPortalCenters = 0;
  474. // scale using the distances to the portals in this zone...
  475. for(i = 0; i < numPortals; i++)
  476. {
  477. const Interior::Portal * portal = portals[i];
  478. if(!portal->triFanCount)
  479. continue;
  480. Point3F center(0, 0, 0);
  481. for(U32 j = 0; j < portal->triFanCount; j++)
  482. {
  483. const Interior::TriFan & fan = interior->mWindingIndices[portal->triFanStart + j];
  484. U32 numPoints = fan.windingCount;
  485. if(!numPoints)
  486. continue;
  487. for(U32 k = 0; k < numPoints; k++)
  488. {
  489. const Point3F & a = interior->mPoints[interior->mWindings[fan.windingStart + k]].point;
  490. center += a;
  491. }
  492. center /= (F32)numPoints;
  493. portalCenters[numPortalCenters++] = center;
  494. }
  495. }
  496. // 'magic' check here...
  497. F32 magic = Con::getFloatVariable("Interior::insideDistanceFalloff", 10.f);
  498. F32 val = 0.f;
  499. for(i = 0; i < numPortalCenters; i++)
  500. val += 1.f - mClampF(Point3F(portalCenters[i] - p).len() / magic, 0.f, 1.f);
  501. *pScale = 1.f - mClampF(val, 0.f, 1.f);
  502. FrameAllocator::setWaterMark(waterMark);
  503. return(true);
  504. }
  505. //-----------------------------------------------------------------------------
  506. void InteriorInstance::_renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat )
  507. {
  508. #ifndef TORQUE_SHIPPING
  509. if (Interior::smRenderMode == 0)
  510. return;
  511. if (overrideMat)
  512. return;
  513. if(gEditingMission && isHidden())
  514. return;
  515. U32 detailLevel = 0;
  516. detailLevel = _calcDetailLevel(state, state->getCameraPosition());
  517. Interior* pInterior = mInteriorRes->getDetailLevel( detailLevel );
  518. if (!pInterior)
  519. return;
  520. PROFILE_START( IRO_DebugRender );
  521. GFX->pushWorldMatrix();
  522. // setup world matrix - for fixed function
  523. MatrixF world = GFX->getWorldMatrix();
  524. world.mul( getRenderTransform() );
  525. world.scale( getScale() );
  526. GFX->setWorldMatrix( world );
  527. // setup world matrix - for shaders
  528. MatrixF proj = GFX->getProjectionMatrix();
  529. proj.mul(world);
  530. SceneData sgData;
  531. sgData = pInterior->setupSceneGraphInfo( this, state );
  532. ZoneVisDeterminer zoneVis = pInterior->setupZoneVis( this, state );
  533. pInterior->debugRender( zoneVis, sgData, this, proj );
  534. GFX->popWorldMatrix();
  535. PROFILE_END();
  536. #endif
  537. }
  538. //-----------------------------------------------------------------------------
  539. U32 InteriorInstance::_calcDetailLevel(SceneRenderState* state, const Point3F& wsPoint)
  540. {
  541. AssertFatal(mInteriorRes, "Error, should not try to calculate the deatil level without a resource to work with!");
  542. AssertFatal(_getNumCurrZones() > 0, "Error, must belong to a zone for this to work");
  543. if (smDetailModification < 0.3f)
  544. smDetailModification = 0.3f;
  545. if (smDetailModification > 1.0f)
  546. smDetailModification = 1.0f;
  547. // Early out for simple interiors
  548. if (mInteriorRes->getNumDetailLevels() == 1)
  549. return 0;
  550. if((mForcedDetailLevel >= 0) && (mForcedDetailLevel < mInteriorRes->getNumDetailLevels()))
  551. return(mForcedDetailLevel);
  552. Point3F osPoint = wsPoint;
  553. mRenderWorldToObj.mulP(osPoint);
  554. osPoint.convolveInverse(mObjScale);
  555. // First, see if the point is in the object space bounding box of the highest detail
  556. // If it is, then the detail level is zero.
  557. if (mObjBox.isContained(osPoint))
  558. return 0;
  559. // Otherwise, we're going to have to do some ugly trickery to get the projection.
  560. // I've stolen the worldToScreenScale from dglMatrix, we'll have to calculate the
  561. // projection of the bounding sphere of the lowest detail level.
  562. // worldToScreenScale = (near * view.extent.x) / (right - left)
  563. F32 worldToScreenScale = state->getWorldToScreenScale().x;
  564. const SphereF& lowSphere = mInteriorRes->getDetailLevel(mInteriorRes->getNumDetailLevels() - 1)->mBoundingSphere;
  565. F32 dist = (lowSphere.center - osPoint).len();
  566. F32 projRadius = (lowSphere.radius / dist) * worldToScreenScale;
  567. // Scale the projRadius based on the objects maximum scale axis
  568. projRadius *= getMax(mFabs(mObjScale.x), getMax(mFabs(mObjScale.y), mFabs(mObjScale.z)));
  569. // Multiply based on detail preference...
  570. projRadius *= smDetailModification;
  571. // Ok, now we have the projected radius, we need to search through the interiors to
  572. // find the largest interior that will support this projection.
  573. U32 final = mInteriorRes->getNumDetailLevels() - 1;
  574. for (U32 i = 0; i< mInteriorRes->getNumDetailLevels() - 1; i++) {
  575. Interior* pDetail = mInteriorRes->getDetailLevel(i);
  576. if (pDetail->mMinPixels < projRadius) {
  577. final = i;
  578. break;
  579. }
  580. }
  581. // Ok, that's it.
  582. return final;
  583. }
  584. //-----------------------------------------------------------------------------
  585. void InteriorInstance::traverseZones( SceneTraversalState* state )
  586. {
  587. U32 startZone = getPointZone( state->getCullingState()->getCameraState().getViewPosition() );
  588. if( startZone != SceneZoneSpaceManager::InvalidZoneId )
  589. startZone = startZone - mZoneRangeStart + 1;
  590. else
  591. startZone = SceneZoneSpaceManager::RootZoneId;
  592. traverseZones( state, startZone );
  593. }
  594. //-----------------------------------------------------------------------------
  595. void InteriorInstance::traverseZones( SceneTraversalState* state, U32 startZoneId )
  596. {
  597. PROFILE_SCOPE( InteriorInstance_traverseZones );
  598. SceneCullingState* cullingState = state->getCullingState();
  599. // [rene, 23-Mar-11] This is a really gross hack. It effectively renders all zoning in interiors
  600. // ineffective and just lets them render with the root frustum. It's just that after untangling
  601. // DMM's mess in the sceneGraph system, I just don't have the energy anymore to also dig through
  602. // the ungodly mess that is the interior code and since this is all quasi-deprecated-and-soon-to-die
  603. // anyway it would just be wasted effort.
  604. for( U32 i = getZoneRangeStart(); i < ( getZoneRangeStart() + getZoneRange() ); ++ i )
  605. cullingState->addCullingVolumeToZone( i, state->getCurrentCullingVolume() );
  606. #if 0
  607. U32 baseZoneForPrep = getCurrZone( 0 );
  608. bool multipleZones = ( getNumCurrZones() > 1 );
  609. Frustum outFrustum;
  610. bool continueOut = mInteriorRes->getDetailLevel( 0 )->traverseZones(
  611. renderState,
  612. frustum,
  613. baseZoneForPrep,
  614. startZoneId,
  615. mZoneRangeStart,
  616. mRenderObjToWorld,
  617. mObjScale,
  618. smDontRestrictOutside | multipleZones,
  619. renderState->isInvertedWorld(),
  620. outFrustum
  621. );
  622. if( smDontRestrictOutside )
  623. continueOut = true;
  624. #endif
  625. if( true )// continueOut )
  626. {
  627. state->pushZone( startZoneId );
  628. _traverseConnectedZoneSpaces( state );
  629. state->popZone();
  630. }
  631. }
  632. //-----------------------------------------------------------------------------
  633. void InteriorInstance::prepRenderImage( SceneRenderState* state )
  634. {
  635. PROFILE_SCOPE( InteriorInstance_prepRenderImage );
  636. U32 detailLevel = _calcDetailLevel( state, state->getCameraPosition() );
  637. Interior* pInterior = getResource()->getDetailLevel( detailLevel );
  638. pInterior->prepBatchRender( this, state );
  639. }
  640. //-----------------------------------------------------------------------------
  641. bool InteriorInstance::castRay(const Point3F& s, const Point3F& e, RayInfo* info)
  642. {
  643. info->object = this;
  644. return mInteriorRes->getDetailLevel(0)->castRay(s, e, info);
  645. }
  646. //-----------------------------------------------------------------------------
  647. bool InteriorInstance::buildPolyList(PolyListContext context, AbstractPolyList* list, const Box3F& wsBox, const SphereF&)
  648. {
  649. if (bool(mInteriorRes) == false)
  650. return false;
  651. // Setup collision state data
  652. list->setTransform(&getTransform(), getScale());
  653. list->setObject(this);
  654. return mInteriorRes->getDetailLevel(0)->buildPolyList(list, wsBox, mWorldToObj, getScale());
  655. }
  656. //-----------------------------------------------------------------------------
  657. void InteriorInstance::buildConvex(const Box3F& box, Convex* convex)
  658. {
  659. if (bool(mInteriorRes) == false)
  660. return;
  661. mConvexList->collectGarbage();
  662. Box3F realBox = box;
  663. mWorldToObj.mul(realBox);
  664. realBox.minExtents.convolveInverse(mObjScale);
  665. realBox.maxExtents.convolveInverse(mObjScale);
  666. if (realBox.isOverlapped(getObjBox()) == false)
  667. return;
  668. U32 waterMark = FrameAllocator::getWaterMark();
  669. if ((convex->getObject()->getTypeMask() & VehicleObjectType) &&
  670. mInteriorRes->getDetailLevel(0)->mVehicleConvexHulls.size() > 0)
  671. {
  672. // Can never have more hulls than there are hulls in the interior...
  673. U16* hulls = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mVehicleConvexHulls.size() * sizeof(U16));
  674. U32 numHulls = 0;
  675. Interior* pInterior = mInteriorRes->getDetailLevel(0);
  676. if (pInterior->getIntersectingVehicleHulls(realBox, hulls, &numHulls) == false) {
  677. FrameAllocator::setWaterMark(waterMark);
  678. return;
  679. }
  680. for (U32 i = 0; i < numHulls; i++) {
  681. // See if this hull exists in the working set already...
  682. Convex* cc = 0;
  683. CollisionWorkingList& wl = convex->getWorkingList();
  684. for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
  685. if (itr->mConvex->getType() == InteriorConvexType &&
  686. (static_cast<InteriorConvex*>(itr->mConvex)->getObject() == this &&
  687. static_cast<InteriorConvex*>(itr->mConvex)->hullId == -S32(hulls[i] + 1))) {
  688. cc = itr->mConvex;
  689. break;
  690. }
  691. }
  692. if (cc)
  693. continue;
  694. // Create a new convex.
  695. InteriorConvex* cp = new InteriorConvex;
  696. mConvexList->registerObject(cp);
  697. convex->addToWorkingList(cp);
  698. cp->mObject = this;
  699. cp->pInterior = pInterior;
  700. cp->hullId = -S32(hulls[i] + 1);
  701. cp->box.minExtents.x = pInterior->mVehicleConvexHulls[hulls[i]].minX;
  702. cp->box.minExtents.y = pInterior->mVehicleConvexHulls[hulls[i]].minY;
  703. cp->box.minExtents.z = pInterior->mVehicleConvexHulls[hulls[i]].minZ;
  704. cp->box.maxExtents.x = pInterior->mVehicleConvexHulls[hulls[i]].maxX;
  705. cp->box.maxExtents.y = pInterior->mVehicleConvexHulls[hulls[i]].maxY;
  706. cp->box.maxExtents.z = pInterior->mVehicleConvexHulls[hulls[i]].maxZ;
  707. }
  708. }
  709. else
  710. {
  711. // Can never have more hulls than there are hulls in the interior...
  712. U16* hulls = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mConvexHulls.size() * sizeof(U16));
  713. U32 numHulls = 0;
  714. Interior* pInterior = mInteriorRes->getDetailLevel(0);
  715. if (pInterior->getIntersectingHulls(realBox, hulls, &numHulls) == false) {
  716. FrameAllocator::setWaterMark(waterMark);
  717. return;
  718. }
  719. for (U32 i = 0; i < numHulls; i++) {
  720. // See if this hull exists in the working set already...
  721. Convex* cc = 0;
  722. CollisionWorkingList& wl = convex->getWorkingList();
  723. for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) {
  724. if (itr->mConvex->getType() == InteriorConvexType &&
  725. (static_cast<InteriorConvex*>(itr->mConvex)->getObject() == this &&
  726. static_cast<InteriorConvex*>(itr->mConvex)->hullId == hulls[i])) {
  727. cc = itr->mConvex;
  728. break;
  729. }
  730. }
  731. if (cc)
  732. continue;
  733. // Create a new convex.
  734. InteriorConvex* cp = new InteriorConvex;
  735. mConvexList->registerObject(cp);
  736. convex->addToWorkingList(cp);
  737. cp->mObject = this;
  738. cp->pInterior = pInterior;
  739. cp->hullId = hulls[i];
  740. cp->box.minExtents.x = pInterior->mConvexHulls[hulls[i]].minX;
  741. cp->box.minExtents.y = pInterior->mConvexHulls[hulls[i]].minY;
  742. cp->box.minExtents.z = pInterior->mConvexHulls[hulls[i]].minZ;
  743. cp->box.maxExtents.x = pInterior->mConvexHulls[hulls[i]].maxX;
  744. cp->box.maxExtents.y = pInterior->mConvexHulls[hulls[i]].maxY;
  745. cp->box.maxExtents.z = pInterior->mConvexHulls[hulls[i]].maxZ;
  746. }
  747. }
  748. FrameAllocator::setWaterMark(waterMark);
  749. }
  750. //-----------------------------------------------------------------------------
  751. U32 InteriorInstance::packUpdate(NetConnection* c, U32 mask, BitStream* stream)
  752. {
  753. U32 retMask = Parent::packUpdate(c, mask, stream);
  754. if (stream->writeFlag((mask & InitMask) != 0)) {
  755. // Initial update, write the whole kit and kaboodle
  756. stream->write(mCRC);
  757. stream->writeString(mInteriorFileName);
  758. // Write the alarm state
  759. stream->writeFlag(mAlarmState);
  760. }
  761. else
  762. {
  763. stream->writeFlag(mAlarmState);
  764. }
  765. return retMask;
  766. }
  767. //-----------------------------------------------------------------------------
  768. void InteriorInstance::unpackUpdate(NetConnection* c, BitStream* stream)
  769. {
  770. Parent::unpackUpdate(c, stream);
  771. MatrixF temp;
  772. Point3F tempScale;
  773. if (stream->readFlag()) {
  774. bool isNewUpdate(mInteriorRes);
  775. if(isNewUpdate)
  776. _unloadInterior();
  777. // Initial Update
  778. // CRC
  779. stream->read(&mCRC);
  780. // File
  781. mInteriorFileName = stream->readSTString();
  782. // Alarm state: Note that we handle this ourselves on the initial update
  783. // so that the state is always full on or full off...
  784. mAlarmState = stream->readFlag();
  785. if(isNewUpdate)
  786. {
  787. if(! _loadInterior())
  788. Con::errorf("InteriorInstance::unpackUpdate - Unable to load new interior");
  789. }
  790. }
  791. else
  792. {
  793. setAlarmMode(stream->readFlag());
  794. }
  795. }
  796. //-----------------------------------------------------------------------------
  797. Interior* InteriorInstance::getDetailLevel(const U32 level)
  798. {
  799. return mInteriorRes->getDetailLevel(level);
  800. }
  801. //-----------------------------------------------------------------------------
  802. U32 InteriorInstance::getNumDetailLevels()
  803. {
  804. return mInteriorRes->getNumDetailLevels();
  805. }
  806. //-----------------------------------------------------------------------------
  807. void InteriorInstance::setAlarmMode(const bool alarm)
  808. {
  809. if (mInteriorRes->getDetailLevel(0)->mHasAlarmState == false)
  810. return;
  811. if (mAlarmState == alarm)
  812. return;
  813. mAlarmState = alarm;
  814. if (isServerObject())
  815. {
  816. setMaskBits(AlarmMask);
  817. }
  818. else
  819. {
  820. // DMMTODO: Invalidate current light state
  821. }
  822. }
  823. //-----------------------------------------------------------------------------
  824. void InteriorInstance::_createTriggerTransform(const InteriorResTrigger* trigger, MatrixF* transform)
  825. {
  826. Point3F offset;
  827. MatrixF xform = getTransform();
  828. xform.getColumn(3, &offset);
  829. Point3F triggerOffset = trigger->mOffset;
  830. triggerOffset.convolve(mObjScale);
  831. getTransform().mulV(triggerOffset);
  832. offset += triggerOffset;
  833. xform.setColumn(3, offset);
  834. *transform = xform;
  835. }
  836. //-----------------------------------------------------------------------------
  837. bool InteriorInstance::readLightmaps(GBitmap**** lightmaps)
  838. {
  839. AssertFatal(mInteriorRes, "Error, no interior loaded!");
  840. AssertFatal(lightmaps, "Error, no lightmaps or numdetails result pointers");
  841. AssertFatal(*lightmaps == NULL, "Error, already have a pointer in the lightmaps result field!");
  842. // Load resource
  843. FileStream* pStream;
  844. if((pStream = FileStream::createAndOpen( mInteriorFileName, Torque::FS::File::Read )) == NULL)
  845. {
  846. Con::errorf(ConsoleLogEntry::General, "Unable to load interior: %s", mInteriorFileName);
  847. return false;
  848. }
  849. InteriorResource* pResource = new InteriorResource;
  850. bool success = pResource->read(*pStream);
  851. delete pStream;
  852. if (success == false)
  853. {
  854. delete pResource;
  855. return false;
  856. }
  857. AssertFatal(pResource->getNumDetailLevels() == mInteriorRes->getNumDetailLevels(),
  858. "Mismatched detail levels!");
  859. *lightmaps = new GBitmap**[mInteriorRes->getNumDetailLevels()];
  860. for (U32 i = 0; i < pResource->getNumDetailLevels(); i++)
  861. {
  862. Interior* pInterior = pResource->getDetailLevel(i);
  863. (*lightmaps)[i] = new GBitmap*[pInterior->mLightmaps.size()];
  864. for (U32 j = 0; j < pInterior->mLightmaps.size(); j++)
  865. {
  866. ((*lightmaps)[i])[j] = pInterior->mLightmaps[j];
  867. pInterior->mLightmaps[j] = NULL;
  868. }
  869. pInterior->mLightmaps.clear();
  870. }
  871. delete pResource;
  872. return true;
  873. }
  874. //-----------------------------------------------------------------------------
  875. S32 InteriorInstance::getSurfaceZone(U32 surfaceindex, Interior *detail)
  876. {
  877. AssertFatal(((surfaceindex >= 0) && (surfaceindex < detail->surfaceZones.size())), "Bad surface index!");
  878. S32 zone = detail->surfaceZones[surfaceindex];
  879. if(zone > -1)
  880. return zone + mZoneRangeStart;
  881. return _getCurrZone(0);
  882. }
  883. //-----------------------------------------------------------------------------
  884. bool InteriorInstance::_setInteriorFile( void *object, const char *, const char *data )
  885. {
  886. if(data == NULL)
  887. return true;
  888. InteriorInstance *inst = static_cast<InteriorInstance *>(object);
  889. if(inst->isProperlyAdded())
  890. inst->_unloadInterior();
  891. inst->mInteriorFileName = StringTable->insert(data);
  892. if(inst->isProperlyAdded())
  893. {
  894. if(! inst->_loadInterior())
  895. Con::errorf("InteriorInstance::setInteriorFile - Unable to load new interior");
  896. }
  897. return false;
  898. }
  899. //=============================================================================
  900. // Console API.
  901. //=============================================================================
  902. // MARK: ---- Console API ----
  903. ConsoleFunctionGroupBegin(Interiors, "");
  904. //-----------------------------------------------------------------------------
  905. #ifndef TORQUE_SHIPPING
  906. DefineEngineFunction( setInteriorRenderMode, void, ( S32 mode ),,
  907. "Globally changes how InteriorInstances are rendered. Useful for debugging geometry and rendering artifacts\n"
  908. "@note This does not work in shipping mode\n\n"
  909. "@param mode The render mode can be one of the following numbers:\n\n"
  910. "NormalRender = 0,\n\n"
  911. "NormalRenderLines = 1,\n\n"
  912. "ShowDetail = 2,\n\n"
  913. "ShowAmbiguous = 3,\n\n"
  914. "ShowOrphan = 4,\n\n"
  915. "ShowLightmaps = 5,\n\n"
  916. "ShowTexturesOnly = 6,\n\n"
  917. "ShowPortalZones = 7,\n\n"
  918. "ShowOutsideVisible = 8,\n\n"
  919. "ShowCollisionFans = 9,\n\n"
  920. "ShowStrips = 10,\n\n"
  921. "ShowNullSurfaces = 11,\n\n"
  922. "ShowLargeTextures = 12,\n\n"
  923. "ShowHullSurfaces = 13,\n\n"
  924. "ShowVehicleHullSurfaces = 14,\n\n"
  925. "ShowVertexColors = 15,\n\n"
  926. "ShowDetailLevel = 16\n\n"
  927. "@ingroup Game" )
  928. {
  929. if (mode < 0 || mode > Interior::ShowDetailLevel)
  930. mode = 0;
  931. Interior::smRenderMode = mode;
  932. }
  933. //-----------------------------------------------------------------------------
  934. ConsoleFunction( setInteriorFocusedDebug, void, 2, 2, "(bool enable)"
  935. "@brief No longer properly supported\n\n"
  936. "@internal")
  937. {
  938. if (dAtob(argv[1])) {
  939. Interior::smFocusedDebug = true;
  940. } else {
  941. Interior::smFocusedDebug = false;
  942. }
  943. }
  944. #endif
  945. //-----------------------------------------------------------------------------
  946. ConsoleDocFragment _isPointInside1(
  947. "@brief Check to see if a point in world space is inside of an interior.\n\n"
  948. "@param position The position to check in world space.\n\n"
  949. "@tsexample\n"
  950. "// Check to see if a point is inside any interior\n"
  951. "%point = \"100 100 100\";\n"
  952. "%isInside = isPointInside(%point);\n"
  953. "@endtsexample\n\n"
  954. "@ingroup Game",
  955. NULL,
  956. "bool isPointInside( Point3F position );"
  957. );
  958. ConsoleDocFragment _isPointInside2(
  959. "Check to see if a set of coordinates in world space are inside of an interior.\n\n"
  960. "@param x X-coordinate for position in world space.\n"
  961. "@param y Y-coordinate for position in world space.\n"
  962. "@param z Z-coordinate for position in world space.\n"
  963. "@tsexample\n\n"
  964. "// Check to see if a point is inside any interior\n"
  965. "%isInside = isPointInside(100, 100, 100);\n"
  966. "@endtsexample\n\n"
  967. "@ingroup Game",
  968. NULL,
  969. "bool isPointInside( F32 x, F32 y, F32 z );"
  970. );
  971. ConsoleFunction( isPointInside, bool, 2, 4, "Check to see if a point in world space is inside of an interior."
  972. "@hide")
  973. {
  974. static bool lastValue = false;
  975. if(!(argc == 2 || argc == 4))
  976. {
  977. Con::errorf(ConsoleLogEntry::General, "cIsPointInside: invalid parameters");
  978. return(lastValue);
  979. }
  980. Point3F pos;
  981. if(argc == 2)
  982. dSscanf(argv[1], "%g %g %g", &pos.x, &pos.y, &pos.z);
  983. else
  984. {
  985. pos.x = dAtof(argv[1]);
  986. pos.y = dAtof(argv[2]);
  987. pos.z = dAtof(argv[3]);
  988. }
  989. RayInfo collision;
  990. if(gClientContainer.castRay(pos, Point3F(pos.x, pos.y, pos.z - 2000.f), InteriorObjectType, &collision))
  991. {
  992. if(collision.face == -1)
  993. Con::errorf(ConsoleLogEntry::General, "cIsPointInside: failed to find hit face on interior");
  994. else
  995. {
  996. InteriorInstance * interior = dynamic_cast<InteriorInstance *>(collision.object);
  997. if(interior)
  998. lastValue = !interior->getDetailLevel(0)->isSurfaceOutsideVisible(collision.face);
  999. else
  1000. Con::errorf(ConsoleLogEntry::General, "cIsPointInside: invalid interior on collision");
  1001. }
  1002. }
  1003. return(lastValue);
  1004. }
  1005. ConsoleFunctionGroupEnd(Interiors);
  1006. //-----------------------------------------------------------------------------
  1007. #ifdef TORQUE_COLLADA
  1008. DefineEngineMethod( InteriorInstance, exportToCollada, void, ( bool bakeTransform ),,
  1009. "@brief Exports the Interior to a Collada file\n\n"
  1010. "@param bakeTransform Bakes the InteriorInstance's transform into the vertex positions\n\n"
  1011. "@tsexample\n"
  1012. "// Export to COLLADA, do not bakeTransform\n"
  1013. "%interiorObject.exportToCollada(0);\n"
  1014. "@endtsexample\n\n")
  1015. {
  1016. object->exportToCollada(bakeTransform);
  1017. }
  1018. #endif
  1019. //-----------------------------------------------------------------------------
  1020. DefineEngineMethod( InteriorInstance, setAlarmMode, void, ( const char* alarmMode),,
  1021. "@brief This sets the alarm mode of the interior\n\n"
  1022. "The alarm mode is used when debugging bad geometry for an interior. When on, the the bad verties "
  1023. "will be rendered a different color.\n\n"
  1024. "@param alarmMode If true the interior will be in an alarm state next frame. Options are \'On\' or \'Off\'.\n\n"
  1025. "@tsexample\n"
  1026. "// Turn on alarm mode debugging for interior\n"
  1027. "%interiorObject.setAlarmMode(\"On\");\n"
  1028. "@endtsexample\n\n")
  1029. {
  1030. AssertFatal(dynamic_cast<InteriorInstance*>(object) != NULL,
  1031. "Error, how did a non-interior get here?");
  1032. bool alarm;
  1033. if (dStricmp(alarmMode, "On") == 0)
  1034. alarm = true;
  1035. else
  1036. alarm = false;
  1037. InteriorInstance* interior = static_cast<InteriorInstance*>(object);
  1038. if (interior->isClientObject()) {
  1039. Con::errorf(ConsoleLogEntry::General, "InteriorInstance: client objects may not receive console commands. Ignored");
  1040. return;
  1041. }
  1042. interior->setAlarmMode(alarm);
  1043. }
  1044. //-----------------------------------------------------------------------------
  1045. DefineEngineMethod( InteriorInstance, getNumDetailLevels, S32, (),,
  1046. "@brief Get the number of detail levels interior was created with\n\n"
  1047. "@tsexample\n"
  1048. "%numLODs = %interiorObject.getNumDetailLevels();\n"
  1049. "echo(%numLODs);\n"
  1050. "@endtsexample\n\n")
  1051. {
  1052. InteriorInstance * instance = static_cast<InteriorInstance*>(object);
  1053. return(instance->getNumDetailLevels());
  1054. }
  1055. //-----------------------------------------------------------------------------
  1056. DefineEngineMethod( InteriorInstance, setDetailLevel, void, (S32 level),,
  1057. "@brief Manually changes the current detail level, rather than automatically via view distance\n\n"
  1058. "@param level Detail level to force.\n\n"
  1059. "@tsexample\n"
  1060. "%interiorObject.setDetailLevel(2);\n"
  1061. "@endtsexample\n\n")
  1062. {
  1063. InteriorInstance * instance = static_cast<InteriorInstance*>(object);
  1064. if(instance->isServerObject())
  1065. {
  1066. NetConnection * toServer = NetConnection::getConnectionToServer();
  1067. NetConnection * toClient = NetConnection::getLocalClientConnection();
  1068. if(!toClient || !toServer)
  1069. return;
  1070. S32 index = toClient->getGhostIndex(instance);
  1071. if(index == -1)
  1072. return;
  1073. InteriorInstance * clientInstance = dynamic_cast<InteriorInstance*>(toServer->resolveGhost(index));
  1074. if(clientInstance)
  1075. clientInstance->setDetailLevel(level);
  1076. }
  1077. else
  1078. instance->setDetailLevel(level);
  1079. }
  1080. //-----------------------------------------------------------------------------
  1081. //These functions are duplicated in tsStatic, shapeBase, and interiorInstance.
  1082. //They each function a little differently; but achieve the same purpose of gathering
  1083. //target names/counts without polluting simObject.
  1084. DefineEngineMethod( InteriorInstance, getTargetName, const char*, (S32 detailLevel, S32 targetNum),,
  1085. "@brief Get the name of the indexed shape material\n\n"
  1086. "@param detailLevel Target LOD\n"
  1087. "@param targetNum Index mapped to the target\n\n"
  1088. "@return The name of the target (material) at the specified detail level and index\n\n"
  1089. "@tsexample\n"
  1090. "// First level of detail, top of the index map\n"
  1091. "%targetName = %interiorObject.getTargetName(1, 0);\n"
  1092. "echo(%targetName);\n"
  1093. "@endtsexample\n\n")
  1094. {
  1095. Interior* obj = object->getDetailLevel(detailLevel);
  1096. if(obj)
  1097. return obj->getTargetName(targetNum);
  1098. return "";
  1099. }
  1100. //-----------------------------------------------------------------------------
  1101. DefineEngineMethod( InteriorInstance, getTargetCount, S32, (U32 detailLevel),,
  1102. "@brief Get the number of materials used by interior\n\n"
  1103. "@param detailLevel Interior level of detail to scan\n"
  1104. "@return The number of materials used by the interior at a specified detail level\n\n"
  1105. "@tsexample\n"
  1106. "// Find materials used at first level of detail\n"
  1107. "%targetCount = %interiorObject.getTargetCount(1);\n"
  1108. "echo(%targetCount);\n"
  1109. "@endtsexample\n\n")
  1110. {
  1111. Interior* obj = object->getDetailLevel(detailLevel);
  1112. if(obj)
  1113. return obj->getTargetCount();
  1114. return -1;
  1115. }
  1116. //-----------------------------------------------------------------------------
  1117. DefineEngineMethod( InteriorInstance, changeMaterial, void, (const char* mapTo, Material* oldMat, Material* newMat),,
  1118. "@brief Change one of the materials on the shape.\n\n"
  1119. "This method changes materials per mapTo with others. The material that "
  1120. "is being replaced is mapped to unmapped_mat as a part of this transition.\n\n"
  1121. "@note Warning, right now this only sort of works. It doesn't do a live "
  1122. "update like it should.\n\b"
  1123. "@param mapTo The name of the material target to remap (from getTargetName)\n"
  1124. "@param oldMat The old Material that was mapped \n"
  1125. "@param newMat The new Material to map\n\n"
  1126. "@tsexample\n"
  1127. "// remap the first material in the shape\n"
  1128. "%mapTo = %interiorObject.getTargetName( 0 );\n"
  1129. "%interiorObject.changeMaterial( %mapTo, 0, MyMaterial );\n"
  1130. "@endtsexample\n" )
  1131. {
  1132. // if no valid new material, theres no reason for doing this
  1133. if( !newMat )
  1134. {
  1135. Con::errorf("InteriorInstance::changeMaterial failed: New material does not exist!");
  1136. return;
  1137. }
  1138. // simple parsing through the interiors detail levels looking for the correct mapto.
  1139. // break when we find the correct detail level to depend on.
  1140. U32 level;
  1141. S32 matIndex = -1;
  1142. for( level = 0; level < object->getNumDetailLevels(); level++ )
  1143. {
  1144. matIndex = object->getDetailLevel(level)->mMaterialList->getMaterialNameList().find_next(String(mapTo));
  1145. if (matIndex >= 0)
  1146. break;
  1147. }
  1148. if (matIndex == -1)
  1149. {
  1150. Con::errorf("InteriorInstance::changeMaterial failed: Invalid mapTo name '%s'", mapTo);
  1151. return;
  1152. }
  1153. // initilize server/client versions
  1154. Interior *serverObj = object->getDetailLevel( level );
  1155. InteriorInstance * instanceClientObj = dynamic_cast< InteriorInstance* > ( object->getClientObject() );
  1156. Interior *clientObj = instanceClientObj ? instanceClientObj->getDetailLevel( level ) : NULL;
  1157. if(serverObj)
  1158. {
  1159. // Lets remap the old material off, so as to let room for our current material room to claim its spot
  1160. if( oldMat )
  1161. oldMat->mMapTo = String("unmapped_mat");
  1162. newMat->mMapTo = mapTo;
  1163. // Map the material in the in the matmgr
  1164. MATMGR->mapMaterial( mapTo, newMat->mMapTo );
  1165. // Replace instances with the new material being traded in. Lets make sure that we only
  1166. // target the specific targets per inst. This technically is only done here for interiors for
  1167. // safe keeping. The remapping that truly matters most (for on the fly changes) are done in the node lists
  1168. delete serverObj->mMaterialList->mMatInstList[matIndex];
  1169. serverObj->mMaterialList->mMatInstList[matIndex] = newMat->createMatInstance();
  1170. // Finishing the safekeeping
  1171. const GFXVertexFormat *flags = getGFXVertexFormat<GFXVertexPNTTB>();
  1172. FeatureSet features = MATMGR->getDefaultFeatures();
  1173. serverObj->mMaterialList->getMaterialInst(matIndex)->init( features, flags );
  1174. if (clientObj)
  1175. {
  1176. delete clientObj->mMaterialList->mMatInstList[matIndex];
  1177. clientObj->mMaterialList->mMatInstList[matIndex] = newMat->createMatInstance();
  1178. clientObj->mMaterialList->getMaterialInst(matIndex)->init( features, flags );
  1179. // These loops are referenced in interior.cpp's initMatInstances
  1180. // Made a couple of alterations to tailor specifically towards one changing one instance
  1181. for( U32 i=0; i<clientObj->getNumZones(); i++ )
  1182. {
  1183. for( U32 j=0; j<clientObj->mZoneRNList[i].renderNodeList.size(); j++ )
  1184. {
  1185. BaseMatInstance *matInst = clientObj->mZoneRNList[i].renderNodeList[j].matInst;
  1186. Material* refMat = dynamic_cast<Material*>(matInst->getMaterial());
  1187. if(refMat == oldMat)
  1188. {
  1189. clientObj->mZoneRNList[i].renderNodeList[j].matInst = newMat->createMatInstance();
  1190. clientObj->mZoneRNList[i].renderNodeList[j].matInst->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTTB>());
  1191. //if ( pMat )
  1192. //mHasTranslucentMaterials |= pMat->mTranslucent && !pMat->mTranslucentZWrite;
  1193. }
  1194. }
  1195. }
  1196. // Lets reset the clientObj settings in order to accomodate the new material
  1197. clientObj->fillSurfaceTexMats();
  1198. clientObj->createZoneVBs();
  1199. clientObj->cloneMatInstances();
  1200. clientObj->createReflectPlanes();
  1201. clientObj->initMatInstances();
  1202. }
  1203. }
  1204. }
  1205. //-----------------------------------------------------------------------------
  1206. DefineEngineMethod( InteriorInstance, getModelFile, const char*, (),,
  1207. "@brief Get the interior file name\n\n"
  1208. "@return The name of the interior's model file in .DIF.\n\n"
  1209. "@tsexample\n"
  1210. "%interiorObject.getModelFile();\n"
  1211. "@endtsexample\n\n")
  1212. {
  1213. return object->getInteriorFileName();
  1214. }