/** * @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; sjmJointRenderData.count() && mMesh->mJointRenderData[mMesh->mJointRenderData.count() - 1]->mWorldMatrix == ¤t_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(¤t_joint->getParent()->getWorldMatrix(), NULL)); // llinfos << "joint " << joint_count << current_joint->getParent()->getName() << llendl; // joint_count++; mMesh->mJointRenderData.put(new LLJointRenderData(¤t_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 // 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& vertices, LLStrider& texcoords, LLStrider& normals, LLStrider& 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& vertices, LLStrider& texcoords, LLStrider& normals, LLStrider& 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& vertices, LLStrider& texcoords, LLStrider& normals, LLStrider& 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 vertices; LLStrider normals; LLStrider tcoords0; LLStrider weights; LLStrider o_vertices; LLStrider o_normals; LLStrider o_texcoords0; LLStrider binormals; LLStrider 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 vertices; LLStrider normals; LLStrider tcoords; LLStrider weights; LLStrider o_vertices; LLStrider o_normals; LLStrider 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 verticesp; LLStrider normalsp; LLStrider binormalsp; LLStrider tex_coordsp; LLStrider vertex_weightsp; LLStrider 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\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, " \n", i, num_bound_joints); apr_file_printf(fp, " %.4f %.4f %.4f\n", coord.mV[VX], coord.mV[VY], coord.mV[VZ]); apr_file_printf(fp, " %.6f %.6f %.6f\n", mesh_normals[i].mV[VX], mesh_normals[i].mV[VY], mesh_normals[i].mV[VZ]); apr_file_printf(fp, " %.6f %.6f\n", mesh_uvs[i].mV[VX], 1.f - mesh_uvs[i].mV[VY]); if (num_bound_joints >= 1) { apr_file_printf(fp, " %.2f\n", joint_a + 1, 1.f - fmod(mesh_weights[i], 1.f)); } if (num_bound_joints == 2) { apr_file_printf(fp, " %.2f\n", joint_b + 1, fmod(mesh_weights[i], 1.f)); } apr_file_printf(fp, " \n"); } LLPolyFace* mesh_faces = mMesh->getFaces(); for (S32 i = 0; i < mMesh->getNumFaces(); i++) { apr_file_printf(fp, " \n", mesh_faces[i][0], mesh_faces[i][1], mesh_faces[i][2]); } apr_file_printf(fp, " \n"); } // End