tge/engine/ts/tsMesh.cc
2017-04-17 06:17:10 -06:00

3401 lines
125 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// 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<Point3F*> TSMesh::smVertsList;
Vector<Point3F*> TSMesh::smNormsList;
Vector<U8*> TSMesh::smEncodedNormsList;
Vector<Point2F*> TSMesh::smTVertsList;
Vector<bool> TSMesh::smDataCopied;
Vector<Point3F> TSMesh::smSaveVerts;
Vector<Point3F> TSMesh::smSaveNorms;
Vector<Point2F> TSMesh::smSaveTVerts;
Vector<MatrixF*> TSSkinMesh::smInitTransformList;
Vector<S32*> TSSkinMesh::smVertexIndexList;
Vector<S32*> TSSkinMesh::smBoneIndexList;
Vector<F32*> TSSkinMesh::smWeightList;
Vector<S32*> TSSkinMesh::smNodeIndexList;
Vector<Point3F> 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;
ToolVector<Point2F> diffuse;
getUVs(tDiffuse, diffuse);
for (j=0; j<mergeIndices.size(); j++)
{
smSaveVerts[j+mergeBufferStart] = verts[j+startMerge];
smSaveTVerts[j+mergeBufferStart] = diffuse[j+startMerge];
}
F32 mult = TSShapeInstance::smRenderData.intraDetailLevel;
for (j=0; j<mergeIndices.size(); j++)
{
verts[j+startMerge] *= mult;
Point3F v = mergeIndices[j]<startMerge ? verts[mergeIndices[j]] : smSaveVerts[mergeIndices[j]-startMerge+mergeBufferStart];
v *= 1.0f - mult;
verts[j+startMerge] += v;
diffuse[j+startMerge] *= mult;
Point2F tv = mergeIndices[j]<startMerge ? diffuse[mergeIndices[j]] : smSaveTVerts[mergeIndices[j]-startMerge+mergeBufferStart];
tv *= 1.0f - mult;
diffuse[j+startMerge] += tv;
}
}
void TSMesh::restoreMergeVerts()
{
S32 startMerge = vertsPerFrame - mergeIndices.size();
ToolVector<Point2F> diffuse;
getUVs(tDiffuse, diffuse);
for (S32 i=0; i<mergeIndices.size(); i++)
{
verts[i+startMerge] = smSaveVerts[i+mergeBufferStart];
diffuse[i+startMerge] = smSaveTVerts[i+mergeBufferStart];
}
}
void TSMesh::saveMergeNormals()
{
S32 startMerge = vertsPerFrame - mergeIndices.size();
S32 j;
smSaveNorms.setSize(mergeIndices.size());
for (j=0; j<mergeIndices.size(); j++)
smSaveNorms[j] = norms[j+startMerge];
F32 mult = TSShapeInstance::smRenderData.intraDetailLevel;
for (j=0; j<mergeIndices.size(); j++)
{
norms[j+startMerge] *= mult;
Point3F n = mergeIndices[j]<startMerge ? norms[mergeIndices[j]] : smSaveNorms[mergeIndices[j]-startMerge];
n *= 1.0f - mult;
norms[j+startMerge] += n;
// norms[j+startMerge].normalize();
}
}
void TSMesh::restoreMergeNormals()
{
if (getFlags(UseEncodedNormals))
return;
S32 startMerge = vertsPerFrame - mergeIndices.size();
for (S32 i=0; i<mergeIndices.size(); i++)
norms[i+startMerge] = smSaveNorms[i];
}
//-----------------------------------------------------
// TSMesh render methods
//-----------------------------------------------------
void TSMesh::fillVB(S32 vb, S32 frame, S32 matFrame, TSMaterialList *materials)
{
S32 firstVert = vertsPerFrame * frame;
S32 firstTVert = vertsPerFrame * matFrame;
const Point3F *normals = getNormals(firstVert);
// set up vertex arrays -- already enabled in TSShapeInstance::render
glVertexPointer(3,GL_FLOAT,0,&verts[firstVert]);
glNormalPointer(GL_FLOAT,0,normals);
ToolVector<Point2F> diffuse;
getUVs(tDiffuse, diffuse);
glTexCoordPointer(2,GL_FLOAT,0,&diffuse[firstTVert]);
TSDrawPrimitive &draw = primitives[0];
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,
"TSMesh::render: rendering of non-indexed meshes no longer supported");
// we do this to enable texturing -- if necessary
if (((TSShapeInstance::smRenderData.materialIndex ^ draw.matIndex) &
(TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial)) != 0)
setMaterial(draw.matIndex,materials);
glFillVertexBufferEXT(vb,0,vertsPerFrame);
}
void TSMesh::morphVB(S32 vb, S32 morph, S32 frame, S32 matFrame, TSMaterialList *materials)
{
S32 firstVert = vertsPerFrame * (frame+1) - morph;
S32 firstTVert = vertsPerFrame * (matFrame+1) - morph;
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);
ToolVector<Point2F> diffuse;
getUVs(tDiffuse, diffuse);
glTexCoordPointer(2,GL_FLOAT,0,&diffuse[firstTVert]);
TSDrawPrimitive &draw = primitives[0];
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,
"TSMesh::render: rendering of non-indexed meshes no longer supported");
// we do this to enable texturing -- if necessary
if (((TSShapeInstance::smRenderData.materialIndex ^ draw.matIndex) &
(TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial)) != 0)
setMaterial(draw.matIndex,materials);
glFillVertexBufferEXT(vb,0,morph);
restoreMergeNormals();
}
void TSMesh::renderVB(S32 frame, S32 matFrame, TSMaterialList *materials)
{
S32 firstVert = vertsPerFrame * frame;
S32 firstTVert = vertsPerFrame * matFrame;
if (getFlags(Billboard))
{
if (getFlags(BillboardZAxis))
forceFaceCameraZAxis();
else
forceFaceCamera();
}
glEnable(GL_NORMALIZE);
const Point3F *normals = getNormals(firstVert);
// lock...
bool lockArrays = dglDoesSupportCompiledVertexArray();
if (lockArrays)
glLockArraysEXT(0,vertsPerFrame);
for (S32 i=0; i<primitives.size(); i++)
{
PROFILE_START(TSShapeInstanceVBMat);
TSDrawPrimitive &draw = primitives[i];
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,
"TSMesh::render: rendering of non-indexed meshes no longer supported");
// material change?
if (((TSShapeInstance::smRenderData.materialIndex ^ draw.matIndex) &
(TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial)) != 0)
setMaterial(draw.matIndex,materials);
PROFILE_END();
PROFILE_START(TSShapeInstanceDE);
S32 drawType = getDrawType(draw.matIndex>>30);
glDrawElements(drawType,draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]);
PROFILE_END();
}
// unlock...
if (lockArrays)
glUnlockArraysEXT();
glDisable(GL_NORMALIZE);
}
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();
}
glEnable(GL_NORMALIZE);
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);
ToolVector<Point2F> diffuse;
getUVs(tDiffuse, diffuse);
glTexCoordPointer(2,GL_FLOAT,0,&diffuse[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,&diffuse[firstTVert]);
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE);
}
if (TSShapeInstance::smRenderData.lightMapMethod == TSShapeInstance::LIGHT_MAP_MULTI)
{
ToolVector<Point2F> lightmap;
getUVs(tLightmap, lightmap);
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.lightMapTE);
glTexCoordPointer(2,GL_FLOAT,0,&lightmap[firstTVert]);
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE);
}
// lock...
bool lockArrays = dglDoesSupportCompiledVertexArray();
if (lockArrays)
glLockArraysEXT(0,vertsPerFrame);
for (S32 i=0; i<primitives.size(); i++)
{
TSDrawPrimitive & draw = primitives[i];
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,
"TSMesh::render: rendering of non-indexed meshes no longer supported");
// material change?
if ( ((TSShapeInstance::smRenderData.materialIndex ^ draw.matIndex) &
(TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial)) != 0)
setMaterial(draw.matIndex,materials);
S32 drawType = getDrawType(draw.matIndex>>30);
glDrawElements(drawType,draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]);
}
// unlock...
if (lockArrays)
glUnlockArraysEXT();
restoreMergeNormals();
glDisable(GL_NORMALIZE);
}
const Point3F * TSMesh::getNormals(S32 firstVert)
{
if (getFlags(UseEncodedNormals))
{
gNormalStore.setSize(vertsPerFrame);
for (S32 i=0; i<encodedNorms.size(); i++)
gNormalStore[i]=decodeNormal(encodedNorms[i+firstVert]);
return gNormalStore.address();
}
return &norms[firstVert];
}
void TSMesh::initMaterials()
{
S32 & emapMethod = TSShapeInstance::smRenderData.environmentMapMethod;
S32 & dmapMethod = TSShapeInstance::smRenderData.detailMapMethod;
S32 & fogMethod = TSShapeInstance::smRenderData.fogMethod;
S32 & lmapMethod = TSShapeInstance::smRenderData.lightMapMethod;
S32 & dmapTE = TSShapeInstance::smRenderData.detailMapTE;
S32 & emapTE = TSShapeInstance::smRenderData.environmentMapTE;
S32 & baseTE = TSShapeInstance::smRenderData.baseTE;
S32 & fogTE = TSShapeInstance::smRenderData.fogTE;
S32 & lmapTE = TSShapeInstance::smRenderData.lightMapTE;
TSShapeInstance::smRenderData.detailTextureScale = 1.0f;
TSShapeInstance::smRenderData.fadeSet = false;
TSShapeInstance::smRenderData.textureMatrixPushed = false;
// initialize various sources of vertex alpha
TSShapeInstance::smRenderData.vertexAlpha.init();
TSShapeInstance::smRenderData.vertexAlpha.always = TSShapeInstance::smRenderData.alwaysAlphaValue;
// -------------------------------------------------
if (dmapMethod == TSShapeInstance::DETAIL_MAP_MULTI_1)
{
glClientActiveTextureARB(GL_TEXTURE0_ARB + dmapTE);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB + baseTE);
// switch to detail map texture unit
glActiveTextureARB(GL_TEXTURE0_ARB + dmapTE);
glEnable(GL_TEXTURE_2D);
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_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_ENV,GL_RGB_SCALE,2.0f);
// switch to base texture unit
glActiveTextureARB(GL_TEXTURE0_ARB + baseTE);
}
else if (dmapMethod == TSShapeInstance::DETAIL_MAP_MULTI_2)
{
glClientActiveTextureARB(GL_TEXTURE0_ARB + dmapTE);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB + baseTE);
// switch to detail map texture unit
glActiveTextureARB(GL_TEXTURE0_ARB + dmapTE);
glEnable(GL_TEXTURE_2D);
// following used to "alpha" out detail texture
F32 f = (1.0f-TSShapeInstance::smRenderData.detailMapAlpha)*0.5f;
glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,Point4F(f,f,f,1.0f-f));
// first unit computes "alpha-ed" detail texture
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB,GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB,GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB,GL_SRC_COLOR);
// alpha: don't need it...
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_CONSTANT); // don't need alpha, maybe this'll be cheaper than alternatives
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
// second unit applies detail texture to base texture
AssertFatal(dmapTE+1==baseTE,"TSShapeInstance::setupTexturing: assertion failed (1)");
glActiveTextureARB(GL_TEXTURE0_ARB+dmapTE+1);
glEnable(GL_TEXTURE_2D);
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_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_ENV,GL_RGB_SCALE,2.0f);
// third unit will apply lighting...
// we need to use the combine extension...we modulate by primary color rather than fragment color
glActiveTextureARB(GL_TEXTURE0_ARB+dmapTE+2);
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);
// switch to base texture unit
glActiveTextureARB(GL_TEXTURE0_ARB + baseTE);
}
if (emapMethod == TSShapeInstance::ENVIRONMENT_MAP_MULTI_1)
{
// reflectance map == alpha of texture map (always preferred if no translucency)
// ---------------------------------
// set up second texure unit (TE #1)
glActiveTextureARB(GL_TEXTURE0_ARB + emapTE);
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);
// use combine extension -- RGB: interpolate between previous and current texture on previous alpha
// Alpha: previous alpha
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB,GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
// set the environment map
glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.environmentMapGLName);
TSShapeInstance::smRenderData.vertexAlpha.emap = TSShapeInstance::smRenderData.environmentMapAlpha;
// switch to base texture unit
glActiveTextureARB(GL_TEXTURE0_ARB + baseTE);
}
else if (emapMethod == TSShapeInstance::ENVIRONMENT_MAP_MULTI_3)
{
// ---------------------------------
// set up first texure unit (TE #1)
// Note: TE #1 bound to refelectance
// map in tsmesh
glActiveTextureARB(GL_TEXTURE0_ARB + emapTE);
glEnable(GL_TEXTURE_2D);
// use combine extension -- RGB: previous
// Alpha: modulate previous with primary color
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_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);
// ---------------------------------
// set up second texure unit (TE #2)
glActiveTextureARB(GL_TEXTURE0_ARB + emapTE + 1);
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);
// use combine extension -- RGB: interpolate between previous and current texture on previous alpha
// Alpha: previous alpha
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_INTERPOLATE); // Interpolate
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_TEXTURE); // texture.color
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB,GL_PREVIOUS); // previous.color
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB,GL_PREVIOUS); // by previous.srcalpha
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB,GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_REPLACE); // alpha replace
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_PREVIOUS); // previous.srcalpha
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
// set the environment map
glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.environmentMapGLName);
// ---------------------------------
// set up third texure unit (TE #3)
// Note: TE #3 bound to shape texture
// in tsmesh (only uses alpha)
glActiveTextureARB(GL_TEXTURE0_ARB + emapTE);
glEnable(GL_TEXTURE_2D);
// use combine extension -- RGB: previous RGB
// Alpha: interpolate between 1 and current texture using previous alpha
// = previous.alpha + (1-previous.alpha) * current.alpha
// = 1 - (1-previous.alpha) * (1-current.alpha)
// = correct alpha value of texture w/ emap pre-blended
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,Point4F(1.0f,1.0f,1.0f,1.0f));
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_ALPHA,GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_ALPHA,GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_ALPHA,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_ALPHA,GL_SRC_ALPHA);
// set vertex color alpha
TSShapeInstance::smRenderData.vertexAlpha.emap = TSShapeInstance::smRenderData.environmentMapAlpha;
// switch to base texture unit
glActiveTextureARB(GL_TEXTURE0_ARB + baseTE);
}
if (fogMethod == TSShapeInstance::FOG_MULTI_1)
{
// ---------------------------------
// set up fog texure unit
glActiveTextureARB(GL_TEXTURE0_ARB + fogTE);
glEnable(GL_TEXTURE_2D);
glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,TSShapeInstance::smRenderData.fogColor);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_RGB,GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_RGB,GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE1_RGB,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND2_RGB,GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
// the ATI Rage 128 needs a forthcoming driver to do do constant alpha blend
if (TSShapeInstance::smRenderData.fogTexture)
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB,GL_TEXTURE);
else
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE2_RGB,GL_CONSTANT);
// bind constant fog bitmap
glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.fogHandle->getGLName());
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);
}
if (lmapMethod == TSShapeInstance::LIGHT_MAP_MULTI)
{
glClientActiveTextureARB(GL_TEXTURE0_ARB + lmapTE);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB + baseTE);
// switch to lightmap texture unit
glActiveTextureARB(GL_TEXTURE0_ARB + lmapTE);
glEnable(GL_TEXTURE_2D);
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_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND1_RGB,GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV,GL_COMBINE_ALPHA,GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV,GL_SOURCE0_ALPHA,GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV,GL_OPERAND0_ALPHA,GL_SRC_ALPHA);
//glTexEnvf(GL_TEXTURE_ENV,GL_RGB_SCALE,2.0f);
// switch to base texture unit
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);
LightManager::sgSetupExposureRendering();
// 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 lmap
if (TSShapeInstance::smRenderData.lightMapMethod==TSShapeInstance::LIGHT_MAP_MULTI)
{
// set lightmaps texture unit
glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.lightMapTE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDisable(GL_TEXTURE_2D);
// disable tcoord's on detail map's TE
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.lightMapTE);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE);
}
// restore gl state changes made for base texture
if (dglDoesSupportARBMultitexture())
glActiveTextureARB(GL_TEXTURE0_ARB + TSShapeInstance::smRenderData.baseTE);
LightManager::sgResetExposureRendering();
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);
}
// lmap texture...
if (TSShapeInstance::smRenderData.lightMapMethod==TSShapeInstance::LIGHT_MAP_MULTI)
{
// set lightmap's texture unit...
glActiveTextureARB(GL_TEXTURE0_ARB+TSShapeInstance::smRenderData.lightMapTE);
TextureHandle * lightMap = materials->getLightMap(matIndex);
if (lightMap)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,lightMap->getGLName());
}
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);
}
if(!(flags & TSMaterialList::SelfIlluminating))
{
if(!TSShapeInstance::smRenderData.lightingOn)
glColor4f(0.5f, 0.5f, 0.5f, TSShapeInstance::smRenderData.vertexAlpha.current);
return;
}
ColorF col(0.5f, 0.5f, 0.5f, TSShapeInstance::smRenderData.vertexAlpha.current);
if(LightManager::sgGetProperty(LightManager::sgAdaptiveSelfIlluminationProp))
{
glDisable(GL_LIGHTING);
col = LightManager::sgGetSelfIlluminationColor(col);
col.alpha = TSShapeInstance::smRenderData.vertexAlpha.current;
}
glColor4fv(col);
}
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; i<primitives.size(); i++)
{
TSDrawPrimitive & draw = primitives[i];
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::render: rendering of non-indexed meshes no longer supported");
if ( (matIndex ^ draw.matIndex) & (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial))
{
// material change
matIndex = draw.matIndex & (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial);
if (matIndex & TSDrawPrimitive::NoMaterial)
// no material...
continue;
else
{
if (materials->getFlags(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]);
ToolVector<Point2F> diffuse;
getUVs(tDiffuse, diffuse);
glTexCoordPointer(2,GL_FLOAT,0,&diffuse[firstTVert]);
// lock...
bool lockArrays = dglDoesSupportCompiledVertexArray();
if (lockArrays)
glLockArraysEXT(0,vertsPerFrame);
S32 matIndex = -1;
for (S32 i=0; i<primitives.size(); i++)
{
TSDrawPrimitive & draw = primitives[i];
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::render: rendering of non-indexed meshes no longer supported");
if ( (matIndex ^ draw.matIndex) & (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial))
{
// material change
matIndex = draw.matIndex & (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial);
if (matIndex & TSDrawPrimitive::NoMaterial)
// no material...
continue;
else
{
TSShapeInstance::smRenderData.detailMapTE = 0; // borrow this variable...use as a bool to flag that we have a dmap
TextureHandle * detailMap = materials->getDetailMap(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 render lightmap (2 pass) methods
//-----------------------------------------------------
void TSMesh::renderLightMap(S32 frame, S32 matFrame, TSMaterialList * materials)
{
// most gl states assumed to be all set up...
// if we're here, then we're drawing lightmap 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]);
ToolVector<Point2F> lightmap;
getUVs(tLightmap, lightmap);
glTexCoordPointer(2,GL_FLOAT,0,&lightmap[firstTVert]);
// lock...
bool lockArrays = dglDoesSupportCompiledVertexArray();
if (lockArrays)
glLockArraysEXT(0,vertsPerFrame);
S32 matIndex = -1;
for (S32 i=0; i<primitives.size(); i++)
{
TSDrawPrimitive & draw = primitives[i];
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::render: rendering of non-indexed meshes no longer supported");
if ( (matIndex ^ draw.matIndex) & (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial))
{
// material change
matIndex = draw.matIndex & (TSDrawPrimitive::MaterialMask|TSDrawPrimitive::NoMaterial);
if (matIndex & TSDrawPrimitive::NoMaterial)
// no material...
continue;
else
{
TSShapeInstance::smRenderData.lightMapTE = 0; // borrow this variable...use as a bool to flag that we have a dmap
TextureHandle * lightMap = materials->getLightMap(matIndex);
if (lightMap)
glBindTexture(GL_TEXTURE_2D,lightMap->getGLName());
else
continue;
TSShapeInstance::smRenderData.lightMapTE = 1;
}
}
if (TSShapeInstance::smRenderData.lightMapTE)
glDrawElements(getDrawType(draw.matIndex>>30),draw.numElements,GL_UNSIGNED_SHORT,&indices[draw.start]);
}
// unlock...
if (lockArrays)
glUnlockArraysEXT();
}
void TSMesh::initLightMapMaterials()
{
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);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_BLEND);
glDisable(GL_LIGHTING);
}
void TSMesh::resetLightMapMaterials()
{
glEnable(GL_LIGHTING);
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<Point2I> 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; i<primitives.size(); i++)
{
TSDrawPrimitive & draw = primitives[i];
if (primitives[i].matIndex & TSDrawPrimitive::NoMaterial ||
materials->getFlags(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; i<vertsPerFrame; i++)
polyList->addPoint(verts[i+firstVert]);
}
// 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; j<draw.numElements; )
{
U32 idx0 = base + indices[start + j + 0];
U32 idx1 = base + indices[start + j + 1];
U32 idx2 = base + indices[start + j + 2];
polyList->begin(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; j<draw.numElements; j++)
{
*nextIdx = idx2;
// nextIdx = (j%2)==0 ? &idx0 : &idx1;
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
idx2 = base + indices[start + j];
if (idx0 == idx1 || idx0 == idx2 || idx1 == idx2)
continue;
polyList->begin(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; j<draw.numElements; j+=3)
{
PlaneF plane(cf->mVertexList[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; j<draw.numElements; j++)
{
*nextIdx = idx2;
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
idx2 = base + indices[start + j];
if (idx0 == idx1 || idx0 == idx2 || idx1 == idx2)
continue;
PlaneF plane(cf->mVertexList[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; i<startPlane+planesPerFrame; i++)
{
// if start & end outside, no collision
// if start & end inside, continue
// if start outside, end inside, or visa versa, find intersection of line with plane
// then update intersection of line with hull (using startTime and endTime)
F32 dot1 = mDot(planeNormals[i],start) - planeConstants[i];
F32 dot2 = mDot(planeNormals[i],end) - planeConstants[i];
if (dot1*dot2>0.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*sgn<endNum*den*sgn && num*startDen*sgn<startNum*den*sgn;
if (num * *pden * sgn < *pnum * den * sgn && !noCollision)
{
*pnum = num;
*pden = den;
*pplane = i;
*pfound = true;
}
else if (noCollision)
return false;
// if (dot1<=0.0f)
// {
// // start is inside plane, end is outside...chop off end
// if (num*endDen<endNum*den) // if (time<endTime)
// {
// if (num*startDen<startNum*den) //if (time<startTime)
// // no intersection of line and hull
// return false;
// // endTime = time;
// endNum = num;
// endDen = den;
// }
// // else, no need to do anything, just continue (we've been more inside than this)
// }
// else // dot2<=0.0f
// {
// // end is inside poly, start is outside...chop off start
// AssertFatal(dot2<=0.0f,"TSMesh::castRay (2)");
// if (num*startDen>startNum*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; i<planeNormals.size(); i++)
{
if (mDot(planeNormals[i],normal)>0.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<numFrames; frame++)
{
S32 firstVert = vertsPerFrame * frame;
S32 firstPlane = planeNormals.size();
for (i=0; i<primitives.size(); i++)
{
TSDrawPrimitive & draw = primitives[i];
U32 start = draw.start;
AssertFatal(draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildConvexHull (1)");
// gonna depend on what kind of primitive it is...
U32 material = draw.matIndex & TSDrawPrimitive::MaterialMask;
if ( (draw.matIndex&TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
{
for (j=0; j<draw.numElements; j+=3)
if (addToHull(indices[start+j+0]+firstVert,indices[start+j+1]+firstVert,indices[start+j+2]+firstVert) && frame==0)
planeMaterials.push_back(draw.matIndex & TSDrawPrimitive::MaterialMask);
}
else
{
AssertFatal((draw.matIndex&TSDrawPrimitive::Strip) == TSDrawPrimitive::Strip,"TSMesh::buildConvexHull (2)");
U32 idx0 = indices[start + 0] + firstVert;
U32 idx1;
U32 idx2 = indices[start + 1] + firstVert;
U32 * nextIdx = &idx1;
for (j=2; j<draw.numElements; j++)
{
*nextIdx = idx2;
// nextIdx = (j%2)==0 ? &idx0 : &idx1;
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
idx2 = indices[start + j] + firstVert;
if (addToHull(idx0,idx1,idx2) && frame==0)
planeMaterials.push_back(draw.matIndex & TSDrawPrimitive::MaterialMask);
}
}
}
// make sure all the verts on this frame are inside all the planes
for (i=0; i<vertsPerFrame; i++)
for (j=firstPlane; j<planeNormals.size(); j++)
if (mDot(verts[firstVert+i],planeNormals[j])-planeConstants[j]<0.01) // .01 == a little slack
error = true;
if (frame==0)
planesPerFrame = planeNormals.size();
if ( (frame+1) * planesPerFrame != planeNormals.size() )
{
// eek, not all frames have same number of planes...
while ( (frame+1) * planesPerFrame > 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; i<numVerts; i++)
{
transform.mulP(v[i],&p);
bounds.max.setMax(p);
bounds.min.setMin(p);
}
Point3F c;
if (!center)
center = &c;
center->x = 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<numVerts; i++)
{
transform.mulP(v[i],&p);
p -= *center;
*radius = getMax(*radius,mDot(p,p));
}
*radius = mSqrt(*radius);
}
}
//-----------------------------------------------------
S32 TSMesh::getNumPolys()
{
S32 count = 0;
for (S32 i=0; i<primitives.size(); i++)
{
if ((primitives[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
count += primitives[i].numElements / 3;
else
count += primitives[i].numElements - 2;
}
return count;
}
//-----------------------------------------------------
// TSMesh destructor
//-----------------------------------------------------
TSMesh::~TSMesh()
{
}
//-----------------------------------------------------
// TSSkinMesh methods
//-----------------------------------------------------
Vector<MatrixF> gBoneTransforms;
Vector<Point3F> gSkinVerts;
Vector<Point3F> 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; i<vertsPerFrame; i++)
gNormalStore[i]=decodeNormal(encodedNorms[i]);
initialNorms.set(gNormalStore.address(),vertsPerFrame);
}
gSkinVerts.setSize(initialVerts.size());
gSkinNorms.setSize(initialNorms.size());
verts.set(gSkinVerts.address(),gSkinVerts.size());
norms.set(gSkinNorms.address(),gSkinNorms.size());
#endif
// set up bone transforms
S32 i;
for (i=0; i<nodeIndex.size(); i++)
{
S32 node = nodeIndex[i];
gBoneTransforms[i].mul(TSShapeInstance::ObjectInstance::smTransforms[node],initialTransforms[i]);
}
// multiply verts and normals by boneTransforms
S32 prevIndex = -1;
S32 vertexIndexSize = vertexIndex.size();
Point3F v0,n0;
// TODO: vectorize this.
for (i=0; i<vertexIndexSize; i++)
{
const S32 &vIndex = vertexIndex[i];
const MatrixF & deltaTransform = gBoneTransforms[boneIndex[i]];
deltaTransform.mulP(initialVerts[vIndex],&v0);
deltaTransform.mulV(initialNorms[vIndex],&n0);
v0 *= weight[i];
n0 *= weight[i];
Point3F & v = verts[vIndex];
Point3F & n = norms[vIndex];
// this should be implemented as a series of conditional moves to avoid the branch
if (vIndex != prevIndex)
{
v.x = 0.0f;
v.y = 0.0f;
v.z = 0.0f;
n.x = 0.0f;
n.y = 0.0f;
n.z = 0.0f;
}
v += v0;
n += n0;
prevIndex = vIndex;
}
// normalize normals...
for (i=0; i<norms.size(); i++)
{
// gotta do a check now since shared verts between meshes
// may result in an unused vert in the list...
Point3F & n = norms[i];
F32 len2 = mDot(n,n);
if (len2>0.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; i<numPrimIn; i++)
{
S32 newMat = primitiveMatIn[i];
newMat &= ~TSDrawPrimitive::TypeMask;
if (newMat!=prevMaterial)
{
if (primitivesOut)
{
newDraw = (TSDrawPrimitive*) &primitivesOut[numPrimOut*2];
newDraw->start = 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; j<numElements; j+=3)
{
if (indicesOut)
{
indicesOut[numIndicesOut+0] = indicesIn[start+j+0];
indicesOut[numIndicesOut+1] = indicesIn[start+j+1];
indicesOut[numIndicesOut+2] = indicesIn[start+j+2];
}
if (newDraw)
newDraw->numElements += 3;
numIndicesOut += 3;
}
}
else
{
U32 idx0 = indicesIn[start + 0];
U32 idx1;
U32 idx2 = indicesIn[start + 1];
U32 * nextIdx = &idx1;
for (S32 j=2; j<numElements; j++)
{
*nextIdx = idx2;
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
idx2 = indicesIn[start + j];
if (idx0==idx1 || idx1==idx2 || idx2==idx0)
continue;
if (indicesOut)
{
indicesOut[numIndicesOut+0] = idx0;
indicesOut[numIndicesOut+1] = idx1;
indicesOut[numIndicesOut+2] = idx2;
}
if (newDraw)
newDraw->numElements += 3;
numIndicesOut += 3;
}
}
}
}
void unwindStrip(S16 * indices, S32 numElements, Vector<S16> & triIndices)
{
U32 idx0 = indices[0];
U32 idx1;
U32 idx2 = indices[1];
U32 * nextIdx = &idx1;
for (S32 j=2; j<numElements; j++)
{
*nextIdx = idx2;
nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
idx2 = indices[j];
if (idx0==idx1 || idx1==idx2 || idx2==idx0)
continue;
triIndices.push_back(idx0);
triIndices.push_back(idx1);
triIndices.push_back(idx2);
}
}
void TSMesh::convertToSingleStrip(S16 * primitiveDataIn, S32 * primitiveMatIn, S16 * indicesIn, S32 numPrimIn,
S32 & numPrimOut, S32 & numIndicesOut,
S32 * primitivesOut, S16 * indicesOut)
{
S32 prevMaterial = -99999;
TSDrawPrimitive * newDraw = NULL;
TSDrawPrimitive * newTris = NULL;
Vector<S16> triIndices;
S32 curDrawOut = 0;
numPrimOut=0;
numIndicesOut=0;
for (S32 i=0; i<numPrimIn; i++)
{
S32 newMat = primitiveMatIn[i];
if (newMat!=prevMaterial)
{
// 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;
}
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; j<numElements; j+=3)
{
if (indicesOut)
{
indicesOut[numIndicesOut+0] = indicesIn[start+j+0];
indicesOut[numIndicesOut+1] = indicesIn[start+j+1];
indicesOut[numIndicesOut+2] = indicesIn[start+j+2];
}
if (newDraw)
newDraw->numElements += 3;
numIndicesOut += 3;
}
}
else
{
// strip primitive...
// if numElements less than smSmallestStripSize, add to triangles...
if (numElements<smMinStripSize+2)
{
// put triangle indices aside until material changes...
if (triIndices.empty())
{
// set up for new triangle primitive and add it if we are copying data right now
if (primitivesOut)
{
newTris = (TSDrawPrimitive*) &primitivesOut[numPrimOut*2];
newTris->matIndex = 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<S16> triIndices;
numPrimOut=0;
numIndicesOut=0;
for (S32 i=0; i<numPrimIn; i++)
{
S32 newMat = primitiveMatIn[i];
U16 start = primitiveDataIn[i*2];
U16 numElements = primitiveDataIn[i*2+1];
if (newMat!=prevMaterial && 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 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; j<numElements; j+=3)
{
if (indicesOut)
{
indicesOut[numIndicesOut+0] = indicesIn[start+j+0];
indicesOut[numIndicesOut+1] = indicesIn[start+j+1];
indicesOut[numIndicesOut+2] = indicesIn[start+j+2];
}
if (newDraw)
newDraw->numElements += 3;
numIndicesOut += 3;
}
}
else
{
// strip primitive...
// if numElements less than smSmallestStripSize, add to triangles...
if (numElements<smMinStripSize+2)
// put triangle indices aside until material changes...
unwindStrip(indicesIn+start,numElements,triIndices);
else
{
// strip primitive...add to it
if (indicesOut)
dMemcpy(indicesOut+numIndicesOut,indicesIn+start,2*numElements);
if (newDraw)
newDraw->numElements = 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);
ToolVector<Point2F> diffuse;
diffuse.set((Point2F*)ptr32,numTVerts);
setUVs(tDiffuse, diffuse);
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());
ToolVector<Point2F> diffuse;
getUVs(tDiffuse, diffuse);
// tverts...
alloc.set32(diffuse.size());
if (parentMesh<0)
// if no parent mesh, then save off our tverts
alloc.copyToBuffer32((S32*)diffuse.address(),2*diffuse.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; i<norms.size(); i++)
{
U8 normIdx = encodedNorms.size() ? encodedNorms[i] : encodeNormal(norms[i]);
alloc.copyToBuffer8((S8*)&normIdx,1);
}
}
// primitives...
alloc.set32(primitives.size());
for (S32 i=0; i<primitives.size(); i++)
{
alloc.copyToBuffer16((S16*)&primitives[i],2);
alloc.copyToBuffer32(((S32*)&primitives[i])+1,1);
}
// indices...
alloc.set32(indices.size());
alloc.copyToBuffer16((S16*)indices.address(),indices.size());
// merge indices...
alloc.set32(mergeIndices.size());
alloc.copyToBuffer16((S16*)mergeIndices.address(),mergeIndices.size());
// small stuff...
alloc.set32(vertsPerFrame);
alloc.set32(getFlags());
alloc.setGuard();
}
//-----------------------------------------------------
// TSSkinMesh assemble from/ dissemble to memory buffer
//-----------------------------------------------------
void TSSkinMesh::assemble(bool skip)
{
// avoid a crash on computeBounds...
initialVerts.set(NULL,0);
TSMesh::assemble(skip);
S32 sz = alloc.get32();
S32 numVerts = sz;
S32 * ptr32 = getSharedData32(parentMesh,3*numVerts,(S32**)smVertsList.address(),skip);
initialVerts.set((Point3F*)ptr32,sz);
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
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<initialNorms.size(); i++)
{
U8 normIdx = encodedNorms.size() ? encodedNorms[i] : encodeNormal(initialNorms[i]);
alloc.copyToBuffer8((S8*)&normIdx,1);
}
}
alloc.set32(initialTransforms.size());
if (parentMesh<0)
alloc.copyToBuffer32((S32*)initialTransforms.address(),initialTransforms.size()*16);
alloc.set32(vertexIndex.size());
if (parentMesh<0)
{
alloc.copyToBuffer32((S32*)vertexIndex.address(),vertexIndex.size());
alloc.copyToBuffer32((S32*)boneIndex.address(),boneIndex.size());
alloc.copyToBuffer32((S32*)weight.address(),weight.size());
}
alloc.set32(nodeIndex.size());
if (parentMesh<0)
alloc.copyToBuffer32((S32*)nodeIndex.address(),nodeIndex.size());
alloc.setGuard();
}
// Multiple UV interface
bool TSMesh::getUVs(U32 type, ToolVector<Point2F>& uvs)
{
for (U32 i = 0; i < mUVs.size(); i++)
{
if (mUVs[i].type == type)
{
uvs = mUVs[i].uvs;
return true;
}
}
return false;
}
bool TSMesh::setUVs(U32 type, ToolVector<Point2F> uvs)
{
// Ensure that we only have one of each type of UV
for (U32 i = 0; i < mUVs.size(); i++)
{
if (mUVs[i].type == type)
return false;
}
mUVs.increment();
mUVs.last().type = type;
mUVs.last().uvs = uvs;
return true;
}