void ObjLoader::generateTangents( const QVector<QVector3D>& points, const QVector<QVector3D>& normals, const QVector<unsigned int>& faces, const QVector<QVector2D>& texCoords, QVector<QVector4D>& tangents ) const { tangents.clear(); QVector<QVector3D> tan1Accum; QVector<QVector3D> tan2Accum; for ( int i = 0; i < points.size(); i++ ) { tan1Accum.append( QVector3D() ); tan2Accum.append( QVector3D() ); tangents.append( QVector4D() ); } // Compute the tangent vector for ( int i = 0; i < faces.size(); i += 3 ) { const QVector3D& p1 = points[ faces[i] ]; const QVector3D& p2 = points[ faces[i+1] ]; const QVector3D& p3 = points[ faces[i+2] ]; const QVector2D& tc1 = texCoords[ faces[i] ]; const QVector2D& tc2 = texCoords[ faces[i+1] ]; const QVector2D& tc3 = texCoords[ faces[i+2] ]; QVector3D q1 = p2 - p1; QVector3D q2 = p3 - p1; float s1 = tc2.x() - tc1.x(), s2 = tc3.x() - tc1.x(); float t1 = tc2.y() - tc1.y(), t2 = tc3.y() - tc1.y(); float r = 1.0f / ( s1 * t2 - s2 * t1 ); QVector3D tan1( ( t2 * q1.x() - t1 * q2.x() ) * r, ( t2 * q1.y() - t1 * q2.y() ) * r, ( t2 * q1.z() - t1 * q2.z() ) * r ); QVector3D tan2( ( s1 * q2.x() - s2 * q1.x() ) * r, ( s1 * q2.y() - s2 * q1.y() ) * r, ( s1 * q2.z() - s2 * q1.z() ) * r ); tan1Accum[ faces[i] ] += tan1; tan1Accum[ faces[i+1] ] += tan1; tan1Accum[ faces[i+2] ] += tan1; tan2Accum[ faces[i] ] += tan2; tan2Accum[ faces[i+1] ] += tan2; tan2Accum[ faces[i+2] ] += tan2; } for ( int i = 0; i < points.size(); ++i ) { const QVector3D& n = normals[i]; QVector3D& t1 = tan1Accum[i]; QVector3D& t2 = tan2Accum[i]; // Gram-Schmidt orthogonalize tangents[i] = QVector4D( QVector3D( t1 - QVector3D::dotProduct( n, t1 ) * n ).normalized(), 0.0f ); // Store handedness in w tangents[i].setW( ( QVector3D::dotProduct( QVector3D::crossProduct( n, t1 ), t2 ) < 0.0f ) ? -1.0f : 1.0f ); } }
void DrawVBOMesh::generateTangents( const std::vector< vrGLMVec3 > & points, const std::vector< vrGLMVec3 > & normals, const std::vector< vrInt > & faces, const std::vector< vrGLMVec2 > & texCoords, std::vector< vrGLMVec4 > & tangents) { std::vector< vrGLMVec3 > tan1Accum; std::vector< vrGLMVec3 > tan2Accum; for (vrUnsigned i = 0; i < points.size(); i++) { tan1Accum.push_back(vrGLMVec3(0.0)); tan2Accum.push_back(vrGLMVec3(0.0)); tangents.push_back(vrGLMVec4(0.0)); } // Compute the tangent vector for (vrUnsigned i = 0; i < faces.size(); i += 3) { const vrGLMVec3 &p1 = points[faces[i]]; const vrGLMVec3 &p2 = points[faces[i + 1]]; const vrGLMVec3 &p3 = points[faces[i + 2]]; const vrGLMVec2 &tc1 = texCoords[faces[i]]; const vrGLMVec2 &tc2 = texCoords[faces[i + 1]]; const vrGLMVec2 &tc3 = texCoords[faces[i + 2]]; vrGLMVec3 q1 = p2 - p1; vrGLMVec3 q2 = p3 - p1; vrFloat s1 = tc2.x - tc1.x, s2 = tc3.x - tc1.x; vrFloat t1 = tc2.y - tc1.y, t2 = tc3.y - tc1.y; vrFloat r = 1.0f / (s1 * t2 - s2 * t1); vrGLMVec3 tan1((t2*q1.x - t1*q2.x) * r, (t2*q1.y - t1*q2.y) * r, (t2*q1.z - t1*q2.z) * r); vrGLMVec3 tan2((s1*q2.x - s2*q1.x) * r, (s1*q2.y - s2*q1.y) * r, (s1*q2.z - s2*q1.z) * r); tan1Accum[faces[i]] += tan1; tan1Accum[faces[i + 1]] += tan1; tan1Accum[faces[i + 2]] += tan1; tan2Accum[faces[i]] += tan2; tan2Accum[faces[i + 1]] += tan2; tan2Accum[faces[i + 2]] += tan2; } for (vrUnsigned i = 0; i < points.size(); ++i) { const vrGLMVec3 &n = normals[i]; vrGLMVec3 &t1 = tan1Accum[i]; vrGLMVec3 &t2 = tan2Accum[i]; // Gram-Schmidt orthogonalize tangents[i] = vrGLMVec4(glm::normalize(t1 - (glm::dot(n, t1) * n)), 0.0); // Store handedness in w tangents[i].w = (glm::dot(glm::cross(n, t1), t2) < 0.0) ? -1.0 : 1.0; } tan1Accum.clear(); tan2Accum.clear(); }
//Deprecated does not work properly void ModelLoader::CalculateTangents(VertexPosNormTanTex& vpntx1, VertexPosNormTanTex& vpntx2, VertexPosNormTanTex& vpntx3) { Vector3& v1 = vpntx1.position; Vector3& v2 = vpntx2.position; Vector3& v3 = vpntx2.position; Vector3& n1 = vpntx1.normal; //Vector3& n2 = vpntx2.normal; //Vector3& n3 = vpntx3.normal; Vector2& tx1 = vpntx1.tex; Vector2& tx2 = vpntx2.tex; Vector2& tx3 = vpntx3.tex; Vector3 t1; Vector3 t2; Vector3 t3; float x1 = v2.X - v1.X; float x2 = v3.X - v1.X; float y1 = v2.Y - v1.Y; float y2 = v3.Y - v1.Y; float z1 = v2.Z - v1.Z; float z2 = v3.Z - v1.Z; float s1 = tx2.X - tx1.X; float s2 = tx3.X - tx1.X; float st1 = tx2.Y - tx1.Y; float st2 = tx3.Y - tx1.Y; float r = 1.0f / (s1 * st2 - s2 * st1); Vector3 tan1((st2 * x1 - st1 * x2) * r, (st2 * y1 - st1 * y2) * r, (st2 * z1 - st1 * z2) * r ); Vector3 tan2((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r ); float n1tan1dot = n1.Dot(tan1); t1 = n1 * n1tan1dot; t1 = tan1 - t1; t1.Normalize(); t2 = n1 * n1tan1dot; t2 = tan1 - t1; t2.Normalize(); t3 = n1 * n1tan1dot; t3 = tan1 - t1; t3.Normalize(); vpntx1.tangent += t1; vpntx2.tangent += t2; vpntx3.tangent += t3; }
void flux(double* par_ax, int ix, int iy, int iz, double* B, int ax, double& ddeltat){ int alarm; double *par, rho1, u1, p1, ltan1, ltan2, rho2, u2, p2, rtan1, rtan2; par = new double[12]; //forward riemparam2(par, ix, iy, iz, ax); riemi(par, &alarm); if(alarm){ std::cerr<<"function 'riemi' sends an alarm at ("<<ix<<" "<<iy<<" "<<iz<<'\n';} tanvel(par[10], rtan1, rtan2, ax, ix, iy, iz); updatetimestep(ax, ix, iy, iz, par[9], par[11], ddeltat); rho2 = par[6]; u2 = par[7]; p2 = par[8]; //backward if(obstacle(ix, iy, iz, -ax) == 1){ //tangential velosity components conserve near an obstacle riemparam2(par, ix, iy, iz, -ax); riemi(par, &alarm); if(alarm){ std::cerr<<"function 'riemi' sends an alarm at ("<<ix<<" "<<iy<<" "<<iz<<'\n';} updatetimestep(-ax, ix, iy, iz, par[9], par[11], ddeltat); par_ax[0] = par[6]; par_ax[1] = par[7]; par_ax[2] = par[8]; par_ax[3] = p[tan1(ax)][ix][iy][iz]; par_ax[4] = p[tan2(ax)][ix][iy][iz]; } rho1 = par_ax[0]; u1 = par_ax[1]; p1 = par_ax[2]; ltan1 = par_ax[3]; ltan2 = par_ax[4]; //fluxes B[0] = u2*rho2 - rho1*u1; switch(ax){ case 'X': B[1] = rho2*u2*u2 + p2 - (rho1*u1*u1 + p1); B[2] = rho2*u2*rtan1 - (rho1*u1*ltan1); B[3] = rho2*u2*rtan2 - (rho1*u1*ltan2); break; case 'Y': B[1] = rho2*u2*rtan1 - (rho1*u1*ltan1); B[2] = rho2*u2*u2 + p2 - (rho1*u1*u1 + p1); B[3] = rho2*u2*rtan2 - (rho1*u1*ltan2); break; case 'Z': B[1] = rho2*u2*rtan1 - rho1*u1*ltan1; B[2] = rho2*u2*rtan2 - rho1*u1*ltan2; B[3] = rho2*u2*u2 + p2 - (rho1*u1*u1 + p1); break; } B[4] = u2*(rho2*(inenergy(rho2,p2) + 0.5*(u2*u2+rtan1*rtan1+rtan2*rtan2)) + p2) - u1*(rho1*(inenergy(rho1,p1) + 0.5*(u1*u1+ltan1*ltan1+ltan2*ltan2)) + p1); delete[] par; //parameters' update par_ax[0] = rho2; par_ax[1] = u2; par_ax[2] = p2; par_ax[3] = rtan1; par_ax[4] = rtan2; return; }
void ObjGPUData::loadObject(const char* fileName) { std::string folderName = fileName; for(int i = folderName.size() - 1; i >= 0; i--) { if(folderName[i] == '/' || folderName[i] == '\\') { folderName = folderName.substr(0, i+1); break; } if(i == 0) folderName = ""; } std::string objFileName = fileName; std::string mtlFileName = fileName; objFileName += ".obj"; mtlFileName += ".mtl"; std::ifstream mtlFile(mtlFileName.c_str(), std::ios::in); if(!mtlFile.is_open()) { printf("Failed to open object %s\n", objFileName.c_str()); exit(1); } std::string dataTypeString; float xVal, yVal, zVal; std::string valString; std::stringstream valStream; int iVal; while(mtlFile >> dataTypeString) { mtlDataType mtlDataTypeVal = getMtlDataType(dataTypeString); switch(mtlDataTypeVal) { case mtlDataType::mtlDataNEWMTL: mtlFile >> valString; materials.push_back(Material(valString)); valString.clear(); break; case mtlDataType::mtlDataNS: mtlFile >> xVal; materials.back().shine = xVal; break; case mtlDataType::mtlDataKA: mtlFile >> xVal; mtlFile >> yVal; mtlFile >> zVal; materials.back().Ka = glm::vec3(xVal, yVal, zVal); break; case mtlDataType::mtlDataKD: mtlFile >> xVal; mtlFile >> yVal; mtlFile >> zVal; materials.back().Kd = glm::vec3(xVal, yVal, zVal); break; case mtlDataType::mtlDataKS: mtlFile >> xVal; mtlFile >> yVal; mtlFile >> zVal; materials.back().Ks = glm::vec3(xVal, yVal, zVal); break; case mtlDataType::mtlDataMAP: { if(materials.back().textureSet) break; getline(mtlFile, valString); int fileNameLocation = valString.find_last_of("/\\"); if(fileNameLocation != -1) valString = valString.substr(fileNameLocation + 1); while(valString[0] == ' ' || valString[0] == '\t') valString = valString.substr(1); if(valString.size() < 4 || valString.substr(valString.size() - 4) != ".dds") { valString = valString.substr(0, valString.find_last_of('.') + 1); valString += "dds"; } materials.back().textureName = valString; bool textureExists = false; for(int i = 0; i < materials.size() - 1; i++) { if(materials[i].textureName == valString) { textureExists = true; materials.back().texture = materials[i].texture; break; } } if(textureExists) { valString.clear(); break; } materials.back().texture = loadImage((folderName + valString).c_str()); materials.back().textureSet = true; valString.clear(); break; } case mtlDataType::mtlDataBUMP: { if(materials.back().bumpSet) break; getline(mtlFile, valString); int fileNameLocation = valString.find_last_of("/\\"); if(fileNameLocation != -1) valString = valString.substr(fileNameLocation + 1); while(valString[0] == ' ' || valString[0] == '\t') valString = valString.substr(1); if(valString.size() < 4 || valString.substr(valString.size() - 4) != ".dds") { valString = valString.substr(0, valString.find_last_of('.') + 1); valString += "dds"; } materials.back().bumpName = valString; bool bumpExists = false; for(int i = 0; i < materials.size() - 1; i++) { if(materials[i].bumpName == valString) { bumpExists = true; materials.back().bump = materials[i].bump; break; } } if(bumpExists) { valString.clear(); break; } materials.back().bump = loadImage((folderName + valString).c_str()); materials.back().bumpSet = true; valString.clear(); break; } default: break; } } mtlFile.close(); printf("Loading object %s... ", objFileName.c_str()); std::ifstream objFile(objFileName.c_str(), std::ios::in); if(!objFile.is_open()) { printf("Failed to open object %s\n", objFileName.c_str()); exit(1); } std::vector<glm::vec3> vList_in; std::vector<glm::vec2> vTextureList_in; std::vector<glm::vec3> vNormalList_in; std::vector<GLuint> iVertex; std::vector<GLuint> iTexture; std::vector<GLuint> iNormal; std::vector<unsigned int> materialIndices_in; while(objFile >> dataTypeString) { dataType dataTypeVal = getDataType(dataTypeString); switch(dataTypeVal) { case dataType::dataV: objFile >> xVal; objFile >> yVal; objFile >> zVal; vList_in.push_back(glm::vec3(xVal, yVal, zVal)); break; case dataType::dataVT: objFile >> xVal; objFile >> yVal; vTextureList_in.push_back(glm::vec2(xVal, yVal)); break; case dataType::dataVN: objFile >> xVal; objFile >> yVal; objFile >> zVal; vNormalList_in.push_back(glm::vec3(xVal, yVal, zVal)); break; case dataType::dataUSEMTL: { int index = -1; objFile >> valString; for(int i = 0; i < materials.size(); i++) { if(materials[i].materialName == valString) { index = i; break; } } if(index == -1) exit(1); valString.clear(); materialIndices_in.push_back(iVertex.size()); materialIndices_in.push_back(index); break; } case dataType::dataF: { char lastChar; for(int i = 0; i < 3; i++) { getline(objFile, valString, '/'); valStream << valString; valString.clear(); valStream >> iVal; valStream.str(std::string()); valStream.clear(); iVertex.push_back(iVal); getline(objFile, valString, '/'); valStream << valString; valString.clear(); valStream >> iVal; valStream.str(std::string()); valStream.clear(); iTexture.push_back(iVal); objFile >> iVal; iNormal.push_back(iVal); lastChar = objFile.get(); } if(lastChar != '\n') { while(objFile.peek() == ' ' || objFile.peek() == '\t') { objFile.get(); } if(objFile.peek() == '\n') break; iVertex.push_back(iVertex[iVertex.size() - 3]); iTexture.push_back(iTexture[iTexture.size() - 3]); iNormal.push_back(iNormal[iNormal.size() - 3]); iVertex.push_back(iVertex[iVertex.size() - 2]); iTexture.push_back(iTexture[iTexture.size() - 2]); iNormal.push_back(iNormal[iNormal.size() - 2]); getline(objFile, valString, '/'); valStream << valString; valString.clear(); valStream >> iVal; valStream.str(std::string()); valStream.clear(); iVertex.push_back(iVal); getline(objFile, valString, '/'); valStream << valString; valString.clear(); valStream >> iVal; valStream.str(std::string()); valStream.clear(); iTexture.push_back(iVal); objFile >> iVal; iNormal.push_back(iVal); } break; } default: break; } } objFile.close(); int first, last; std::map<FullVertex,unsigned int> vertexToOutIndex; for(int k = 0; k < materialIndices_in.size()/2; ++k) { first = materialIndices_in[2*k]; if((2*k + 2) > (materialIndices_in.size() - 1)) last = iVertex.size(); else last = materialIndices_in[2*k + 2]; materialIndices.push_back(fList.size()); materialIndices.push_back(materialIndices_in[2*k + 1]); for(int i = first; i < last; i++) { FullVertex nextVertex = {vList_in[iVertex[i] - 1], vTextureList_in[iTexture[i] - 1], vNormalList_in[iNormal[i] - 1]}; std::map<FullVertex,unsigned int>::iterator vLocation = vertexToOutIndex.find(nextVertex); if(vLocation == vertexToOutIndex.end()) { vList.push_back(vList_in[iVertex[i]-1]); vTextureList.push_back(vTextureList_in[iTexture[i]-1]); vNormalList.push_back(vNormalList_in[iNormal[i]-1]); vertexToOutIndex[nextVertex] = vList.size() - 1; fList.push_back(vList.size() - 1); } else { fList.push_back(vLocation->second); } } } // Invert all texture v-coordinates for use with DXT compression textures for(int i = 0; i < vTextureList.size(); i++) { vTextureList[i][1] = 1 - vTextureList[i][1]; } // Generate tangent vectors for normals // Sourced from http://www.terathon.com/code/tangent.html std::vector<glm::vec3> tan1(vList.size()); std::vector<glm::vec3> tan2(vList.size()); for (unsigned int a = 0; a < fList.size(); a+=3) { GLuint i1 = fList[a + 0]; GLuint i2 = fList[a + 1]; GLuint i3 = fList[a + 2]; glm::vec3 v1 = vList[i1]; glm::vec3 v2 = vList[i2]; glm::vec3 v3 = vList[i3]; glm::vec2 w1 = vTextureList[i1]; glm::vec2 w2 = vTextureList[i2]; glm::vec2 w3 = vTextureList[i3]; float x1 = v2.x - v1.x; float x2 = v3.x - v1.x; float y1 = v2.y - v1.y; float y2 = v3.y - v1.y; float z1 = v2.z - v1.z; float z2 = v3.z - v1.z; float s1 = w2.x - w1.x; float s2 = w3.x - w1.x; float t1 = w2.y - w1.y; float t2 = w3.y - w1.y; float r = 1.0f / (s1 * t2 - s2 * t1); glm::vec3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); glm::vec3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } for (unsigned int a = 0; a < vList.size(); a++) { glm::vec3 n = vNormalList[a]; glm::vec3 t = tan1[a]; // Gram-Schmidt orthogonalize glm::vec4 tangent = glm::vec4(glm::normalize(t - n * glm::dot(n, t)), 0.0f); // Calculate handedness tangent.w = (glm::dot(glm::cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f; vTangentList.push_back(tangent); } printf("DONE\n"); return; }
void BufferGeometry::computeTangents() { // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if ( !attributes.contains( AttributeKey::index() ) || !attributes.contains( AttributeKey::position() ) || !attributes.contains( AttributeKey::normal() ) || !attributes.contains( AttributeKey::uv() ) ) { console().warn( "Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()" ); return; } const auto& indices = attributes[ AttributeKey::index() ].array; const auto& positions = attributes[ AttributeKey::position() ].array; const auto& normals = attributes[ AttributeKey::normal() ].array; const auto& uvs = attributes[ AttributeKey::uv() ].array; const auto nVertices = ( int )positions.size() / 3; if ( !attributes.contains( AttributeKey::tangent() ) ) { const auto nTangentElements = 4 * nVertices; attributes[ AttributeKey::tangent() ] = Attribute( THREE::v4, nTangentElements ); } auto& tangents = attributes[ AttributeKey::tangent() ].array; std::vector<Vector3> tan1( nVertices ), tan2( nVertices ); float xA, yA, zA, xB, yB, zB, xC, yC, zC, uA, vA, uB, vB, uC, vC, x1, x2, y1, y2, z1, z2, s1, s2, t1, t2, r; Vector3 sdir, tdir; auto handleTriangle = [&]( size_t a, size_t b, size_t c ) { xA = positions[ a * 3 ]; yA = positions[ a * 3 + 1 ]; zA = positions[ a * 3 + 2 ]; xB = positions[ b * 3 ]; yB = positions[ b * 3 + 1 ]; zB = positions[ b * 3 + 2 ]; xC = positions[ c * 3 ]; yC = positions[ c * 3 + 1 ]; zC = positions[ c * 3 + 2 ]; uA = uvs[ a * 2 ]; vA = uvs[ a * 2 + 1 ]; uB = uvs[ b * 2 ]; vB = uvs[ b * 2 + 1 ]; uC = uvs[ c * 2 ]; vC = uvs[ c * 2 + 1 ]; x1 = xB - xA; x2 = xC - xA; y1 = yB - yA; y2 = yC - yA; z1 = zB - zA; z2 = zC - zA; s1 = uB - uA; s2 = uC - uA; t1 = vB - vA; t2 = vC - vA; r = 1.0f / ( s1 * t2 - s2 * t1 ); sdir.set( ( t2 * x1 - t1 * x2 ) * r, ( t2 * y1 - t1 * y2 ) * r, ( t2 * z1 - t1 * z2 ) * r ); tdir.set( ( s1 * x2 - s2 * x1 ) * r, ( s1 * y2 - s2 * y1 ) * r, ( s1 * z2 - s2 * z1 ) * r ); tan1[ a ].add( sdir ); tan1[ b ].add( sdir ); tan1[ c ].add( sdir ); tan2[ a ].add( tdir ); tan2[ b ].add( tdir ); tan2[ c ].add( tdir ); }; for ( size_t j = 0, jl = offsets.size(); j < jl; ++ j ) { const auto start = offsets[ j ].start; const auto count = offsets[ j ].count; const auto index = offsets[ j ].index; for ( auto i = start, il = start + count; i < il; i += 3 ) { const auto iA = index + ( int )indices[ i ]; const auto iB = index + ( int )indices[ i + 1 ]; const auto iC = index + ( int )indices[ i + 2 ]; handleTriangle( iA, iB, iC ); } } Vector3 tmp, tmp2; Vector3 n, n2; auto handleVertex = [&]( size_t v ) { n.x = normals[ v * 3 ]; n.y = normals[ v * 3 + 1 ]; n.z = normals[ v * 3 + 2 ]; n2.copy( n ); const auto& t = tan1[ v ]; // Gram-Schmidt orthogonalize tmp.copy( t ); tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); // Calculate handedness tmp2.crossVectors( n2, t ); const auto test = tmp2.dot( tan2[ v ] ); const auto w = ( test < 0.0f ) ? -1.0f : 1.0f; tangents[ v * 4 ] = tmp.x; tangents[ v * 4 + 1 ] = tmp.y; tangents[ v * 4 + 2 ] = tmp.z; tangents[ v * 4 + 3 ] = w; }; for ( size_t j = 0, jl = offsets.size(); j < jl; ++ j ) { const auto start = offsets[ j ].start; const auto count = offsets[ j ].count; const auto index = offsets[ j ].index; for ( auto i = start, il = start + count; i < il; i += 3 ) { const auto iA = index + ( int )indices[ i ]; const auto iB = index + ( int )indices[ i + 1 ]; const auto iC = index + ( int )indices[ i + 2 ]; handleVertex( iA ); handleVertex( iB ); handleVertex( iC ); } } hasTangents = true; tangentsNeedUpdate = true; }
void calculateTangents( const void* pVertices, size_t numVertices, uint32 posOff, uint32 texOff, uint32 normOff, uint32 vertStride, const void* pIndices, size_t numIndices, gfx::IndexStride indexStride, he::PrimitiveList<vec3>& outTangents) { he::PrimitiveList<vec3> tan1(numVertices); tan1.resize(numVertices); const char* pCharVertices(static_cast<const char*>(pVertices)); const uint16* indicesUShort(nullptr); const uint32* indicesUInt(nullptr); if (indexStride == gfx::IndexStride_UShort) indicesUShort = static_cast<const uint16*>(pIndices); else if (indexStride == gfx::IndexStride_UInt) indicesUInt = static_cast<const uint32*>(pIndices); else LOG(LogType_ProgrammerAssert, "unkown index stride: %d", indexStride); for (uint32 i = 0; i < numIndices; i += 3) //per triangle { uint32 i1(0), i2(0), i3(0); if (indexStride == gfx::IndexStride_UShort) { i1 = indicesUShort[i]; i2 = indicesUShort[i + 1]; i3 = indicesUShort[i + 2]; } else if (indexStride == gfx::IndexStride_UInt) { i1 = indicesUInt[i]; i2 = indicesUInt[i + 1]; i3 = indicesUInt[i + 2]; } const vec3& v1 = *reinterpret_cast<const vec3*>(pCharVertices + (i1 * vertStride + posOff)); const vec3& v2 = *reinterpret_cast<const vec3*>(pCharVertices + (i2 * vertStride + posOff)); const vec3& v3 = *reinterpret_cast<const vec3*>(pCharVertices + (i3 * vertStride + posOff)); const vec2& tx1 = *reinterpret_cast<const vec2*>(pCharVertices + (i1 * vertStride + texOff)); const vec2& tx2 = *reinterpret_cast<const vec2*>(pCharVertices + (i2 * vertStride + texOff)); const vec2& tx3 = *reinterpret_cast<const vec2*>(pCharVertices + (i3 * vertStride + texOff)); float x1 = v2.x - v1.x; float x2 = v3.x - v1.x; float y1 = v2.y - v1.y; float y2 = v3.y - v1.y; float z1 = v2.z - v1.z; float z2 = v3.z - v1.z; float s1 = tx2.x - tx1.x; float s2 = tx3.x - tx1.x; float t1 = tx2.y - tx1.y; float t2 = tx3.y - tx1.y; float r = 1.0f / (s1 * t2 - s2 * t1); vec3 sdir( (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r ); vec3 tdir( (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r ); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; } for (uint32 i = 0; i < numVertices; ++i) { const vec3& n = *reinterpret_cast<const vec3*>(pCharVertices + (i * vertStride + normOff)); const vec3& t = tan1[i]; outTangents.add(normalize(t - n * dot(n, t))); } }