//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "ts/tsMesh.h" #include "dgl/dgl.h" #include "math/mMath.h" #include "math/mathIO.h" #include "ts/tsShape.h" #include "console/console.h" #include "ts/tsShapeInstance.h" #include "sim/sceneObject.h" #include "ts/tsSortedMesh.h" #include "core/bitRender.h" #include "collision/convex.h" #include "core/frameAllocator.h" #include "platform/profiler.h" // Not worth the effort, much less the effort to comment, but if the draw types // are consecutive use addition rather than a table to go from index to command value... #if ((GL_TRIANGLES+1==GL_TRIANGLE_STRIP) && (GL_TRIANGLE_STRIP+1==GL_TRIANGLE_FAN)) #define getDrawType(a) (GL_TRIANGLES+(a)) #else U32 drawTypes[] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }; #define getDrawType(a) (drawTypes[a]) #endif // structures used to share data between detail levels... // used (and valid) during load only Vector TSMesh::smVertsList; Vector TSMesh::smNormsList; Vector TSMesh::smEncodedNormsList; Vector TSMesh::smTVertsList; Vector TSMesh::smDataCopied; Vector TSMesh::smSaveVerts; Vector TSMesh::smSaveNorms; Vector TSMesh::smSaveTVerts; Vector TSSkinMesh::smInitTransformList; Vector TSSkinMesh::smVertexIndexList; Vector TSSkinMesh::smBoneIndexList; Vector TSSkinMesh::smWeightList; Vector TSSkinMesh::smNodeIndexList; Vector gNormalStore; F32 TSMesh::overrideFadeVal = 1.0; bool TSMesh::smUseTriangles = false; // convert all primitives to triangle lists on load bool TSMesh::smUseOneStrip = false; // join triangle strips into one long strip on load S32 TSMesh::smMinStripSize = 1; // smallest number of _faces_ allowed per strip (all else put in tri list) bool TSMesh::smUseEncodedNormals = false; // quick function to force object to face camera -- currently throws out roll :( void forceFaceCamera() { MatrixF mat; Point4F p; dglGetModelview(&mat); mat.getColumn(3,&p); mat.identity(); mat.setColumn(3,p); dglLoadMatrix(&mat); if (TSShapeInstance::smRenderData.objectScale) glScalef(TSShapeInstance::smRenderData.objectScale->x, TSShapeInstance::smRenderData.objectScale->y, TSShapeInstance::smRenderData.objectScale->z); } void forceFaceCameraZAxis() { MatrixF mat; dglGetModelview(&mat); Point3F z; mat.getColumn(2,&z); // this is where the z-axis goes, keep it here but reset x and y Point3F x,y; if (mFabs(z.y) < 0.99f) { // mCross(Point3F(0,1,0),tAxis,&x); x.set(z.z,0,-z.x); x.normalize(); mCross(z,x,&y); } else { // mCross(z,Point3F(1,0,0),&y); y.set(0,z.z,-z.y); y.normalize(); mCross(y,z,&x); } mat.setColumn(0,x); mat.setColumn(1,y); mat.setColumn(2,z); dglLoadMatrix(&mat); if (TSShapeInstance::smRenderData.objectScale) glScalef(TSShapeInstance::smRenderData.objectScale->x,TSShapeInstance::smRenderData.objectScale->y,TSShapeInstance::smRenderData.objectScale->z); } void TSMesh::saveMergeVerts() { S32 startMerge = vertsPerFrame - mergeIndices.size(); S32 j; for (j=0; j>30); glDrawElements(drawType,draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]); PROFILE_END(); } // unlock... if (lockArrays) glUnlockArraysEXT(); } void TSMesh::render(S32 frame, S32 matFrame, TSMaterialList * materials) { if( vertsPerFrame <= 0 ) { return; } S32 firstVert = vertsPerFrame * frame; S32 firstTVert = vertsPerFrame * matFrame; if (getFlags(Billboard)) { if (getFlags(BillboardZAxis)) forceFaceCameraZAxis(); else forceFaceCamera(); } const Point3F * normals = getNormals(firstVert); saveMergeNormals(); // verts & tverts saved and restored on tsshapeinstance::setStatics // set up vertex arrays -- already enabled in TSShapeInstance::render glVertexPointer(3,GL_FLOAT,0,&verts[firstVert]); glNormalPointer(GL_FLOAT,0,normals); glTexCoordPointer(2,GL_FLOAT,0,&tverts[firstTVert]); if (TSShapeInstance::smRenderData.detailMapMethod == TSShapeInstance::DETAIL_MAP_MULTI_1 || TSShapeInstance::smRenderData.detailMapMethod == TSShapeInstance::DETAIL_MAP_MULTI_2) { glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.detailMapTE); glTexCoordPointer(2,GL_FLOAT,0,&tverts[firstTVert]); glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); } // lock... bool lockArrays = dglDoesSupportCompiledVertexArray(); if (lockArrays) glLockArraysEXT(0,vertsPerFrame); for (S32 i=0; i>30); glDrawElements(drawType,draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]); } // unlock... if (lockArrays) glUnlockArraysEXT(); restoreMergeNormals(); } const Point3F * TSMesh::getNormals(S32 firstVert) { if (getFlags(UseEncodedNormals)) { gNormalStore.setSize(vertsPerFrame); for (S32 i=0; igetGLName()); glActiveTextureARB(GL_TEXTURE0_ARB + baseTE); } else if (fogMethod == TSShapeInstance::FOG_MULTI_1_TEXGEN) { // set up fog texure unit glActiveTextureARB(GL_TEXTURE0_ARB + fogTE); glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); // set up fog map glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.fogMapHandle->getGLName()); // set up texgen equations glTexGeni(GL_S,GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glTexGenfv(GL_S,GL_OBJECT_PLANE,&TSShapeInstance::smRenderData.fogTexGenS.x); glTexGenfv(GL_T,GL_OBJECT_PLANE,&TSShapeInstance::smRenderData.fogTexGenT.x); // return to base TE glActiveTextureARB(GL_TEXTURE0_ARB + baseTE); } //---------------------------------- // set up texture enviroment for base texture TSShapeInstance::smRenderData.materialFlags = TSMaterialList::S_Wrap | TSMaterialList::T_Wrap; TSShapeInstance::smRenderData.materialIndex = TSDrawPrimitive::NoMaterial; // draw one-sided... glEnable(GL_CULL_FACE); glFrontFace(GL_CW); // enable vertex arrays... glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // when blending we modulate and draw using src_alpha, 1-src_alpha... glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // we modulate in order to apply lighting... glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); // but we don't blend by default... glDisable(GL_BLEND); glDepthMask(GL_TRUE); // lighting? TSShapeInstance::smRenderData.lightingOn = glIsEnabled(GL_LIGHTING)!=0; // set vertex color (if emapping, vertexColor.w holds emapAlpha) TSShapeInstance::smRenderData.vertexAlpha.set(); Point4F vertexColor(1,1,1,TSShapeInstance::smRenderData.vertexAlpha.current); glColor4fv(vertexColor); glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,vertexColor); // this should be off by default, but we'll end up turning it on asap... glDisable(GL_TEXTURE_2D); } void TSMesh::resetMaterials() { // restore gl state changes made for dmap if (TSShapeInstance::smRenderData.detailMapMethod==TSShapeInstance::DETAIL_MAP_MULTI_1 || TSShapeInstance::smRenderData.detailMapMethod==TSShapeInstance::DETAIL_MAP_MULTI_2) { // set detail maps texture unit glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.detailMapTE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_TEXTURE_2D); // may have changed texture matrix of one of detail-texture texture-environment if (mFabs(TSShapeInstance::smRenderData.detailTextureScale-1.0f) > 0.001f) { glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); } if (TSShapeInstance::smRenderData.detailMapMethod==TSShapeInstance::DETAIL_MAP_MULTI_2) { glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,Point4F(0,0,0,0)); glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.detailMapTE+1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV,GL_RGB_SCALE,1.0f); } else glTexEnvf(GL_TEXTURE_ENV,GL_RGB_SCALE,1.0f); // disable tcoord's on detail map's TE glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.detailMapTE); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); } // restore gl state changes made for emap if (TSShapeInstance::smRenderData.environmentMapMethod==TSShapeInstance::ENVIRONMENT_MAP_MULTI_1) { // set second texure unit (TE #1) glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.environmentMapTE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); } else if (TSShapeInstance::smRenderData.environmentMapMethod==TSShapeInstance::ENVIRONMENT_MAP_MULTI_3) { // set second texure unit (TE #1) glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.environmentMapTE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_TEXTURE_2D); // set third texure unit (TE #2) glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.environmentMapTE + 1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); // set fourth texure unit (TE #3) glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.environmentMapTE + 2); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_TEXTURE_2D); } if (TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_MULTI_1 || TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_MULTI_1_TEXGEN) { glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.fogTE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_2D); } // restore gl state changes made for base texture if (dglDoesSupportARBMultitexture()) glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDepthMask(GL_TRUE); // don't cull by default... glDisable(GL_CULL_FACE); // if lights were on when we started, make sure they're on when we leave if (TSShapeInstance::smRenderData.lightingOn && !glIsEnabled(GL_LIGHTING)) glEnable(GL_LIGHTING); // baseTE != 0? if (TSShapeInstance::smRenderData.baseTE!=0) { glActiveTextureARB(GL_TEXTURE0_ARB); glClientActiveTextureARB(GL_TEXTURE0_ARB); } // restore cloak shifting if (TSShapeInstance::smRenderData.textureMatrixPushed) { glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_MODELVIEW); TSShapeInstance::smRenderData.textureMatrixPushed = false; } } // set up materials for mesh rendering // keeps track of flags via TSShapeInstance::smRenderData.materialFlags and only changes what needs to be changed // keeps track of material index via TSShapeInstance::smRenderData.materialIndex void TSMesh::setMaterial(S32 matIndex, TSMaterialList* materials) { if ((matIndex|TSShapeInstance::smRenderData.materialIndex) & TSDrawPrimitive::NoMaterial) { if (matIndex & TSDrawPrimitive::NoMaterial) { glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDepthMask(GL_TRUE); TSShapeInstance::smRenderData.materialIndex = matIndex; TSShapeInstance::smRenderData.materialFlags &= ~TSMaterialList::Translucent; return; } glEnable(GL_TEXTURE_2D); } matIndex &= TSDrawPrimitive::MaterialMask; U32 flags = materials->getFlags(matIndex); U32 bareFlags = flags; if (TSShapeInstance::smRenderData.alwaysAlpha || TSShapeInstance::smRenderData.fadeSet) flags |= TSMaterialList::Translucent; U32 deltaFlags = flags ^ TSShapeInstance::smRenderData.materialFlags; if (TSShapeInstance::smRenderData.environmentMapMethod!=TSShapeInstance::ENVIRONMENT_MAP_MULTI_1) deltaFlags &= ~TSMaterialList::NeverEnvMap; // update flags and material index... TSShapeInstance::smRenderData.materialFlags = flags; TSShapeInstance::smRenderData.materialIndex = matIndex; if (TSShapeInstance::smRenderData.useOverride == false || bareFlags & TSMaterialList::Translucent) { TextureHandle & tex = materials->getMaterial(matIndex); glBindTexture(GL_TEXTURE_2D, tex.getGLName()); } else glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.override.getGLName()); // anything change...? if (deltaFlags) { if (deltaFlags & TSMaterialList::NeverEnvMap && !TSShapeInstance::smRenderData.useOverride ) { glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.environmentMapTE); if (bareFlags & TSMaterialList::NeverEnvMap) glDisable(GL_TEXTURE_2D); else glEnable(GL_TEXTURE_2D); glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); } if (flags & TSMaterialList::Translucent) { if (TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_TWO_PASS || TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_TWO_PASS_TEXGEN) { TSShapeInstance::smRenderData.vertexAlpha.fog = 1.0f - TSShapeInstance::smRenderData.fogColor.w; } else if ((TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_MULTI_1 || TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_MULTI_1_TEXGEN) && flags & (TSMaterialList::Additive|TSMaterialList::Subtractive)) { glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.fogTE); glDisable(GL_TEXTURE_2D); glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); TSShapeInstance::smRenderData.vertexAlpha.fog = 1.0f - TSShapeInstance::smRenderData.fogColor.w; } glEnable(GL_BLEND); glDepthMask(GL_FALSE); } else { if (TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_TWO_PASS || TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_TWO_PASS_TEXGEN) { TSShapeInstance::smRenderData.vertexAlpha.fog = 1.0f; } else if ((TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_MULTI_1 || TSShapeInstance::smRenderData.fogMethod == TSShapeInstance::FOG_MULTI_1_TEXGEN) && flags & (TSMaterialList::Additive|TSMaterialList::Subtractive)) { glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.fogTE); glEnable(GL_TEXTURE_2D); glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); TSShapeInstance::smRenderData.vertexAlpha.fog = 1.0f; } glDisable(GL_BLEND); glDepthMask(GL_TRUE); } if (deltaFlags & (TSMaterialList::Additive|TSMaterialList::Subtractive)) { if (flags & TSMaterialList::Additive) glBlendFunc(GL_SRC_ALPHA, GL_ONE); else if (flags & TSMaterialList::Subtractive) glBlendFunc(GL_ZERO,GL_ONE_MINUS_SRC_COLOR); else glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); } if (deltaFlags & TSMaterialList::SelfIlluminating) { if (TSShapeInstance::smRenderData.detailMapMethod==TSShapeInstance::DETAIL_MAP_MULTI_2) { // special case: lighting done on different TE than texture... glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE + 1); if (flags & TSMaterialList::SelfIlluminating) { // modulate... glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); } else { // we need to use the combine extension...we modulate by primary color rather than fragment color glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB,GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_ALPHA,GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_ALPHA,GL_SRC_ALPHA); } glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); } // turn off lights if self-illuminating (or back on if not) if (flags & TSMaterialList::SelfIlluminating || !TSShapeInstance::smRenderData.lightingOn) glDisable(GL_LIGHTING); else glEnable(GL_LIGHTING); } } // gotta set these every time since present value depends on texture not gl state glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,flags & TSMaterialList::S_Wrap ? GL_REPEAT : GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,flags & TSMaterialList::T_Wrap ? GL_REPEAT : GL_CLAMP); // emap texture... if (TSShapeInstance::smRenderData.environmentMapMethod==TSShapeInstance::ENVIRONMENT_MAP_MULTI_3) { // set emap's texture unit... glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.environmentMapTE + 2); if (TSShapeInstance::smRenderData.useOverride == false || flags & TSMaterialList::Translucent) { TextureHandle & tex = materials->getMaterial(matIndex); glBindTexture(GL_TEXTURE_2D, tex.getGLName()); } else glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.override.getGLName()); glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.environmentMapTE); glBindTexture(GL_TEXTURE_2D, materials->getReflectionMap(matIndex)->getGLName()); // set default texture unit... glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); } // dmap texture... if (TSShapeInstance::smRenderData.detailMapMethod==TSShapeInstance::DETAIL_MAP_MULTI_1 || TSShapeInstance::smRenderData.detailMapMethod==TSShapeInstance::DETAIL_MAP_MULTI_2) { // set detail map's texture unit... glActiveTextureARB(GL_TEXTURE0_ARB+TSShapeInstance::smRenderData.detailMapTE); TextureHandle * detailMap = materials->getDetailMap(matIndex); if (detailMap) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,detailMap->getGLName()); // has texture scale changed? F32 tscale = materials->getDetailMapScale(matIndex); if (mFabs(tscale-TSShapeInstance::smRenderData.detailTextureScale) > 0.001f) { // yes, update scale TSShapeInstance::smRenderData.detailTextureScale = tscale; glMatrixMode(GL_TEXTURE); MatrixF scaleMat; scaleMat.identity(); for (S32 i=0; i<15; i++) ((F32*)scaleMat)[i] *= tscale; dglLoadMatrix(&scaleMat); glMatrixMode(GL_MODELVIEW); } } else glDisable(GL_TEXTURE_2D); // set default texture unit... glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE); } // translucent materials shouldn't get cloak shifting // 1: pushed -> pushed // 2: pushed -> not pushed // 3: not pushed -> pushed // 4: not pushed -> not pushed if (TSShapeInstance::smRenderData.textureMatrixPushed) { if (TSShapeInstance::smRenderData.useOverride && bareFlags & TSMaterialList::Translucent) { // Leave it alone } else { // Pop it off glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_MODELVIEW); TSShapeInstance::smRenderData.textureMatrixPushed = false; } } else { if (TSShapeInstance::smRenderData.useOverride && bareFlags & TSMaterialList::Translucent) { // Push it up glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); TSShapeInstance::smRenderData.textureMatrixPushed = true; } else { // leave it alone } } // handle environment map if (flags & TSMaterialList::NeverEnvMap) TSShapeInstance::smRenderData.vertexAlpha.emap = 1.0f; else TSShapeInstance::smRenderData.vertexAlpha.emap = TSShapeInstance::smRenderData.environmentMapAlpha * materials->getReflectionAmount(matIndex); // handle vertex alpha if (TSShapeInstance::smRenderData.vertexAlpha.set()) { Point4F v(1,1,1,TSShapeInstance::smRenderData.vertexAlpha.current); glColor4fv(v); glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,v); } // set up fade if( overrideFadeVal < 1.0f && dglDoesSupportTextureEnvCombine() ) { S32 & emapTE = TSShapeInstance::smRenderData.environmentMapTE; S32 & baseTE = TSShapeInstance::smRenderData.baseTE; if( TSShapeInstance::smRenderData.environmentMapMethod == TSShapeInstance::ENVIRONMENT_MAP_MULTI_1 ) { glActiveTextureARB(GL_TEXTURE0_ARB + emapTE); glDisable(GL_TEXTURE_2D); } glActiveTextureARB(GL_TEXTURE0_ARB + baseTE); glEnable( GL_BLEND ); if( TSShapeInstance::smRenderData.materialFlags & TSMaterialList::Translucent ) { glBlendFunc( GL_SRC_ALPHA, GL_ONE ); } else { glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } ColorF curColor; glGetFloatv( GL_FOG_COLOR, (GLfloat*)&curColor ); curColor.alpha = overrideFadeVal; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, curColor); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA); } } void TSMesh::setFade(F32 fadeValue) { TSShapeInstance::smRenderData.vertexAlpha.vis = fadeValue; if (TSShapeInstance::smRenderData.vertexAlpha.set()) { Point4F v(1,1,1,TSShapeInstance::smRenderData.vertexAlpha.current); glColor4fv(v); glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,v); } TSShapeInstance::smRenderData.fadeSet = true; } void TSMesh::clearFade() { setFade(1.0f); TSShapeInstance::smRenderData.fadeSet = false; } //----------------------------------------------------- // TSMesh render environment map (2-pass) methods //----------------------------------------------------- void TSMesh::renderEnvironmentMap(S32 frame, S32 matFrame, TSMaterialList * materials) { matFrame; // most gl states assumed to be all set up... // if we're here, then we're drawing environment map in two passes... S32 firstVert = vertsPerFrame * frame; // set up vertex arrays -- already enabled in TSShapeInstance::render glVertexPointer(3,GL_FLOAT,0,&verts[firstVert]); glNormalPointer(GL_FLOAT,0,&norms[firstVert]); // lock... bool lockArrays = dglDoesSupportCompiledVertexArray(); if (lockArrays) glLockArraysEXT(0,vertsPerFrame); S32 matIndex = -1; for (S32 i=0; igetFlags(matIndex) & TSMaterialList::NeverEnvMap) continue; TSShapeInstance::smRenderData.vertexAlpha.emap = TSShapeInstance::smRenderData.environmentMapAlpha * materials->getReflectionAmount(matIndex); glBindTexture(GL_TEXTURE_2D, materials->getReflectionMap(matIndex)->getGLName()); if (TSShapeInstance::smRenderData.vertexAlpha.set()) { Point4F v(1,1,1,TSShapeInstance::smRenderData.vertexAlpha.current); glColor4fv(v); glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,v); } } } glDrawElements(getDrawType(draw.matIndex>>30),draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]); } // unlock... if (lockArrays) glUnlockArraysEXT(); } void TSMesh::initEnvironmentMapMaterials() { // set up gl environment // TE0 glActiveTextureARB(GL_TEXTURE0_ARB); glEnable(GL_TEXTURE_2D); glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glDepthMask(GL_TRUE); glFrontFace(GL_CW); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); // TE1 glActiveTextureARB(GL_TEXTURE1_ARB); glEnable(GL_TEXTURE_2D); glTexGeni(GL_S,GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.environmentMapGLName); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); // should leave alpha alone since emap has no alpha // TE0 again glActiveTextureARB(GL_TEXTURE0_ARB); TSShapeInstance::smRenderData.vertexAlpha.init(); TSShapeInstance::smRenderData.vertexAlpha.emap = TSShapeInstance::smRenderData.environmentMapAlpha; TSShapeInstance::smRenderData.vertexAlpha.always = TSShapeInstance::smRenderData.alwaysAlphaValue; TSShapeInstance::smRenderData.vertexAlpha.set(); glColor4fv(Point4F(1.0f,1.0f,1.0f,TSShapeInstance::smRenderData.vertexAlpha.current)); } void TSMesh::resetEnvironmentMapMaterials() { // restore texture environmnet 0 glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDepthMask(GL_TRUE); glDisable(GL_CULL_FACE); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); // restore texture environment 1 glActiveTextureARB(GL_TEXTURE1_ARB); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_2D); glActiveTextureARB(GL_TEXTURE0_ARB); } //----------------------------------------------------- // TSMesh render detail map (2 pass) methods //----------------------------------------------------- void TSMesh::renderDetailMap(S32 frame, S32 matFrame, TSMaterialList * materials) { // most gl states assumed to be all set up... // if we're here, then we're drawing detail map in two passes... S32 firstVert = vertsPerFrame * frame; S32 firstTVert = vertsPerFrame * matFrame; // set up vertex arrays -- already enabled in TSShapeInstance::render glVertexPointer(3,GL_FLOAT,0,&verts[firstVert]); glNormalPointer(GL_FLOAT,0,&norms[firstVert]); glTexCoordPointer(2,GL_FLOAT,0,&tverts[firstTVert]); // lock... bool lockArrays = dglDoesSupportCompiledVertexArray(); if (lockArrays) glLockArraysEXT(0,vertsPerFrame); S32 matIndex = -1; for (S32 i=0; igetDetailMap(matIndex); if (detailMap) glBindTexture(GL_TEXTURE_2D,detailMap->getGLName()); else continue; TSShapeInstance::smRenderData.detailMapTE = 1; F32 tscale = materials->getDetailMapScale(matIndex); if (mFabs(tscale-TSShapeInstance::smRenderData.detailTextureScale) > 0.001f) { // yes, update scale TSShapeInstance::smRenderData.detailTextureScale = tscale; glMatrixMode(GL_TEXTURE); MatrixF scaleMat; scaleMat.identity(); for (S32 i=0; i<15; i++) ((F32*)scaleMat)[i] *= tscale; dglLoadMatrix(&scaleMat); glMatrixMode(GL_MODELVIEW); } } } if (TSShapeInstance::smRenderData.detailMapTE) glDrawElements(getDrawType(draw.matIndex>>30),draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]); } // unlock... if (lockArrays) glUnlockArraysEXT(); } void TSMesh::initDetailMapMaterials() { TSShapeInstance::smRenderData.detailTextureScale = 1.0f; glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glEnable(GL_CULL_FACE); glFrontFace(GL_CW); glDepthMask(GL_TRUE); glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // set texture environment color and fragment color // so that c=1-k/2 and f=k/2, where k is 1-detailMapAlpha // result is that distance from 0.5 is reduced as // detailMapAlpha goes to 0... F32 k = 0.5f * (1.0f - TSShapeInstance::smRenderData.detailMapAlpha); Point4F TEColor(1.0f-k,1.0f-k,1.0f-k,1.0f); glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,TEColor); Point4F fColor(k,k,k,1.0f); glColor4fv(fColor); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_BLEND); glDisable(GL_LIGHTING); } void TSMesh::resetDetailMapMaterials() { glEnable(GL_LIGHTING); if (mFabs(TSShapeInstance::smRenderData.detailTextureScale-1.0f)>0.001f) { glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); } glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glDepthMask(GL_TRUE); glDisable(GL_CULL_FACE); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glColor4fv(Point4F(1,1,1,1)); glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,Point4F(0,0,0,0)); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); } //----------------------------------------------------- // TSMesh renderShadow //----------------------------------------------------- Vector gShadowVerts(__FILE__, __LINE__); void shadowTransform(const MatrixF & mat, Point3F * v, Point3F * vend, S32 max) { const F32 * m = (const F32*)mat; F32 ax = m[0]; F32 ay = m[1]; F32 az = m[2]; F32 at = m[3]; F32 bx = m[8]; F32 by = m[9]; F32 bz = m[10]; F32 bt = m[11]; Point2I * dest = gShadowVerts.address(); S32 val; while (v < vend) { val = (S32)(ax*v->x + ay*v->y + az*v->z + at); dest->x = val<0 ? 0 : (val>max ? max : val); val = (S32)(bx*v->x + by*v->y + bz*v->z + bt); dest->y = val<0 ? 0 : (val>max ? max : val); dest++; v++; } } void TSMesh::renderShadow(S32 frame, const MatrixF & mat, S32 dim, U32 * bits, TSMaterialList * materials) { if (!vertsPerFrame || !primitives.size()) return; if (!(primitives[0].matIndex & TSDrawPrimitive::NoMaterial) && (TSMaterialList::Translucent & materials->getFlags(primitives[0].matIndex & TSDrawPrimitive::MaterialMask))) // if no material...it would be nice to just exit...but may have some real polys back there... return; AssertFatal(primitives[0].matIndex & TSDrawPrimitive::Indexed,"TSMesh::renderShadow: indexed polys only"); gShadowVerts.setSize(vertsPerFrame); Point3F * vstart = &verts[frame*vertsPerFrame]; Point3F * vend = vstart + vertsPerFrame; // result placed into gShadowVerts shadowTransform(mat,vstart,vend,dim-1); // pick correct bit render routine...we assume all strips or all triangles if ( (primitives[0].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip) BitRender::render_strips((U8*)primitives.address(),primitives.size(),sizeof(TSDrawPrimitive),indices.address(),gShadowVerts.address(),dim,bits); else if ( (primitives[0].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) BitRender::render_tris((U8*)primitives.address(),primitives.size(),sizeof(TSDrawPrimitive),indices.address(),gShadowVerts.address(),dim,bits); else AssertFatal(0,"TSMesh::renderShadow: strips or triangles only...how'd you get in here."); } //----------------------------------------------------- // TSMesh render fog //----------------------------------------------------- void TSMesh::renderFog(S32 frame, TSMaterialList* materials) { if (getFlags(Billboard)) { if (getFlags(BillboardZAxis)) forceFaceCameraZAxis(); else forceFaceCamera(); } S32 firstVert = vertsPerFrame * frame; // set up vertex arrays -- already enabled in TSShapeInstance::render glVertexPointer(3,GL_FLOAT,0,&verts[firstVert]); // lock... bool lockArrays = dglDoesSupportCompiledVertexArray(); if (lockArrays) glLockArraysEXT(0,vertsPerFrame); for (S32 i=0; igetFlags(primitives[i].matIndex & TSDrawPrimitive::MaterialMask) & (TSMaterialList::Translucent | TSMaterialList::Additive)) continue; glDrawElements(getDrawType(draw.matIndex>>30),draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]); } // unlock... if (lockArrays) glUnlockArraysEXT(); } //----------------------------------------------------- // TSMesh collision methods //----------------------------------------------------- bool TSMesh::buildPolyList(S32 frame, AbstractPolyList * polyList, U32 & surfaceKey) { S32 firstVert = vertsPerFrame * frame, i, base; // add the verts... if (vertsPerFrame) { base = polyList->addPoint(verts[firstVert]); for (i=1; iaddPoint(verts[i+firstVert]); } // add the polys... for (i=0; ibegin(material,surfaceKey++); polyList->vertex(idx0); polyList->vertex(idx1); polyList->vertex(idx2); polyList->plane(idx0,idx1,idx2); polyList->end(); j += 3; } } else { AssertFatal((draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)"); U32 idx0 = base + indices[start + 0]; U32 idx1; U32 idx2 = base + indices[start + 1]; U32 * nextIdx = &idx1; for (S32 j=2; jbegin(material,surfaceKey++); polyList->vertex(idx0); polyList->vertex(idx1); polyList->vertex(idx2); polyList->plane(idx0,idx1,idx2); polyList->end(); } } } return true; } bool TSMesh::getFeatures(S32 frame, const MatrixF& mat, const VectorF& /*n*/, ConvexFeature* cf, U32& /*surfaceKey*/) { // DMM NOTE! Do not change without talking to Dave Moore. ShapeBase assumes that // this will return ALL information from the mesh. S32 firstVert = vertsPerFrame * frame; S32 i; S32 base = cf->mVertexList.size(); for (i = 0; i < vertsPerFrame; i++) { cf->mVertexList.increment(); mat.mulP(verts[firstVert + i], &cf->mVertexList.last()); } // add the polys... for (i=0; i < primitives.size(); i++) { TSDrawPrimitive & draw = primitives[i]; U32 start = draw.start; AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)"); U32 material = draw.matIndex & TSDrawPrimitive::MaterialMask; // gonna depend on what kind of primitive it is... if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) { for (S32 j=0; jmVertexList[base + indices[start + j + 0]], cf->mVertexList[base + indices[start + j + 1]], cf->mVertexList[base + indices[start + j + 2]]); cf->mFaceList.increment(); cf->mFaceList.last().normal = plane; cf->mFaceList.last().vertex[0] = base + indices[start + j + 0]; cf->mFaceList.last().vertex[1] = base + indices[start + j + 1]; cf->mFaceList.last().vertex[2] = base + indices[start + j + 2]; for (U32 l = 0; l < 3; l++) { U32 newEdge0, newEdge1; U32 zero = base + indices[start + j + l]; U32 one = base + indices[start + j + ((l+1)%3)]; newEdge0 = getMin(zero, one); newEdge1 = getMax(zero, one); bool found = false; for (S32 k = 0; k < cf->mEdgeList.size(); k++) { if (cf->mEdgeList[k].vertex[0] == newEdge0 && cf->mEdgeList[k].vertex[1] == newEdge1) { found = true; break; } } if (!found) { cf->mEdgeList.increment(); cf->mEdgeList.last().vertex[0] = newEdge0; cf->mEdgeList.last().vertex[1] = newEdge1; } } } } else { AssertFatal((draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)"); U32 idx0 = base + indices[start + 0]; U32 idx1; U32 idx2 = base + indices[start + 1]; U32 * nextIdx = &idx1; for (S32 j=2; jmVertexList[idx0], cf->mVertexList[idx1], cf->mVertexList[idx2]); cf->mFaceList.increment(); cf->mFaceList.last().normal = plane; cf->mFaceList.last().vertex[0] = idx0; cf->mFaceList.last().vertex[1] = idx1; cf->mFaceList.last().vertex[2] = idx2; U32 newEdge0, newEdge1; newEdge0 = getMin(idx0, idx1); newEdge1 = getMax(idx0, idx1); bool found = false; S32 k; for (k = 0; k < cf->mEdgeList.size(); k++) { if (cf->mEdgeList[k].vertex[0] == newEdge0 && cf->mEdgeList[k].vertex[1] == newEdge1) { found = true; break; } } if (!found) { cf->mEdgeList.increment(); cf->mEdgeList.last().vertex[0] = newEdge0; cf->mEdgeList.last().vertex[1] = newEdge1; } newEdge0 = getMin(idx1, idx2); newEdge1 = getMax(idx1, idx2); found = false; for (k = 0; k < cf->mEdgeList.size(); k++) { if (cf->mEdgeList[k].vertex[0] == newEdge0 && cf->mEdgeList[k].vertex[1] == newEdge1) { found = true; break; } } if (!found) { cf->mEdgeList.increment(); cf->mEdgeList.last().vertex[0] = newEdge0; cf->mEdgeList.last().vertex[1] = newEdge1; } newEdge0 = getMin(idx0, idx2); newEdge1 = getMax(idx0, idx2); found = false; for (k = 0; k < cf->mEdgeList.size(); k++) { if (cf->mEdgeList[k].vertex[0] == newEdge0 && cf->mEdgeList[k].vertex[1] == newEdge1) { found = true; break; } } if (!found) { cf->mEdgeList.increment(); cf->mEdgeList.last().vertex[0] = newEdge0; cf->mEdgeList.last().vertex[1] = newEdge1; } } } } return false; } void TSMesh::support(S32 frame, const Point3F& v, F32* currMaxDP, Point3F* currSupport) { if (vertsPerFrame == 0) return; U32 waterMark = FrameAllocator::getWaterMark(); F32* pDots = (F32*)FrameAllocator::alloc(sizeof(F32) * vertsPerFrame); S32 firstVert = vertsPerFrame * frame; m_point3F_bulk_dot(&v.x, &verts[firstVert].x, vertsPerFrame, sizeof(Point3F), pDots); F32 localdp = *currMaxDP; S32 index = -1; for (S32 i = 0; i < vertsPerFrame; i++) { if (pDots[i] > localdp) { localdp = pDots[i]; index = i; } } FrameAllocator::setWaterMark(waterMark); if (index != -1) { *currMaxDP = localdp; *currSupport = verts[index + firstVert]; } } bool TSMesh::castRay(S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo) { if (planeNormals.empty()) // if haven't done it yet... buildConvexHull(); // Keep track of startTime and endTime. They start out at just under 0 and just over 1, respectively. // As we check against each plane, prune start and end times back to represent current intersection of // line with all the planes (or rather with all the half-spaces defined by the planes). // But, instead of explicitly keeping track of startTime and endTime, keep track as numerator and denominator // so that we can avoid as many divisions as possible. // F32 startTime = -0.01f; F32 startNum = -0.01f; F32 startDen = 1.00f; // F32 endTime = 1.01f; F32 endNum = 1.01f; F32 endDen = 1.00f; S32 curPlane = 0; U32 curMaterial = 0; bool found = false; // the following block of code is an optimization... // it isn't necessary if the longer version of the main loop is used bool tmpFound; S32 tmpPlane; F32 sgn = -1.0f; F32 * pnum = &startNum; F32 * pden = &startDen; S32 * pplane = &curPlane; bool * pfound = &found; S32 startPlane = frame * planesPerFrame; for (S32 i=startPlane; i0.0f) { // same side of the plane...which side -- dot==0 considered inside if (dot1>0.0f) // start and end outside of this plane, no collision return false; // start and end inside plane, continue continue; } AssertFatal(dot1/(dot1-dot2)>=0.0f && dot1/(dot1-dot2)<=1.0f,"TSMesh::castRay (1)"); // find intersection (time) with this plane... // F32 time = dot1 / (dot1-dot2); F32 num = mFabs(dot1); F32 den = mFabs(dot1-dot2); // the following block of code is an optimized version... // this can be commented out and the following block of code used instead // if debugging a problem in this code, that should probably be done // if you want to see how this works, look at the following block of code, // not this one... // Note that this does not get optimized appropriately...it is included this way // as an idea for future optimization. if (sgn*dot1>=0) { sgn *= -1.0f; pnum = (F32*) ((dsize_t)pnum ^ (dsize_t)&endNum ^ (dsize_t)&startNum); pden = (F32*) ((dsize_t)pden ^ (dsize_t)&endDen ^ (dsize_t)&startDen); pplane = (S32*) ((dsize_t)pplane ^ (dsize_t)&tmpPlane ^ (dsize_t)&curPlane); pfound = (bool*) ((dsize_t)pfound ^ (dsize_t)&tmpFound ^ (dsize_t)&found); } bool noCollision = num*endDen*sgnstartNum*den) // if (time>startTime) // { // if (num*endDen>endNum*den) //if (time>endTime) // // no intersection of line and hull // return false; // // startTime = time; // startNum = num; // startDen = den; // curPlane = i; // curMaterial = planeMaterials[i-startPlane]; // found = true; // } // // else, no need to do anything, just continue (we've been more inside than this) // } } // setup rayInfo if (found && rayInfo) { rayInfo->t = (F32)startNum/(F32)startDen; // finally divide... rayInfo->normal = planeNormals[curPlane]; rayInfo->material = curMaterial; return true; } else if (found) return true; // only way to get here is if start is inside hull... // we could return null and just plug in garbage for the material and normal... return false; } bool TSMesh::addToHull(U32 idx0, U32 idx1, U32 idx2) { Point3F normal; mCross(verts[idx2]-verts[idx0],verts[idx1]-verts[idx0],&normal); if (mDot(normal,normal)<0.001f) { mCross(verts[idx0]-verts[idx1],verts[idx2]-verts[idx1],&normal); if (mDot(normal,normal)<0.001f) { mCross(verts[idx1]-verts[idx2],verts[idx0]-verts[idx2],&normal); if (mDot(normal,normal)<0.001f) return false; } } normal.normalize(); F32 k = mDot(normal,verts[idx0]); for (S32 i=0; i0.99f && mFabs(k-planeConstants[i])<0.01f) // this is a repeat... return false; } // new plane, add it to the list... planeNormals.push_back(normal); planeConstants.push_back(k); return true; } bool TSMesh::buildConvexHull() { // already done, return without error if (planeNormals.size()) return true; bool error = false; // should probably only have 1 frame, but just in case... planesPerFrame = 0; S32 frame, i, j; for (frame=0; frame planeNormals.size() ) { // we're short, duplicate last plane till we match U32 sz = planeNormals.size(); planeNormals.increment(); planeNormals.last() = planeNormals[sz-1]; planeConstants.increment(); planeConstants.last() = planeConstants[sz-1]; } while ( (frame+1) * planesPerFrame < planeNormals.size() ) { // harsh -- last frame has more than other frames // duplicate last plane in each frame for (S32 k=frame-1; k>=0; k--) { planeNormals.insert(k*planesPerFrame+planesPerFrame); planeNormals[k*planesPerFrame+planesPerFrame] = planeNormals[k*planesPerFrame+planesPerFrame-1]; planeConstants.insert(k*planesPerFrame+planesPerFrame); planeConstants[k*planesPerFrame+planesPerFrame] = planeConstants[k*planesPerFrame+planesPerFrame-1]; if (k==0) { planeMaterials.increment(); planeMaterials.last() = planeMaterials[planeMaterials.size()-2]; } } planesPerFrame++; } } AssertFatal((frame+1) * planesPerFrame == planeNormals.size(),"TSMesh::buildConvexHull (3)"); } return !error; } //----------------------------------------------------- // TSMesh bounds methods //----------------------------------------------------- void TSMesh::computeBounds() { MatrixF mat(true); computeBounds(mat,mBounds,-1,&mCenter,&mRadius); } void TSMesh::computeBounds(MatrixF & transform, Box3F & bounds, S32 frame, Point3F * center, F32 * radius) { if (frame<0) computeBounds(verts.address(),verts.size(),transform,bounds,center,radius); else computeBounds(verts.address() + frame * vertsPerFrame,vertsPerFrame,transform,bounds,center,radius); } void TSMesh::computeBounds(Point3F * v, S32 numVerts, MatrixF & transform, Box3F & bounds, Point3F * center, F32 * radius) { if (!numVerts) { bounds.min.set(0,0,0); bounds.max.set(0,0,0); if (center) center->set(0,0,0); if (radius) *radius = 0; return; } S32 i; Point3F p; transform.mulP(*v,&bounds.min); bounds.max = bounds.min; for (i=0; ix = 0.5f * (bounds.min.x + bounds.max.x); center->y = 0.5f * (bounds.min.y + bounds.max.y); center->z = 0.5f * (bounds.min.z + bounds.max.z); if (radius) { *radius = 0.0f; for (i=0; i gBoneTransforms; Vector gSkinVerts; Vector gSkinNorms; /// Chris Lomont version of fast inverse sqrt, /// based on Newton method, 1 iteration, more accurate /// We use here for skinning cuz we don't need much accuracy -- BJG F32 InvSqrt_Lomont(F32 x) { F32 xhalf = 0.5f*x; S32 i = *(S32*)&x; i = 0x5f375a84 - (i>>1); // hidden initial guess, fast x = *(F32*)&i; x = x*(1.5f-xhalf*x*x); return x; } void TSSkinMesh::updateSkin() { // setup arrays gBoneTransforms.setSize(nodeIndex.size()); #if defined(TORQUE_LIB) verts.setSize(initialVerts.size()); norms.setSize(initialVerts.size()); #else if (encodedNorms.size()) { // we co-opt responsibility for updating encoded normals from mesh gNormalStore.setSize(vertsPerFrame); for (S32 i=0; i0.01f) norms[i] *= InvSqrt_Lomont(len2); // 1.0f/mSqrt(len2); } } void TSSkinMesh::render(S32 frame, S32 matFrame, TSMaterialList * materials) { // update verts and normals... updateSkin(); // render... Parent::render(frame,matFrame,materials); } void TSSkinMesh::renderShadow(S32 frame, const MatrixF & mat, S32 dim, U32 * bits, TSMaterialList * materials) { // update verts and normals... updateSkin(); // render... Parent::renderShadow(frame,mat,dim,bits,materials); } bool TSSkinMesh::buildPolyList(S32 frame, AbstractPolyList * polyList, U32 & surfaceKey) { // update verts and normals... updateSkin(); // render... return Parent::buildPolyList(frame,polyList,surfaceKey); } bool TSSkinMesh::castRay(S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo) { frame,start,end,rayInfo; return false; } bool TSSkinMesh::buildConvexHull() { return false; // no error, but we don't do anything either... } void TSSkinMesh::computeBounds(MatrixF & transform, Box3F & bounds, S32 frame, Point3F * center, F32 * radius) { frame; TSMesh::computeBounds(initialVerts.address(),initialVerts.size(),transform,bounds,center,radius); } //----------------------------------------------------- // encoded normals //----------------------------------------------------- const Point3F TSMesh::smU8ToNormalTable[] = { Point3F( 0.565061f, -0.270644f, -0.779396f ), Point3F( -0.309804f, -0.731114f, 0.607860f ), Point3F( -0.867412f, 0.472957f, 0.154619f ), Point3F( -0.757488f, 0.498188f, -0.421925f ), Point3F( 0.306834f, -0.915340f, 0.260778f ), Point3F( 0.098754f, 0.639153f, -0.762713f ), Point3F( 0.713706f, -0.558862f, -0.422252f ), Point3F( -0.890431f, -0.407603f, -0.202466f ), Point3F( 0.848050f, -0.487612f, -0.207475f ), Point3F( -0.232226f, 0.776855f, 0.585293f ), Point3F( -0.940195f, 0.304490f, -0.152706f ), Point3F( 0.602019f, -0.491878f, -0.628991f ), Point3F( -0.096835f, -0.494354f, -0.863850f ), Point3F( 0.026630f, -0.323659f, -0.945799f ), Point3F( 0.019208f, 0.909386f, 0.415510f ), Point3F( 0.854440f, 0.491730f, 0.167731f ), Point3F( -0.418835f, 0.866521f, -0.271512f ), Point3F( 0.465024f, 0.409667f, 0.784809f ), Point3F( -0.674391f, -0.691087f, -0.259992f ), Point3F( 0.303858f, -0.869270f, -0.389922f ), Point3F( 0.991333f, 0.090061f, -0.095640f ), Point3F( -0.275924f, -0.369550f, 0.887298f ), Point3F( 0.426545f, -0.465962f, 0.775202f ), Point3F( -0.482741f, -0.873278f, -0.065920f ), Point3F( 0.063616f, 0.932012f, -0.356800f ), Point3F( 0.624786f, -0.061315f, 0.778385f ), Point3F( -0.530300f, 0.416850f, 0.738253f ), Point3F( 0.312144f, -0.757028f, -0.573999f ), Point3F( 0.399288f, -0.587091f, -0.704197f ), Point3F( -0.132698f, 0.482877f, 0.865576f ), Point3F( 0.950966f, 0.306530f, 0.041268f ), Point3F( -0.015923f, -0.144300f, 0.989406f ), Point3F( -0.407522f, -0.854193f, 0.322925f ), Point3F( -0.932398f, 0.220464f, 0.286408f ), Point3F( 0.477509f, 0.876580f, 0.059936f ), Point3F( 0.337133f, 0.932606f, -0.128796f ), Point3F( -0.638117f, 0.199338f, 0.743687f ), Point3F( -0.677454f, 0.445349f, 0.585423f ), Point3F( -0.446715f, 0.889059f, -0.100099f ), Point3F( -0.410024f, 0.909168f, 0.072759f ), Point3F( 0.708462f, 0.702103f, -0.071641f ), Point3F( -0.048801f, -0.903683f, -0.425411f ), Point3F( -0.513681f, -0.646901f, 0.563606f ), Point3F( -0.080022f, 0.000676f, -0.996793f ), Point3F( 0.066966f, -0.991150f, -0.114615f ), Point3F( -0.245220f, 0.639318f, -0.728793f ), Point3F( 0.250978f, 0.855979f, 0.452006f ), Point3F( -0.123547f, 0.982443f, -0.139791f ), Point3F( -0.794825f, 0.030254f, -0.606084f ), Point3F( -0.772905f, 0.547941f, 0.319967f ), Point3F( 0.916347f, 0.369614f, -0.153928f ), Point3F( -0.388203f, 0.105395f, 0.915527f ), Point3F( -0.700468f, -0.709334f, 0.078677f ), Point3F( -0.816193f, 0.390455f, 0.425880f ), Point3F( -0.043007f, 0.769222f, -0.637533f ), Point3F( 0.911444f, 0.113150f, 0.395560f ), Point3F( 0.845801f, 0.156091f, -0.510153f ), Point3F( 0.829801f, -0.029340f, 0.557287f ), Point3F( 0.259529f, 0.416263f, 0.871418f ), Point3F( 0.231128f, -0.845982f, 0.480515f ), Point3F( -0.626203f, -0.646168f, 0.436277f ), Point3F( -0.197047f, -0.065791f, 0.978184f ), Point3F( -0.255692f, -0.637488f, -0.726794f ), Point3F( 0.530662f, -0.844385f, -0.073567f ), Point3F( -0.779887f, 0.617067f, -0.104899f ), Point3F( 0.739908f, 0.113984f, 0.662982f ), Point3F( -0.218801f, 0.930194f, -0.294729f ), Point3F( -0.374231f, 0.818666f, 0.435589f ), Point3F( -0.720250f, -0.028285f, 0.693137f ), Point3F( 0.075389f, 0.415049f, 0.906670f ), Point3F( -0.539724f, -0.106620f, 0.835063f ), Point3F( -0.452612f, -0.754669f, -0.474991f ), Point3F( 0.682822f, 0.581234f, -0.442629f ), Point3F( 0.002435f, -0.618462f, -0.785811f ), Point3F( -0.397631f, 0.110766f, -0.910835f ), Point3F( 0.133935f, -0.985438f, 0.104754f ), Point3F( 0.759098f, -0.608004f, 0.232595f ), Point3F( -0.825239f, -0.256087f, 0.503388f ), Point3F( 0.101693f, -0.565568f, 0.818408f ), Point3F( 0.386377f, 0.793546f, -0.470104f ), Point3F( -0.520516f, -0.840690f, 0.149346f ), Point3F( -0.784549f, -0.479672f, 0.392935f ), Point3F( -0.325322f, -0.927581f, -0.183735f ), Point3F( -0.069294f, -0.428541f, 0.900861f ), Point3F( 0.993354f, -0.115023f, -0.004288f ), Point3F( -0.123896f, -0.700568f, 0.702747f ), Point3F( -0.438031f, -0.120880f, -0.890795f ), Point3F( 0.063314f, 0.813233f, 0.578484f ), Point3F( 0.322045f, 0.889086f, -0.325289f ), Point3F( -0.133521f, 0.875063f, -0.465228f ), Point3F( 0.637155f, 0.564814f, 0.524422f ), Point3F( 0.260092f, -0.669353f, 0.695930f ), Point3F( 0.953195f, 0.040485f, -0.299634f ), Point3F( -0.840665f, -0.076509f, 0.536124f ), Point3F( -0.971350f, 0.202093f, 0.125047f ), Point3F( -0.804307f, -0.396312f, -0.442749f ), Point3F( -0.936746f, 0.069572f, 0.343027f ), Point3F( 0.426545f, -0.465962f, 0.775202f ), Point3F( 0.794542f, -0.227450f, 0.563000f ), Point3F( -0.892172f, 0.091169f, -0.442399f ), Point3F( -0.312654f, 0.541264f, 0.780564f ), Point3F( 0.590603f, -0.735618f, -0.331743f ), Point3F( -0.098040f, -0.986713f, 0.129558f ), Point3F( 0.569646f, 0.283078f, -0.771603f ), Point3F( 0.431051f, -0.407385f, -0.805129f ), Point3F( -0.162087f, -0.938749f, -0.304104f ), Point3F( 0.241533f, -0.359509f, 0.901341f ), Point3F( -0.576191f, 0.614939f, 0.538380f ), Point3F( -0.025110f, 0.085740f, 0.996001f ), Point3F( -0.352693f, -0.198168f, 0.914515f ), Point3F( -0.604577f, 0.700711f, 0.378802f ), Point3F( 0.465024f, 0.409667f, 0.784809f ), Point3F( -0.254684f, -0.030474f, -0.966544f ), Point3F( -0.604789f, 0.791809f, 0.085259f ), Point3F( -0.705147f, -0.399298f, 0.585943f ), Point3F( 0.185691f, 0.017236f, -0.982457f ), Point3F( 0.044588f, 0.973094f, 0.226052f ), Point3F( -0.405463f, 0.642367f, 0.650357f ), Point3F( -0.563959f, 0.599136f, -0.568319f ), Point3F( 0.367162f, -0.072253f, -0.927347f ), Point3F( 0.960429f, -0.213570f, -0.178783f ), Point3F( -0.192629f, 0.906005f, 0.376893f ), Point3F( -0.199718f, -0.359865f, -0.911378f ), Point3F( 0.485072f, 0.121233f, -0.866030f ), Point3F( 0.467163f, -0.874294f, 0.131792f ), Point3F( -0.638953f, -0.716603f, 0.279677f ), Point3F( -0.622710f, 0.047813f, -0.780990f ), Point3F( 0.828724f, -0.054433f, -0.557004f ), Point3F( 0.130241f, 0.991080f, 0.028245f ), Point3F( 0.310995f, -0.950076f, -0.025242f ), Point3F( 0.818118f, 0.275336f, 0.504850f ), Point3F( 0.676328f, 0.387023f, 0.626733f ), Point3F( -0.100433f, 0.495114f, -0.863004f ), Point3F( -0.949609f, -0.240681f, -0.200786f ), Point3F( -0.102610f, 0.261831f, -0.959644f ), Point3F( -0.845732f, -0.493136f, 0.203850f ), Point3F( 0.672617f, -0.738838f, 0.041290f ), Point3F( 0.380465f, 0.875938f, 0.296613f ), Point3F( -0.811223f, 0.262027f, -0.522742f ), Point3F( -0.074423f, -0.775670f, -0.626736f ), Point3F( -0.286499f, 0.755850f, -0.588735f ), Point3F( 0.291182f, -0.276189f, -0.915933f ), Point3F( -0.638117f, 0.199338f, 0.743687f ), Point3F( 0.439922f, -0.864433f, -0.243359f ), Point3F( 0.177649f, 0.206919f, 0.962094f ), Point3F( 0.277107f, 0.948521f, 0.153361f ), Point3F( 0.507629f, 0.661918f, -0.551523f ), Point3F( -0.503110f, -0.579308f, -0.641313f ), Point3F( 0.600522f, 0.736495f, -0.311364f ), Point3F( -0.691096f, -0.715301f, -0.103592f ), Point3F( -0.041083f, -0.858497f, 0.511171f ), Point3F( 0.207773f, -0.480062f, -0.852274f ), Point3F( 0.795719f, 0.464614f, 0.388543f ), Point3F( -0.100433f, 0.495114f, -0.863004f ), Point3F( 0.703249f, 0.065157f, -0.707951f ), Point3F( -0.324171f, -0.941112f, 0.096024f ), Point3F( -0.134933f, -0.940212f, 0.312722f ), Point3F( -0.438240f, 0.752088f, -0.492249f ), Point3F( 0.964762f, -0.198855f, 0.172311f ), Point3F( -0.831799f, 0.196807f, 0.519015f ), Point3F( -0.508008f, 0.819902f, 0.263986f ), Point3F( 0.471075f, -0.001146f, 0.882092f ), Point3F( 0.919512f, 0.246162f, -0.306435f ), Point3F( -0.960050f, 0.279828f, -0.001187f ), Point3F( 0.110232f, -0.847535f, -0.519165f ), Point3F( 0.208229f, 0.697360f, 0.685806f ), Point3F( -0.199680f, -0.560621f, 0.803637f ), Point3F( 0.170135f, -0.679985f, -0.713214f ), Point3F( 0.758371f, -0.494907f, 0.424195f ), Point3F( 0.077734f, -0.755978f, 0.649965f ), Point3F( 0.612831f, -0.672475f, 0.414987f ), Point3F( 0.142776f, 0.836698f, -0.528726f ), Point3F( -0.765185f, 0.635778f, 0.101382f ), Point3F( 0.669873f, -0.419737f, 0.612447f ), Point3F( 0.593549f, 0.194879f, 0.780847f ), Point3F( 0.646930f, 0.752173f, 0.125368f ), Point3F( 0.837721f, 0.545266f, -0.030127f ), Point3F( 0.541505f, 0.768070f, 0.341820f ), Point3F( 0.760679f, -0.365715f, -0.536301f ), Point3F( 0.381516f, 0.640377f, 0.666605f ), Point3F( 0.565794f, -0.072415f, -0.821361f ), Point3F( -0.466072f, -0.401588f, 0.788356f ), Point3F( 0.987146f, 0.096290f, 0.127560f ), Point3F( 0.509709f, -0.688886f, -0.515396f ), Point3F( -0.135132f, -0.988046f, -0.074192f ), Point3F( 0.600499f, 0.476471f, -0.642166f ), Point3F( -0.732326f, -0.275320f, -0.622815f ), Point3F( -0.881141f, -0.470404f, 0.048078f ), Point3F( 0.051548f, 0.601042f, 0.797553f ), Point3F( 0.402027f, -0.763183f, 0.505891f ), Point3F( 0.404233f, -0.208288f, 0.890624f ), Point3F( -0.311793f, 0.343843f, 0.885752f ), Point3F( 0.098132f, -0.937014f, 0.335223f ), Point3F( 0.537158f, 0.830585f, -0.146936f ), Point3F( 0.725277f, 0.298172f, -0.620538f ), Point3F( -0.882025f, 0.342976f, -0.323110f ), Point3F( -0.668829f, 0.424296f, -0.610443f ), Point3F( -0.408835f, -0.476442f, -0.778368f ), Point3F( 0.809472f, 0.397249f, -0.432375f ), Point3F( -0.909184f, -0.205938f, -0.361903f ), Point3F( 0.866930f, -0.347934f, -0.356895f ), Point3F( 0.911660f, -0.141281f, -0.385897f ), Point3F( -0.431404f, -0.844074f, -0.318480f ), Point3F( -0.950593f, -0.073496f, 0.301614f ), Point3F( -0.719716f, 0.626915f, -0.298305f ), Point3F( -0.779887f, 0.617067f, -0.104899f ), Point3F( -0.475899f, -0.542630f, 0.692151f ), Point3F( 0.081952f, -0.157248f, -0.984153f ), Point3F( 0.923990f, -0.381662f, -0.024025f ), Point3F( -0.957998f, 0.120979f, -0.260008f ), Point3F( 0.306601f, 0.227975f, -0.924134f ), Point3F( -0.141244f, 0.989182f, 0.039601f ), Point3F( 0.077097f, 0.186288f, -0.979466f ), Point3F( -0.630407f, -0.259801f, 0.731499f ), Point3F( 0.718150f, 0.637408f, 0.279233f ), Point3F( 0.340946f, 0.110494f, 0.933567f ), Point3F( -0.396671f, 0.503020f, -0.767869f ), Point3F( 0.636943f, -0.245005f, 0.730942f ), Point3F( -0.849605f, -0.518660f, -0.095724f ), Point3F( -0.388203f, 0.105395f, 0.915527f ), Point3F( -0.280671f, -0.776541f, -0.564099f ), Point3F( -0.601680f, 0.215451f, -0.769131f ), Point3F( -0.660112f, -0.632371f, -0.405412f ), Point3F( 0.921096f, 0.284072f, 0.266242f ), Point3F( 0.074850f, -0.300846f, 0.950731f ), Point3F( 0.943952f, -0.067062f, 0.323198f ), Point3F( -0.917838f, -0.254589f, 0.304561f ), Point3F( 0.889843f, -0.409008f, 0.202219f ), Point3F( -0.565849f, 0.753721f, -0.334246f ), Point3F( 0.791460f, 0.555918f, -0.254060f ), Point3F( 0.261936f, 0.703590f, -0.660568f ), Point3F( -0.234406f, 0.952084f, 0.196444f ), Point3F( 0.111205f, 0.979492f, -0.168014f ), Point3F( -0.869844f, -0.109095f, -0.481113f ), Point3F( -0.337728f, -0.269701f, -0.901777f ), Point3F( 0.366793f, 0.408875f, -0.835634f ), Point3F( -0.098749f, 0.261316f, 0.960189f ), Point3F( -0.272379f, -0.847100f, 0.456324f ), Point3F( -0.319506f, 0.287444f, -0.902935f ), Point3F( 0.873383f, -0.294109f, 0.388203f ), Point3F( -0.088950f, 0.710450f, 0.698104f ), Point3F( 0.551238f, -0.786552f, 0.278340f ), Point3F( 0.724436f, -0.663575f, -0.186712f ), Point3F( 0.529741f, -0.606539f, 0.592861f ), Point3F( -0.949743f, -0.282514f, 0.134809f ), Point3F( 0.155047f, 0.419442f, -0.894443f ), Point3F( -0.562653f, -0.329139f, -0.758346f ), Point3F( 0.816407f, -0.576953f, 0.024576f ), Point3F( 0.178550f, -0.950242f, -0.255266f ), Point3F( 0.479571f, 0.706691f, 0.520192f ), Point3F( 0.391687f, 0.559884f, -0.730145f ), Point3F( 0.724872f, -0.205570f, -0.657496f ), Point3F( -0.663196f, -0.517587f, -0.540624f ), Point3F( -0.660054f, -0.122486f, -0.741165f ), Point3F( -0.531989f, 0.374711f, -0.759328f ), Point3F( 0.194979f, -0.059120f, 0.979024f ) }; U8 TSMesh::encodeNormal(const Point3F & normal) { U8 bestIndex=0; F32 bestDot=-10E30f; for (U32 i=0; i<256; i++) { F32 dot = mDot(normal,smU8ToNormalTable[i]); if (dot>bestDot) { bestIndex = i; bestDot = dot; } } return bestIndex; } //----------------------------------------------------- // TSMesh assemble from/ dissemble to memory buffer //----------------------------------------------------- #define alloc TSShape::alloc TSMesh * TSMesh::assembleMesh(U32 meshType, bool skip) { static TSMesh tempStandardMesh; static TSSkinMesh tempSkinMesh; static TSDecalMesh tempDecalMesh; static TSSortedMesh tempSortedMesh; bool justSize = skip || !alloc.allocShape32(0); // if this returns NULL, we're just sizing memory block // a little funny business because we pretend decals are derived from meshes S32 * ret = NULL; TSMesh * mesh = NULL; TSDecalMesh * decal = NULL; if (justSize) { switch (meshType) { case StandardMeshType : { ret = (S32*)&tempStandardMesh; mesh = &tempStandardMesh; alloc.allocShape32(sizeof(TSMesh)>>2); break; } case SkinMeshType : { ret = (S32*)&tempSkinMesh; mesh = &tempSkinMesh; alloc.allocShape32(sizeof(TSSkinMesh)>>2); break; } case DecalMeshType : { ret = (S32*)&tempDecalMesh; decal = &tempDecalMesh; alloc.allocShape32(sizeof(TSDecalMesh)>>2); break; } case SortedMeshType : { ret = (S32*)&tempSortedMesh; mesh = &tempSortedMesh; alloc.allocShape32(sizeof(TSSortedMesh)>>2); break; } } } else { switch (meshType) { case StandardMeshType : { ret = alloc.allocShape32(sizeof(TSMesh)>>2); constructInPlace((TSMesh*)ret); mesh = (TSMesh*)ret; break; } case SkinMeshType : { ret = alloc.allocShape32(sizeof(TSSkinMesh)>>2); constructInPlace((TSSkinMesh*)ret); mesh = (TSSkinMesh*)ret; break; } case DecalMeshType : { ret = alloc.allocShape32(sizeof(TSDecalMesh)>>2); constructInPlace((TSDecalMesh*)ret); decal = (TSDecalMesh*)ret; break; } case SortedMeshType : { ret = alloc.allocShape32(sizeof(TSSortedMesh)>>2); constructInPlace((TSSortedMesh*)ret); mesh = (TSSortedMesh*)ret; break; } } } alloc.setSkipMode(skip); if (mesh) mesh->assemble(skip); if (decal) decal->assemble(skip); alloc.setSkipMode(false); return (TSMesh*)ret; } void TSMesh::convertToTris(S16 * primitiveDataIn, S32 * primitiveMatIn, S16 * indicesIn, S32 numPrimIn, S32 & numPrimOut, S32 & numIndicesOut, S32 * primitivesOut, S16 * indicesOut) { S32 prevMaterial = -99999; TSDrawPrimitive * newDraw = NULL; numPrimOut=0; numIndicesOut=0; for (S32 i=0; istart = numIndicesOut; newDraw->numElements = 0; newDraw->matIndex = newMat | TSDrawPrimitive::Triangles; } numPrimOut++; prevMaterial = newMat; } U16 start = primitiveDataIn[i*2]; U16 numElements = primitiveDataIn[i*2+1]; // gonna depend on what kind of primitive it is... if ( (primitiveMatIn[i] & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) { for (S32 j=0; jnumElements += 3; numIndicesOut += 3; } } else { U32 idx0 = indicesIn[start + 0]; U32 idx1; U32 idx2 = indicesIn[start + 1]; U32 * nextIdx = &idx1; for (S32 j=2; jnumElements += 3; numIndicesOut += 3; } } } } void unwindStrip(S16 * indices, S32 numElements, Vector & triIndices) { U32 idx0 = indices[0]; U32 idx1; U32 idx2 = indices[1]; U32 * nextIdx = &idx1; for (S32 j=2; j triIndices; S32 curDrawOut = 0; numPrimOut=0; numIndicesOut=0; for (S32 i=0; istart = numIndicesOut; newTris->numElements = triIndices.size(); dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U16)); } numIndicesOut += triIndices.size(); triIndices.clear(); newTris = NULL; } if (primitivesOut) { newDraw = (TSDrawPrimitive*) &primitivesOut[numPrimOut*2]; newDraw->start = numIndicesOut; newDraw->numElements = 0; newDraw->matIndex = newMat; } numPrimOut++; curDrawOut = 0; prevMaterial = newMat; } U16 start = primitiveDataIn[i*2]; U16 numElements = primitiveDataIn[i*2+1]; // gonna depend on what kind of primitive it is... // from above we know it's the same kind as the one we're building... if ( (primitiveMatIn[i] & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) { // triangles primitive...add to it for (S32 j=0; jnumElements += 3; numIndicesOut += 3; } } else { // strip primitive... // if numElements less than smSmallestStripSize, add to triangles... if (numElementsmatIndex = newMat; newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip); newTris->matIndex |= TSDrawPrimitive::Triangles; } numPrimOut++; } unwindStrip(indicesIn+start,numElements,triIndices); } else { // strip primitive...add to it if (indicesOut) { if (curDrawOut&1) { indicesOut[numIndicesOut+0] = indicesOut[numIndicesOut-1]; indicesOut[numIndicesOut+1] = indicesOut[numIndicesOut-1]; indicesOut[numIndicesOut+2] = indicesIn[start]; dMemcpy(indicesOut+numIndicesOut+3,indicesIn+start,2*numElements); } else if (curDrawOut) { indicesOut[numIndicesOut+0] = indicesOut[numIndicesOut-1]; indicesOut[numIndicesOut+1] = indicesIn[start]; dMemcpy(indicesOut+numIndicesOut+2,indicesIn+start,2*numElements); } else dMemcpy(indicesOut+numIndicesOut,indicesIn+start,2*numElements); } S32 added = numElements; added += curDrawOut ? (curDrawOut&1 ? 3 : 2) : 0; if (newDraw) newDraw->numElements += added; numIndicesOut += added; curDrawOut += added; } } } // spit out tris before leaving // before adding the new primitive, transfer triangle indices if (triIndices.size()) { if (newTris && indicesOut) { newTris->start = numIndicesOut; newTris->numElements = triIndices.size(); dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U16)); } numIndicesOut += triIndices.size(); triIndices.clear(); newTris = NULL; } } // this method does none of the converting that the above methods do, except that small strips are converted // to triangle lists... void TSMesh::leaveAsMultipleStrips(S16 * primitiveDataIn, S32 * primitiveMatIn, S16 * indicesIn, S32 numPrimIn, S32 & numPrimOut, S32 & numIndicesOut, S32 * primitivesOut, S16 * indicesOut) { S32 prevMaterial = -99999; TSDrawPrimitive * newDraw = NULL; Vector triIndices; numPrimOut=0; numIndicesOut=0; for (S32 i=0; imatIndex = prevMaterial; newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip); newTris->matIndex |= TSDrawPrimitive::Triangles; newTris->start = numIndicesOut; newTris->numElements = triIndices.size(); dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U16)); } numPrimOut++; numIndicesOut += triIndices.size(); triIndices.clear(); } // this is a little convoluted because this code was adapted from convertToSingleStrip // but we will need a new primitive only if it is a triangle primitive coming in // or we have more elements than the min strip size... if ( (primitiveMatIn[i] & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles || numElements>=smMinStripSize+2) { if (primitivesOut) { newDraw = (TSDrawPrimitive*) &primitivesOut[numPrimOut*2]; newDraw->start = numIndicesOut; newDraw->numElements = 0; newDraw->matIndex = newMat; } numPrimOut++; } prevMaterial = newMat; // gonna depend on what kind of primitive it is... // from above we know it's the same kind as the one we're building... if ( (primitiveMatIn[i] & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) { // triangles primitive...add to it for (S32 j=0; jnumElements += 3; numIndicesOut += 3; } } else { // strip primitive... // if numElements less than smSmallestStripSize, add to triangles... if (numElementsnumElements = numElements; numIndicesOut += numElements; } } } // spit out tris before leaving if (triIndices.size()) { // material just changed and we have triangles lying around // add primitive and indices for triangles and clear triIndices if (indicesOut) { TSDrawPrimitive * newTris = (TSDrawPrimitive*) &primitivesOut[numPrimOut*2]; newTris->matIndex = prevMaterial; newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip); newTris->matIndex |= TSDrawPrimitive::Triangles; newTris->start = numIndicesOut; newTris->numElements = triIndices.size(); dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U16)); } numPrimOut++; numIndicesOut += triIndices.size(); triIndices.clear(); } } // This method retrieves data that is shared (or possibly shared) between different meshes. // This adds an extra step to the copying of data from the memory buffer to the shape data buffer. // If we have no parentMesh, then we either return a pointer to the data in the memory buffer // (in the case that we skip this mesh) or copy the data into the shape data buffer and return // that pointer (in the case that we don't skip this mesh). // If we do have a parent mesh, then we return a pointer to the data in the shape buffer, // copying the data in there ourselves if our parent didn't already do it (i.e., if it was skipped). S32 * TSMesh::getSharedData32(S32 parentMesh, S32 size, S32 ** source, bool skip) { S32 * ptr; if(parentMesh<0) ptr = skip ? alloc.getPointer32(size) : alloc.copyToShape32(size); else { ptr = source[parentMesh]; // if we skipped the previous mesh (and we're not skipping this one) then // we still need to copy points into the shape... if (!smDataCopied[parentMesh] && !skip) { S32 * tmp = ptr; ptr = alloc.allocShape32(size); if (ptr && tmp) dMemcpy(ptr,tmp,size*sizeof(S32)); } } return ptr; } S8 * TSMesh::getSharedData8(S32 parentMesh, S32 size, S8 ** source, bool skip) { S8 * ptr; if(parentMesh<0) ptr = skip ? alloc.getPointer8(size) : alloc.copyToShape8(size); else { ptr = source[parentMesh]; // if we skipped the previous mesh (and we're not skipping this one) then // we still need to copy points into the shape... if (!smDataCopied[parentMesh] && !skip) { S8 * tmp = ptr; ptr = alloc.allocShape8(size); if (ptr && tmp) dMemcpy(ptr,tmp,size*sizeof(S32)); } } return ptr; } void TSMesh::assemble(bool skip) { alloc.checkGuard(); numFrames = alloc.get32(); numMatFrames = alloc.get32(); parentMesh = alloc.get32(); alloc.get32((S32*)&mBounds,6); alloc.get32((S32*)&mCenter,3); mRadius = (F32)alloc.get32(); S32 numVerts = alloc.get32(); S32 * ptr32 = getSharedData32(parentMesh,3*numVerts,(S32**)smVertsList.address(),skip); verts.set((Point3F*)ptr32,numVerts); S32 numTVerts = alloc.get32(); ptr32 = getSharedData32(parentMesh,2*numTVerts,(S32**)smTVertsList.address(),skip); tverts.set((Point2F*)ptr32,numTVerts); S8 * ptr8; if (TSShape::smReadVersion>21 && TSMesh::smUseEncodedNormals) { // we have encoded normals and we want to use them... if (parentMesh<0) alloc.getPointer32(numVerts*3); // advance past norms, don't use norms.set(NULL,0); ptr8 = getSharedData8(parentMesh,numVerts,(S8**)smEncodedNormsList.address(),skip); encodedNorms.set(ptr8,numVerts); } else if (TSShape::smReadVersion>21) { // we have encoded normals but we don't want to use them... ptr32 = getSharedData32(parentMesh,3*numVerts,(S32**)smNormsList.address(),skip); norms.set((Point3F*)ptr32,numVerts); if (parentMesh<0) alloc.getPointer8(numVerts); // advance past encoded normls, don't use encodedNorms.set(NULL,0); } else { // no encoded normals... ptr32 = getSharedData32(parentMesh,3*numVerts,(S32**)smNormsList.address(),skip); norms.set((Point3F*)ptr32,numVerts); encodedNorms.set(NULL,0); } // copy the primitives and indices...how we do this depends on what // form we want them in when copied...just get pointers to data for now S32 szPrim = alloc.get32(); S16 * prim16 = alloc.getPointer16(szPrim*2); S32 * prim32 = alloc.getPointer32(szPrim); S32 szInd = alloc.get32(); S16 * ind16 = alloc.getPointer16(szInd); // count then copy... S32 cpyPrim = szPrim, cpyInd = szInd; if (smUseTriangles) convertToTris(prim16,prim32,ind16,szPrim,cpyPrim,cpyInd,NULL,NULL); else if (smUseOneStrip) convertToSingleStrip(prim16,prim32,ind16,szPrim,cpyPrim,cpyInd,NULL,NULL); else leaveAsMultipleStrips(prim16,prim32,ind16,szPrim,cpyPrim,cpyInd,NULL,NULL); ptr32 = alloc.allocShape32(2*cpyPrim); S16 * ptr16 = alloc.allocShape16(cpyInd); alloc.align32(); S32 chkPrim = szPrim, chkInd = szInd; if (smUseTriangles) convertToTris(prim16,prim32,ind16,szPrim,chkPrim,chkInd,ptr32,ptr16); else if (smUseOneStrip) convertToSingleStrip(prim16,prim32,ind16,szPrim,chkPrim,chkInd,ptr32,ptr16); else leaveAsMultipleStrips(prim16,prim32,ind16,szPrim,chkPrim,chkInd,ptr32,ptr16); AssertFatal(chkPrim==cpyPrim && chkInd==cpyInd,"TSMesh::primitive conversion"); primitives.set(ptr32,cpyPrim); indices.set(ptr16,cpyInd); S32 sz = alloc.get32(); ptr16 = alloc.copyToShape16(sz); alloc.align32(); mergeIndices.set(ptr16,sz); vertsPerFrame = alloc.get32(); U32 flags = (U32)alloc.get32(); if (encodedNorms.size()) flags |= UseEncodedNormals; setFlags(flags); alloc.checkGuard(); if (alloc.allocShape32(0) && TSShape::smReadVersion<19) // only do this if we copied the data... computeBounds(); } void TSMesh::disassemble() { alloc.setGuard(); alloc.set32(numFrames); alloc.set32(numMatFrames); alloc.set32(parentMesh); alloc.copyToBuffer32((S32*)&mBounds,6); alloc.copyToBuffer32((S32*)&mCenter,3); alloc.set32((S32)mRadius); // verts... alloc.set32(verts.size()); if (parentMesh<0) // if no parent mesh, then save off our verts alloc.copyToBuffer32((S32*)verts.address(),3*verts.size()); // tverts... alloc.set32(tverts.size()); if (parentMesh<0) // if no parent mesh, then save off our tverts alloc.copyToBuffer32((S32*)tverts.address(),2*tverts.size()); // norms... if (parentMesh<0) // if no parent mesh, then save off our norms alloc.copyToBuffer32((S32*)norms.address(),3*norms.size()); // norms.size()==verts.size() or error... // encoded norms... if (parentMesh<0) { // if no parent mesh, compute encoded normals and copy over for (S32 i=0; i21 && TSMesh::smUseEncodedNormals) { // we have encoded normals and we want to use them... if (parentMesh<0) alloc.getPointer32(numVerts*3); // advance past norms, don't use initialNorms.set(NULL,0); ptr8 = getSharedData8(parentMesh,numVerts,(S8**)smEncodedNormsList.address(),skip); encodedNorms.set(ptr8,numVerts); // Note: we don't set the encoded normals flag because we handle them in updateSkin and // hide the fact that we are using them from base class (TSMesh) } else if (TSShape::smReadVersion>21) { // we have encoded normals but we don't want to use them... ptr32 = getSharedData32(parentMesh,3*numVerts,(S32**)smNormsList.address(),skip); initialNorms.set((Point3F*)ptr32,numVerts); if (parentMesh<0) alloc.getPointer8(numVerts); // advance past encoded normls, don't use encodedNorms.set(NULL,0); } else { // no encoded normals... ptr32 = getSharedData32(parentMesh,3*numVerts,(S32**)smNormsList.address(),skip); initialNorms.set((Point3F*)ptr32,numVerts); encodedNorms.set(NULL,0); } sz = alloc.get32(); ptr32 = getSharedData32(parentMesh,16*sz,(S32**)smInitTransformList.address(),skip); initialTransforms.set(ptr32,sz); sz = alloc.get32(); ptr32 = getSharedData32(parentMesh,sz,(S32**)smVertexIndexList.address(),skip); vertexIndex.set(ptr32,sz); ptr32 = getSharedData32(parentMesh,sz,(S32**)smBoneIndexList.address(),skip); boneIndex.set(ptr32,sz); ptr32 = getSharedData32(parentMesh,sz,(S32**)smWeightList.address(),skip); weight.set((F32*)ptr32,sz); sz = alloc.get32(); ptr32 = getSharedData32(parentMesh,sz,(S32**)smNodeIndexList.address(),skip); nodeIndex.set(ptr32,sz); alloc.checkGuard(); if (alloc.allocShape32(0) && TSShape::smReadVersion<19) // only do this if we copied the data... TSMesh::computeBounds(); } void TSSkinMesh::disassemble() { TSMesh::disassemble(); alloc.set32(initialVerts.size()); // if we have no parent mesh, then save off our verts & norms if (parentMesh<0) { alloc.copyToBuffer32((S32*)initialVerts.address(),3*initialVerts.size()); // no longer do this here...let tsmesh handle this alloc.copyToBuffer32((S32*)initialNorms.address(),3*initialNorms.size()); // if no parent mesh, compute encoded normals and copy over for (S32 i=0; i