//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "interior/interior.h" #include "core/bitVector.h" #include "core/stream.h" #include "math/mathIO.h" #include "dgl/gBitmap.h" #include "dgl/materialList.h" #include "interior/interiorSubObject.h" #include "platform/platformGL.h" #include "console/console.h" #include "core/frameAllocator.h" int QSORT_CALLBACK cmpU32(const void* p1, const void* p2) { return S32(*((U32*)p1)) - S32(*((U32*)p2)); } //------------------------------------------------------------------------------ //-------------------------------------- PERSISTENCE IMPLEMENTATION // U32 Interior::smFileVersion = 13; bool Interior::read(Stream& stream) { AssertFatal(stream.hasCapability(Stream::StreamRead), "Interior::read: non-read capable stream passed"); AssertFatal(stream.getStatus() == Stream::Ok, "Interior::read: Error, stream in inconsistent state"); S32 i; // Version this stream. We only load stream of the current version U32 fileVersion; stream.read(&fileVersion); // We need to store the version in case there is any post processing that // needs to take place that is dependent on the file version mFileVersion = fileVersion; if (fileVersion > smFileVersion) { Con::errorf(ConsoleLogEntry::General, "Interior::read: incompatible file version found."); return false; } // Geometry factors... stream.read(&mDetailLevel); stream.read(&mMinPixels); mathRead(stream, &mBoundingBox); mathRead(stream, &mBoundingSphere); stream.read(&mHasAlarmState); stream.read(&mNumLightStateEntries); // Now read in our data vectors. S32 vectorSize; // mPlanes readPlaneVector(stream); // mPoints stream.read(&vectorSize); mPoints.setSize(vectorSize); for (i = 0; i < mPoints.size(); i++) mathRead(stream, &mPoints[i].point); // mPointVisibility stream.read(&vectorSize); mPointVisibility.setSize(vectorSize); stream.read(vectorSize, mPointVisibility.address()); // mTexGenEQs stream.read(&vectorSize); mTexGenEQs.setSize(vectorSize); for(i = 0; i < mTexGenEQs.size(); i++) { mathRead(stream, &mTexGenEQs[i].planeX); mathRead(stream, &mTexGenEQs[i].planeY); } // mBSPNodes; stream.read(&vectorSize); mBSPNodes.setSize(vectorSize); for(i = 0; i < mBSPNodes.size(); i++) { stream.read(&mBSPNodes[i].planeIndex); stream.read(&mBSPNodes[i].frontIndex); stream.read(&mBSPNodes[i].backIndex); } // mBSPSolidLeaves stream.read(&vectorSize); mBSPSolidLeaves.setSize(vectorSize); for(i = 0; i < mBSPSolidLeaves.size(); i++) { stream.read(&mBSPSolidLeaves[i].surfaceIndex); stream.read(&mBSPSolidLeaves[i].surfaceCount); } // MaterialList if(mMaterialList != NULL) delete mMaterialList; mMaterialList = new MaterialList; mMaterialList->read(stream); // mWindings stream.read(&vectorSize); mWindings.setSize(vectorSize); for(i = 0; i < mWindings.size(); i++) { stream.read(&mWindings[i]); } // mWindingIndices stream.read(&vectorSize); mWindingIndices.setSize(vectorSize); for(i = 0; i < mWindingIndices.size(); i++) { stream.read(&mWindingIndices[i].windingStart); stream.read(&mWindingIndices[i].windingCount); } // mEdges if (fileVersion >= 12) { stream.read(&vectorSize); mEdges.setSize(vectorSize); for (i = 0; i < mEdges.size(); i++) { stream.read(&mEdges[i].vertexes[0]); stream.read(&mEdges[i].vertexes[1]); stream.read(&mEdges[i].faces[0]); stream.read(&mEdges[i].faces[1]); } } // mZones stream.read(&vectorSize); mZones.setSize(vectorSize); for(i = 0; i < mZones.size(); i++) { stream.read(&mZones[i].portalStart); stream.read(&mZones[i].portalCount); stream.read(&mZones[i].surfaceStart); stream.read(&mZones[i].surfaceCount); if (fileVersion >= 12) { stream.read(&mZones[i].staticMeshStart); stream.read(&mZones[i].staticMeshCount); } stream.read(&mZones[i].flags); mZones[i].zoneId = 0; } // Zone surfaces stream.read(&vectorSize); mZoneSurfaces.setSize(vectorSize); for(i = 0; i < mZoneSurfaces.size(); i++) stream.read(&mZoneSurfaces[i]); // Zone static meshes if (fileVersion >= 12) { stream.read(&vectorSize); mZoneStaticMeshes.setSize(vectorSize); for (i = 0; i < mZoneStaticMeshes.size(); i++) stream.read(&mZoneStaticMeshes[i]); } // mZonePortalList; stream.read(&vectorSize); mZonePortalList.setSize(vectorSize); for(i = 0; i < mZonePortalList.size(); i++) stream.read(&mZonePortalList[i]); // mPortals stream.read(&vectorSize); mPortals.setSize(vectorSize); for(i = 0; i < mPortals.size(); i++) { stream.read(&mPortals[i].planeIndex); stream.read(&mPortals[i].triFanCount); stream.read(&mPortals[i].triFanStart); stream.read(&mPortals[i].zoneFront); stream.read(&mPortals[i].zoneBack); } // mSurfaces stream.read(&vectorSize); mSurfaces.setSize(vectorSize); mLMTexGenEQs.setSize(vectorSize); for(i = 0; i < mSurfaces.size(); i++) { stream.read(&mSurfaces[i].windingStart); if (fileVersion >= 13) stream.read(&mSurfaces[i].windingCount); else { U8 count; stream.read(&count); mSurfaces[i].windingCount = (U32)count; } stream.read(&mSurfaces[i].planeIndex); stream.read(&mSurfaces[i].textureIndex); stream.read(&mSurfaces[i].texGenIndex); stream.read(&mSurfaces[i].surfaceFlags); stream.read(&mSurfaces[i].fanMask); readLMapTexGen(stream, mLMTexGenEQs[i].planeX, mLMTexGenEQs[i].planeY); stream.read(&mSurfaces[i].lightCount); stream.read(&mSurfaces[i].lightStateInfoStart); if (fileVersion >= 13) { stream.read(&mSurfaces[i].mapOffsetX); stream.read(&mSurfaces[i].mapOffsetY); stream.read(&mSurfaces[i].mapSizeX); stream.read(&mSurfaces[i].mapSizeY); } else { U8 offX, offY, sizeX, sizeY; stream.read(&offX); stream.read(&offY); stream.read(&sizeX); stream.read(&sizeY); mSurfaces[i].mapOffsetX = (U32)offX; mSurfaces[i].mapOffsetY = (U32)offY; mSurfaces[i].mapSizeX = (U32)sizeX; mSurfaces[i].mapSizeY = (U32)sizeY; } if (fileVersion == 1 || fileVersion >= 12) stream.read(&mSurfaces[i].unused); } // NormalLMapIndices stream.read(&vectorSize); mNormalLMapIndices.setSize(vectorSize); for (U32 i = 0; i < mNormalLMapIndices.size(); i++) { if (fileVersion >= 13) stream.read(&mNormalLMapIndices[i]); else { U8 index = 0; stream.read(&index); mNormalLMapIndices[i] = (U32)index; } } // AlarmLMapIndices stream.read(&vectorSize); mAlarmLMapIndices.setSize(vectorSize); for (U32 i = 0; i < mAlarmLMapIndices.size(); i++) { if (fileVersion >= 13) stream.read(&mAlarmLMapIndices[i]); else { U8 index = 0; stream.read(&index); mAlarmLMapIndices[i] = (U32)index; } } // mNullSurfaces stream.read(&vectorSize); mNullSurfaces.setSize(vectorSize); for(i = 0; i < mNullSurfaces.size(); i++) { stream.read(&mNullSurfaces[i].windingStart); stream.read(&mNullSurfaces[i].planeIndex); stream.read(&mNullSurfaces[i].surfaceFlags); if (fileVersion >= 13) stream.read(&mNullSurfaces[i].windingCount); else { U8 count; stream.read(&count); mNullSurfaces[i].windingCount = (U32)count; } } // mLightmaps stream.read(&vectorSize); mLightmaps.setSize(vectorSize); mLightDirMaps.setSize(vectorSize); mLightmapKeep.setSize(vectorSize); for(i = 0; i < mLightmaps.size(); i++) { mLightmaps[i] = new GBitmap; mLightmaps[i]->readPNG(stream); if (fileVersion == 1 || fileVersion >= 12) { mLightDirMaps[i] = new GBitmap; mLightDirMaps[i]->readPNG(stream); } else { mLightDirMaps[i] = new GBitmap(*mLightmaps[i]); VectorF normal(0.0f, 0.0f, 1.0f); for (U32 j = 0; j < mLightDirMaps[i]->getHeight(); j++) { for (U32 k = 0; k < mLightDirMaps[i]->getWidth(); k++) { U8 * data = mLightDirMaps[i]->getAddress(j,k); data[0] = 127 + normal.x * 128; data[1] = 127 + normal.y * 128; data[2] = 127 + normal.z * 128; } } } stream.read(&mLightmapKeep[i]); } // mSolidLeafSurfaces stream.read(&vectorSize); mSolidLeafSurfaces.setSize(vectorSize); for(i = 0; i < mSolidLeafSurfaces.size(); i++) { stream.read(&mSolidLeafSurfaces[i]); } // mAnimatedLights mNumTriggerableLights = 0; stream.read(&vectorSize); mAnimatedLights.setSize(vectorSize); for(i = 0; i < mAnimatedLights.size(); i++) { stream.read(&mAnimatedLights[i].nameIndex); stream.read(&mAnimatedLights[i].stateIndex); stream.read(&mAnimatedLights[i].stateCount); stream.read(&mAnimatedLights[i].flags); stream.read(&mAnimatedLights[i].duration); if((mAnimatedLights[i].flags & AnimationAmbient) == 0) mNumTriggerableLights++; } // mLightStates stream.read(&vectorSize); mLightStates.setSize(vectorSize); for(i = 0; i < mLightStates.size(); i++) { stream.read(&mLightStates[i].red); stream.read(&mLightStates[i].green); stream.read(&mLightStates[i].blue); stream.read(&mLightStates[i].activeTime); stream.read(&mLightStates[i].dataIndex); stream.read(&mLightStates[i].dataCount); } // mStateData stream.read(&vectorSize); mStateData.setSize(vectorSize); for(i = 0; i < mStateData.size(); i++) { stream.read(&mStateData[i].surfaceIndex); stream.read(&mStateData[i].mapIndex); stream.read(&mStateData[i].lightStateIndex); } // mStateDataBuffer stream.read(&vectorSize); mStateDataBuffer.setSize(vectorSize); U32 flags; stream.read(&flags); stream.read(mStateDataBuffer.size(), mStateDataBuffer.address()); // mNameBuffer stream.read(&vectorSize); mNameBuffer.setSize(vectorSize); stream.read(mNameBuffer.size(), mNameBuffer.address()); // mSubObjects stream.read(&vectorSize); mSubObjects.setSize(vectorSize); for (i = 0; i < mSubObjects.size(); i++) { InteriorSubObject* iso = InteriorSubObject::readISO(stream); AssertFatal(iso != NULL, "Error, bad sub object in stream!"); mSubObjects[i] = iso; } // Convex hulls stream.read(&vectorSize); mConvexHulls.setSize(vectorSize); for(i = 0; i < mConvexHulls.size(); i++) { stream.read(&mConvexHulls[i].hullStart); stream.read(&mConvexHulls[i].hullCount); stream.read(&mConvexHulls[i].minX); stream.read(&mConvexHulls[i].maxX); stream.read(&mConvexHulls[i].minY); stream.read(&mConvexHulls[i].maxY); stream.read(&mConvexHulls[i].minZ); stream.read(&mConvexHulls[i].maxZ); stream.read(&mConvexHulls[i].surfaceStart); stream.read(&mConvexHulls[i].surfaceCount); stream.read(&mConvexHulls[i].planeStart); stream.read(&mConvexHulls[i].polyListPlaneStart); stream.read(&mConvexHulls[i].polyListPointStart); stream.read(&mConvexHulls[i].polyListStringStart); if (fileVersion >= 12) stream.read(&mConvexHulls[i].staticMesh); } // Convex hull emit strings stream.read(&vectorSize); mConvexHullEmitStrings.setSize(vectorSize); stream.read(mConvexHullEmitStrings.size(), mConvexHullEmitStrings.address()); // Hull indices stream.read(&vectorSize); mHullIndices.setSize(vectorSize); for(i = 0; i < mHullIndices.size(); i++) stream.read(&mHullIndices[i]); // Hull plane indices stream.read(&vectorSize); mHullPlaneIndices.setSize(vectorSize); for(i = 0; i < mHullPlaneIndices.size(); i++) stream.read(&mHullPlaneIndices[i]); // Hull emit string indices stream.read(&vectorSize); mHullEmitStringIndices.setSize(vectorSize); for(i = 0; i < mHullEmitStringIndices.size(); i++) stream.read(&mHullEmitStringIndices[i]); // Hull surface indices stream.read(&vectorSize); mHullSurfaceIndices.setSize(vectorSize); for(i = 0; i < mHullSurfaceIndices.size(); i++) stream.read(&mHullSurfaceIndices[i]); // PolyList planes stream.read(&vectorSize); mPolyListPlanes.setSize(vectorSize); for(i = 0; i < mPolyListPlanes.size(); i++) stream.read(&mPolyListPlanes[i]); // PolyList points stream.read(&vectorSize); mPolyListPoints.setSize(vectorSize); for(i = 0; i < mPolyListPoints.size(); i++) stream.read(&mPolyListPoints[i]); // PolyList strings stream.read(&vectorSize); mPolyListStrings.setSize(vectorSize); for(i = 0; i < mPolyListStrings.size(); i++) stream.read(&mPolyListStrings[i]); // Coord bins for(i = 0; i < NumCoordBins * NumCoordBins; i++) { stream.read(&mCoordBins[i].binStart); stream.read(&mCoordBins[i].binCount); } // Coord bin indices stream.read(&vectorSize); mCoordBinIndices.setSize(vectorSize); for(i = 0; i < mCoordBinIndices.size(); i++) stream.read(&mCoordBinIndices[i]); // Coord bin mode stream.read(&mCoordBinMode); // Ambient colors stream.read(&mBaseAmbient); stream.read(&mAlarmAmbient); if (fileVersion >= 10) { // Static meshes stream.read(&vectorSize); mStaticMeshes.setSize(vectorSize); for (i = 0; i < mStaticMeshes.size(); i++) { mStaticMeshes[i] = new ConstructorSimpleMesh; mStaticMeshes[i]->read(stream); } } if (fileVersion >= 11) { // Normals stream.read(&vectorSize); mNormals.setSize(vectorSize); for (i = 0; i < mNormals.size(); i++) mathRead(stream, &mNormals[i]); // TexMatrices stream.read(&vectorSize); mTexMatrices.setSize(vectorSize); for (i = 0; i < mTexMatrices.size(); i++) { stream.read(&mTexMatrices[i].T); stream.read(&mTexMatrices[i].N); stream.read(&mTexMatrices[i].B); } // TexMatIndices stream.read(&vectorSize); mTexMatIndices.setSize(vectorSize); for (i = 0; i < mTexMatIndices.size(); i++) stream.read(&mTexMatIndices[i]); } // For future expandability U32 dummy; if (fileVersion < 10) { stream.read(&dummy); if (dummy != 0) return false; } if (fileVersion < 11) { stream.read(&dummy); if (dummy != 0) return false; stream.read(&dummy); if (dummy != 0) return false; } // // Support for interior light map border sizes. // U32 extendedlightmapdata; stream.read(&extendedlightmapdata); if(extendedlightmapdata == 1) { stream.read(&mLightMapBorderSize); //future expansion under current block (avoid using too //many of the above expansion slots by allowing nested //blocks)... stream.read(&dummy); if (dummy != 0) return false; } // Setup the zone planes setupZonePlanes(); truncateZoneTree(); buildSurfaceZones(); return (stream.getStatus() == Stream::Ok); } bool Interior::write(Stream& stream) const { AssertFatal(stream.hasCapability(Stream::StreamWrite), "Interior::write: non-write capable stream passed"); AssertFatal(stream.getStatus() == Stream::Ok, "Interior::write: Error, stream in inconsistent state"); U32 i; // Version this stream stream.write(smFileVersion); stream.write(mDetailLevel); stream.write(mMinPixels); mathWrite(stream, mBoundingBox); mathWrite(stream, mBoundingSphere); stream.write(mHasAlarmState); stream.write(mNumLightStateEntries); // Now write out our data vectors. Remember, for cross-platform capability, no // structure writing is allowed... // mPlanes writePlaneVector(stream); // mPoints stream.write(mPoints.size()); for (i = 0; i < mPoints.size(); i++) mathWrite(stream, mPoints[i].point); // mPointVisibility stream.write(mPointVisibility.size()); stream.write(mPointVisibility.size(), mPointVisibility.address()); // mTexGenEQs stream.write(mTexGenEQs.size()); for (i = 0; i < mTexGenEQs.size(); i++) { mathWrite(stream, mTexGenEQs[i].planeX); mathWrite(stream, mTexGenEQs[i].planeY); } // mBSPNodes; stream.write(mBSPNodes.size()); for (i = 0; i < mBSPNodes.size(); i++) { stream.write(mBSPNodes[i].planeIndex); stream.write(mBSPNodes[i].frontIndex); stream.write(mBSPNodes[i].backIndex); } // mBSPSolidLeaves stream.write(mBSPSolidLeaves.size()); for (i = 0; i < mBSPSolidLeaves.size(); i++) { stream.write(mBSPSolidLeaves[i].surfaceIndex); stream.write(mBSPSolidLeaves[i].surfaceCount); } // MaterialList mMaterialList->write(stream); // mWindings stream.write(mWindings.size()); for (i = 0; i < mWindings.size(); i++) { stream.write(mWindings[i]); } // mWindingIndices stream.write(mWindingIndices.size()); for (i = 0; i < mWindingIndices.size(); i++) { stream.write(mWindingIndices[i].windingStart); stream.write(mWindingIndices[i].windingCount); } // mEdges if (smFileVersion >= 12) { stream.write(mEdges.size()); for (i = 0; i < mEdges.size(); i++) { stream.write(mEdges[i].vertexes[0]); stream.write(mEdges[i].vertexes[1]); stream.write(mEdges[i].faces[0]); stream.write(mEdges[i].faces[1]); } } // mZones stream.write(mZones.size()); for (i = 0; i < mZones.size(); i++) { stream.write(mZones[i].portalStart); stream.write(mZones[i].portalCount); stream.write(mZones[i].surfaceStart); stream.write(mZones[i].surfaceCount); if (smFileVersion >= 12) { stream.write(mZones[i].staticMeshStart); stream.write(mZones[i].staticMeshCount); } stream.write(mZones[i].flags); } // Zone surfaces stream.write(mZoneSurfaces.size()); for (i = 0; i < mZoneSurfaces.size(); i++) stream.write(mZoneSurfaces[i]); // Zone static meshes if (smFileVersion >= 12) { stream.write(mZoneStaticMeshes.size()); for (i = 0; i < mZoneStaticMeshes.size(); i++) stream.write(mZoneStaticMeshes[i]); } // mZonePortalList; stream.write(mZonePortalList.size()); for (i = 0; i < mZonePortalList.size(); i++) stream.write(mZonePortalList[i]); // mPortals stream.write(mPortals.size()); for (i = 0; i < mPortals.size(); i++) { stream.write(mPortals[i].planeIndex); stream.write(mPortals[i].triFanCount); stream.write(mPortals[i].triFanStart); stream.write(mPortals[i].zoneFront); stream.write(mPortals[i].zoneBack); } // mSurfaces stream.write(mSurfaces.size()); for (i = 0; i < mSurfaces.size(); i++) { stream.write(mSurfaces[i].windingStart); if (smFileVersion >= 13) stream.write(mSurfaces[i].windingCount); else { U8 count = (U8)mSurfaces[i].windingCount; stream.write(count); } stream.write(mSurfaces[i].planeIndex); stream.write(mSurfaces[i].textureIndex); stream.write(mSurfaces[i].texGenIndex); stream.write(mSurfaces[i].surfaceFlags); stream.write(mSurfaces[i].fanMask); writeLMapTexGen(stream, mLMTexGenEQs[i].planeX, mLMTexGenEQs[i].planeY); stream.write(mSurfaces[i].lightCount); stream.write(mSurfaces[i].lightStateInfoStart); if (smFileVersion >= 13) { stream.write(mSurfaces[i].mapOffsetX); stream.write(mSurfaces[i].mapOffsetY); stream.write(mSurfaces[i].mapSizeX); stream.write(mSurfaces[i].mapSizeY); } else { U8 offX, offY, sizeX, sizeY; offX = (U8)mSurfaces[i].mapOffsetX; offY = (U8)mSurfaces[i].mapOffsetY; sizeX = (U8)mSurfaces[i].mapSizeX; sizeY = (U8)mSurfaces[i].mapSizeY; stream.write(offX); stream.write(offY); stream.write(sizeX); stream.write(sizeY); } if (smFileVersion == 1 || smFileVersion >= 12) stream.write(mSurfaces[i].unused); } // NormalLMapIndices stream.write(mNormalLMapIndices.size()); for (U32 i = 0; i < mNormalLMapIndices.size(); i++) { if (smFileVersion >= 13) stream.write(mNormalLMapIndices[i]); else { U8 index = (U8)mNormalLMapIndices[i]; stream.write(index); } } // AlarmLMapIndices stream.write(mAlarmLMapIndices.size()); for (U32 i = 0; i < mAlarmLMapIndices.size(); i++) { if (smFileVersion >= 13) stream.write(mAlarmLMapIndices[i]); else { U8 index = (U8)mAlarmLMapIndices[i]; stream.write(index); } } // mNullSurfaces stream.write(mNullSurfaces.size()); for (i = 0; i < mNullSurfaces.size(); i++) { stream.write(mNullSurfaces[i].windingStart); stream.write(mNullSurfaces[i].planeIndex); stream.write(mNullSurfaces[i].surfaceFlags); if (smFileVersion >= 13) stream.write(mNullSurfaces[i].windingCount); else { U8 count = (U8)mNullSurfaces[i].windingCount; stream.write(count); } } // mLightmaps stream.write(mLightmaps.size()); for(i = 0; i < mLightmaps.size(); i++) { mLightmaps[i]->writePNG(stream); if (smFileVersion == 1 || smFileVersion >= 12) mLightDirMaps[i]->writePNG(stream); stream.write(mLightmapKeep[i]); } // mSolidLeafSurfaces stream.write(mSolidLeafSurfaces.size()); for(i = 0; i < mSolidLeafSurfaces.size(); i++) { stream.write(mSolidLeafSurfaces[i]); } // Animated lights stream.write(mAnimatedLights.size()); for(i = 0; i < mAnimatedLights.size(); i++) { stream.write(mAnimatedLights[i].nameIndex); stream.write(mAnimatedLights[i].stateIndex); stream.write(mAnimatedLights[i].stateCount); stream.write(mAnimatedLights[i].flags); stream.write(mAnimatedLights[i].duration); } stream.write(mLightStates.size()); for(i = 0; i < mLightStates.size(); i++) { stream.write(mLightStates[i].red); stream.write(mLightStates[i].green); stream.write(mLightStates[i].blue); stream.write(mLightStates[i].activeTime); stream.write(mLightStates[i].dataIndex); stream.write(mLightStates[i].dataCount); } // mStateData stream.write(mStateData.size()); for(i = 0; i < mStateData.size(); i++) { stream.write(mStateData[i].surfaceIndex); stream.write(mStateData[i].mapIndex); stream.write(mStateData[i].lightStateIndex); } // mStateDataBuffer: Note: superfluous 0 is for flags in future versions. // that may add compression. This way, we can maintain // compatability with previous versions. stream.write(mStateDataBuffer.size()); stream.write(U32(0)); stream.write(mStateDataBuffer.size(), mStateDataBuffer.address()); // mNameBuffer stream.write(mNameBuffer.size()); stream.write(mNameBuffer.size(), mNameBuffer.address()); // mSubObjects stream.write(mSubObjects.size()); for (i = 0; i < mSubObjects.size(); i++) { bool writeSuccess = mSubObjects[i]->writeISO(stream); AssertFatal(writeSuccess == true, "Error writing sub object to stream!"); } // Convex hulls stream.write(mConvexHulls.size()); for(i = 0; i < mConvexHulls.size(); i++) { stream.write(mConvexHulls[i].hullStart); stream.write(mConvexHulls[i].hullCount); stream.write(mConvexHulls[i].minX); stream.write(mConvexHulls[i].maxX); stream.write(mConvexHulls[i].minY); stream.write(mConvexHulls[i].maxY); stream.write(mConvexHulls[i].minZ); stream.write(mConvexHulls[i].maxZ); stream.write(mConvexHulls[i].surfaceStart); stream.write(mConvexHulls[i].surfaceCount); stream.write(mConvexHulls[i].planeStart); stream.write(mConvexHulls[i].polyListPlaneStart); stream.write(mConvexHulls[i].polyListPointStart); stream.write(mConvexHulls[i].polyListStringStart); if (smFileVersion >= 12) stream.write(mConvexHulls[i].staticMesh); } stream.write(mConvexHullEmitStrings.size()); stream.write(mConvexHullEmitStrings.size(), mConvexHullEmitStrings.address()); stream.write(mHullIndices.size()); for(i = 0; i < mHullIndices.size(); i++) stream.write(mHullIndices[i]); stream.write(mHullPlaneIndices.size()); for(i = 0; i < mHullPlaneIndices.size(); i++) stream.write(mHullPlaneIndices[i]); stream.write(mHullEmitStringIndices.size()); for(i = 0; i < mHullEmitStringIndices.size(); i++) stream.write(mHullEmitStringIndices[i]); stream.write(mHullSurfaceIndices.size()); for(i = 0; i < mHullSurfaceIndices.size(); i++) stream.write(mHullSurfaceIndices[i]); stream.write(mPolyListPlanes.size()); for(i = 0; i < mPolyListPlanes.size(); i++) stream.write(mPolyListPlanes[i]); stream.write(mPolyListPoints.size()); for(i = 0; i < mPolyListPoints.size(); i++) stream.write(mPolyListPoints[i]); stream.write(mPolyListStrings.size()); for(i = 0; i < mPolyListStrings.size(); i++) stream.write(mPolyListStrings[i]); // Coord bins for(i = 0; i < NumCoordBins * NumCoordBins; i++) { stream.write(mCoordBins[i].binStart); stream.write(mCoordBins[i].binCount); } stream.write(mCoordBinIndices.size()); for(i = 0; i < mCoordBinIndices.size(); i++) stream.write(mCoordBinIndices[i]); stream.write(mCoordBinMode); // Ambient colors... stream.write(mBaseAmbient); stream.write(mAlarmAmbient); if (smFileVersion >= 10) { // Static meshes stream.write(mStaticMeshes.size()); for (i = 0; i < mStaticMeshes.size(); i++) mStaticMeshes[i]->write(stream); } else stream.write(U32(0)); if (smFileVersion >= 11) { // Normals stream.write(mNormals.size()); for (i = 0; i < mNormals.size(); i++) mathWrite(stream, mNormals[i]); // TexMatrices stream.write(mTexMatrices.size()); for (i = 0; i < mTexMatrices.size(); i++) { stream.write(mTexMatrices[i].T); stream.write(mTexMatrices[i].N); stream.write(mTexMatrices[i].B); } // TexMatIndices stream.write(mTexMatIndices.size()); for (i = 0; i < mTexMatIndices.size(); i++) stream.write(mTexMatIndices[i]); } else { stream.write(U32(0)); stream.write(U32(0)); } // // Support for interior light map border sizes. // if(mLightMapBorderSize > 0) { stream.write(U32(1));//flag new block... stream.write(U32(mLightMapBorderSize));//new block data.. //future expansion under current block (avoid using too //many of the above expansion slots by allowing nested //blocks)... stream.write(U32(0)); } else { stream.write(U32(0)); } return stream.getStatus() == Stream::Ok; } bool Interior::readVehicleCollision(Stream& stream) { AssertFatal(stream.hasCapability(Stream::StreamRead), "Interior::read: non-read capable stream passed"); AssertFatal(stream.getStatus() == Stream::Ok, "Interior::read: Error, stream in inconsistent state"); S32 i; // Version this stream. We only load stream of the current version U32 fileVersion; stream.read(&fileVersion); if (fileVersion > smFileVersion) { Con::errorf(ConsoleLogEntry::General, "Interior::read: incompatible file version found."); return false; } U32 vectorSize; // Convex hulls stream.read(&vectorSize); mVehicleConvexHulls.setSize(vectorSize); for(i = 0; i < mVehicleConvexHulls.size(); i++) { stream.read(&mVehicleConvexHulls[i].hullStart); stream.read(&mVehicleConvexHulls[i].hullCount); stream.read(&mVehicleConvexHulls[i].minX); stream.read(&mVehicleConvexHulls[i].maxX); stream.read(&mVehicleConvexHulls[i].minY); stream.read(&mVehicleConvexHulls[i].maxY); stream.read(&mVehicleConvexHulls[i].minZ); stream.read(&mVehicleConvexHulls[i].maxZ); stream.read(&mVehicleConvexHulls[i].surfaceStart); stream.read(&mVehicleConvexHulls[i].surfaceCount); stream.read(&mVehicleConvexHulls[i].planeStart); stream.read(&mVehicleConvexHulls[i].polyListPlaneStart); stream.read(&mVehicleConvexHulls[i].polyListPointStart); stream.read(&mVehicleConvexHulls[i].polyListStringStart); } stream.read(&vectorSize); mVehicleConvexHullEmitStrings.setSize(vectorSize); stream.read(mVehicleConvexHullEmitStrings.size(), mVehicleConvexHullEmitStrings.address()); stream.read(&vectorSize); mVehicleHullIndices.setSize(vectorSize); for(i = 0; i < mVehicleHullIndices.size(); i++) stream.read(&mVehicleHullIndices[i]); stream.read(&vectorSize); mVehicleHullPlaneIndices.setSize(vectorSize); for(i = 0; i < mVehicleHullPlaneIndices.size(); i++) stream.read(&mVehicleHullPlaneIndices[i]); stream.read(&vectorSize); mVehicleHullEmitStringIndices.setSize(vectorSize); for(i = 0; i < mVehicleHullEmitStringIndices.size(); i++) stream.read(&mVehicleHullEmitStringIndices[i]); stream.read(&vectorSize); mVehicleHullSurfaceIndices.setSize(vectorSize); for(i = 0; i < mVehicleHullSurfaceIndices.size(); i++) stream.read(&mVehicleHullSurfaceIndices[i]); stream.read(&vectorSize); mVehiclePolyListPlanes.setSize(vectorSize); for(i = 0; i < mVehiclePolyListPlanes.size(); i++) stream.read(&mVehiclePolyListPlanes[i]); stream.read(&vectorSize); mVehiclePolyListPoints.setSize(vectorSize); for(i = 0; i < mVehiclePolyListPoints.size(); i++) stream.read(&mVehiclePolyListPoints[i]); stream.read(&vectorSize); mVehiclePolyListStrings.setSize(vectorSize); for(i = 0; i < mVehiclePolyListStrings.size(); i++) stream.read(&mVehiclePolyListStrings[i]); stream.read(&vectorSize); mVehicleNullSurfaces.setSize(vectorSize); for(i = 0; i < mVehicleNullSurfaces.size(); i++) { stream.read(&mVehicleNullSurfaces[i].windingStart); stream.read(&mVehicleNullSurfaces[i].planeIndex); stream.read(&mVehicleNullSurfaces[i].surfaceFlags); stream.read(&mVehicleNullSurfaces[i].windingCount); } stream.read(&vectorSize); mVehiclePoints.setSize(vectorSize); for(i = 0; i < mVehiclePoints.size(); i++) mathRead(stream, &mVehiclePoints[i].point); stream.read(&vectorSize); mVehiclePlanes.setSize(vectorSize); for(i = 0; i < mVehiclePlanes.size(); i++) mathRead(stream, &mVehiclePlanes[i]); stream.read(&vectorSize); mVehicleWindings.setSize(vectorSize); for(i = 0; i < mVehicleWindings.size(); i++) { stream.read(&mVehicleWindings[i]); } stream.read(&vectorSize); mVehicleWindingIndices.setSize(vectorSize); for(i = 0; i < mVehicleWindingIndices.size(); i++) { stream.read(&mVehicleWindingIndices[i].windingStart); stream.read(&mVehicleWindingIndices[i].windingCount); } return true; } bool Interior::writeVehicleCollision(Stream& stream) const { AssertFatal(stream.hasCapability(Stream::StreamWrite), "Interior::write: non-write capable stream passed"); AssertFatal(stream.getStatus() == Stream::Ok, "Interior::write: Error, stream in inconsistent state"); U32 i; // Version this stream stream.write(smFileVersion); // Convex hulls stream.write(mVehicleConvexHulls.size()); for(i = 0; i < mVehicleConvexHulls.size(); i++) { stream.write(mVehicleConvexHulls[i].hullStart); stream.write(mVehicleConvexHulls[i].hullCount); stream.write(mVehicleConvexHulls[i].minX); stream.write(mVehicleConvexHulls[i].maxX); stream.write(mVehicleConvexHulls[i].minY); stream.write(mVehicleConvexHulls[i].maxY); stream.write(mVehicleConvexHulls[i].minZ); stream.write(mVehicleConvexHulls[i].maxZ); stream.write(mVehicleConvexHulls[i].surfaceStart); stream.write(mVehicleConvexHulls[i].surfaceCount); stream.write(mVehicleConvexHulls[i].planeStart); stream.write(mVehicleConvexHulls[i].polyListPlaneStart); stream.write(mVehicleConvexHulls[i].polyListPointStart); stream.write(mVehicleConvexHulls[i].polyListStringStart); } stream.write(mVehicleConvexHullEmitStrings.size()); stream.write(mVehicleConvexHullEmitStrings.size(), mVehicleConvexHullEmitStrings.address()); stream.write(mVehicleHullIndices.size()); for(i = 0; i < mVehicleHullIndices.size(); i++) stream.write(mVehicleHullIndices[i]); stream.write(mVehicleHullPlaneIndices.size()); for(i = 0; i < mVehicleHullPlaneIndices.size(); i++) stream.write(mVehicleHullPlaneIndices[i]); stream.write(mVehicleHullEmitStringIndices.size()); for(i = 0; i < mVehicleHullEmitStringIndices.size(); i++) stream.write(mVehicleHullEmitStringIndices[i]); stream.write(mVehicleHullSurfaceIndices.size()); for(i = 0; i < mVehicleHullSurfaceIndices.size(); i++) stream.write(mVehicleHullSurfaceIndices[i]); stream.write(mVehiclePolyListPlanes.size()); for(i = 0; i < mVehiclePolyListPlanes.size(); i++) stream.write(mVehiclePolyListPlanes[i]); stream.write(mVehiclePolyListPoints.size()); for(i = 0; i < mVehiclePolyListPoints.size(); i++) stream.write(mVehiclePolyListPoints[i]); stream.write(mVehiclePolyListStrings.size()); for(i = 0; i < mVehiclePolyListStrings.size(); i++) stream.write(mVehiclePolyListStrings[i]); stream.write(mVehicleNullSurfaces.size()); for(i = 0; i < mVehicleNullSurfaces.size(); i++) { stream.write(mVehicleNullSurfaces[i].windingStart); stream.write(mVehicleNullSurfaces[i].planeIndex); stream.write(mVehicleNullSurfaces[i].surfaceFlags); stream.write(mVehicleNullSurfaces[i].windingCount); } stream.write(mVehiclePoints.size()); for(i = 0; i < mVehiclePoints.size(); i++) mathWrite(stream, mVehiclePoints[i].point); stream.write(mVehiclePlanes.size()); for(i = 0; i < mVehiclePlanes.size(); i++) mathWrite(stream, mVehiclePlanes[i]); stream.write(mVehicleWindings.size()); for(i = 0; i < mVehicleWindings.size(); i++) stream.write(mVehicleWindings[i]); stream.write(mVehicleWindingIndices.size()); for(i = 0; i < mVehicleWindingIndices.size(); i++) { stream.write(mVehicleWindingIndices[i].windingStart); stream.write(mVehicleWindingIndices[i].windingCount); } return true; } bool Interior::readLMapTexGen(Stream& stream, PlaneF& planeX, PlaneF& planeY) { F32 genX[4]; F32 genY[4]; for(U32 i = 0; i < 4; i++) { genX[i] = 0.0f; genY[i] = 0.0f; } U16 finalWord; stream.read(&finalWord); stream.read(&genX[3]); stream.read(&genY[3]); // Unpack the final word. U32 logScaleY = (finalWord >> 0) & ((1 << 6) - 1); U32 logScaleX = (finalWord >> 6) & ((1 << 6) - 1); U16 stEnc = (finalWord >> 13) & 7; S32 sc, tc; switch(stEnc) { case 0: sc = 0; tc = 1; break; case 1: sc = 0; tc = 2; break; case 2: sc = 1; tc = 0; break; case 3: sc = 1; tc = 2; break; case 4: sc = 2; tc = 0; break; case 5: sc = 2; tc = 1; break; default: AssertISV(false, "Invalid st coord encoding in Interior::readLMapTG"); } U32 invScaleX = 1 << logScaleX; U32 invScaleY = 1 << logScaleY; genX[sc] = F32(1.0 / F64(invScaleX)); genY[tc] = F32(1.0 / F64(invScaleY)); planeX.x = genX[0]; planeX.y = genX[1]; planeX.z = genX[2]; planeX.d = genX[3]; planeY.x = genY[0]; planeY.y = genY[1]; planeY.z = genY[2]; planeY.d = genY[3]; return stream.getStatus() == Stream::Ok; } bool Interior::writeLMapTexGen(Stream& stream, const PlaneF& planeX, const PlaneF& planeY) const { F32 genX[4], genY[4]; genX[0] = planeX.x; genX[1] = planeX.y; genX[2] = planeX.z; genX[3] = planeX.d; genY[0] = planeY.x; genY[1] = planeY.y; genY[2] = planeY.z; genY[3] = planeY.d; // The tex gen for lmaps is a special case. // there are only 4 parameters that matter, // an inverse power of 2 in the x and y, and the // fp offsets in x and y. We can encode the // scales completely in U16 and we'll just write out // the offsets. First, determine which coords we're // writing... // S32 sc = -1; S32 tc = -1; if(genX[0] != 0.0) sc = 0; else if(genX[1] != 0.0) sc = 1; else if(genX[2] != 0.0) sc = 2; if(genY[0] != 0.0) tc = 0; else if(genY[1] != 0.0) tc = 1; else if(genY[2] != 0.0) tc = 2; AssertFatal(sc != -1 && tc != -1 && sc != tc, "Hm, something wrong here."); U32 invScaleX = U32((1.0f / genX[sc]) + 0.5); U32 invScaleY = U32((1.0f / genY[tc]) + 0.5); AssertISV(invScaleX && isPow2(invScaleX) && invScaleY && isPow2(invScaleY), "Not a power of 2? Something wrong"); U32 logScaleX = getBinLog2(invScaleX); U32 logScaleY = getBinLog2(invScaleY); AssertFatal(logScaleX < 63 && logScaleY < 63, "Error, you've set the lightmap scale WAAYYY to high!"); // We need 3 bits to encode sc and tc, which leaves us 6 bits for logScaleX // and logScaleY S16 stEnc = -1; if(sc == 0 && tc == 1) stEnc = 0; else if(sc == 0 && tc == 2) stEnc = 1; else if(sc == 1 && tc == 0) stEnc = 2; else if(sc == 1 && tc == 2) stEnc = 3; else if(sc == 2 && tc == 0) stEnc = 4; else if(sc == 2 && tc == 1) stEnc = 5; AssertFatal(stEnc != -1, avar("Hm. This should never happen. (%d, %d)", sc, tc)); U16 finalWord = U16(stEnc) << 13; finalWord |= logScaleX << 6; finalWord |= logScaleY << 0; stream.write(finalWord); stream.write(genX[3]); stream.write(genY[3]); return stream.getStatus() == Stream::Ok; } bool Interior::writePlaneVector(Stream& stream) const { // This is pretty slow, but who cares? // Vector uniqueNormals(mPlanes.size()); Vector uniqueIndices(mPlanes.size()); U32 i; for(i = 0; i < mPlanes.size(); i++) { bool inserted = false; for(U32 j = 0; j < uniqueNormals.size(); j++) { if(mPlanes[i] == uniqueNormals[j]) { // Hah! Already have this one... uniqueIndices.push_back(j); inserted = true; break; } } if(inserted == false) { // Gotta do it ourselves... uniqueIndices.push_back(uniqueNormals.size()); uniqueNormals.push_back(Point3F(mPlanes[i].x, mPlanes[i].y, mPlanes[i].z)); } } // Ok, what we have now, is a list of unique normals, a set of indices into // that vector, and the distances that we still have to write out by hand. // Hop to it! stream.write(uniqueNormals.size()); for(i = 0; i < uniqueNormals.size(); i++) mathWrite(stream, uniqueNormals[i]); stream.write(mPlanes.size()); for(i = 0; i < mPlanes.size(); i++) { stream.write(uniqueIndices[i]); stream.write(mPlanes[i].d); } return(stream.getStatus() == Stream::Ok); } bool Interior::readPlaneVector(Stream& stream) { U32 vectorSize; stream.read(&vectorSize); Point3F* normals = new Point3F[vectorSize]; U32 i; for(i = 0; i < vectorSize; i++) mathRead(stream, &normals[i]); U16 index; stream.read(&vectorSize); mPlanes.setSize(vectorSize); for(i = 0; i < mPlanes.size(); i++) { stream.read(&index); stream.read(&mPlanes[i].d); mPlanes[i].x = normals[index].x; mPlanes[i].y = normals[index].y; mPlanes[i].z = normals[index].z; } delete [] normals; return(stream.getStatus() == Stream::Ok); } bool Interior::getUnifiedZone(const U16 index, S32* zone) { if(isBSPLeafIndex(index)) { if(isBSPSolidLeaf(index)) *zone = -1; else *zone = S32(getBSPEmptyLeafZone(index)); return true; } else { S32 frontZone, backZone; bool frontUnified = getUnifiedZone(mBSPNodes[index].frontIndex, &frontZone); bool backUnified = getUnifiedZone(mBSPNodes[index].backIndex, &backZone); if(frontUnified && backUnified) { if(frontZone == backZone) { *zone = frontZone; return true; } else { if(frontZone == -1 || backZone == -1) { // DMMFIX: Once the interior file format is able to distinguish // between structural and detail nodes in the runtime bsp, // we can make this work a little better. return false; } else { // Not equal, and neither is -1, no unified zone possible return false; } } } else { return false; } } } void Interior::truncateZoneNode(const U16 index) { S32 unifiedZone; bool unified = getUnifiedZone(index, &unifiedZone); if(unified) { // Aha! if(isBSPLeafIndex(index)) return; if(unifiedZone == -1) mBSPNodes[index].terminalZone = U16(0xFFFF); else mBSPNodes[index].terminalZone = U16(0x8000) | U16(unifiedZone); } else { // Sigh. if(isBSPLeafIndex(mBSPNodes[index].frontIndex) == false) truncateZoneNode(mBSPNodes[index].frontIndex); if(isBSPLeafIndex(mBSPNodes[index].backIndex) == false) truncateZoneNode(mBSPNodes[index].backIndex); } } void Interior::truncateZoneTree() { for(U32 i = 0; i < mBSPNodes.size(); i++) { mBSPNodes[i].terminalZone = 0; } if(mBSPNodes.size() > 0) truncateZoneNode(0); } void Interior::setupZonePlanes() { U16* temp = new U16[mPlanes.size() * mZones.size()]; U32 tempSize = 0; for(U32 i = 0; i < mZones.size(); i++) { Zone& rZone = mZones[i]; BitVector usedPlanes; usedPlanes.setSize(mPlanes.size()); usedPlanes.clear(); U32 j; for(j = 0; j < rZone.surfaceCount; j++) { Surface& rSurface = mSurfaces[mZoneSurfaces[rZone.surfaceStart + j]]; usedPlanes.set(getPlaneIndex(rSurface.planeIndex)); } rZone.planeStart = tempSize; for(j = 0; j < mPlanes.size(); j++) { if(usedPlanes.test(j)) { AssertFatal(tempSize < mPlanes.size() * mZones.size(), "Error, out of bounds plane list!"); temp[tempSize++] = j; } } rZone.planeCount = tempSize - rZone.planeStart; } mZonePlanes.setSize(tempSize); for(U32 j = 0; j < tempSize; j++) mZonePlanes[j] = temp[j]; delete [] temp; }