//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. // // 09/03/03 MM: Changes made by JF to use a stencil mask for mirrors. // //----------------------------------------------------------------------------- #include "interior/mirrorSubObject.h" #include "interior/interiorInstance.h" #include "interior/interior.h" #include "dgl/materialList.h" #include "core/stream.h" #include "dgl/dgl.h" #include "sceneGraph/sgUtil.h" IMPLEMENT_CONOBJECT(MirrorSubObject); #include "platform/platformVideo.h" #include "sceneGraph/sceneGraph.h" //-------------------------------------------------------------------------- MirrorSubObject::MirrorSubObject() { mTypeMask = StaticObjectType; mInitialized = false; } MirrorSubObject::~MirrorSubObject() { } //-------------------------------------------------------------------------- void MirrorSubObject::initPersistFields() { Parent::initPersistFields(); // } //-------------------------------------------------------------------------- void MirrorSubObject::renderObject(SceneState* state, SceneRenderImage* image) { // the surface is rendered when the portal is closed return; } //-------------------------------------------------------------------------- void MirrorSubObject::transformModelview(const U32 portalIndex, const MatrixF& oldMV, MatrixF* pNewMV) { AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!"); AssertFatal(portalIndex == 0, "Error, we only have one portal!"); *pNewMV = oldMV; pNewMV->mul(mReflectionMatrix); } //-------------------------------------------------------------------------- void MirrorSubObject::transformPosition(const U32 portalIndex, Point3F& ioPosition) { AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!"); AssertFatal(portalIndex == 0, "Error, we only have one portal!"); mReflectionMatrix.mulP(ioPosition); } //-------------------------------------------------------------------------- bool MirrorSubObject::computeNewFrustum(const U32 portalIndex, const F64* oldFrustum, const F64 nearPlane, const F64 farPlane, const RectI& oldViewport, F64* newFrustum, RectI& newViewport, const bool flippedMatrix) { AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!"); AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!"); Interior* interior = getInstance()->getDetailLevel(mDetailLevel); static Vector mirrorWindings; mirrorWindings.setSize(surfaceCount); for (U32 i = 0; i < surfaceCount; i++) { SGWinding& rSGWinding = mirrorWindings[i]; const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart + i]; U32 fanIndices[32]; U32 numFanIndices = 0; interior->collisionFanFromSurface(rSurface, fanIndices, &numFanIndices); for (U32 j = 0; j < numFanIndices; j++) rSGWinding.points[j] = interior->mPoints[fanIndices[j]].point; rSGWinding.numPoints = numFanIndices; } MatrixF finalModelView; dglGetModelview(&finalModelView); finalModelView.mul(getSOTransform()); finalModelView.scale(getSOScale()); return sgComputeNewFrustum(oldFrustum, nearPlane, farPlane, oldViewport, mirrorWindings.address(), mirrorWindings.size(), finalModelView, newFrustum, newViewport, flippedMatrix); } //-------------------------------------------------------------------------- void MirrorSubObject::openPortal(const U32 portalIndex, SceneState* pCurrState, SceneState* pParentState) { AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!"); AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!"); if (mZone == 0) pParentState->setupZoneProjection(getInstance()->getCurrZone(0)); else pParentState->setupZoneProjection(mZone + getInstance()->getZoneRangeStart() - 1); // setup transformation glPushMatrix(); dglMultMatrix(&getSOTransform()); glScalef(getSOScale().x, getSOScale().y, getSOScale().z); // setup stencil buffer: glClearStencil(0x0); glStencilMask(~0u); glEnable(GL_STENCIL_TEST); static U32 lastStateKey = 0; U32 stateKey = gClientSceneGraph->getStateKey(); // dont clear the stencil for every portal if(lastStateKey != stateKey) { lastStateKey = stateKey; glClear(GL_STENCIL_BUFFER_BIT); } // clear with the fog color ColorF fogColor = gClientSceneGraph->getFogColor(); glColor3f(fogColor.red, fogColor.green, fogColor.blue); Interior * interior = getInstance()->getDetailLevel(mDetailLevel); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(ItrPaddedPoint), interior->mPoints.address()); // render the visible surface into the stencil buffer (also render the fog color // since terrain may not be rendered and it assumes a fog clear) glStencilFunc(GL_ALWAYS, 1, 0xffffffff); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); for (U32 i = 0; i < surfaceCount; i++) { glDrawElements(GL_TRIANGLE_STRIP, interior->mSurfaces[surfaceStart+i].windingCount, GL_UNSIGNED_INT, &interior->mWindings[interior->mSurfaces[surfaceStart+i].windingStart]); } // now clear the visible surface depth (use stencil). disable color buffers (already // rendered fog in previous step). glDepthRange(1, 1); glDepthFunc(GL_ALWAYS); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glStencilFunc(GL_EQUAL, 1, 0xffffffff); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); for (U32 i = 0; i < surfaceCount; i++) { glDrawElements(GL_TRIANGLE_STRIP, interior->mSurfaces[surfaceStart+i].windingCount, GL_UNSIGNED_INT, &interior->mWindings[interior->mSurfaces[surfaceStart+i].windingStart]); } // reset states (stencil is setup correctly) glDepthFunc(GL_LEQUAL); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthRange(0, 1); glDisableClientState(GL_VERTEX_ARRAY); // reset transform glPopMatrix(); dglSetCanonicalState(); } void MirrorSubObject::closePortal(const U32 portalIndex, SceneState* pCurrState, SceneState* pParentState) { AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!"); AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!"); // setup transformation glPushMatrix(); dglMultMatrix(&getSOTransform()); glScalef(getSOScale().x, getSOScale().y, getSOScale().z); // update depth over portal Interior * interior = getInstance()->getDetailLevel(mDetailLevel); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(ItrPaddedPoint), interior->mPoints.address()); // want to clear stencil value glStencilFunc(GL_EQUAL, 1, 0xffffffff); glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); // render the alpha surface glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glActiveTextureARB(GL_TEXTURE0_ARB); glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_GEN_S); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glEnable(GL_TEXTURE_GEN_T); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glActiveTextureARB(GL_TEXTURE1_ARB); glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_GEN_S); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glEnable(GL_TEXTURE_GEN_T); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glActiveTextureARB(GL_TEXTURE0_ARB); glEnableClientState(GL_VERTEX_ARRAY); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBindTexture(GL_TEXTURE_2D, interior->mMaterialList->getMaterial(interior->mSurfaces[surfaceStart].textureIndex).getGLName()); glActiveTextureARB(GL_TEXTURE1_ARB); Vector* pLMapIndices = &interior->mNormalLMapIndices; U8 baseIndex = (*pLMapIndices)[surfaceStart]; TextureHandle *tex = gInteriorLMManager.getHandle( interior->mLMHandle, getInstance()->getLMHandle(), baseIndex); glBindTexture(GL_TEXTURE_2D, tex->getGLName() ); glActiveTextureARB(GL_TEXTURE0_ARB); glTexGenfv(GL_S, GL_OBJECT_PLANE, (GLfloat*)interior->mTexGenEQs[interior->mSurfaces[surfaceStart].texGenIndex].planeX); glTexGenfv(GL_T, GL_OBJECT_PLANE, (GLfloat*)interior->mTexGenEQs[interior->mSurfaces[surfaceStart].texGenIndex].planeY); glActiveTextureARB(GL_TEXTURE1_ARB); glColor4f(1, 1, 1, mAlphaLevel); for (U32 i = 0; i < surfaceCount; i++) { glTexGenfv(GL_S, GL_OBJECT_PLANE, (GLfloat*)interior->mLMTexGenEQs[surfaceStart+i].planeX); glTexGenfv(GL_T, GL_OBJECT_PLANE, (GLfloat*)interior->mLMTexGenEQs[surfaceStart+i].planeY); glDrawElements(GL_TRIANGLE_STRIP, interior->mSurfaces[surfaceStart+i].windingCount, GL_UNSIGNED_INT, &interior->mWindings[interior->mSurfaces[surfaceStart+i].windingStart]); } glDisableClientState(GL_VERTEX_ARRAY); glActiveTextureARB(GL_TEXTURE0_ARB); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glActiveTextureARB(GL_TEXTURE1_ARB); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_2D); glActiveTextureARB(GL_TEXTURE0_ARB); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDepthFunc(GL_LEQUAL); // reset transform glPopMatrix(); // disable stencil test glDisable(GL_STENCIL_TEST); dglSetCanonicalState(); } //-------------------------------------------------------------------------- void MirrorSubObject::getWSPortalPlane(const U32 portalIndex, PlaneF* pPlane) { AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!"); Interior* interior = getInstance()->getDetailLevel(mDetailLevel); const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart]; PlaneF temp = interior->getPlane(rSurface.planeIndex); if (Interior::planeIsFlipped(rSurface.planeIndex)) temp.neg(); mTransformPlane(getSOTransform(), getSOScale(), temp, pPlane); } //-------------------------------------------------------------------------- U32 MirrorSubObject::getSubObjectKey() const { return InteriorSubObject::MirrorSubObjectKey; } bool MirrorSubObject::_readISO(Stream& stream) { AssertFatal(isInitialized() == false, "Error, should not be initialized here!"); if (Parent::_readISO(stream) == false) return false; stream.read(&mDetailLevel); stream.read(&mZone); stream.read(&mAlphaLevel); stream.read(&surfaceCount); stream.read(&surfaceStart); stream.read(&mCentroid.x); stream.read(&mCentroid.y); stream.read(&mCentroid.z); return true; } bool MirrorSubObject::_writeISO(Stream& stream) const { if (Parent::_writeISO(stream) == false) return false; stream.write(mDetailLevel); stream.write(mZone); stream.write(mAlphaLevel); stream.write(surfaceCount); stream.write(surfaceStart); stream.write(mCentroid.x); stream.write(mCentroid.y); stream.write(mCentroid.z); return true; } SubObjectRenderImage* MirrorSubObject::getRenderImage(SceneState* state, const Point3F& osPoint) { if (isInitialized() == false) setupTransforms(); // Check to make sure that we´re on the right side of the plane... Interior* interior = getInstance()->getDetailLevel(mDetailLevel); const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart]; PlaneF plane = interior->getPlane(rSurface.planeIndex); if (Interior::planeIsFlipped(rSurface.planeIndex)) plane.neg(); if (plane.whichSide(osPoint) != PlaneF::Front) return NULL; // On the right side, guess we have to return an image and a portal... // SubObjectRenderImage* ri = new SubObjectRenderImage; ri->obj = this; ri->isTranslucent = false; U32 realZone; if (getInstance()->getZoneRangeStart() == 0xFFFFFFFF || mZone == 0) { realZone = getInstance()->getCurrZone(0); } else { realZone = getInstance()->getZoneRangeStart() + mZone - 1; } // Create the WS start point. this will be the centroid of the first poly in os space, // transformed out for the sceneGraph, with a smidge of our normal added in to pull // it off the surface plane... Point3F startPoint = mCentroid; PlaneF temp = interior->getPlane(rSurface.planeIndex); if (Interior::planeIsFlipped(rSurface.planeIndex)) temp.neg(); startPoint += Point3F(temp.x, temp.y, temp.z) * 0.01f; getSOTransform().mulP(startPoint); startPoint.convolve(getSOScale()); state->insertTransformPortal(this, 0, realZone, startPoint, true); return ri; } bool MirrorSubObject::renderDetailDependant() const { return true; } U32 MirrorSubObject::getZone() const { return mZone; } void MirrorSubObject::setupTransforms() { mInitialized = true; Interior* interior = getInstance()->getDetailLevel(mDetailLevel); const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart]; for( U32 i=0; imSurfaces[surfaceStart + i]; //surface.mirrored = true; } PlaneF plane = interior->getPlane(rSurface.planeIndex); if (Interior::planeIsFlipped(rSurface.planeIndex)) plane.neg(); Point3F n(plane.x, plane.y, plane.z); Point3F q = n; q *= -plane.d; MatrixF t(true); t.scale(getSOScale()); t.mul(getSOTransform()); t.mulV(n); t.mulP(q); F32* ra = mReflectionMatrix; ra[0] = 1.0f - 2.0f*(n.x*n.x); ra[1] = 0.0f - 2.0f*(n.x*n.y); ra[2] = 0.0f - 2.0f*(n.x*n.z); ra[3] = 0.0f; ra[4] = 0.0f - 2.0f*(n.y*n.x); ra[5] = 1.0f - 2.0f*(n.y*n.y); ra[6] = 0.0f - 2.0f*(n.y*n.z); ra[7] = 0.0f; ra[8] = 0.0f - 2.0f*(n.z*n.x); ra[9] = 0.0f - 2.0f*(n.z*n.y); ra[10] = 1.0f - 2.0f*(n.z*n.z); ra[11] = 0.0f; Point3F qnn = n * mDot(n, q); ra[12] = qnn.x * 2.0f; ra[13] = qnn.y * 2.0f; ra[14] = qnn.z * 2.0f; ra[15] = 1.0f; // Now, the GGems series (as of v1) uses row vectors (arg) mReflectionMatrix.transpose(); } void MirrorSubObject::noteTransformChange() { setupTransforms(); Parent::noteTransformChange(); } InteriorSubObject* MirrorSubObject::clone(InteriorInstance* instance) const { MirrorSubObject* pClone = new MirrorSubObject; pClone->mDetailLevel = mDetailLevel; pClone->mZone = mZone; pClone->mAlphaLevel = mAlphaLevel; pClone->mCentroid = mCentroid; pClone->surfaceCount = surfaceCount; pClone->surfaceStart = surfaceStart; pClone->mInteriorInstance = instance; return pClone; }