// not the general case. just the simple case of a square bool MyPolygon::calcTangents() { const Vec3& v1 = vtx[0]->p; const Vec3& v2 = vtx[1]->p; const Vec3& v3 = vtx[2]->p; const Vec2& w1 = texAncs[0]; const Vec2& w2 = texAncs[1]; const Vec2& w3 = texAncs[2]; 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); 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); tangent = sdir; bitangent = tdir; return true; }
void Mesh::updateTangents() { glm::vec3 * tan1 = new glm::vec3[verts.size()];// * 2]; //glm::vec3 * tan2 = tan1 + verts.size(); //ZeroMemory(tan1, vertexCount * sizeof(Vector3D) * 2); for (uint i = 0; i < indices.size()/3; i++) { long i1 = indices[0 + i*3]; long i2 = indices[1 + i*3]; long i3 = indices[2 + i*3]; Vert& v1 = verts[i1]; Vert& v2 = verts[i2]; Vert& v3 = verts[i3]; float x1 = v2.pos.x - v1.pos.x; float x2 = v3.pos.x - v1.pos.x; float y1 = v2.pos.y - v1.pos.y; float y2 = v3.pos.y - v1.pos.y; float z1 = v2.pos.z - v1.pos.z; float z2 = v3.pos.z - v1.pos.z; float s1 = v2.uv.x - v1.uv.x; float s2 = v3.uv.x - v1.uv.x; float t1 = v2.uv.y - v1.uv.y; float t2 = v3.uv.y - v1.uv.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 (uint i = 0; i < verts.size(); i++) { glm::vec3& n = verts[i].norm; glm::vec3& t = tan1[i]; // Gram-Schmidt orthogonalized verts[i].tan = glm::vec4(glm::normalize((t - n * glm::dot(n, t))),0); // Calculate handedness //verts[i].tan.w = (glm::dot(glm::cross(n, t), tan2[i]) < 0.0F) ? -1.0F : 1.0F; } delete[] tan1; }
void calcTangent(Face& face) { glm::vec3 v1({face.verts[0].pos[0], face.verts[0].pos[1], face.verts[0].pos[2]}); glm::vec3 v2({face.verts[1].pos[0], face.verts[1].pos[1], face.verts[1].pos[2]}); glm::vec3 v3({face.verts[2].pos[0], face.verts[2].pos[1], face.verts[2].pos[2]}); glm::vec2 w1({face.verts[0].uv[0], face.verts[0].uv[1]}); glm::vec2 w2({face.verts[1].uv[0], face.verts[1].uv[1]}); glm::vec2 w3({face.verts[2].uv[0], face.verts[2].uv[1]}); 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 }); for(Vertex& v: face.verts) { // Gram-Schmidt orthogonalize glm::vec3 t = sdir; glm::vec3 n = glm::vec3{ v.normal[0], v.normal[1], v.normal[2] }; const auto& bt = glm::normalize(glm::vec3{(t - n * glm::dot(n, t))}); v.tangent[0] = bt.x; v.tangent[1] = bt.y; v.tangent[2] = bt.z; } }
status_t ThemeManager::LoadThemes() { FENTRY; int dirwhich; BPath path; BDirectory dir; entry_ref ref; status_t err; for (dirwhich = 0; dirwhich < 2; dirwhich++) { if (!dirwhich) /* find system settings dir */ err = find_directory(B_BEOS_ETC_DIRECTORY, &path); else /* find user settings dir */ err = find_directory(B_USER_SETTINGS_DIRECTORY, &path); if (err) return err; err = dir.SetTo(path.Path()); if (err) return err; BEntry ent; if (dir.FindEntry(Z_THEMES_FOLDER_NAME, &ent) < B_OK) { dir.CreateDirectory(Z_THEMES_FOLDER_NAME, NULL); } path.Append(Z_THEMES_FOLDER_NAME); err = dir.SetTo(path.Path()); if (err) return err; err = dir.Rewind(); if (err) return err; while ((err = dir.GetNextRef(&ref)) == B_OK) { BPath themepath(&ref); BDirectory tdir(themepath.Path()); err = tdir.InitCheck(); if (err) /* not a dir */ continue; err = LoadTheme(themepath.Path()); } } return B_OK; }
void CalculateTangents(vector<VertexPosNormTanTex>& vecVPNTData, const vector<DWORD>& vecIndexData) { Vector3* tan1 = new Vector3[vecVPNTData.size()]; Vector3* tan2 = new Vector3[vecVPNTData.size()]; ZeroMemory(tan1, vecVPNTData.size() * 12); ZeroMemory(tan2, vecVPNTData.size() * 12); for (DWORD i = 0; i < vecIndexData.size(); i += 3) { int i1 = vecIndexData[i], i2 = vecIndexData[i+1], i3 = vecIndexData[i+2]; const Vector3& v1 = vecVPNTData[i1].position; const Vector3& v2 = vecVPNTData[i2].position; const Vector3& v3 = vecVPNTData[i3].position; const Vector2& tx1 = vecVPNTData[i1].tex; const Vector2& tx2 = vecVPNTData[i2].tex; const Vector2& tx3 = vecVPNTData[i3].tex; 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); Vector3 sdir( (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r ); Vector3 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(DWORD i = 0; i < vecVPNTData.size(); ++i) { const Vector3& n = vecVPNTData[i].normal; const Vector3& t = tan1[i]; vecVPNTData[i].tangent = (t - n * n.Dot(t)); vecVPNTData[i].tangent.Normalize(); if (vecVPNTData[i].tangent.Dot(vecVPNTData[i].normal) > 0.0001f) PANIC("Tangent not loodrecht op Normal"); } delete[] tan1; delete[] tan2; }
bool CalculateMeshTangents (LPD3DXBASEMESH pMesh, DWORD dwTextureCoordsIndex) { long vertexCount; long triangleCount; DWORD dwPosOffset = 0xFFFFFFFF, dwNormalOffset = 0xFFFFFFFF, dwTangentOffset = 0xFFFFFFFF; DWORD dwBinormalOffset = 0xFFFFFFFF, dwTexccordOffset = 0xFFFFFFFF; DWORD i, dwVertexStride; bool b16bitIndices; unsigned short *indicex16bit; unsigned long *indicex32bit; if (pMesh == NULL) return false; vertexCount = pMesh->GetNumVertices (); triangleCount = pMesh->GetNumFaces (); dwVertexStride = pMesh->GetNumBytesPerVertex(); b16bitIndices = !(pMesh->GetOptions() & D3DXMESH_32BIT); D3DVERTEXELEMENT9 meshDecl[MAX_FVF_DECL_SIZE] = {0}; pMesh->GetDeclaration (meshDecl); for (i=0; ;i++) { switch (meshDecl[i].Usage) { case D3DDECLUSAGE_POSITION: dwPosOffset = meshDecl[i].Offset; break; case D3DDECLUSAGE_NORMAL: dwNormalOffset = meshDecl[i].Offset; break; case D3DDECLUSAGE_TANGENT: dwTangentOffset = meshDecl[i].Offset; break; case D3DDECLUSAGE_BINORMAL: dwBinormalOffset = meshDecl[i].Offset; break; case D3DDECLUSAGE_TEXCOORD: if (meshDecl[i].UsageIndex == (BYTE)dwTextureCoordsIndex) dwTexccordOffset = meshDecl[i].Offset; } // posledny element if ((i == MAX_FVF_DECL_SIZE) || ((meshDecl[i].Stream == 0xFF) && (meshDecl[i].Type == D3DDECLTYPE_UNUSED))) break; } if ( (dwPosOffset==0xFFFFFFFF) || (dwNormalOffset==0xFFFFFFFF) || \ (dwTangentOffset==0xFFFFFFFF) || (dwBinormalOffset==0xFFFFFFFF) || \ (dwTexccordOffset==0xFFFFFFFF)) return false; ////////////////////////////////////////////////////////////////////////// LPVOID pVertexData; LPBYTE pVertexDataByte; if (FAILED (pMesh->LockVertexBuffer (0, &pVertexData))) return false; pVertexDataByte = (LPBYTE)pVertexData; LPVOID pIndexData; if (FAILED (pMesh->LockIndexBuffer (0, &pIndexData))) { pMesh->UnlockVertexBuffer(); return false; } indicex16bit = (unsigned short*)pIndexData; indicex32bit = (unsigned long*)pIndexData; P3DXVector3D *tan1 = new P3DXVector3D[vertexCount * 2]; P3DXVector3D *tan2 = tan1 + vertexCount; memset (tan1, 0, vertexCount * sizeof(P3DXVector3D) * 2); for (long a = 0; a < triangleCount; a++) { long i1, i2, i3; if (b16bitIndices) { i1 = indicex16bit[a*3]; i2 = indicex16bit[a*3 + 1]; i3 = indicex16bit[a*3 + 2]; } else { i1 = indicex32bit[a*3]; i2 = indicex32bit[a*3 + 1]; i3 = indicex32bit[a*3 + 2]; } const P3DXVector3D &v1 = (P3DXVector3D&) pVertexDataByte[i1*dwVertexStride + dwPosOffset]; const P3DXVector3D &v2 = (P3DXVector3D&) pVertexDataByte[i2*dwVertexStride + dwPosOffset]; const P3DXVector3D &v3 = (P3DXVector3D&) pVertexDataByte[i3*dwVertexStride + dwPosOffset]; const P3DPoint2D &w1 = (P3DPoint2D&) pVertexDataByte[i1*dwVertexStride + dwTexccordOffset]; const P3DPoint2D &w2 = (P3DPoint2D&) pVertexDataByte[i2*dwVertexStride + dwTexccordOffset]; const P3DPoint2D &w3 = (P3DPoint2D&) pVertexDataByte[i3*dwVertexStride + dwTexccordOffset]; 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); P3DXVector3D sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); P3DXVector3D 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 (long a = 0; a < vertexCount; a++) { const P3DXVector3D &n = (P3DXVector3D&) pVertexDataByte[a*dwVertexStride + dwNormalOffset]; const P3DXVector3D& t = tan1[a]; // Gram-Schmidt orthogonalize P3DXVector3D vecTangent = t - n * (n * t); vecTangent.Normalize (); // set tangent P3DXVector3D &tangent = (P3DXVector3D&) pVertexDataByte[a*dwVertexStride + dwTangentOffset]; tangent = vecTangent; // Calculate handedness float handedness = (((n % t) * tan2[a]) < 0.0F) ? -1.0F : 1.0F; // binormal = normal x tangent P3DXVector3D vecBinormal = (n % vecTangent) * handedness; vecBinormal.Normalize(); // set binormal P3DXVector3D &binormal = (P3DXVector3D&) pVertexDataByte[a*dwVertexStride + dwBinormalOffset]; binormal = vecBinormal; } delete[] tan1; pMesh->UnlockIndexBuffer(); pMesh->UnlockVertexBuffer(); return true; }
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 Model::computeTangentSpace() { for(int iMesh = 0; iMesh < m_NumMeshes; iMesh++) { if(m_pMesh.size() <= 0)break; Mesh *p_Mesh = &m_pMesh[iMesh]; t_TangentSpace *p_TmpTangentSpace = new t_TangentSpace[p_Mesh->m_NumFaces]; memset(p_TmpTangentSpace, 0, sizeof(t_TangentSpace) * p_Mesh->m_NumFaces); p_Mesh->m_pTangentSpace = new t_TangentSpace [p_Mesh->m_NumVerts]; for(int i = 0; i < p_Mesh->m_NumFaces; i++) { int i1 = p_Mesh->m_pFaces[i].vertIndex[0]; int i2 = p_Mesh->m_pFaces[i].vertIndex[1]; int i3 = p_Mesh->m_pFaces[i].vertIndex[2]; Vec3 p1 = p_Mesh->m_pVerts[i1]; Vec3 p2 = p_Mesh->m_pVerts[i2]; Vec3 p3 = p_Mesh->m_pVerts[i3]; Vec3 v1 = p2 - p1; Vec3 v2 = p3 - p1; p_TmpTangentSpace[i].normal = Cross(v1, v2); Vec2 w1 = p_Mesh->m_pTexVerts[i1]; Vec2 w2 = p_Mesh->m_pTexVerts[i2]; Vec2 w3 = p_Mesh->m_pTexVerts[i3]; 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); Vec3 sdir((t2 * v1.x - t1 * v2.x) * r, (t2 * v1.y - t1 * v2.y) * r, (t2 * v1.z - t1 * v2.z) * r); Vec3 tdir((s1 * v2.x - s2 * v1.x) * r, (s1 * v2.y - s2 * v1.y) * r, (s1 * v2.z - s2 * v1.z) * r); p_TmpTangentSpace[i].tangent += sdir; p_TmpTangentSpace[i].tangent.Normalize(); p_TmpTangentSpace[i].binormal += tdir; p_TmpTangentSpace[i].binormal = -Normalize(p_TmpTangentSpace[i].binormal); } Vec3 normalSum = Vec3(0.0, 0.0, 0.0); Vec3 binormalSum = Vec3(0.0, 0.0, 0.0); Vec3 tangentSum = Vec3(0.0, 0.0, 0.0); int shared = 0; int sharedn = 0; for (int i = 0; i < p_Mesh->m_NumVerts; i++) { DWORD smoothGroup; for (int j = 0; j < p_Mesh->m_NumFaces; j++) { if (p_Mesh->m_pFaces[j].vertIndex[0] == i || p_Mesh->m_pFaces[j].vertIndex[1] == i || p_Mesh->m_pFaces[j].vertIndex[2] == i) { smoothGroup = p_Mesh->m_pFaces[j].smoothGroup; normalSum += p_TmpTangentSpace[j].normal; binormalSum += p_TmpTangentSpace[j].binormal; tangentSum += p_TmpTangentSpace[j].tangent; shared++; sharedn++; } } Vec3 vertPos = p_Mesh->m_pVerts[i]; for (int j = 0; j < p_Mesh->m_NumFaces; j++) { t_Face *p_Face = &p_Mesh->m_pFaces[j]; for(int k = 0; k < 3; k++) { if (p_Face->vertIndex[k] != i && p_Mesh->m_pVerts[ p_Face->vertIndex[k] ] == vertPos && p_Face->smoothGroup == smoothGroup) { //smoothGroup = p_Mesh->m_pFaces[j].smoothGroup; normalSum += p_TmpTangentSpace[j].normal; binormalSum += p_TmpTangentSpace[j].binormal; tangentSum += p_TmpTangentSpace[j].tangent; sharedn++; } } } p_Mesh->m_pTangentSpace[i].normal = normalSum / (float)sharedn; p_Mesh->m_pTangentSpace[i].normal.Normalize(); p_Mesh->m_pTangentSpace[i].binormal = binormalSum / (float)sharedn; p_Mesh->m_pTangentSpace[i].binormal.Normalize(); p_Mesh->m_pTangentSpace[i].tangent = tangentSum / (float)sharedn; p_Mesh->m_pTangentSpace[i].tangent.Normalize(); normalSum = Vec3(0.0, 0.0, 0.0); binormalSum = Vec3(0.0, 0.0, 0.0); tangentSum = Vec3(0.0, 0.0, 0.0); shared = 0; } delete [] p_TmpTangentSpace; } }
static void generateTangentAndHandednessBuffer( osgCal::MeshData* m, const CalIndex* indexBuffer ) { if ( !m->texCoordBuffer.valid() ) { return; } int vertexCount = m->vertexBuffer->size(); int faceCount = m->getIndicesCount() / 3; m->tangentAndHandednessBuffer = new TangentAndHandednessBuffer( vertexCount ); CalVector* tan1 = new CalVector[vertexCount]; CalVector* tan2 = new CalVector[vertexCount]; const GLfloat* texCoordBufferData = (GLfloat*) m->texCoordBuffer->getDataPointer(); const GLfloat* vb = (GLfloat*) m->vertexBuffer->getDataPointer(); #ifdef OSG_CAL_BYTE_BUFFERS GLfloat* thb = new GLfloat[ vertexCount*4 ]; const GLfloat* nb = floatNormalBuffer; #else GLfloat* thb = (GLfloat*) m->tangentAndHandednessBuffer->getDataPointer(); // GLshort* thb = (GLshort*) m->tangentAndHandednessBuffer->getDataPointer(); const GLfloat* nb = (GLfloat*) m->normalBuffer->getDataPointer(); #endif for ( int face = 0; face < faceCount; face++ ) { for ( int j = 0; j < 3; j++ ) { // there seems to be no visual difference in calculating // tangent per vertex (as is tan1[i1] += spos(j=0,1,2)) // or per face (tan1[i1,i2,i3] += spos) CalIndex i1 = indexBuffer[face*3+(j+0)%3]; CalIndex i2 = indexBuffer[face*3+(j+1)%3]; CalIndex i3 = indexBuffer[face*3+(j+2)%3]; const float* v1 = &vb[i1*3]; const float* v2 = &vb[i2*3]; const float* v3 = &vb[i3*3]; const float* w1 = &texCoordBufferData[i1*2]; const float* w2 = &texCoordBufferData[i2*2]; const float* w3 = &texCoordBufferData[i3*2]; #define x(_a) (_a[0]) #define y(_a) (_a[1]) #define z(_a) (_a[2]) float x1 = x(v2) - x(v1); float x2 = x(v3) - x(v1); float y1 = y(v2) - y(v1); float y2 = y(v3) - y(v1); float z1 = z(v2) - z(v1); float z2 = z(v3) - z(v1); float s1 = x(w2) - x(w1); float s2 = x(w3) - x(w1); float t1 = y(w2) - y(w1); float t2 = y(w3) - y(w1); #undef x #undef y #undef z //float r = 1.0F / (s1 * t2 - s2 * t1); float r = (s1 * t2 - s2 * t1) < 0 ? -1.0 : 1.0; CalVector sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); CalVector tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); // sdir & tdir can be 0 (when UV unwrap doesn't exists // or has errors like coincide points) // we ignore them if ( sdir.length() > 0 ) { sdir.normalize(); tan1[i1] += sdir; //tan1[i2] += sdir; //tan1[i3] += sdir; } if ( tdir.length() > 0 ) { tdir.normalize(); tan2[i1] += tdir; //tan2[i2] += tdir; //tan2[i3] += tdir; } } } for (long a = 0; a < vertexCount; a++) { CalVector tangent; CalVector binormal; CalVector t = tan1[a]; CalVector b = tan2[a]; CalVector n = CalVector( nb[a*3+0], nb[a*3+1], nb[a*3+2] ); // tangent & bitangent can be zero when UV unwrap doesn't exists // or has errors like coincide points if ( t.length() > 0 ) { t.normalize(); // Gram-Schmidt orthogonalize tangent = t - n * (n*t); tangent.normalize(); // Calculate handedness binormal = CalVector(n % tangent) * ((((n % t) * b) < 0.0F) ? -1.0f : 1.0f); binormal.normalize(); } else if ( b.length() > 0 ) { b.normalize(); // Gram-Schmidt orthogonalize binormal = b - n * (n*b); binormal.normalize(); // Calculate handedness tangent = CalVector(n % binormal) * ((((n % b) * t) < 0.0F) ? -1.0f : 1.0f); tangent.normalize(); } // std::cout << "t = " << tangent.x << '\t' << tangent.y << '\t' << tangent.z << '\n'; // std::cout << "b = " << binormal.x << '\t' << binormal.y << '\t' << binormal.z << '\n'; // std::cout << "n = " << n.x << '\t' << n.y << '\t' << n.z << '\n'; // thb[a*4+0] = floatToHalf( tangent.x ); //tangent.x * 0x7FFF; // thb[a*4+1] = floatToHalf( tangent.y ); //tangent.y * 0x7FFF; // thb[a*4+2] = floatToHalf( tangent.z ); //tangent.z * 0x7FFF; // thb[a*4+3] = floatToHalf((((n % tangent) * binormal) > 0.0F) ? -1.0f : 1.0f); // handedness thb[a*4+0] = tangent.x; thb[a*4+1] = tangent.y; thb[a*4+2] = tangent.z; thb[a*4+3] = ((((n % tangent) * binormal) > 0.0F) ? -1.0f : 1.0f); // handedness } delete[] tan1; delete[] tan2; #ifdef OSG_CAL_BYTE_BUFFERS GLbyte* tangents = (GLbyte*) tangentBuffer->getDataPointer(); GLbyte* binormals = (GLbyte*) binormalBuffer->getDataPointer(); for ( int i = 0; i < vertexCount*3; i++ ) { tangents[i] = static_cast< GLbyte >( tangentBuffer[i]*127.0 ); binormals[i] = static_cast< GLbyte >( binormalBuffer[i]*127.0 ); //std::cout << (int)tangents[i] << '\n'; } delete[] tangentBuffer; delete[] binormalBuffer; #endif }
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))); } }
void CHLSL_Mesh::GenerateTangentSpace() { //for ( int t = 0; t < m_iNumTriangles; t++ ) //{ // CHLSL_Triangle &tri = m_Triangles[ t ]; // for ( int v = 0; v < 3; v++ ) // { // CHLSL_Vertex &vert = tri.vertices[ v ]; // } //} for (int a = 0; a < m_iNumVertices; a++) { CHLSL_Vertex &vert = m_Vertices[a]; Q_memset( vert.tangent_s, 0, sizeof( float ) * 4 ); Q_memset( vert.tangent_t, 0, sizeof( float ) * 3 ); } CHLSL_Triangle *tri = m_Triangles; for (int a = 0; a < m_iNumTriangles; a++) { CHLSL_Vertex &vert_1 = *tri->vertices[0]; CHLSL_Vertex &vert_2 = *tri->vertices[1]; CHLSL_Vertex &vert_3 = *tri->vertices[2]; Vector v1; Vector v2; Vector v3; VectorCopy( vert_1.pos, v1.Base() ); VectorCopy( vert_2.pos, v2.Base() ); VectorCopy( vert_3.pos, v3.Base() ); #if 0 Vector w1( vert_1.uv[0][0], vert_1.uv[0][1], 0 ); Vector w2( vert_2.uv[0][0], vert_2.uv[0][1], 0 ); Vector w3( vert_3.uv[0][0], vert_3.uv[0][1], 0 ); Vector tan_s_2d( 1, 0, 0 ); Vector tan_t_2d( 0, 1, 0 ); Vector delta_vert2 = v2 - v1; Vector delta_vert3 = v3 - v1; Vector delta_uv2 = w2 - w1; Vector delta_uv3 = w3 - w1; Vector n; VectorCopy( vert_1.normal, n.Base() ); delta_vert2 -= DotProduct( delta_vert2, n ) * n; delta_vert3 -= DotProduct( delta_vert3, n ) * n; //delta_vert2.NormalizeInPlace(); //delta_vert3.NormalizeInPlace(); delta_uv2.NormalizeInPlace(); delta_uv3.NormalizeInPlace(); Vector sdir( vec3_origin ); Vector tdir( vec3_origin ); sdir += DotProduct( tan_s_2d, delta_uv2 ) * delta_vert2; sdir += DotProduct( tan_s_2d, delta_uv3 ) * delta_vert3; tdir += DotProduct( tan_t_2d, delta_uv2 ) * delta_vert2; tdir += DotProduct( tan_t_2d, delta_uv3 ) * delta_vert3; #else Vector2D w1( vert_1.uv[0][0], vert_1.uv[0][1] ); Vector2D w2( vert_2.uv[0][0], vert_2.uv[0][1] ); Vector2D w3( vert_3.uv[0][0], vert_3.uv[0][1] ); 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 = (s1 * t2 - s2 * t1); if ( r != 0 ) r = 1.0f / r; Vector sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); #endif for ( int i = 0; i < 3; i++ ) { Assert( IsFinite( vert_1.tangent_s[i] ) ); Assert( IsFinite( vert_2.tangent_s[i] ) ); Assert( IsFinite( vert_3.tangent_s[i] ) ); Assert( IsFinite( vert_1.tangent_t[i] ) ); Assert( IsFinite( vert_2.tangent_t[i] ) ); Assert( IsFinite( vert_3.tangent_t[i] ) ); vert_1.tangent_s[i] += sdir[i]; vert_2.tangent_s[i] += sdir[i]; vert_3.tangent_s[i] += sdir[i]; vert_1.tangent_t[i] += tdir[i]; vert_2.tangent_t[i] += tdir[i]; vert_3.tangent_t[i] += tdir[i]; } //tan1[i1] += sdir; //tan1[i2] += sdir; //tan1[i3] += sdir; // //tan2[i1] += tdir; //tan2[i2] += tdir; //tan2[i3] += tdir; tri++; } for (int a = 0; a < m_iNumVertices; a++) { CHLSL_Vertex &vert = m_Vertices[a]; Vector n; Vector s; Vector t; VectorCopy( vert.normal, n.Base() ); VectorCopy( vert.tangent_s, s.Base() ); VectorCopy( vert.tangent_t, t.Base() ); n.NormalizeInPlace(); s.NormalizeInPlace(); t.NormalizeInPlace(); #if 0 Vector delta = s + ( t - s ) * 0.5f; Vector bidelta; CrossProduct( delta, n, bidelta ); t = bidelta + ( delta - bidelta ) * 0.5f; t -= n * DotProduct( t, n ); t.NormalizeInPlace(); CrossProduct( n, t, s ); s.NormalizeInPlace(); #endif #if 1 //if ( !IsFinite(t.x) || !IsFinite(t.y) || !IsFinite(t.z) ) // t.Init(0,0,1); //if ( !IsFinite(s.x) || !IsFinite(s.y) || !IsFinite(s.z) ) // s.Init(0,0,1); s = (s - n * DotProduct(n, s)); t = (t - n * DotProduct(n, t)) * -1.0f; s.NormalizeInPlace(); t.NormalizeInPlace(); float w = (DotProduct(CrossProduct(n, s), t) < 0.0F) ? 1.0F : -1.0F; #endif //t *= -1.0f; VectorCopy( s.Base(), vert.tangent_s ); VectorCopy( t.Base(), vert.tangent_t ); vert.tangent_s[3] = w; } // for (long a = 0; a < m_iNumVertices; a++) // { //CHLSL_Vertex &vert = *m_Vertices[a]; // // const Vector& n = //Vector normal( vert.pos[0], vert.pos[1], vert.pos[2] ); // const Vector& t = tan1[a]; // // // Gram-Schmidt orthogonalize // tangent[a] = VectorNormalize( (t - n * DotProduct(n, t)) ); // // // Calculate handedness // tangent[a].w = (Dot(Cross(n, t), tan2[a]) < 0.0F) ? -1.0F : 1.0F; // } }
bool BatchGeom::add_tangent_space(){ if (m_indices == NULL) return false; if (!find_attribute(ATT_TEXCOORD) ) return false; if (m_primitive_type != PRIM_TRIANGLES) return false; if (!find_attribute(ATT_TANGENT_S) && !insert_attribute(ATT_TANGENT_S, 3) ) return false; if (!find_attribute(ATT_TANGENT_T) && !insert_attribute(ATT_TANGENT_T, 3) ) return false; for(unsigned int i=0; i<m_num_indices; ++i) { get_tangent_s_by_index( i ) = noVec3( 0.0f, 0.0f, 0.0f ); get_tangent_t_by_index( i ) = noVec3( 0.0f, 0.0f, 0.0f ); } for(unsigned int i=0; i<m_num_indices; i+=3) { const noVec3 v1 = get_vertex_by_index( i+0 ); const noVec3 v2 = get_vertex_by_index( i+1 ); const noVec3 v3 = get_vertex_by_index( i+2 ); const noVec2 w1 = get_texcoord_by_index( i+0 ); const noVec2 w2 = get_texcoord_by_index( i+1 ); const noVec2 w3 = get_texcoord_by_index( i+2 ); 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; if(fabs(s1*t2 - s2*t1) <= 0.0001f) { r = 1.0f; } else { r = 1.0f / (s1*t2 - s2*t1); } noVec3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); noVec3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); get_tangent_s_by_index( i+0 ) += sdir; get_tangent_s_by_index( i+1 ) += sdir; get_tangent_s_by_index( i+2 ) += sdir; get_tangent_t_by_index( i+0 ) += tdir; get_tangent_t_by_index( i+1 ) += tdir; get_tangent_t_by_index( i+2 ) += tdir; } for(unsigned int i=0; i<m_num_vertices; ++i) { const noVec3 n = get_normal(i); // Gram-Schmidt orthogonalize get_tangent_t(i) = (get_tangent_t(i) - n * n * get_tangent_t(i)).NormalizeCopy(); get_tangent_s(i) = (get_tangent_s(i) - n * n * get_tangent_s(i) - n * (n * get_tangent_t(i))).NormalizeCopy(); } return true; }
/*! SLMesh::calcTangents computes the tangent and bi-tangent per vertex used for GLSL normal map bumb mapping. The code and mathematical derivation is in detail explained in: http://www.terathon.com/code/tangent.html */ void SLMesh::calcTangents() { if (P && N && Tc) { // allocat tangents delete[] T; T = new SLVec4f[numV]; // allocate temp arrays for tangents SLVec3f* T1 = new SLVec3f[numV * 2]; SLVec3f* T2 = T1 + numV; memset(T1, 0, numV * sizeof(SLVec3f) * 2); for (SLuint m = 0; m < numM; ++m) { for (SLuint f = 0; f < M[m].numF; ++f) { // Get the 3 vertex indexes SLushort iVA = F[M[m].startF + f].iA; SLushort iVB = F[M[m].startF + f].iB; SLushort iVC = F[M[m].startF + f].iC; float x1 = P[iVB].x - P[iVA].x; float x2 = P[iVC].x - P[iVA].x; float y1 = P[iVB].y - P[iVA].y; float y2 = P[iVC].y - P[iVA].y; float z1 = P[iVB].z - P[iVA].z; float z2 = P[iVC].z - P[iVA].z; float s1 = Tc[iVB].x - Tc[iVA].x; float s2 = Tc[iVC].x - Tc[iVA].x; float t1 = Tc[iVB].y - Tc[iVA].y; float t2 = Tc[iVC].y - Tc[iVA].y; float r = 1.0F / (s1*t2 - s2*t1); SLVec3f sdir((t2*x1 - t1*x2) * r, (t2*y1 - t1*y2) * r, (t2*z1 - t1*z2) * r); SLVec3f tdir((s1*x2 - s2*x1) * r, (s1*y2 - s2*y1) * r, (s1*z2 - s2*z1) * r); T1[iVA] += sdir; T1[iVB] += sdir; T1[iVC] += sdir; T2[iVA] += tdir; T2[iVB] += tdir; T2[iVC] += tdir; } } for (SLuint i=0; i < numV; ++i) { // Gram-Schmidt orthogonalize T[i] = T1[i] - N[i] * N[i].dot(T1[i]); T[i].normalize(); // Calculate temp. bitangent and store its handedness in T.w SLVec3f bitangent; bitangent.cross(N[i], T1[i]); T[i].w = (bitangent.dot(T2[i]) < 0.0f) ? -1.0f : 1.0f; } delete[] T1; } }
/* http://www.terathon.com/code/tangent.html */ bool CalculateTangents(std::vector<Vertex>& vertices, std::vector<TriangleFace>& faces) { if ( vertices.size() == 0 ) { std::cerr << "[Mesh:calculateTangents] Error: Vertex array of length 0." << std::endl; return false; } if ( faces.size() == 0 ) { std::cerr << "[Mesh:calculateNormals] Error: Face count = 0." << std::endl; return false; } std::size_t triangleCount = faces.size(); std::vector<Vector3f> tan1 = std::vector<Vector3f>(vertices.size()); std::vector<Vector3f> tan2 = std::vector<Vector3f>(vertices.size()); std::size_t i0 = 0, i1 = 0, i2 = 0; Vector3f p1, p2, p3; Vector3f w1, w2, w3; for ( std::size_t i = 0; i < triangleCount; i++ ) { i0 = faces[i].indices[A]; i1 = faces[i].indices[B]; i2 = faces[i].indices[C]; p1 = vertices[i0].position; p2 = vertices[i1].position; p3 = vertices[i2].position; w1 = vertices[i0].textureCoord; w2 = vertices[i1].textureCoord; w3 = vertices[i2].textureCoord; float x1 = p2.x() - p1.x(); float x2 = p3.x() - p1.x(); float y1 = p2.y() - p1.y(); float y2 = p3.y() - p1.y(); float z1 = p2.z() - p1.z(); float z2 = p3.z() - p1.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); Vector3f sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector3f tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i0] += sdir; tan1[i1] += sdir; tan1[i2] += sdir; tan2[i0] += tdir; tan2[i1] += tdir; tan2[i2] += tdir; } for ( std::size_t i = 0; i < vertices.size(); i++ ) { const Vector3f& n = vertices[i].normal; const Vector3f& t = tan1[i]; vertices[i].tangent = Vector4f((t - n * (float)Vector3f::Dot(n, t)).normalized()); if ( vertices[i].tangent.isEquivalent(Vector3f::Zero(), 0.01f) ) { if ( n.isEquivalent(Vector3f::UnitY(), 1.0e-4f) ) vertices[i].tangent = Vector3f::UnitX(); if ( n.isEquivalent(Vector3f::UnitNY(), 1.0e-4f) ) vertices[i].tangent = Vector3f::UnitX(); } if ( Vector3f::Dot(Vector3f::Cross(n, t), tan2[i]) < 0.0f ) vertices[i].tangent.w() = -1.0f; else vertices[i].tangent.w() = 1.0f; } return false; }
BOOL JsonPlugin::SetDirectory(PCTSTR dir, int iOpMode) { tstring path; if(!_tcscmp(dir, _T(".."))) // go to updir (..) { path = CurrentDir; TCHAR* pSlash = _tcsrchr(CurrentDir, '/'); if(pSlash) { path = path.substr(0, pSlash - CurrentDir); } return GoToPath(path.c_str()); } // First, try the name immediately if(curObject->IsObject()) { auto mem = curObject->FindMember((PWSTR)WideFromOem(dir)); if(mem != curObject->MemberEnd()) { if(!mem->value.IsObject() && !mem->value.IsArray()) return FALSE; curObject = &mem->value; _tcsncat(CurrentDir, _T("/"), _countof(CurrentDir)); size_t l = _tcslen(CurrentDir); EncodeToBuf(CurrentDir + l, dir, _countof(CurrentDir) - l); CurrentDir[_countof(CurrentDir) - 1] = 0; return TRUE; } } else if(curObject->IsArray()) //try the value immediately { PTSTR end; long index = _tcstol(dir, &end, 10); if(*end == 0) { auto val = &(*curObject)[index]; if(!val->IsObject() && !val->IsArray()) return FALSE; curObject = val; _tcsncat(CurrentDir, _T("/"), _countof(CurrentDir)); _tcsncat(CurrentDir, dir, _countof(CurrentDir)); CurrentDir[_countof(CurrentDir) - 1] = 0; return TRUE; } } // Then, try without converting backslashes to slashes (there may be a name with backslashes) if(dir[0] != '/') //relative path, add CurrentDir first { path = CurrentDir; path += _T("/"); } if(_tcscmp(dir, _T("/"))) path += dir; if(GoToPath(path.c_str())) return TRUE; // Convert \ to / and retry path.clear(); tstring tdir(dir); for(size_t i = 0; i < tdir.size(); i++) if(tdir[i] == '\\') tdir[i] = '/'; if(tdir[0] != '/') //relative path, add CurrentDir first { path = CurrentDir; path += _T("/"); } if(_tcscmp(tdir.c_str(), _T("/"))) path += tdir; return GoToPath(path.c_str()); } //SetDirectory
void calculate_tangents(V* vertices, size_t vertices_number, I* indices, size_t indices_number) { vec3* tan1 = new vec3[vertices_number * 2]; vec3* tan2 = tan1 + vertices_number; ::memset((void*)tan1, 0, vertices_number * 2 * sizeof(vec3)); for (size_t a = 0; a < indices_number; a += 3) { I i1 = indices[a]; I i2 = indices[a + 1]; I i3 = indices[a + 2]; const vec3& v1 = vertices[i1].pos; const vec3& v2 = vertices[i2].pos; const vec3& v3 = vertices[i3].pos; const vec2& w1 = vertices[i1].tex; const vec2& w2 = vertices[i2].tex; const vec2& w3 = vertices[i3].tex; 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 d = (s1 * t2 - s2 * t1); float r = d != 0.0f ? 1.0f / d : 0.0f; 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; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } for (size_t a = 0; a < vertices_number; ++a) { const vec3& n = vertices[a].nrm; const vec3& t = tan1[a]; vec3 tng; // Gram-Schmidt orthogonalize tng = (t - n * dot(n, t)).normalized(); // Calculate handedness float w = (dot(cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f; vertices[a].tng = vec4(tng, w); } delete [] tan1; }
// Based on the example at http://www.terathon.com/code/tangent.php void MeshCompiler::CalculateTangents() { // Notes: // Bitangent = (Normal x Tangent) * Bitangent orientation // Tangent is the tangent-space vector along the +U axis (right) // Bitangent is the tangent-space vector along the +V axis (up) // Use Gram-Schmidt to orthonormalize the vector // Write the handedness (direction of bitangent) in w Vector* tan1 = new Vector[ m_Header.m_NumVertices * 2]; Vector* tan2 = tan1 + m_Header.m_NumVertices; memset( tan1, 0, m_Header.m_NumVertices * 2 * sizeof( Vector ) ); for( uint32 i = 0; i < m_Header.m_NumIndices; i += 3 ) { uint i1 = m_Indices[ i ]; uint i2 = m_Indices[ i + 1 ]; uint i3 = m_Indices[ i + 2 ]; const Vector& v1 = m_Positions[ i1 ]; const Vector& v2 = m_Positions[ i2 ]; const Vector& v3 = m_Positions[ i3 ]; const Vector2& w1 = m_UVs[ i1 ]; const Vector2& w2 = m_UVs[ i2 ]; const Vector2& w3 = m_UVs[ 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.f / ( s1 * t2 - s2 * t1 ); Vector sdir( ( t2 * x1 - t1 * x2 ) * r, ( t2 * y1 - t1 * y2 ) * r, ( t2 * z1 - t1 * z2 ) * r ); Vector 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( uint32 i = 0; i < m_Header.m_NumVertices; ++i ) { const Vector& n = m_Normals[i]; const Vector& t = tan1[i]; // Gram-Schmidt orthogonalize m_Tangents[i] = ( t - n * n.Dot(t) ).GetNormalized(); // Calculate handedness m_Tangents[i].w = ( n.Cross(t).Dot( tan2[i] ) < 0.f ) ? -1.f : 1.f; } SafeDeleteArray( tan1 ); }
void GenerateTangents(void* vertexData, unsigned vertexSize, const void* indexData, unsigned indexSize, unsigned indexStart, unsigned indexCount, unsigned normalOffset, unsigned texCoordOffset, unsigned tangentOffset) { // Tangent generation from // http://www.terathon.com/code/tangent.html unsigned minVertex = M_MAX_UNSIGNED; unsigned maxVertex = 0; auto* vertices = (unsigned char*)vertexData; auto* indexPointer = const_cast<void*>(indexData); for (unsigned i = indexStart; i < indexStart + indexCount; ++i) { unsigned v = GetIndex(indexPointer, indexSize); if (v < minVertex) minVertex = v; if (v > maxVertex) maxVertex = v; } unsigned vertexCount = maxVertex + 1; auto* tan1 = new Vector3[vertexCount * 2]; Vector3* tan2 = tan1 + vertexCount; memset(tan1, 0, sizeof(Vector3) * vertexCount * 2); indexPointer = const_cast<void*>(indexData); for (unsigned i = indexStart; i < indexStart + indexCount; i += 3) { unsigned i1 = GetIndex(indexPointer, indexSize); unsigned i2 = GetIndex(indexPointer, indexSize); unsigned i3 = GetIndex(indexPointer, indexSize); const Vector3& v1 = *((Vector3*)(vertices + i1 * vertexSize)); const Vector3& v2 = *((Vector3*)(vertices + i2 * vertexSize)); const Vector3& v3 = *((Vector3*)(vertices + i3 * vertexSize)); const Vector2& w1 = *((Vector2*)(vertices + i1 * vertexSize + texCoordOffset)); const Vector2& w2 = *((Vector2*)(vertices + i2 * vertexSize + texCoordOffset)); const Vector2& w3 = *((Vector2*)(vertices + i3 * vertexSize + texCoordOffset)); 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); Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector3 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 i = minVertex; i <= maxVertex; i++) { const Vector3& n = *((Vector3*)(vertices + i * vertexSize + normalOffset)); const Vector3& t = tan1[i]; Vector3 xyz; float w; // Gram-Schmidt orthogonalize xyz = (t - n * n.DotProduct(t)).Normalized(); // Calculate handedness w = n.CrossProduct(t).DotProduct(tan2[i]) < 0.0f ? -1.0f : 1.0f; Vector4& tangent = *((Vector4*)(vertices + i * vertexSize + tangentOffset)); tangent = Vector4(xyz, w); } delete[] tan1; }