//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "interior/forceField.h" #include "core/stream.h" #include "math/mathIO.h" #include "console/console.h" #include "dgl/gTexManager.h" #include "dgl/dgl.h" #include "collision/abstractPolyList.h" #include "sim/sceneObject.h" //-------------------------------------------------------------------------- ForceField::ForceField() { mPreppedForRender = false; mWhite = NULL; } ForceField::~ForceField() { mPreppedForRender = false; delete mWhite; mWhite = NULL; } bool ForceField::prepForRendering() { if (mPreppedForRender == true) return true; mPreppedForRender = true; return true; } void ForceField::render(const ColorF& rColor, const F32 fade) { // All our transform what not has already been specified... glDisable(GL_TEXTURE_2D); glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glVertexPointer(3, GL_FLOAT, sizeof(Point3F), mPoints.address()); glEnable(GL_VERTEX_ARRAY); for (U32 i = 0; i < mSurfaces.size(); i++) { Surface& rSurface = mSurfaces[i]; glColor4f(rColor.red, rColor.green, rColor.blue, fade); glBegin(GL_TRIANGLE_STRIP); for (U32 j = rSurface.windingStart; j < rSurface.windingStart + rSurface.windingCount; j++) glArrayElement(mWindings[j]); glEnd(); } glDisable(GL_VERTEX_ARRAY); glDisable(GL_CULL_FACE); } //-------------------------------------------------------------------------- //-------------------------------------- Persistence interfaces // const U32 ForceField::smFileVersion = 0; bool ForceField::read(Stream& stream) { AssertFatal(stream.hasCapability(Stream::StreamRead), "ForceField::read: non-read capable stream passed"); AssertFatal(stream.getStatus() == Stream::Ok, "ForceField::read: Error, stream in inconsistent state"); U32 i; // Version this stream U32 fileVersion; stream.read(&fileVersion); if (fileVersion != smFileVersion) { Con::errorf(ConsoleLogEntry::General, "ForceField::read: incompatible file version found."); return false; } mName = stream.readSTString(); U32 numTriggers; stream.read(&numTriggers); mTriggers.setSize(numTriggers); for (i = 0; i < mTriggers.size(); i++) mTriggers[i] = stream.readSTString(); // Geometry factors... mathRead(stream, &mBoundingBox); mathRead(stream, &mBoundingSphere); // Now read in our data vectors. U32 vectorSize; // mPlanes readPlaneVector(stream); // mPoints stream.read(&vectorSize); mPoints.setSize(vectorSize); for (i = 0; i < mPoints.size(); i++) mathRead(stream, &mPoints[i]); // 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); } // mWindings stream.read(&vectorSize); mWindings.setSize(vectorSize); for (i = 0; i < mWindings.size(); i++) { stream.read(&mWindings[i]); } // mSurfaces stream.read(&vectorSize); mSurfaces.setSize(vectorSize); for (i = 0; i < mSurfaces.size(); i++) { stream.read(&mSurfaces[i].windingStart); stream.read(&mSurfaces[i].windingCount); stream.read(&mSurfaces[i].planeIndex); stream.read(&mSurfaces[i].surfaceFlags); stream.read(&mSurfaces[i].fanMask); } // mSolidLeafSurfaces stream.read(&vectorSize); mSolidLeafSurfaces.setSize(vectorSize); for (i = 0; i < mSolidLeafSurfaces.size(); i++) { stream.read(&mSolidLeafSurfaces[i]); } stream.read(&mColor); return stream.getStatus() == Stream::Ok; } bool ForceField::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.writeString(mName); stream.write(mTriggers.size()); for (i = 0; i < mTriggers.size(); i++) stream.writeString(mTriggers[i]); mathWrite(stream, mBoundingBox); mathWrite(stream, mBoundingSphere); // 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]); // 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); } // mWindings stream.write(mWindings.size()); for (i = 0; i < mWindings.size(); i++) { stream.write(mWindings[i]); } // mSurfaces stream.write(mSurfaces.size()); for (i = 0; i < mSurfaces.size(); i++) { stream.write(mSurfaces[i].windingStart); stream.write(mSurfaces[i].windingCount); stream.write(mSurfaces[i].planeIndex); stream.write(mSurfaces[i].surfaceFlags); stream.write(mSurfaces[i].fanMask); } // mSolidLeafSurfaces stream.write(mSolidLeafSurfaces.size()); for (i = 0; i < mSolidLeafSurfaces.size(); i++) { stream.write(mSolidLeafSurfaces[i]); } stream.write(mColor); return stream.getStatus() == Stream::Ok; } bool ForceField::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 ForceField::readPlaneVector(Stream& stream) { Vector normals; U32 vectorSize; stream.read(&vectorSize); normals.setSize(vectorSize); U32 i; for (i = 0; i < normals.size(); 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; } return (stream.getStatus() == Stream::Ok); } //-------------------------------------------------------------------------- //-------------------------------------- Collision support. Essentially // copied from the interiorCollision // void ForceField::collisionFanFromSurface(const Surface& rSurface, U32* fanIndices, U32* numIndices) const { U32 tempIndices[32]; tempIndices[0] = 0; U32 idx = 1; U32 i; for (i = 1; i < rSurface.windingCount; i += 2) tempIndices[idx++] = i; for (i = ((rSurface.windingCount - 1) & (~0x1)); i > 0; i -= 2) tempIndices[idx++] = i; idx = 0; for (i = 0; i < rSurface.windingCount; i++) { if (rSurface.fanMask & (1 << i)) { fanIndices[idx++] = mWindings[rSurface.windingStart + tempIndices[i]]; } } *numIndices = idx; } bool ForceField::castRay(const Point3F& s, const Point3F& e, RayInfo* info) { bool hit = castRay_r(0, s, e, info); if (hit) { Point3F vec = e - s; F32 len = vec.len(); vec /= len; info->t = mDot(info->point - s, vec) / len; } return hit; } bool ForceField::castRay_r(const U16 node, const Point3F& s, const Point3F& e, RayInfo* info) { if (isBSPLeafIndex(node) == false) { const IBSPNode& rNode = mBSPNodes[node]; const PlaneF& rPlane = getPlane(rNode.planeIndex); PlaneF::Side sSide = rPlane.whichSide(s); PlaneF::Side eSide = rPlane.whichSide(e); switch (PlaneSwitchCode(sSide, eSide)) { case PlaneSwitchCode(PlaneF::Front, PlaneF::Front): case PlaneSwitchCode(PlaneF::Front, PlaneF::On): case PlaneSwitchCode(PlaneF::On, PlaneF::Front): return castRay_r(rNode.frontIndex, s, e, info); break; case PlaneSwitchCode(PlaneF::On, PlaneF::Back): case PlaneSwitchCode(PlaneF::Back, PlaneF::On): case PlaneSwitchCode(PlaneF::Back, PlaneF::Back): return castRay_r(rNode.backIndex, s, e, info); break; case PlaneSwitchCode(PlaneF::On, PlaneF::On): // Line lies on the plane if (isBSPLeafIndex(rNode.backIndex) == false) { if (castRay_r(rNode.backIndex, s, e, info)) return true; } if (isBSPLeafIndex(rNode.frontIndex) == false) { if (castRay_r(rNode.frontIndex, s, e, info)) return true; } return false; break; case PlaneSwitchCode(PlaneF::Front, PlaneF::Back): { Point3F ip; F32 intersectT = rPlane.intersect(s, e); AssertFatal(intersectT != PARALLEL_PLANE, "Error, this should never happen in this case!"); ip.interpolate(s, e, intersectT); if (castRay_r(rNode.frontIndex, s, ip, info)) return true; return castRay_r(rNode.backIndex, ip, e, info); } break; case PlaneSwitchCode(PlaneF::Back, PlaneF::Front): { Point3F ip; F32 intersectT = rPlane.intersect(s, e); AssertFatal(intersectT != PARALLEL_PLANE, "Error, this should never happen in this case!"); ip.interpolate(s, e, intersectT); if (castRay_r(rNode.backIndex, s, ip, info)) return true; return castRay_r(rNode.frontIndex, ip, e, info); } break; default: AssertFatal(false, "Misunderstood switchCode in ForceField::castRay_r"); return false; } } if (isBSPSolidLeaf(node)) { // DMM: Set material info here info->point = s; return true; } return false; } void ForceField::buildPolyList_r(const U16 node, Vector& collPlanes, AbstractPolyList* list, SphereF& s) { if (isBSPLeafIndex(node) == false) { const IBSPNode& rNode = mBSPNodes[node]; const PlaneF& rPlane = getPlane(rNode.planeIndex); F32 dist = rPlane.distToPlane(s.center); if (mFabs(dist) <= s.radius) { // Have to do both, and push the plane back on the list... collPlanes.push_back(rNode.planeIndex); buildPolyList_r(rNode.frontIndex, collPlanes, list, s); buildPolyList_r(rNode.backIndex, collPlanes, list, s); collPlanes.pop_back(); } else if (dist > 0.0f) { buildPolyList_r(rNode.frontIndex, collPlanes, list, s); } else { buildPolyList_r(rNode.backIndex, collPlanes, list, s); } return; } if (isBSPSolidLeaf(node)) { const IBSPLeafSolid& rLeaf = mBSPSolidLeaves[getBSPSolidLeafIndex(node)]; for (U32 i = 0; i < rLeaf.surfaceCount; i++) { U32 surfaceIndex = mSolidLeafSurfaces[rLeaf.surfaceIndex + i]; const Surface& rSurface = mSurfaces[surfaceIndex]; for (U32 j = 0; j < collPlanes.size(); j++) { if (areEqualPlanes(rSurface.planeIndex, collPlanes[j]) == true) { U32 fanVerts[32]; U32 numVerts; collisionFanFromSurface(rSurface, fanVerts, &numVerts); // DMM: Material here list->begin(0, rSurface.planeIndex); U32 vertStart = list->addPoint(mPoints[fanVerts[0]]); list->vertex(vertStart); for (U32 k = 1; k < numVerts; k++) { list->addPoint(mPoints[fanVerts[k]]); list->vertex(vertStart + k); } list->plane(vertStart, vertStart + 1, vertStart + 2); list->end(); break; } } } } } bool ForceField::buildPolyList(AbstractPolyList* list, SphereF& sphere) { Vector planes; buildPolyList_r(0, planes, list, sphere); AssertFatal(planes.size() == 0, "Error, unbalanced plane stack!"); return !list->isEmpty(); }