phoenix-firestorm/indra/newview/llviewerjointmesh.cpp

1602 lines
49 KiB
C++

/**
* @file llviewerjointmesh.cpp
* @brief Implementation of LLViewerJointMesh class
*
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
* $License$
*/
//-----------------------------------------------------------------------------
// Header Files
//-----------------------------------------------------------------------------
#include "llviewerprecompiledheaders.h"
#if LL_WINDOWS // For Intel vector classes
#include "fvec.h"
#endif
#include "imageids.h"
#include "llfasttimer.h"
#include "llagent.h"
#include "llagparray.h"
#include "llbox.h"
#include "lldrawable.h"
#include "lldrawpoolavatar.h"
#include "lldrawpoolbump.h"
#include "lldynamictexture.h"
#include "llface.h"
#include "llgldbg.h"
#include "llglheaders.h"
#include "lltexlayer.h"
#include "llviewercamera.h"
#include "llviewerimagelist.h"
#include "llviewerjointmesh.h"
#include "llvoavatar.h"
#include "llsky.h"
#include "pipeline.h"
#if !LL_DARWIN && !LL_LINUX
extern PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB;
extern PFNGLWEIGHTFVARBPROC glWeightfvARB;
extern PFNGLVERTEXBLENDARBPROC glVertexBlendARB;
#endif
extern BOOL gRenderForSelect;
LLMatrix4 gBlendMat;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// LLViewerJointMesh::LLSkinJoint
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// LLSkinJoint
//-----------------------------------------------------------------------------
LLSkinJoint::LLSkinJoint()
{
mJoint = NULL;
}
//-----------------------------------------------------------------------------
// ~LLSkinJoint
//-----------------------------------------------------------------------------
LLSkinJoint::~LLSkinJoint()
{
mJoint = NULL;
}
//-----------------------------------------------------------------------------
// LLSkinJoint::setupSkinJoint()
//-----------------------------------------------------------------------------
BOOL LLSkinJoint::setupSkinJoint( LLViewerJoint *joint)
{
// find the named joint
mJoint = joint;
if ( !mJoint )
{
llinfos << "Can't find joint" << llendl;
}
// compute the inverse root skin matrix
mRootToJointSkinOffset.clearVec();
LLVector3 rootSkinOffset;
while (joint)
{
rootSkinOffset += joint->getSkinOffset();
joint = (LLViewerJoint*)joint->getParent();
}
mRootToJointSkinOffset = -rootSkinOffset;
mRootToParentJointSkinOffset = mRootToJointSkinOffset;
mRootToParentJointSkinOffset += mJoint->getSkinOffset();
return TRUE;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// LLViewerJointMesh
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
BOOL LLViewerJointMesh::sPipelineRender = FALSE;
EAvatarRenderPass LLViewerJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
U32 LLViewerJointMesh::sClothingMaskImageName = 0;
LLColor4 LLViewerJointMesh::sClothingInnerColor;
//-----------------------------------------------------------------------------
// LLViewerJointMesh()
//-----------------------------------------------------------------------------
LLViewerJointMesh::LLViewerJointMesh()
:
mTexture( NULL ),
mLayerSet( NULL ),
mTestImageName( 0 ),
mIsTransparent(FALSE)
{
mColor[0] = 1.0f;
mColor[1] = 1.0f;
mColor[2] = 1.0f;
mColor[3] = 1.0f;
mShiny = 0.0f;
mCullBackFaces = TRUE;
mMesh = NULL;
mNumSkinJoints = 0;
mSkinJoints = NULL;
mFace = NULL;
mMeshID = 0;
mUpdateXform = FALSE;
mValid = FALSE;
}
//-----------------------------------------------------------------------------
// ~LLViewerJointMesh()
// Class Destructor
//-----------------------------------------------------------------------------
LLViewerJointMesh::~LLViewerJointMesh()
{
mMesh = NULL;
mTexture = NULL;
freeSkinData();
}
//-----------------------------------------------------------------------------
// LLViewerJointMesh::allocateSkinData()
//-----------------------------------------------------------------------------
BOOL LLViewerJointMesh::allocateSkinData( U32 numSkinJoints )
{
mSkinJoints = new LLSkinJoint[ numSkinJoints ];
mNumSkinJoints = numSkinJoints;
return TRUE;
}
//-----------------------------------------------------------------------------
// getSkinJointByIndex()
//-----------------------------------------------------------------------------
S32 LLViewerJointMesh::getBoundJointsByIndex(S32 index, S32 &joint_a, S32& joint_b)
{
S32 num_joints = 0;
if (mNumSkinJoints == 0)
{
return num_joints;
}
joint_a = -1;
joint_b = -1;
LLPolyMesh *reference_mesh = mMesh->getReferenceMesh();
if (index < reference_mesh->mJointRenderData.count())
{
LLJointRenderData* render_datap = reference_mesh->mJointRenderData[index];
if (render_datap->mSkinJoint)
{
joint_a = render_datap->mSkinJoint->mJoint->mJointNum;
}
num_joints++;
}
if (index + 1 < reference_mesh->mJointRenderData.count())
{
LLJointRenderData* render_datap = reference_mesh->mJointRenderData[index + 1];
if (render_datap->mSkinJoint)
{
joint_b = render_datap->mSkinJoint->mJoint->mJointNum;
}
if (joint_a == -1)
{
joint_a = render_datap->mSkinJoint->mJoint->getParent()->mJointNum;
}
num_joints++;
}
return num_joints;
}
//-----------------------------------------------------------------------------
// LLViewerJointMesh::freeSkinData()
//-----------------------------------------------------------------------------
void LLViewerJointMesh::freeSkinData()
{
mNumSkinJoints = 0;
delete [] mSkinJoints;
mSkinJoints = NULL;
}
//--------------------------------------------------------------------
// LLViewerJointMesh::getColor()
//--------------------------------------------------------------------
void LLViewerJointMesh::getColor( F32 *red, F32 *green, F32 *blue, F32 *alpha )
{
*red = mColor[0];
*green = mColor[1];
*blue = mColor[2];
*alpha = mColor[3];
}
//--------------------------------------------------------------------
// LLViewerJointMesh::setColor()
//--------------------------------------------------------------------
void LLViewerJointMesh::setColor( F32 red, F32 green, F32 blue, F32 alpha )
{
mColor[0] = red;
mColor[1] = green;
mColor[2] = blue;
mColor[3] = alpha;
}
//--------------------------------------------------------------------
// LLViewerJointMesh::getTexture()
//--------------------------------------------------------------------
//LLViewerImage *LLViewerJointMesh::getTexture()
//{
// return mTexture;
//}
//--------------------------------------------------------------------
// LLViewerJointMesh::setTexture()
//--------------------------------------------------------------------
void LLViewerJointMesh::setTexture( LLViewerImage *texture )
{
mTexture = texture;
// texture and dynamic_texture are mutually exclusive
if( texture )
{
mLayerSet = NULL;
//texture->bindTexture(0);
//texture->setClamp(TRUE, TRUE);
}
}
//--------------------------------------------------------------------
// LLViewerJointMesh::setLayerSet()
// Sets the shape texture (takes precedence over normal texture)
//--------------------------------------------------------------------
void LLViewerJointMesh::setLayerSet( LLTexLayerSet* layer_set )
{
mLayerSet = layer_set;
// texture and dynamic_texture are mutually exclusive
if( layer_set )
{
mTexture = NULL;
}
}
//--------------------------------------------------------------------
// LLViewerJointMesh::getMesh()
//--------------------------------------------------------------------
LLPolyMesh *LLViewerJointMesh::getMesh()
{
return mMesh;
}
//-----------------------------------------------------------------------------
// LLViewerJointMesh::setMesh()
//-----------------------------------------------------------------------------
void LLViewerJointMesh::setMesh( LLPolyMesh *mesh )
{
// set the mesh pointer
mMesh = mesh;
// release any existing skin joints
freeSkinData();
if ( mMesh == NULL )
{
return;
}
// acquire the transform from the mesh object
setPosition( mMesh->getPosition() );
setRotation( mMesh->getRotation() );
setScale( mMesh->getScale() );
// create skin joints if necessary
if ( mMesh->hasWeights() && !mMesh->isLOD())
{
U32 numJointNames = mMesh->getNumJointNames();
allocateSkinData( numJointNames );
std::string *jointNames = mMesh->getJointNames();
U32 jn;
for (jn = 0; jn < numJointNames; jn++)
{
//llinfos << "Setting up joint " << jointNames[jn].c_str() << llendl;
LLViewerJoint* joint = (LLViewerJoint*)(getRoot()->findJoint(jointNames[jn]) );
mSkinJoints[jn].setupSkinJoint( joint );
}
}
// setup joint array
if (!mMesh->isLOD())
{
setupJoint((LLViewerJoint*)getRoot());
}
// llinfos << "joint render entries: " << mMesh->mJointRenderData.count() << llendl;
}
//-----------------------------------------------------------------------------
// setupJoint()
//-----------------------------------------------------------------------------
void LLViewerJointMesh::setupJoint(LLViewerJoint* current_joint)
{
// llinfos << "Mesh: " << getName() << llendl;
// S32 joint_count = 0;
U32 sj;
for (sj=0; sj<mNumSkinJoints; sj++)
{
LLSkinJoint &js = mSkinJoints[sj];
if (js.mJoint != current_joint)
{
continue;
}
// we've found a skinjoint for this joint..
// is the last joint in the array our parent?
if(mMesh->mJointRenderData.count() && mMesh->mJointRenderData[mMesh->mJointRenderData.count() - 1]->mWorldMatrix == &current_joint->getParent()->getWorldMatrix())
{
// ...then just add ourselves
LLViewerJoint* jointp = js.mJoint;
mMesh->mJointRenderData.put(new LLJointRenderData(&jointp->getWorldMatrix(), &js));
// llinfos << "joint " << joint_count << js.mJoint->getName() << llendl;
// joint_count++;
}
// otherwise add our parent and ourselves
else
{
mMesh->mJointRenderData.put(new LLJointRenderData(&current_joint->getParent()->getWorldMatrix(), NULL));
// llinfos << "joint " << joint_count << current_joint->getParent()->getName() << llendl;
// joint_count++;
mMesh->mJointRenderData.put(new LLJointRenderData(&current_joint->getWorldMatrix(), &js));
// llinfos << "joint " << joint_count << current_joint->getName() << llendl;
// joint_count++;
}
}
// depth-first traversal
for (LLJoint *child_joint = current_joint->mChildren.getFirstData();
child_joint;
child_joint = current_joint->mChildren.getNextData())
{
setupJoint((LLViewerJoint*)child_joint);
}
}
const S32 NUM_AXES = 3;
// register layoud
// rotation X 0-n
// rotation Y 0-n
// rotation Z 0-n
// pivot parent 0-n -- child = n+1
static LLMatrix4 gJointMat[32];
static LLMatrix3 gJointRot[32];
static LLVector4 gJointPivot[32];
//-----------------------------------------------------------------------------
// uploadJointMatrices()
//-----------------------------------------------------------------------------
void LLViewerJointMesh::uploadJointMatrices()
{
S32 joint_num;
LLPolyMesh *reference_mesh = mMesh->getReferenceMesh();
LLDrawPool *poolp = mFace ? mFace->getPool() : NULL;
BOOL hardware_skinning = (poolp && poolp->getVertexShaderLevel() > 0) ? TRUE : FALSE;
//calculate joint matrices
for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
{
LLMatrix4 joint_mat = *reference_mesh->mJointRenderData[joint_num]->mWorldMatrix;
if (hardware_skinning)
{
joint_mat *= gCamera->getModelview();
}
gJointMat[joint_num] = joint_mat;
gJointRot[joint_num] = joint_mat.getMat3();
}
BOOL last_pivot_uploaded = FALSE;
S32 j = 0;
//upload joint pivots
for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
{
LLSkinJoint *sj = reference_mesh->mJointRenderData[joint_num]->mSkinJoint;
if (sj)
{
if (!last_pivot_uploaded)
{
LLVector4 parent_pivot(sj->mRootToParentJointSkinOffset);
parent_pivot.mV[VW] = 0.f;
gJointPivot[j++] = parent_pivot;
}
LLVector4 child_pivot(sj->mRootToJointSkinOffset);
child_pivot.mV[VW] = 0.f;
gJointPivot[j++] = child_pivot;
last_pivot_uploaded = TRUE;
}
else
{
last_pivot_uploaded = FALSE;
}
}
//add pivot point into transform
for (S32 i = 0; i < j; i++)
{
LLVector3 pivot;
pivot = LLVector3(gJointPivot[i]);
pivot = pivot * gJointRot[i];
gJointMat[i].translate(pivot);
}
// upload matrices
if (hardware_skinning)
{
GLfloat mat[45*4];
memset(mat, 0, sizeof(GLfloat)*45*4);
for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
{
gJointMat[joint_num].transpose();
for (S32 axis = 0; axis < NUM_AXES; axis++)
{
F32* vector = gJointMat[joint_num].mMatrix[axis];
//glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, LL_CHARACTER_MAX_JOINTS_PER_MESH * axis + joint_num+5, (GLfloat*)vector);
U32 offset = LL_CHARACTER_MAX_JOINTS_PER_MESH*axis+joint_num;
memcpy(mat+offset*4, vector, sizeof(GLfloat)*4);
//glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, LL_CHARACTER_MAX_JOINTS_PER_MESH * axis + joint_num+6, (GLfloat*)vector);
//cgGLSetParameterArray4f(gPipeline.mAvatarMatrix, offset, 1, vector);
}
}
glUniform4fvARB(gPipeline.mAvatarMatrixParam, 45, mat);
}
}
//--------------------------------------------------------------------
// LLViewerJointMesh::drawBone()
//--------------------------------------------------------------------
void LLViewerJointMesh::drawBone()
{
}
//--------------------------------------------------------------------
// LLViewerJointMesh::isTransparent()
//--------------------------------------------------------------------
BOOL LLViewerJointMesh::isTransparent()
{
return mIsTransparent;
}
//--------------------------------------------------------------------
// DrawElementsBLEND and utility code
//--------------------------------------------------------------------
// compate_int is used by the qsort function to sort the index array
int compare_int(const void *a, const void *b)
{
if (*(U32*)a < *(U32*)b)
{
return -1;
}
else if (*(U32*)a > *(U32*)b)
{
return 1;
}
else return 0;
}
#if LL_WINDOWS || (LL_DARWIN && __i386__) // SSE optimizations in avatar code
#if LL_DARWIN
#include <xmmintrin.h>
// On Windows, this class is defined in fvec.h. I've only reproduced the parts of it we use here for now.
#pragma pack(push,16) /* Must ensure class & union 16-B aligned */
class F32vec4
{
protected:
__m128 vec;
public:
/* Constructors: __m128, 4 floats, 1 float */
F32vec4() {}
/* initialize 4 SP FP with __m128 data type */
F32vec4(__m128 m) { vec = m;}
/* Explicitly initialize each of 4 SP FPs with same float */
explicit F32vec4(float f) { vec = _mm_set_ps1(f); }
};
#pragma pack(pop) /* 16-B aligned */
#endif
void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
{
F32 last_weight = F32_MAX;
LLMatrix4 *blend_mat = &gBlendMat;
for (S32 index = vert_offset; index < vert_offset + vert_count; index++)
{
F32 w = weights [index]; // register copy of weight
F32 *vin = &vertices[index].mV[0]; // pointer to input vertex data, assumed to be V3+T2+N3+whatever
F32 *vout = output + index * (AVATAR_VERTEX_BYTES/sizeof(F32)); // pointer to the output vertex data, assumed to be 16 byte aligned
if (w == last_weight)
{
// load input and output vertices, and last blended matrix
__asm {
mov esi, vin
mov edi, vout
mov edx, blend_mat
movaps xmm4, [edx]
movaps xmm5, [edx+0x10]
movaps xmm6, [edx+0x20]
movaps xmm7, [edx+0x30]
}
}
else
{
last_weight = w;
S32 joint = llfloor(w);
w -= joint;
LLMatrix4 *m0 = &(gJointMat[joint+1]);
LLMatrix4 *m1 = &(gJointMat[joint+0]);
// some initial code to load Matrix 0 into SSE registers
__asm {
mov esi, vin
mov edi, vout
//matrix2
mov edx, m0
movaps xmm4, [edx]
movaps xmm5, [edx+0x10]
movaps xmm6, [edx+0x20]
movaps xmm7, [edx+0x30]
};
// if w == 1.0f, we don't need to blend.
// but since we do the trick of blending the matrices, here, if w != 1.0,
// we load Matrix 1 into the other 4 SSE registers and blend both matrices
// based on the weight (which we load ingo a 16-byte aligned vector: w,w,w,w)
if (w != 1.0f)
{
F32vec4 weight(w);
__asm { // do blending of matrices instead of verts and normals -- faster
mov edx, m1
movaps xmm0, [edx]
movaps xmm1, [edx+0x10]
movaps xmm2, [edx+0x20]
movaps xmm3, [edx+0x30]
subps xmm4, xmm0 // do blend for each matrix column
subps xmm5, xmm1 // diff, then multiply weight and re-add
subps xmm6, xmm2
subps xmm7, xmm3
mulps xmm4, weight
mulps xmm5, weight
mulps xmm6, weight
mulps xmm7, weight
addps xmm4, xmm0
addps xmm5, xmm1
addps xmm6, xmm2
addps xmm7, xmm3
};
}
__asm {
// save off blended matrix
mov edx, blend_mat;
movaps [edx], xmm4;
movaps [edx+0x10], xmm5;
movaps [edx+0x20], xmm6;
movaps [edx+0x30], xmm7;
}
}
// now, we have either a blended matrix in xmm4-7 or the original Matrix 0
// we then multiply each vertex and normal by this one matrix.
// For SSE2, we would try to keep the original two matrices in other registers
// and avoid reloading them. However, they should ramain in L1 cache in the
// current case.
// One possible optimization would be to sort the vertices by weight instead
// of just index (we still want to uniqify). If we note when two or more vertices
// share the same weight, we can avoid doing the middle SSE code above and just
// re-use the blended matrix for those vertices
// now, we do the actual vertex blending
__asm {
// load Vertex into xmm0.
movaps xmm0, [esi] // change aps to ups when input is no longer 16-baligned
movaps xmm1, xmm0 // copy vector into xmm0 through xmm2 (x,y,z)
movaps xmm2, xmm0
shufps xmm0, xmm0, _MM_SHUFFLE(0,0,0,0); // clone vertex (x) across vector
shufps xmm1, xmm1, _MM_SHUFFLE(1,1,1,1); // clone vertex (y) across vector
shufps xmm2, xmm2, _MM_SHUFFLE(2,2,2,2); // same for Z
mulps xmm0, xmm4 // do the actual matrix multipication for r0
mulps xmm1, xmm5 // for r1
mulps xmm2, xmm6 // for r2
addps xmm0, xmm1 // accumulate
addps xmm0, xmm2 // accumulate
addps xmm0, xmm7 // add in the row 4 which holds the x,y,z translation. assumes w=1 (vertex-w, not weight)
movaps [edi], xmm0 // store aligned in output array
// load Normal into xmm0.
movaps xmm0, [esi + 0x10] // change aps to ups when input no longer 16-byte aligned
movaps xmm1, xmm0 //
movaps xmm2, xmm0
shufps xmm0, xmm0, _MM_SHUFFLE(0,0,0,0); // since UV sits between vertex and normal, normal starts at element 1, not 0
shufps xmm1, xmm1, _MM_SHUFFLE(1,1,1,1);
shufps xmm2, xmm2, _MM_SHUFFLE(2,2,2,2);
mulps xmm0, xmm4 // multiply by matrix
mulps xmm1, xmm5 // multiply
mulps xmm2, xmm6 // multiply
addps xmm0, xmm1 // accumulate
addps xmm0, xmm2 // accumulate. note: do not add translation component to normals, save time too
movaps [edi + 0x10], xmm0 // store aligned
}
*(LLVector2*)(vout + (AVATAR_OFFSET_TEX0/sizeof(F32))) = texcoords[index]; // write texcoord into appropriate spot.
}
}
#elif LL_LINUX
void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
{
assert(0);
}
#elif LL_DARWIN
// AltiVec versions of the same...
static inline vector float loadAlign(int offset, vector float *addr)
{
vector float in0 = vec_ld(offset, addr);
vector float in1 = vec_ld(offset + 16, addr);
vector unsigned char perm = vec_lvsl(0, (unsigned char*)addr);
return(vec_perm(in0, in1, perm));
}
static inline void storeAlign(vector float v, int offset, vector float *addr)
{
vector float in0 = vec_ld(offset, addr);
vector float in1 = vec_ld(offset + 16, addr);
vector unsigned char perm = vec_lvsr(0, (unsigned char *)addr);
vector float temp = vec_perm(v, v, perm);
vector unsigned char mask = (vector unsigned char)vec_cmpgt(perm, vec_splat_u8(15));
in0 = vec_sel(in0, temp, (vector unsigned int)mask);
in1 = vec_sel(temp, in1, (vector unsigned int)mask);
vec_st(in0, offset, addr);
vec_st(in1, offset + 16, addr);
}
void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
{
F32 last_weight = F32_MAX;
// LLMatrix4 &blend_mat = gBlendMat;
vector float matrix0_0, matrix0_1, matrix0_2, matrix0_3;
vector unsigned char out0perm = (vector unsigned char) ( 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F );
// vector unsigned char out1perm = (vector unsigned char) ( 0x00,0x01,0x02,0x03, 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B );
vector unsigned char out1perm = (vector unsigned char) ( 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F );
vector float zero = (vector float)vec_splat_u32(0);
for (U32 index = vert_offset; index < vert_offset + vert_count; index++)
{
F32 w = weights [index]; // register copy of weight
F32 *vin = &vertices[index].mV[0]; // pointer to input vertex data, assumed to be V3+T2+N3+whatever
F32 *vout = output + index * (AVATAR_VERTEX_BYTES/sizeof(F32)); // pointer to the output vertex data, assumed to be 16 byte aligned
// MBW -- XXX -- If this isn't the case, this code gets more complicated.
if(0x0000000F & (U32)vin)
{
llerrs << "blend_SSE_batch: input not 16-byte aligned!" << llendl;
}
if(0x0000000F & (U32)vout)
{
llerrs << "blend_SSE_batch: output not 16-byte aligned!" << llendl;
}
// if(0x0000000F & (U32)&(blend_mat.mMatrix))
// {
// llerrs << "blend_SSE_batch: blend_mat not 16-byte aligned!" << llendl;
// }
if (w == last_weight)
{
// load last blended matrix
// Still loaded from last time through the loop.
// matrix0_0 = vec_ld(0x00, (vector float*)&(blend_mat.mMatrix));
// matrix0_1 = vec_ld(0x10, (vector float*)&(blend_mat.mMatrix));
// matrix0_2 = vec_ld(0x20, (vector float*)&(blend_mat.mMatrix));
// matrix0_3 = vec_ld(0x30, (vector float*)&(blend_mat.mMatrix));
}
else
{
last_weight = w;
S32 joint = llfloor(w);
w -= joint;
LLMatrix4 &m0 = gJointMat[joint+1];
LLMatrix4 &m1 = gJointMat[joint+0];
// load Matrix 0 into vector registers
matrix0_0 = vec_ld(0x00, (vector float*)&(m0.mMatrix));
matrix0_1 = vec_ld(0x10, (vector float*)&(m0.mMatrix));
matrix0_2 = vec_ld(0x20, (vector float*)&(m0.mMatrix));
matrix0_3 = vec_ld(0x30, (vector float*)&(m0.mMatrix));
// if w == 1.0f, we don't need to blend.
// but since we do the trick of blending the matrices, here, if w != 1.0,
// we load Matrix 1 into the other 4 SSE registers and blend both matrices
// based on the weight (which we load ingo a 16-byte aligned vector: w,w,w,w)
if (w != 1.0f)
{
vector float matrix1_0, matrix1_1, matrix1_2, matrix1_3;
// This loads the weight somewhere in the vector register
vector float weight = vec_lde(0, &(w));
// and this splats it to all elements.
weight = vec_splat(vec_perm(weight, weight, vec_lvsl(0, &(w))), 0);
// do blending of matrices instead of verts and normals -- faster
matrix1_0 = vec_ld(0x00, (vector float*)&(m1.mMatrix));
matrix1_1 = vec_ld(0x10, (vector float*)&(m1.mMatrix));
matrix1_2 = vec_ld(0x20, (vector float*)&(m1.mMatrix));
matrix1_3 = vec_ld(0x30, (vector float*)&(m1.mMatrix));
// m0[col] = ((m0[col] - m1[col]) * weight) + m1[col];
matrix0_0 = vec_madd(vec_sub(matrix0_0, matrix1_0), weight, matrix1_0);
matrix0_1 = vec_madd(vec_sub(matrix0_1, matrix1_1), weight, matrix1_1);
matrix0_2 = vec_madd(vec_sub(matrix0_2, matrix1_2), weight, matrix1_2);
matrix0_3 = vec_madd(vec_sub(matrix0_3, matrix1_3), weight, matrix1_3);
}
// save off blended matrix
// vec_st(matrix0_0, 0x00, (vector float*)&(blend_mat.mMatrix));
// vec_st(matrix0_1, 0x10, (vector float*)&(blend_mat.mMatrix));
// vec_st(matrix0_2, 0x20, (vector float*)&(blend_mat.mMatrix));
// vec_st(matrix0_3, 0x30, (vector float*)&(blend_mat.mMatrix));
}
// now, we have either a blended matrix in matrix0_0-3 or the original Matrix 0
// we then multiply each vertex and normal by this one matrix.
// For SSE2, we would try to keep the original two matrices in other registers
// and avoid reloading them. However, they should ramain in L1 cache in the
// current case.
// One possible optimization would be to sort the vertices by weight instead
// of just index (we still want to uniqify). If we note when two or more vertices
// share the same weight, we can avoid doing the middle SSE code above and just
// re-use the blended matrix for those vertices
// now, we do the actual vertex blending
vector float in0 = vec_ld(AVATAR_OFFSET_POS, (vector float*)vin);
vector float in1 = vec_ld(AVATAR_OFFSET_NORMAL, (vector float*)vin);
// Matrix multiply vertex
vector float out0 = vec_madd
(
vec_splat(in0, 0),
matrix0_0,
vec_madd
(
vec_splat(in0, 1),
matrix0_1,
vec_madd
(
vec_splat(in0, 2),
matrix0_2,
matrix0_3
)
)
);
// Matrix multiply normal
vector float out1 = vec_madd
(
vec_splat(in1, 0),
matrix0_0,
vec_madd
(
vec_splat(in1, 1),
matrix0_1,
vec_madd
(
vec_splat(in1, 2),
matrix0_2,
// no translation for normals
(vector float)vec_splat_u32(0)
)
)
);
// indexed store
vec_stl(vec_perm(in0, out0, out0perm), AVATAR_OFFSET_POS, (vector float*)vout); // Pos
vec_stl(vec_perm(in1, out1, out1perm), AVATAR_OFFSET_NORMAL, (vector float*)vout); // Norm
*(LLVector2*)(vout + (AVATAR_OFFSET_TEX0/sizeof(F32))) = texcoords[index]; // write texcoord into appropriate spot.
}
}
#endif
void llDrawElementsBatchBlend(const U32 vert_offset, const U32 vert_count, LLFace *face, const S32 index_count, const U32 *indices)
{
U8* gAGPVertices = gPipeline.bufferGetScratchMemory();
if (gAGPVertices)
{
LLStrider<LLVector3> vertices;
LLStrider<LLVector3> normals;
LLStrider<LLVector2> tcoords0;
LLStrider<F32> weights;
LLStrider<LLVector3> o_vertices;
LLStrider<LLVector3> o_normals;
LLStrider<LLVector2> o_texcoords0;
LLStrider<LLVector3> binormals;
LLStrider<LLVector2> o_texcoords1;
// get the source vertices from the draw pool. We index these ourselves, as there was
// no guarantee the indices for a single jointmesh were contigious
LLDrawPool *pool = face->getPool();
pool->getVertexStrider (vertices, 0);
pool->getTexCoordStrider (tcoords0, 0, 0);
pool->getNormalStrider (normals, 0);
pool->getBinormalStrider (binormals, 0);
pool->getVertexWeightStrider(weights, 0);
// load the addresses of the output striders
o_vertices = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_POS); o_vertices.setStride( AVATAR_VERTEX_BYTES);
o_normals = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_NORMAL); o_normals.setStride( AVATAR_VERTEX_BYTES);
o_texcoords0= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX0); o_texcoords0.setStride(AVATAR_VERTEX_BYTES);
o_texcoords1= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX1); o_texcoords1.setStride(AVATAR_VERTEX_BYTES);
#if !LL_LINUX // !!! *TODO: do the linux implementation
if (gGLManager.mSoftwareBlendSSE)
{
// do SSE blend without binormals or extra texcoords
blend_SSE_32_32_batch(vert_offset, vert_count, (float*)gAGPVertices,
vertices, tcoords0, normals, weights);
}
else // fully backwards compatible software blending, no SSE
#endif
{
LLVector4 tpos0, tnorm0, tpos1, tnorm1, tbinorm0, tbinorm1;
F32 last_weight = F32_MAX;
LLMatrix3 gBlendRotMat;
{
for (U32 index=vert_offset; index < vert_offset + vert_count; index++)
{
// blend by first matrix
F32 w = weights [index];
if (w != last_weight)
{
last_weight = w;
S32 joint = llfloor(w);
w -= joint;
LLMatrix4 &m0 = gJointMat[joint+1];
LLMatrix4 &m1 = gJointMat[joint+0];
LLMatrix3 &n0 = gJointRot[joint+1];
LLMatrix3 &n1 = gJointRot[joint+0];
if (w == 1.0f)
{
gBlendMat = m0;
gBlendRotMat = n0;
}
else
{
gBlendMat.mMatrix[VX][VX] = lerp(m1.mMatrix[VX][VX], m0.mMatrix[VX][VX], w);
gBlendMat.mMatrix[VX][VY] = lerp(m1.mMatrix[VX][VY], m0.mMatrix[VX][VY], w);
gBlendMat.mMatrix[VX][VZ] = lerp(m1.mMatrix[VX][VZ], m0.mMatrix[VX][VZ], w);
gBlendMat.mMatrix[VY][VX] = lerp(m1.mMatrix[VY][VX], m0.mMatrix[VY][VX], w);
gBlendMat.mMatrix[VY][VY] = lerp(m1.mMatrix[VY][VY], m0.mMatrix[VY][VY], w);
gBlendMat.mMatrix[VY][VZ] = lerp(m1.mMatrix[VY][VZ], m0.mMatrix[VY][VZ], w);
gBlendMat.mMatrix[VZ][VX] = lerp(m1.mMatrix[VZ][VX], m0.mMatrix[VZ][VX], w);
gBlendMat.mMatrix[VZ][VY] = lerp(m1.mMatrix[VZ][VY], m0.mMatrix[VZ][VY], w);
gBlendMat.mMatrix[VZ][VZ] = lerp(m1.mMatrix[VZ][VZ], m0.mMatrix[VZ][VZ], w);
gBlendMat.mMatrix[VW][VX] = lerp(m1.mMatrix[VW][VX], m0.mMatrix[VW][VX], w);
gBlendMat.mMatrix[VW][VY] = lerp(m1.mMatrix[VW][VY], m0.mMatrix[VW][VY], w);
gBlendMat.mMatrix[VW][VZ] = lerp(m1.mMatrix[VW][VZ], m0.mMatrix[VW][VZ], w);
gBlendRotMat.mMatrix[VX][VX] = lerp(n1.mMatrix[VX][VX], n0.mMatrix[VX][VX], w);
gBlendRotMat.mMatrix[VX][VY] = lerp(n1.mMatrix[VX][VY], n0.mMatrix[VX][VY], w);
gBlendRotMat.mMatrix[VX][VZ] = lerp(n1.mMatrix[VX][VZ], n0.mMatrix[VX][VZ], w);
gBlendRotMat.mMatrix[VY][VX] = lerp(n1.mMatrix[VY][VX], n0.mMatrix[VY][VX], w);
gBlendRotMat.mMatrix[VY][VY] = lerp(n1.mMatrix[VY][VY], n0.mMatrix[VY][VY], w);
gBlendRotMat.mMatrix[VY][VZ] = lerp(n1.mMatrix[VY][VZ], n0.mMatrix[VY][VZ], w);
gBlendRotMat.mMatrix[VZ][VX] = lerp(n1.mMatrix[VZ][VX], n0.mMatrix[VZ][VX], w);
gBlendRotMat.mMatrix[VZ][VY] = lerp(n1.mMatrix[VZ][VY], n0.mMatrix[VZ][VY], w);
gBlendRotMat.mMatrix[VZ][VZ] = lerp(n1.mMatrix[VZ][VZ], n0.mMatrix[VZ][VZ], w);
}
}
// write result
o_vertices [index] = vertices[index] * gBlendMat;
o_normals [index] = normals [index] * gBlendRotMat;
o_texcoords0[index] = tcoords0[index];
/*
// Verification code. Leave this here. It's useful for keeping the SSE and non-SSE versions in sync.
LLVector3 temp;
temp = tpos0;
if( (o_vertices[index] - temp).magVecSquared() > 0.001f )
{
llerrs << "V SSE: " << o_vertices[index] << " v. " << temp << llendl;
}
temp = tnorm0;
if( (o_normals[index] - temp).magVecSquared() > 0.001f )
{
llerrs << "N SSE: " << o_normals[index] << " v. " << temp << llendl;
}
if( (o_texcoords0[index] - tcoords0[index]).magVecSquared() > 0.001f )
{
llerrs << "T0 SSE: " << o_texcoords0[index] << " v. " << tcoords0[index] << llendl;
}
*/
}
}
}
#if LL_DARWIN
// *HACK* *CHOKE* *PUKE*
// No way does this belong here.
glFlushVertexArrayRangeAPPLE(AVATAR_VERTEX_BYTES * vert_count, gAGPVertices + (AVATAR_VERTEX_BYTES * vert_offset));
#endif
glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, indices); // draw it!
}
else
{
glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, indices);
}
}
//--------------------------------------------------------------------
// DrawElements
// works just like glDrawElements, except it assumes GL_TRIANGLES and GL_UNSIGNED_INT indices
// why? because the destination buffer may not be the AGP buffer and the eyes do not use blending
// separate the eyes into their own drawpools and this code goes away.
//--------------------------------------------------------------------
void llDrawElements(const S32 count, const U32 *indices, LLFace *face)
{
U8* gAGPVertices = gPipeline.bufferGetScratchMemory();
if (gAGPVertices)
{
#if LL_DARWIN
U32 minIndex = indices[0];
U32 maxIndex = indices[0];
#endif
{
LLStrider<LLVector3> vertices;
LLStrider<LLVector3> normals;
LLStrider<LLVector2> tcoords;
LLStrider<F32> weights;
LLStrider<LLVector3> o_vertices;
LLStrider<LLVector3> o_normals;
LLStrider<LLVector2> o_texcoords0;
LLDrawPool *pool = face->getPool();
pool->getVertexStrider (vertices,0);
pool->getNormalStrider (normals, 0);
pool->getTexCoordStrider (tcoords, 0);
o_vertices = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_POS); o_vertices.setStride( AVATAR_VERTEX_BYTES);
o_normals = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_NORMAL); o_normals.setStride( AVATAR_VERTEX_BYTES);
o_texcoords0= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX0); o_texcoords0.setStride(AVATAR_VERTEX_BYTES);
for (S32 i=0; i < count; i++)
{
U32 index = indices[i];
o_vertices [index] = vertices[index];
o_normals [index] = normals [index];
o_texcoords0[index] = tcoords [index];
#if LL_DARWIN
maxIndex = llmax(index, maxIndex);
minIndex = llmin(index, minIndex);
#endif
}
}
#if LL_DARWIN
// *HACK* *CHOKE* *PUKE*
// No way does this belong here.
glFlushVertexArrayRangeAPPLE(AVATAR_VERTEX_BYTES * (maxIndex + 1 - minIndex), gAGPVertices + (AVATAR_VERTEX_BYTES * minIndex));
#endif
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indices);
}
else
{
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indices);
}
}
//--------------------------------------------------------------------
// LLViewerJointMesh::drawShape()
//--------------------------------------------------------------------
U32 LLViewerJointMesh::drawShape( F32 pixelArea )
{
if (!mValid || !mVisible) return 0;
U32 triangle_count = 0;
//----------------------------------------------------------------
// if no mesh bail out now
//----------------------------------------------------------------
if ( !mMesh || !mFace)
{
return 0;
}
//----------------------------------------------------------------
// if we have no faces, bail out now
//----------------------------------------------------------------
if ( mMesh->getNumFaces() == 0 )
{
return 0;
}
stop_glerror();
//----------------------------------------------------------------
// setup current color
//----------------------------------------------------------------
if (gRenderForSelect)
{
S32 name = mFace->getDrawable() ? mFace->getDrawable()->getVObj()->mGLName : 0;
LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name, 0xff);
LLColor4 color_float(color);
glColor4f(color_float.mV[0], color_float.mV[1], color_float.mV[2], 1.f);
}
else
{
if ((mFace->getPool()->getVertexShaderLevel() > 0))
{
glColor4f(0,0,0,1);
if (gPipeline.mMaterialIndex > 0)
{
glVertexAttrib4fvARB(gPipeline.mMaterialIndex, mColor.mV);
}
if (mShiny && gPipeline.mSpecularIndex > 0)
{
glVertexAttrib4fARB(gPipeline.mSpecularIndex, 1,1,1,1);
}
}
else
{
glColor4fv(mColor.mV);
}
}
stop_glerror();
// LLGLSSpecular specular(mSpecular, gRenderForSelect ? 0.0f : mShiny);
LLGLSSpecular specular(LLColor4(1.f,1.f,1.f,1.f), gRenderForSelect ? 0.0f : mShiny && !(mFace->getPool()->getVertexShaderLevel() > 0));
LLGLEnable texture_2d((gRenderForSelect && isTransparent()) ? GL_TEXTURE_2D : 0);
//----------------------------------------------------------------
// setup current texture
//----------------------------------------------------------------
llassert( !(mTexture.notNull() && mLayerSet) ); // mutually exclusive
//GLuint test_image_name = 0;
//
LLGLState force_alpha_test(GL_ALPHA_TEST, isTransparent());
if (mTestImageName)
{
LLImageGL::bindExternalTexture( mTestImageName, 0, GL_TEXTURE_2D );
if (mIsTransparent)
{
glColor4f(1.f, 1.f, 1.f, 1.f);
}
else
{
glColor4f(0.7f, 0.6f, 0.3f, 1.f);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_ONE_MINUS_SRC_ALPHA);
}
}
else if( mLayerSet )
{
if( mLayerSet->hasComposite() )
{
mLayerSet->getComposite()->bindTexture();
}
else
{
llwarns << "Layerset without composite" << llendl;
gImageList.getImage(IMG_DEFAULT)->bind();
}
}
else
if ( mTexture.notNull() )
{
mTexture->bind();
if (!mTexture->getClampS()) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
}
if (!mTexture->getClampT()) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
else
{
gImageList.getImage(IMG_DEFAULT_AVATAR)->bind();
}
if (gRenderForSelect)
{
if (isTransparent())
{
//gGLSObjectSelectDepthAlpha.set();
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); // GL_TEXTURE_ENV_COLOR is set in renderPass1
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
}
else
{
//gGLSObjectSelectDepth.set();
}
}
else
{
//----------------------------------------------------------------
// by default, backface culling is enabled
//----------------------------------------------------------------
if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_INNER)
{
//LLGLSPipelineAvatar gls_pipeline_avatar;
LLImageGL::bindExternalTexture( sClothingMaskImageName, 1, GL_TEXTURE_2D );
glClientActiveTextureARB(GL_TEXTURE0_ARB);
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D); // Texture unit 1
glActiveTextureARB(GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, sClothingInnerColor.mV);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_CONSTANT_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);
}
else if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_OUTER)
{
//gGLSPipelineAvatarAlphaPass1.set();
glAlphaFunc(GL_GREATER, 0.1f);
LLImageGL::bindExternalTexture( sClothingMaskImageName, 1, GL_TEXTURE_2D );
glClientActiveTextureARB(GL_TEXTURE0_ARB);
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D); // Texture unit 1
glActiveTextureARB(GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
}
else if ( isTransparent())
{
//gGLSNoCullFaces.set();
}
else
{
//gGLSCullFaces.set();
}
}
if (mMesh->hasWeights())
{
uploadJointMatrices();
if ((mFace->getPool()->getVertexShaderLevel() > 0))
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glDrawElements(GL_TRIANGLES, mMesh->mFaceIndexCount, GL_UNSIGNED_INT, mMesh->getIndices());
glPopMatrix();
}
else
{
if (mFace->getGeomIndex() < 0)
{
llerrs << "Invalid geometry index in LLViewerJointMesh::drawShape() " << mFace->getGeomIndex() << llendl;
}
if ((S32)(mMesh->mFaceVertexOffset + mMesh->mFaceVertexCount) > mFace->getGeomCount())
{
((LLVOAvatar*)mFace->getDrawable()->getVObj())->mRoot.dump();
llerrs << "Rendering outside of vertex bounds with mesh " << mName << " at pixel area " << pixelArea << llendl;
}
llDrawElementsBatchBlend(mMesh->mFaceVertexOffset, mMesh->mFaceVertexCount,
mFace, mMesh->mFaceIndexCount, mMesh->getIndices());
}
}
else
{
glPushMatrix();
LLMatrix4 jointToWorld = getWorldMatrix();
jointToWorld *= gCamera->getModelview();
glLoadMatrixf((GLfloat*)jointToWorld.mMatrix);
if ((mFace->getPool()->getVertexShaderLevel() > 0))
{
glDrawElements(GL_TRIANGLES, mMesh->mFaceIndexCount, GL_UNSIGNED_INT, mMesh->getIndices());
}
else // this else clause handles non-weighted vertices. llDrawElements just copies and draws
{
llDrawElements(mMesh->mFaceIndexCount, mMesh->getIndices(), mFace);
}
glPopMatrix();
}
triangle_count += mMesh->mFaceIndexCount;
if (gRenderForSelect)
{
glColor4fv(mColor.mV);
}
if (mTestImageName)
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
if (sRenderPass != AVATAR_RENDER_PASS_SINGLE)
{
LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
// Return to the default texture.
LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glAlphaFunc(GL_GREATER, 0.01f);
}
if (mTexture.notNull()) {
if (!mTexture->getClampS()) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
}
if (!mTexture->getClampT()) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
}
return triangle_count;
}
//-----------------------------------------------------------------------------
// updateFaceSizes()
//-----------------------------------------------------------------------------
void LLViewerJointMesh::updateFaceSizes(U32 &num_vertices, F32 pixel_area)
{
// Do a pre-alloc pass to determine sizes of data.
if (mMesh && mValid)
{
mMesh->mFaceVertexOffset = num_vertices;
mMesh->mFaceVertexCount = mMesh->getNumVertices();
mMesh->getReferenceMesh()->mCurVertexCount = mMesh->mFaceVertexCount;
num_vertices += mMesh->getNumVertices();
mMesh->mFaceIndexCount = mMesh->getSharedData()->mNumTriangleIndices;
mMesh->getSharedData()->genIndices(mMesh->mFaceVertexOffset);
}
}
//-----------------------------------------------------------------------------
// updateFaceData()
//-----------------------------------------------------------------------------
void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind)
{
U32 i;
if (!mValid) return;
mFace = face;
LLStrider<LLVector3> verticesp;
LLStrider<LLVector3> normalsp;
LLStrider<LLVector3> binormalsp;
LLStrider<LLVector2> tex_coordsp;
LLStrider<F32> vertex_weightsp;
LLStrider<LLVector4> clothing_weightsp;
// Copy data into the faces from the polymesh data.
if (mMesh)
{
if (mMesh->getNumVertices())
{
S32 index = face->getGeometryAvatar(verticesp, normalsp, binormalsp, tex_coordsp, vertex_weightsp, clothing_weightsp);
if (-1 == index)
{
return;
}
for (i = 0; i < mMesh->getNumVertices(); i++)
{
verticesp[mMesh->mFaceVertexOffset + i] = *(mMesh->getCoords() + i);
tex_coordsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getTexCoords() + i);
normalsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getNormals() + i);
binormalsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getBinormals() + i);
vertex_weightsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getWeights() + i);
if (damp_wind)
{
clothing_weightsp[mMesh->mFaceVertexOffset + i].setVec(0,0,0,0);
}
else
{
clothing_weightsp[mMesh->mFaceVertexOffset + i].setVec(*(mMesh->getClothingWeights() + i));
}
}
}
}
}
//-----------------------------------------------------------------------------
// updateLOD()
//-----------------------------------------------------------------------------
BOOL LLViewerJointMesh::updateLOD(F32 pixel_area, BOOL activate)
{
BOOL valid = mValid;
setValid(activate, TRUE);
return (valid != activate);
}
void LLViewerJointMesh::dump()
{
if (mValid)
{
llinfos << "Usable LOD " << mName << llendl;
}
}
void LLViewerJointMesh::writeCAL3D(apr_file_t* fp, S32 material_num, LLCharacter* characterp)
{
apr_file_printf(fp, "\t<SUBMESH NUMVERTICES=\"%d\" NUMFACES=\"%d\" MATERIAL=\"%d\" NUMLODSTEPS=\"0\" NUMSPRINGS=\"0\" NUMTEXCOORDS=\"1\">\n", mMesh->getNumVertices(), mMesh->getNumFaces(), material_num);
const LLVector3* mesh_coords = mMesh->getCoords();
const LLVector3* mesh_normals = mMesh->getNormals();
const LLVector2* mesh_uvs = mMesh->getTexCoords();
const F32* mesh_weights = mMesh->getWeights();
LLVector3 mesh_offset;
LLVector3 scale(1.f, 1.f, 1.f);
S32 joint_a = -1;
S32 joint_b = -1;
S32 num_bound_joints = 0;
if(!mMesh->hasWeights())
{
num_bound_joints = 1;
LLJoint* cur_joint = this;
while(cur_joint)
{
if (cur_joint->mJointNum != -1 && joint_a == -1)
{
joint_a = cur_joint->mJointNum;
}
mesh_offset += cur_joint->getSkinOffset();
cur_joint = cur_joint->getParent();
}
}
for (S32 i = 0; i < (S32)mMesh->getNumVertices(); i++)
{
LLVector3 coord = mesh_coords[i];
if (mMesh->hasWeights())
{
// calculate joint to which this skinned vertex is bound
num_bound_joints = getBoundJointsByIndex(llfloor(mesh_weights[i]), joint_a, joint_b);
LLJoint* first_joint = characterp->getCharacterJoint(joint_a);
LLJoint* second_joint = characterp->getCharacterJoint(joint_b);
LLVector3 first_joint_offset;
LLJoint* cur_joint = first_joint;
while(cur_joint)
{
first_joint_offset += cur_joint->getSkinOffset();
cur_joint = cur_joint->getParent();
}
LLVector3 second_joint_offset;
cur_joint = second_joint;
while(cur_joint)
{
second_joint_offset += cur_joint->getSkinOffset();
cur_joint = cur_joint->getParent();
}
LLVector3 first_coord = coord - first_joint_offset;
first_coord.scaleVec(first_joint->getScale());
LLVector3 second_coord = coord - second_joint_offset;
if (second_joint)
{
second_coord.scaleVec(second_joint->getScale());
}
coord = lerp(first_joint_offset + first_coord, second_joint_offset + second_coord, fmodf(mesh_weights[i], 1.f));
}
// add offset to move rigid mesh to target location
coord += mesh_offset;
coord *= 100.f;
apr_file_printf(fp, " <VERTEX ID=\"%d\" NUMINFLUENCES=\"%d\">\n", i, num_bound_joints);
apr_file_printf(fp, " <POS>%.4f %.4f %.4f</POS>\n", coord.mV[VX], coord.mV[VY], coord.mV[VZ]);
apr_file_printf(fp, " <NORM>%.6f %.6f %.6f</NORM>\n", mesh_normals[i].mV[VX], mesh_normals[i].mV[VY], mesh_normals[i].mV[VZ]);
apr_file_printf(fp, " <TEXCOORD>%.6f %.6f</TEXCOORD>\n", mesh_uvs[i].mV[VX], 1.f - mesh_uvs[i].mV[VY]);
if (num_bound_joints >= 1)
{
apr_file_printf(fp, " <INFLUENCE ID=\"%d\">%.2f</INFLUENCE>\n", joint_a + 1, 1.f - fmod(mesh_weights[i], 1.f));
}
if (num_bound_joints == 2)
{
apr_file_printf(fp, " <INFLUENCE ID=\"%d\">%.2f</INFLUENCE>\n", joint_b + 1, fmod(mesh_weights[i], 1.f));
}
apr_file_printf(fp, " </VERTEX>\n");
}
LLPolyFace* mesh_faces = mMesh->getFaces();
for (S32 i = 0; i < mMesh->getNumFaces(); i++)
{
apr_file_printf(fp, " <FACE VERTEXID=\"%d %d %d\" />\n", mesh_faces[i][0], mesh_faces[i][1], mesh_faces[i][2]);
}
apr_file_printf(fp, " </SUBMESH>\n");
}
// End