Matrix3 Matrix3::Adjoint () { /* Find the Adjoint * http://www.mathwords.com/a/adjoint.htm */ Matrix3 temp; // CoFactor(Mirror of minors) temp._data[0] = this->_data[4]*this->_data[8] - this->_data[5]*this->_data[7]; temp._data[1] = this->_data[3]*this->_data[8] - this->_data[5]*this->_data[6]; temp._data[2] = this->_data[3]*this->_data[7] - this->_data[4]*this->_data[6]; temp._data[3] = this->_data[1]*this->_data[8] - this->_data[2]*this->_data[7]; temp._data[4] = this->_data[0]*this->_data[8] - this->_data[2]*this->_data[6]; temp._data[5] = this->_data[0]*this->_data[7] - this->_data[1]*this->_data[6]; temp._data[6] = this->_data[1]*this->_data[5] - this->_data[2]*this->_data[4]; temp._data[7] = this->_data[0]*this->_data[5] - this->_data[2]*this->_data[3]; temp._data[8] = this->_data[0]*this->_data[4] - this->_data[1]*this->_data[3]; // Matrix of cofactors // + - + // - + - // + - + for(int i=1;i<9;i+=2) temp._data[i] *= -1; // Finally the adjoint of A is the transpose of the cofactor matrix temp = temp.Transpose(); return temp; }
Matrix4 Camera::view_matrix() { if (!view_needs_rebuild_) { return view_matrix_; } // http://stackoverflow.com/questions/12777675/view-matrix-from-quaternion // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-4-geometry/conventions-again-row-major-vs-column-major-vector/ // TODO Might need to revist this if this doesn't work out. // Figure out what the transpose does. view_matrix_ = Matrix4::IDENTITY; // This is most efficiently done using 3x3 Matrices Matrix3 rotation; orientation_.ToRotationMatrix(rotation); // Make the translation relative to new axes Matrix3 transposeRotation = rotation.Transpose(); Vector3 trans = -transposeRotation * position_; // Make final matrix view_matrix_ = transposeRotation; // fills upper 3x3 view_matrix_[0][3] = trans.x; view_matrix_[1][3] = trans.y; view_matrix_[2][3] = trans.z; view_needs_rebuild_ = false; return view_matrix_; }
//----------------------------------------------------------------------- void Frustum::updateViewImpl(void) const { // ---------------------- // Update the view matrix // ---------------------- // View matrix is: // // [ Lx Uy Dz Tx ] // [ Lx Uy Dz Ty ] // [ Lx Uy Dz Tz ] // [ 0 0 0 1 ] // // Where T = -(Transposed(Rot) * Pos) // This is most efficiently done using 3x3 Matrices // Get orientation from quaternion if (!mCustomViewMatrix) { Matrix3 rot; const Quaternion& orientation = getOrientationForViewUpdate(); const Vector3& position = getPositionForViewUpdate(); orientation.ToRotationMatrix(rot); // Make the translation relative to new axes Matrix3 rotT = rot.Transpose(); Vector3 trans = -rotT * position; // Make final matrix mViewMatrix = Matrix4::IDENTITY; mViewMatrix = rotT; // fills upper 3x3 mViewMatrix[0][3] = trans.x; mViewMatrix[1][3] = trans.y; mViewMatrix[2][3] = trans.z; // Deal with reflections if (mReflect) { mViewMatrix = mViewMatrix * mReflectMatrix; } } mRecalcView = false; // Signal to update frustum clipping planes mRecalcFrustumPlanes = true; // Signal to update world space corners mRecalcWorldSpaceCorners = true; // Signal to update frustum if oblique plane enabled, // since plane needs to be in view space if (mObliqueDepthProjection) { mRecalcFrustum = true; } }
void SpriteShader::SetParameter(const std::string ¶meterName, const Matrix3 &value) { if(pimpl->uniforms.find(parameterName) == pimpl->uniforms.end()) throw std::runtime_error("Couldn't find the named SpriteEffect parameter: " + parameterName); auto &uniformData = pimpl->uniforms[parameterName]; if(uniformData.type != GL_FLOAT_MAT3) throw std::runtime_error("Type mismatch for named SpriteEffect paramater: " + parameterName); glUniformMatrix3fv(uniformData.location, 1, GL_FALSE, value.Transpose()); }
Matrix3 RigidBody::GetInverseInertialTensor() const { Vector3 diagonal = GetLocalInverseInertialTensor(); Matrix3 localInverseTensor = Matrix3::ZERO; localInverseTensor[0][0] = diagonal.x; localInverseTensor[1][1] = diagonal.y; localInverseTensor[2][2] = diagonal.z; Matrix3 rotation; GetTransformation().extract3x3Matrix(rotation); return rotation.Transpose() * localInverseTensor * rotation; }
void GLSLProgram<real>::SetupCamera() { RenderState<real>* glState = RenderState<real>::Get(); Camera<real>* camera = glState->GetCamera(); Matrix4<float> projectionMatrix = camera->GetProjectionMatrix(); Matrix4<float> viewMatrix = camera->GetGlobalToLocal(); Matrix4<float> modelMatrix = glState->GetModelMatrix(); Matrix4<float> modelViewProjectionMatrix = projectionMatrix * viewMatrix * modelMatrix; Matrix3<float> normalMatrix = modelMatrix.SubMatrix3( 0, 0 ); normalMatrix.Inverse(); normalMatrix.Transpose(); // Model, view and projection matrices, camera position glUniformMatrix4fv( mModelMatrixLocation, 1, true, modelMatrix.GetArray() ); glUniformMatrix4fv( mViewMatrixLocation, 1, true, viewMatrix.GetArray() ); glUniformMatrix4fv( mProjectionMatrixLocation, 1, true, projectionMatrix.GetArray() ); glUniformMatrix4fv( mModelViewProjectionMatrixLocation, 1, true, modelViewProjectionMatrix.GetArray() ); glUniformMatrix3fv( mNormalMatrixLocation, 1, true, normalMatrix.GetArray() ); Vector3<float> cameraPosition = camera->GetGlobalPosition(); glUniform3fv( mCameraPositionLocation, 1, (float*)&cameraPosition ); }
//--------------------------------------------------------------------- Matrix4 Math::makeViewMatrix(const Vector3& position, const Quaternion& orientation, const Matrix4* reflectMatrix) { Matrix4 viewMatrix; // View matrix is: // // [ Lx Uy Dz Tx ] // [ Lx Uy Dz Ty ] // [ Lx Uy Dz Tz ] // [ 0 0 0 1 ] // // Where T = -(Transposed(Rot) * Pos) // This is most efficiently done using 3x3 Matrices Matrix3 rot; orientation.ToRotationMatrix(rot); // Make the translation relative to new axes Matrix3 rotT = rot.Transpose(); Vector3 trans = -rotT * position; // Make final matrix viewMatrix = Matrix4::IDENTITY; viewMatrix = rotT; // fills upper 3x3 viewMatrix[0][3] = trans.x; viewMatrix[1][3] = trans.y; viewMatrix[2][3] = trans.z; // Deal with reflections if (reflectMatrix) { viewMatrix = viewMatrix * (*reflectMatrix); } return viewMatrix; }
/* Take increments in strain and calculate new Particle: strains, rotation strain, stresses, strain energy, dvij are (gradient rates X time increment) to give deformation gradient change For Axisymmetry: x->R, y->Z, z->theta, np==AXISYMMEtRIC_MPM, otherwise dvzz=0 This material tracks pressure and stores deviatoric stress only in particle stress tensor */ void ClampedNeohookean::MPMConstitutiveLaw(MPMBase *mptr,Matrix3 du,double delTime,int np,void *properties, ResidualStrains *res,int historyOffset) const { // Update total deformation gradient, and calculate trial B Tensor Btrial; double detDF = IncrementDeformation(mptr,du,&Btrial,np); // global J double J = detDF * mptr->GetHistoryDble(J_History,historyOffset); mptr->SetHistoryDble(J_History,J,historyOffset); // convert Btrial to matrix to get eigenvalues and check for clamping Matrix3 Belas(Btrial.xx,Btrial.xy,Btrial.xz, Btrial.xy,Btrial.yy,Btrial.yz, Btrial.xz,Btrial.yz,Btrial.zz); if(np!=THREED_MPM) Belas.setIs2D(true); // get Eigenvalues and Eigenvectors Vector lam2 = Belas.Eigenvalues(); // clamp eigenvalues if needed bool clamped = false; if(lam2.x<lamMin2 || lam2.x>lamMax2 || lam2.y<lamMin2 || lam2.y>lamMax2 || lam2.z<lamMin2 || lam2.z>lamMax2) clamped = true; // Get Je and Jp, adjusting if clamped double Je,Jp; Matrix3 Ucol; if(clamped) { // Find Belas = U.LAM.UT Ucol = Belas.Eigenvectors(lam2); // clamp values now if(lam2.x<lamMin2) lam2.x = lamMin2; else if(lam2.x>lamMax2) lam2.x = lamMax2; if(lam2.y<lamMin2) lam2.y = lamMin2; else if(lam2.y>lamMax2) lam2.y = lamMax2; if(lam2.z<lamMin2) lam2.z = lamMin2; else if(lam2.z>lamMax2) lam2.z = lamMax2; Matrix3 UcolT = Ucol.Transpose(); Matrix3 Lam(lam2.x,0.,0.,lam2.y,lam2.z); Matrix3 LamUcolT = Lam*UcolT; Belas = Ucol*LamUcolT; // get Je and Jp Je = sqrt(lam2.x*lam2.y*lam2.z); Jp = J/Je; mptr->SetHistoryDble(JP_HISTORY,Jp,historyOffset); } else { Jp = mptr->GetHistoryDble(JP_HISTORY,historyOffset); Je = J/Jp; if(elasticModel==ELASTIC_DISNEY) Ucol = Belas.Eigenvectors(lam2); } // store B elastic Tensor *sp=mptr->GetStressTensor(); Tensor *B = mptr->GetAltStrainTensor(); B->xx = Belas(0,0); B->yy = Belas(1,1); B->zz = Belas(2,2); B->xy = Belas(0,1); if(np==THREED_MPM) { B->xz = Belas(0,2); B->yz = Belas(1,2); } // change mechanical properties by hardening double arg = exp(hardening*(1.-Jp)); double altGsp = pr.Gsp*arg; double altLamesp = pr.Lamesp*arg; // account for residual stresses double dJres = GetIncrementalResJ(mptr,res); double Jres = dJres*mptr->GetHistoryDble(J_History+1,historyOffset); mptr->SetHistoryDble(J_History+1,Jres,historyOffset); double resStretch = pow(Jres,1./3.); double Jres23 = resStretch*resStretch; // account for residual stresses relative to elastic J double Jeff = Je/Jres; // for incremental energy, store initial stress and pressure Tensor *sporig=mptr->GetStressTensor(); Tensor st0 = *sporig; double Pfinal,p0=mptr->GetPressure(); if(elasticModel==ELASTIC_DISNEY) { // Use model from Disney paper // Get Cauchy stress/rho0 double sig[3][3]; double lam[3]; lam[0]=sqrt(lam2.x)/resStretch; lam[1]=sqrt(lam2.y)/resStretch; lam[2]=sqrt(lam2.z)/resStretch; for(int i=0;i<3;i++) { for(int j=i;j<3;j++) { sig[i][j] = 0.; for(int k=0;k<3;k++) { sig[i][j] += (2.*altGsp*lam[k]*(lam[k]-1)/Jeff + altLamesp*(Jeff-1))*Ucol(i,k)*Ucol(j,k); } } } // update pressure (*J to get Kirchoff pressure) Pfinal = -J*(sig[0][0]+sig[1][1]+sig[2][2])/3.; mptr->SetPressure(Pfinal); // get and set deviatoric stress // find eviatoric (Kirchoff stress)/rho0 = deviatoric (Cauchy stress)J/rho0 sp->xx = J*sig[0][0]+Pfinal; sp->yy = J*sig[1][1]+Pfinal; sp->zz = J*sig[2][2]+Pfinal; sp->xy = J*sig[0][1]; if(np==THREED_MPM) { sp->xz = J*sig[0][2]; sp->yz = J*sig[1][2]; } } else { // Use standard neo-Hookean law // update pressure (*J to get Kirchoff pressure) Pfinal = -J*(GetVolumetricTerms(Jeff,altLamesp) + (altGsp/Jeff)*((B->xx+B->yy+B->zz)/(3.*Jres23) - 1.)); mptr->SetPressure(Pfinal); // Account for density change in specific stress // i.e.. Get (Kirchoff Stress)/rho0 double GJeff = J*resStretch*altGsp/Je; // = J*(Jres^(1/3) G/Je) to get Kirchoff // find deviatoric (Kirchoff stress)/rho0 = deviatoric (Cauchy stress)J/rho0 double I1third = (B->xx+B->yy+B->zz)/3.; sp->xx = GJeff*(B->xx-I1third); sp->yy = GJeff*(B->yy-I1third); sp->zz = GJeff*(B->zz-I1third); sp->xy = GJeff*B->xy; if(np==THREED_MPM) { sp->xz = GJeff*B->xz; sp->yz = GJeff*B->yz; } } // work and residual energies double delV = 1. - 1./detDF; // total volume change double avgP = 0.5*(p0+Pfinal); double dilEnergy = -avgP*delV; // incremental residual energy double delVres = 1. - 1./dJres; double resEnergy = -avgP*delVres; // incremental work energy = shear energy double shearEnergy = 0.5*((sp->xx+st0.xx)*du(0,0) + (sp->yy+st0.yy)*du(1,1) + (sp->zz+st0.zz)*du(2,2)+ (sp->xy+st0.xy)*(du(0,1)+du(1,0))); if(np==THREED_MPM) { shearEnergy += 0.5*((sp->xz+st0.xz)*(du(0,2)+du(2,0)) + (sp->yz+st0.yz)*(du(1,2)+du(2,1))); } // strain energy double dU = dilEnergy + shearEnergy; mptr->AddWorkEnergyAndResidualEnergy(dU,resEnergy); // thermodynamics heat and temperature // Should find energy dissipated by plasticity and add in third term IncrementHeatEnergy(mptr,res->dT,0.,0.); }
/* ============ Matrix3::InertiaRotate ============ */ Matrix3 Matrix3::InertiaRotate( const Matrix3 &rotation ) const { // NOTE: the rotation matrix is stored column-major return rotation.Transpose() * (*this) * rotation; }
void MeshRenderer::DoRender( BufferIndex bufferIndex, const Matrix& modelViewMatrix, const Matrix& modelMatrix, const Matrix& viewMatrix, const Matrix& projectionMatrix, const Vector4& color ) { MeshInfo& meshInfo = mMeshInfo[bufferIndex]; Mesh* mesh = meshInfo.mesh; RenderMaterial& material = *(meshInfo.material); BoneTransforms& boneTransforms = meshInfo.boneTransforms; if( ! meshInfo.boneTransforms.transforms.empty() ) { ApplyViewToBoneTransforms( meshInfo.boneTransforms, viewMatrix ); } const int stride = sizeof(Dali::MeshData::Vertex); Dali::MeshData::Vertex *v = 0; mesh->UploadVertexData( *mContext, bufferIndex ); mesh->BindBuffers( *mContext ); GeometryType geometryType = GEOMETRY_TYPE_TEXTURED_MESH; if( ! material.HasTexture() ) { geometryType = GEOMETRY_TYPE_MESH; } GLsizei numBoneMatrices = (GLsizei)mesh->GetMeshData(Mesh::RENDER_THREAD).GetBoneCount(); // Select program type ShaderSubTypes shaderType = SHADER_DEFAULT; if( mShader->AreSubtypesRequired( geometryType ) ) { if( numBoneMatrices ) { if( mesh->GetMeshData(Mesh::RENDER_THREAD).HasColor() ) { shaderType = SHADER_RIGGED_AND_VERTEX_COLOR; } else if( mAffectedByLighting ) { shaderType = SHADER_RIGGED_AND_LIT; } else { shaderType = SHADER_RIGGED_AND_EVENLY_LIT; } } else { if( mesh->GetMeshData(Mesh::RENDER_THREAD).HasColor() ) { shaderType = SHADER_VERTEX_COLOR; } else if( ! mAffectedByLighting ) { shaderType = SHADER_EVENLY_LIT; } // else default } } if( geometryType != mGeometryType || shaderType != mShaderType ) { mGeometryType = geometryType; mShaderType = shaderType; ResetCustomUniforms(); } Program& program = mShader->Apply( *mContext, bufferIndex, geometryType, modelMatrix, viewMatrix, modelViewMatrix, projectionMatrix, color, mShaderType ); GLint location = Program::UNIFORM_UNKNOWN; GLint positionLoc = program.GetAttribLocation(Program::ATTRIB_POSITION); GLint texCoordLoc = Program::ATTRIB_UNKNOWN; GLint boneWeightsLoc = Program::ATTRIB_UNKNOWN; GLint boneIndicesLoc = Program::ATTRIB_UNKNOWN; GLint normalLoc = Program::ATTRIB_UNKNOWN; GLint colorLoc = Program::ATTRIB_UNKNOWN; mContext->VertexAttribPointer( positionLoc, 3, GL_FLOAT, GL_FALSE, stride, &v->x ); mContext->EnableVertexAttributeArray( positionLoc ); if( numBoneMatrices > 0 ) { location = mCustomUniform[ shaderType ][ 0 ].GetUniformLocation( program, "uBoneCount" ); if( Program::UNIFORM_UNKNOWN != location ) { program.SetUniform1i(location, numBoneMatrices); } location = mCustomUniform[ shaderType ][ 1 ].GetUniformLocation( program, "uBoneMatrices" ); if( Program::UNIFORM_UNKNOWN != location ) { program.SetUniformMatrix4fv(location, numBoneMatrices, boneTransforms.viewTransforms[0].AsFloat()); } if( mesh->GetMeshData(Mesh::RENDER_THREAD).HasNormals() ) { location = mCustomUniform[ shaderType ][ 2 ].GetUniformLocation( program, "uBoneMatricesIT" ); if( Program::UNIFORM_UNKNOWN != location ) { program.SetUniformMatrix3fv( location, numBoneMatrices, boneTransforms.inverseTransforms[0].AsFloat() ); } } boneWeightsLoc = program.GetAttribLocation( Program::ATTRIB_BONE_WEIGHTS ); if( Program::ATTRIB_UNKNOWN != boneWeightsLoc ) { mContext->VertexAttribPointer( boneWeightsLoc, 4, GL_FLOAT, GL_FALSE, stride, &v->boneWeights[0] ); mContext->EnableVertexAttributeArray( boneWeightsLoc ); } boneIndicesLoc = program.GetAttribLocation( Program::ATTRIB_BONE_INDICES ); if( Program::ATTRIB_UNKNOWN != boneIndicesLoc ) { mContext->VertexAttribPointer( boneIndicesLoc, 4, GL_UNSIGNED_BYTE, GL_FALSE, stride, &v->boneIndices[0] ); mContext->EnableVertexAttributeArray( boneIndicesLoc ); } } if( material.HasTexture() ) { material.BindTextures( program ); } // Always use UV's - may be being used for another purpose by a custom shader! texCoordLoc = program.GetAttribLocation(Program::ATTRIB_TEXCOORD); if( Program::ATTRIB_UNKNOWN != texCoordLoc ) { mContext->VertexAttribPointer( texCoordLoc, 2, GL_FLOAT, GL_FALSE, stride, &v->u ); mContext->EnableVertexAttributeArray( texCoordLoc ); } if( mesh->GetMeshData(Mesh::RENDER_THREAD).HasNormals() ) { normalLoc = program.GetAttribLocation(Program::ATTRIB_NORMAL); if( Program::ATTRIB_UNKNOWN != normalLoc ) { mContext->VertexAttribPointer( normalLoc, 3, GL_FLOAT, GL_FALSE, stride, &v->nX ); mContext->EnableVertexAttributeArray( normalLoc ); } } else if( mesh->GetMeshData(Mesh::RENDER_THREAD).HasColor() ) // Normals & color are mutually exclusive { colorLoc = program.GetAttribLocation(Program::ATTRIB_COLOR); if( Program::ATTRIB_UNKNOWN != colorLoc) { mContext->VertexAttribPointer( colorLoc, 3, GL_FLOAT, GL_FALSE, stride, &v->vertexR ); mContext->EnableVertexAttributeArray( colorLoc ); } } material.SetUniforms( mRenderMaterialUniforms, program, shaderType ); if( mAffectedByLighting ) { // Set light parameters location = mCustomUniform[ shaderType ][ 3 ].GetUniformLocation( program, "uNumberOfLights" ); if( Program::UNIFORM_UNKNOWN != location ) { program.SetUniform1i( location, mLightController->GetNumberOfLights() ); } // Model View IT matrix required for vertex normal lighting calculation location = mCustomUniform[ shaderType ][ 4 ].GetUniformLocation( program, "uModelViewIT" ); if( Program::UNIFORM_UNKNOWN != location ) { Matrix3 modelViewInverseTranspose = modelViewMatrix; modelViewInverseTranspose.Invert(); modelViewInverseTranspose.Transpose(); program.SetUniformMatrix3fv( location, 1, modelViewInverseTranspose.AsFloat() ); } // only one active light supported at the moment (due to performance) //if( numberOfLights > 0 ) { Vector2 tempVector2; Vector3 tempVector3; Node& lightNode = mLightController->GetLight( 0 ); LightAttachment& light = dynamic_cast< LightAttachment& >( lightNode.GetAttachment() ); location = mCustomUniform[ shaderType ][ 5 ].GetUniformLocation( program, "uLight0.mType" ); if( Program::UNIFORM_UNKNOWN != location ) { program.SetUniform1i( location, (GLint)light.GetType() ); } location = mCustomUniform[ shaderType ][ 6 ].GetUniformLocation( program, "uLight0.mFallOff" ); if( Program::UNIFORM_UNKNOWN != location ) { tempVector2 = light.GetFallOff(); program.SetUniform2f( location, tempVector2.x, tempVector2.y ); } location = mCustomUniform[ shaderType ][ 7 ].GetUniformLocation( program, "uLight0.mSpotAngle" ); if( Program::UNIFORM_UNKNOWN != location ) { tempVector2 = light.GetSpotAngle(); program.SetUniform2f( location, tempVector2.x, tempVector2.y ); } location = mCustomUniform[ shaderType ][ 8 ].GetUniformLocation( program, "uLight0.mLightPos" ); if( Program::UNIFORM_UNKNOWN != location ) { // light position in eyespace Vector3 tempVector3( viewMatrix * Vector4(lightNode.GetWorldPosition(bufferIndex)) ); program.SetUniform3f( location, tempVector3.x, tempVector3.y, tempVector3.z ); } location = mCustomUniform[ shaderType ][ 9 ].GetUniformLocation( program, "uLight0.mLightDir" ); if( Program::UNIFORM_UNKNOWN != location ) { tempVector3 = light.GetDirection(); tempVector3.Normalize(); program.SetUniform3f( location, tempVector3.x, tempVector3.y, tempVector3.z ); } location = mCustomUniform[ shaderType ][ 10 ].GetUniformLocation( program, "uLight0.mAmbient" ); if( Program::UNIFORM_UNKNOWN != location ) { tempVector3 = light.GetAmbientColor(); program.SetUniform3f( location, tempVector3.r, tempVector3.g, tempVector3.b ); } location = mCustomUniform[ shaderType ][ 11 ].GetUniformLocation( program, "uLight0.mDiffuse" ); if( Program::UNIFORM_UNKNOWN != location ) { tempVector3 = light.GetDiffuseColor(); program.SetUniform3f( location, tempVector3.r, tempVector3.g, tempVector3.b ); } location = mCustomUniform[ shaderType ][ 12 ].GetUniformLocation( program, "uLight0.mSpecular" ); if( Program::UNIFORM_UNKNOWN != location ) { tempVector3 = light.GetSpecularColor(); program.SetUniform3f( location, tempVector3.r, tempVector3.g, tempVector3.b ); } } } Dali::MeshData::VertexGeometryType vertexGeometry = mesh->GetMeshData(Mesh::RENDER_THREAD).GetVertexGeometryType(); switch( vertexGeometry ) { case Dali::MeshData::TRIANGLES: mContext->DrawElements(GL_TRIANGLES, mesh->GetFaceIndexCount(Mesh::RENDER_THREAD), GL_UNSIGNED_SHORT, 0); DRAW_ELEMENT_RECORD(mesh->GetFaceIndexCount()); break; case Dali::MeshData::LINES: mContext->DrawElements(GL_LINES, mesh->GetFaceIndexCount(Mesh::RENDER_THREAD), GL_UNSIGNED_SHORT, 0); DRAW_ELEMENT_RECORD(mesh->GetFaceIndexCount()); break; case Dali::MeshData::POINTS: mContext->DrawArrays(GL_POINTS, 0, mesh->GetFaceIndexCount(Mesh::RENDER_THREAD) ); DRAW_ARRAY_RECORD(mesh->GetFaceIndexCount()); } if( normalLoc != Program::ATTRIB_UNKNOWN ) { mContext->DisableVertexAttributeArray( normalLoc ); } if( colorLoc != Program::ATTRIB_UNKNOWN ) { mContext->DisableVertexAttributeArray( colorLoc ); } mContext->DisableVertexAttributeArray( positionLoc ); if( texCoordLoc != Program::ATTRIB_UNKNOWN ) { mContext->DisableVertexAttributeArray( texCoordLoc ); } if( boneWeightsLoc != Program::ATTRIB_UNKNOWN ) { mContext->DisableVertexAttributeArray( boneWeightsLoc ); } if( boneIndicesLoc != Program::ATTRIB_UNKNOWN ) { mContext->DisableVertexAttributeArray( boneIndicesLoc ); } return; }