1095 lines
32 KiB
C++
1095 lines
32 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 "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"
|
|
#include "llglslshader.h"
|
|
|
|
#if !LL_DARWIN && !LL_LINUX
|
|
extern PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB;
|
|
extern PFNGLWEIGHTFVARBPROC glWeightfvARB;
|
|
extern PFNGLVERTEXBLENDARBPROC glVertexBlendARB;
|
|
#endif
|
|
extern BOOL gRenderForSelect;
|
|
|
|
static LLPointer<LLVertexBuffer> sRenderBuffer = NULL;
|
|
static const U32 sRenderMask = LLVertexBuffer::MAP_VERTEX |
|
|
LLVertexBuffer::MAP_NORMAL |
|
|
LLVertexBuffer::MAP_TEXCOORD;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
// 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 == ¤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_list_t::iterator iter = current_joint->mChildren.begin();
|
|
iter != current_joint->mChildren.end(); ++iter)
|
|
{
|
|
LLViewerJoint* child_joint = (LLViewerJoint*)(*iter);
|
|
setupJoint(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 *= LLDrawPoolAvatar::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(gAvatarMatrixParam, 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;
|
|
}
|
|
|
|
void llDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices)
|
|
{
|
|
if (end-start+1 > (U32) gGLManager.mGLMaxVertexRange ||
|
|
count > gGLManager.mGLMaxIndexRange)
|
|
{
|
|
glDrawElements(mode,count,type,indices);
|
|
}
|
|
else
|
|
{
|
|
glDrawRangeElements(mode,start,end,count,type,indices);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// LLViewerJointMesh::drawShape()
|
|
//--------------------------------------------------------------------
|
|
U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass)
|
|
{
|
|
if (!mValid || !mMesh || !mFace || !mVisible ||
|
|
mFace->mVertexBuffer.isNull() ||
|
|
mMesh->getNumFaces() == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
U32 triangle_count = 0;
|
|
|
|
stop_glerror();
|
|
|
|
//----------------------------------------------------------------
|
|
// setup current color
|
|
//----------------------------------------------------------------
|
|
if (!gRenderForSelect)
|
|
{
|
|
if ((mFace->getPool()->getVertexShaderLevel() > 0))
|
|
{
|
|
glColor4f(0,0,0,1);
|
|
|
|
if (gMaterialIndex > 0)
|
|
{
|
|
glVertexAttrib4fvARB(gMaterialIndex, mColor.mV);
|
|
}
|
|
|
|
if (mShiny && gSpecularIndex > 0)
|
|
{
|
|
glVertexAttrib4fARB(gSpecularIndex, 1,1,1,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
glColor4fv(mColor.mV);
|
|
}
|
|
}
|
|
|
|
stop_glerror();
|
|
|
|
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
|
|
|
|
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();
|
|
}
|
|
|
|
LLGLDisable tex(gRenderForSelect && !isTransparent() ? GL_TEXTURE_2D : 0);
|
|
|
|
if (gRenderForSelect)
|
|
{
|
|
if (isTransparent())
|
|
{
|
|
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
|
|
{
|
|
//----------------------------------------------------------------
|
|
// by default, backface culling is enabled
|
|
//----------------------------------------------------------------
|
|
/*if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_INNER)
|
|
{
|
|
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)
|
|
{
|
|
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);
|
|
}*/
|
|
}
|
|
|
|
mFace->mVertexBuffer->setBuffer(sRenderMask);
|
|
|
|
U32 start = mMesh->mFaceVertexOffset;
|
|
U32 end = start + mMesh->mFaceVertexCount - 1;
|
|
U32 count = mMesh->mFaceIndexCount;
|
|
U32* indicesp = ((U32*) mFace->mVertexBuffer->getIndicesPointer()) + mMesh->mFaceIndexOffset;
|
|
|
|
if (mMesh->hasWeights())
|
|
{
|
|
if ((mFace->getPool()->getVertexShaderLevel() > 0))
|
|
{
|
|
if (first_pass)
|
|
{
|
|
uploadJointMatrices();
|
|
}
|
|
llDrawRangeElements(GL_TRIANGLES, start, end, count, GL_UNSIGNED_INT, indicesp);
|
|
}
|
|
else
|
|
{
|
|
llDrawRangeElements(GL_TRIANGLES, start, end, count, GL_UNSIGNED_INT, indicesp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
glPushMatrix();
|
|
LLMatrix4 jointToWorld = getWorldMatrix();
|
|
glMultMatrixf((GLfloat*)jointToWorld.mMatrix);
|
|
llDrawRangeElements(GL_TRIANGLES, start, end, count, GL_UNSIGNED_INT, indicesp);
|
|
glPopMatrix();
|
|
}
|
|
|
|
triangle_count += mMesh->mFaceIndexCount;
|
|
|
|
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, U32& num_indices, 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->mFaceIndexOffset = num_indices;
|
|
mMesh->mFaceIndexCount = mMesh->getSharedData()->mNumTriangleIndices;
|
|
|
|
mMesh->getReferenceMesh()->mCurVertexCount = mMesh->mFaceVertexCount;
|
|
|
|
num_vertices += mMesh->getNumVertices();
|
|
num_indices += mMesh->mFaceIndexCount;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// updateFaceData()
|
|
//-----------------------------------------------------------------------------
|
|
void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind)
|
|
{
|
|
U32 i;
|
|
|
|
mFace = face;
|
|
|
|
LLStrider<LLVector3> verticesp;
|
|
LLStrider<LLVector3> normalsp;
|
|
LLStrider<LLVector3> binormalsp;
|
|
LLStrider<LLVector2> tex_coordsp;
|
|
LLStrider<F32> vertex_weightsp;
|
|
LLStrider<LLVector4> clothing_weightsp;
|
|
LLStrider<U32> indicesp;
|
|
|
|
// Copy data into the faces from the polymesh data.
|
|
if (mMesh && mValid)
|
|
{
|
|
if (mMesh->getNumVertices())
|
|
{
|
|
S32 index = face->getGeometryAvatar(verticesp, normalsp, binormalsp, tex_coordsp, vertex_weightsp, clothing_weightsp);
|
|
face->mVertexBuffer->getIndexStrider(indicesp);
|
|
|
|
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] = LLVector4(0,0,0,0);
|
|
}
|
|
else
|
|
{
|
|
clothing_weightsp[mMesh->mFaceVertexOffset + i] = (*(mMesh->getClothingWeights() + i));
|
|
}
|
|
}
|
|
|
|
for (S32 i = 0; i < mMesh->getNumFaces(); i++)
|
|
{
|
|
for (U32 j = 0; j < 3; j++)
|
|
{
|
|
U32 k = i*3+j+mMesh->mFaceIndexOffset;
|
|
indicesp[k] = mMesh->getFaces()[i][j] + mMesh->mFaceVertexOffset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// updateLOD()
|
|
//-----------------------------------------------------------------------------
|
|
BOOL LLViewerJointMesh::updateLOD(F32 pixel_area, BOOL activate)
|
|
{
|
|
BOOL valid = mValid;
|
|
setValid(activate, TRUE);
|
|
return (valid != activate);
|
|
}
|
|
|
|
|
|
void LLViewerJointMesh::updateGeometry()
|
|
{
|
|
if (!(mValid
|
|
&& mMesh
|
|
&& mFace
|
|
&& mMesh->hasWeights()
|
|
&& mFace->mVertexBuffer.notNull()
|
|
&& LLShaderMgr::getVertexShaderLevel(LLShaderMgr::SHADER_AVATAR) == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
uploadJointMatrices();
|
|
|
|
LLStrider<LLVector3> o_vertices;
|
|
LLStrider<LLVector3> o_normals;
|
|
|
|
//get vertex and normal striders
|
|
LLVertexBuffer *buffer = mFace->mVertexBuffer;
|
|
buffer->getVertexStrider(o_vertices, 0);
|
|
buffer->getNormalStrider(o_normals, 0);
|
|
|
|
F32 last_weight = F32_MAX;
|
|
LLMatrix4 gBlendMat;
|
|
LLMatrix3 gBlendRotMat;
|
|
|
|
const F32* weights = mMesh->getWeights();
|
|
const LLVector3* coords = mMesh->getCoords();
|
|
const LLVector3* normals = mMesh->getNormals();
|
|
for (U32 index = 0; index < mMesh->getNumVertices(); index++)
|
|
{
|
|
U32 bidx = index + mMesh->mFaceVertexOffset;
|
|
|
|
// blend by first matrix
|
|
F32 w = weights[index];
|
|
|
|
// Maybe we don't have to change gBlendMat.
|
|
// Profiles of a single-avatar scene on a Mac show this to be a very
|
|
// common case. JC
|
|
if (w == last_weight)
|
|
{
|
|
o_vertices[bidx] = coords[index] * gBlendMat;
|
|
o_normals[bidx] = normals[index] * gBlendRotMat;
|
|
continue;
|
|
}
|
|
|
|
last_weight = w;
|
|
|
|
S32 joint = llfloor(w);
|
|
w -= joint;
|
|
|
|
// No lerp required in this case.
|
|
if (w == 1.0f)
|
|
{
|
|
gBlendMat = gJointMat[joint+1];
|
|
o_vertices[bidx] = coords[index] * gBlendMat;
|
|
gBlendRotMat = gJointRot[joint+1];
|
|
o_normals[bidx] = normals[index] * gBlendRotMat;
|
|
continue;
|
|
}
|
|
|
|
// Try to keep all the accesses to the matrix data as close
|
|
// together as possible. This function is a hot spot on the
|
|
// Mac. JC
|
|
LLMatrix4 &m0 = gJointMat[joint+1];
|
|
LLMatrix4 &m1 = gJointMat[joint+0];
|
|
|
|
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);
|
|
|
|
o_vertices[bidx] = coords[index] * gBlendMat;
|
|
|
|
LLMatrix3 &n0 = gJointRot[joint+1];
|
|
LLMatrix3 &n1 = gJointRot[joint+0];
|
|
|
|
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);
|
|
|
|
o_normals[bidx] = normals[index] * gBlendRotMat;
|
|
}
|
|
}
|
|
|
|
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
|