/// <summary> /// Calcular Tangent y Binormal en base a los 3 vertices de un triangulo y la normal del primero de ellos /// Basado en: http://www.dhpoware.com/demos/d3d9NormalMapping.html /// </summary> public static void computeTangentBinormal(BumpMappingVertex v1, BumpMappingVertex v2, BumpMappingVertex v3, out Vector3 tangent, out Vector3 binormal) { // Given the 3 vertices (position and texture coordinates) of a triangle // calculate and return the triangle's tangent vector. The handedness of // the local coordinate system is stored in tangent.w. The bitangent is // then: float3 bitangent = cross(normal, tangent.xyz) * tangent.w. // Create 2 vectors in object space. // // edge1 is the vector from vertex positions v1 to v2. // edge2 is the vector from vertex positions v1 to v3. Vector3 edge1 = v2.Position - v1.Position; Vector3 edge2 = v3.Position - v1.Position; edge1.Normalize(); edge2.Normalize(); // Create 2 vectors in tangent (texture) space that point in the same // direction as edge1 and edge2 (in object space). // // texEdge1 is the vector from texture coordinates texCoord1 to texCoord2. // texEdge2 is the vector from texture coordinates texCoord1 to texCoord3. Vector2 texEdge1 = new Vector2(v2.Tu - v1.Tu, v2.Tv - v1.Tv); Vector2 texEdge2 = new Vector2(v3.Tu - v1.Tu, v3.Tv - v1.Tv); texEdge1.Normalize(); texEdge2.Normalize(); // These 2 sets of vectors form the following system of equations: // // edge1 = (texEdge1.x * tangent) + (texEdge1.y * bitangent) // edge2 = (texEdge2.x * tangent) + (texEdge2.y * bitangent) // // Using matrix notation this system looks like: // // [ edge1 ] [ texEdge1.x texEdge1.y ] [ tangent ] // [ ] = [ ] [ ] // [ edge2 ] [ texEdge2.x texEdge2.y ] [ bitangent ] // // The solution is: // // [ tangent ] 1 [ texEdge2.y -texEdge1.y ] [ edge1 ] // [ ] = ------- [ ] [ ] // [ bitangent ] det A [-texEdge2.x texEdge1.x ] [ edge2 ] // // where: // [ texEdge1.x texEdge1.y ] // A = [ ] // [ texEdge2.x texEdge2.y ] // // det A = (texEdge1.x * texEdge2.y) - (texEdge1.y * texEdge2.x) // // From this solution the tangent space basis vectors are: // // tangent = (1 / det A) * ( texEdge2.y * edge1 - texEdge1.y * edge2) // bitangent = (1 / det A) * (-texEdge2.x * edge1 + texEdge1.x * edge2) // normal = cross(tangent, bitangent) float det = (texEdge1.X * texEdge2.Y) - (texEdge1.Y * texEdge2.X); if (FastMath.Abs(det) < 0.0001f) // almost equal to zero { tangent.X = 1.0f; tangent.Y = 0.0f; tangent.Z = 0.0f; binormal.X = 0.0f; binormal.Y = 1.0f; binormal.Z = 0.0f; } else { det = 1.0f / det; tangent.X = (texEdge2.Y * edge1.X - texEdge1.Y * edge2.X) * det; tangent.Y = (texEdge2.Y * edge1.Y - texEdge1.Y * edge2.Y) * det; tangent.Z = (texEdge2.Y * edge1.Z - texEdge1.Y * edge2.Z) * det; //tangent.W = 0.0f; binormal.X = (-texEdge2.X * edge1.X + texEdge1.X * edge2.X) * det; binormal.Y = (-texEdge2.X * edge1.Y + texEdge1.X * edge2.Y) * det; binormal.Z = (-texEdge2.X * edge1.Z + texEdge1.X * edge2.Z) * det; tangent.Normalize(); binormal.Normalize(); } // Calculate the handedness of the local tangent space. // The bitangent vector is the cross product between the triangle face // normal vector and the calculated tangent vector. The resulting bitangent // vector should be the same as the bitangent vector calculated from the // set of linear equations above. If they point in different directions // then we need to invert the cross product calculated bitangent vector. Vector3 b = Vector3.Cross(v1.Normal, tangent); float w = Vector3.Dot(b, binormal) < 0.0f ? -1.0f : 1.0f; binormal = b * w; }
/// <summary> /// Crear un TgcMeshBumpMapping en base a un TgcMesh y su normalMap. /// Solo esta soportado un TgcMehs MeshRenderType = DiffuseMap /// </summary> public static TgcMeshBumpMapping fromTgcMesh(TgcMesh mesh, TgcTexture[] normalMaps) { if (mesh.RenderType != MeshRenderType.DIFFUSE_MAP) { throw new Exception("Solo esta soportado MeshRenderType = DiffuseMap"); } //Obtener vertexBuffer original TgcSceneLoader.DiffuseMapVertex[] origVertexBuffer = (TgcSceneLoader.DiffuseMapVertex[])mesh.D3dMesh.LockVertexBuffer( typeof(TgcSceneLoader.DiffuseMapVertex), LockFlags.ReadOnly, mesh.D3dMesh.NumberVertices); mesh.D3dMesh.UnlockVertexBuffer(); //Crear nuevo Mesh de DirectX int triCount = origVertexBuffer.Length / 3; Mesh d3dMesh = new Mesh(triCount, origVertexBuffer.Length, MeshFlags.Managed, BumpMappingVertexElements, GuiController.Instance.D3dDevice); //Calcular normales recorriendo los triangulos Vector3[] normals = new Vector3[origVertexBuffer.Length]; for (int i = 0; i < normals.Length; i++) { normals[i] = new Vector3(0, 0, 0); } for (int i = 0; i < triCount; i++) { //Los 3 vertices del triangulo TgcSceneLoader.DiffuseMapVertex v1 = origVertexBuffer[i * 3]; TgcSceneLoader.DiffuseMapVertex v2 = origVertexBuffer[i * 3 + 1]; TgcSceneLoader.DiffuseMapVertex v3 = origVertexBuffer[i * 3 + 2]; //Face-normal (left-handend) Vector3 a = v2.Position - v1.Position; Vector3 b = v3.Position - v1.Position; Vector3 n = Vector3.Cross(a, b); //Acumular normal del vertice segun todas sus Face-normal normals[i * 3] += n; normals[i * 3 + 1] += n; normals[i * 3 + 2] += n; } //Normalizar normales for (int i = 0; i < normals.Length; i++) { normals[i] = Vector3.Normalize(normals[i]); } //Crear nuevo VertexBuffer using (VertexBuffer vb = d3dMesh.VertexBuffer) { //Iterar sobre triangulos GraphicsStream data = vb.Lock(0, 0, LockFlags.None); for (int i = 0; i < triCount; i++) { //Vertices originales TgcSceneLoader.DiffuseMapVertex vOrig1 = origVertexBuffer[i * 3]; TgcSceneLoader.DiffuseMapVertex vOrig2 = origVertexBuffer[i * 3 + 1]; TgcSceneLoader.DiffuseMapVertex vOrig3 = origVertexBuffer[i * 3 + 2]; //Nuevo vertice 1 BumpMappingVertex v1 = new BumpMappingVertex(); v1.Position = vOrig1.Position; v1.Color = vOrig1.Color; v1.Tu = vOrig1.Tu; v1.Tv = vOrig1.Tv; v1.Normal = normals[i * 3]; //Nuevo vertice 2 BumpMappingVertex v2 = new BumpMappingVertex(); v2.Position = vOrig2.Position; v2.Color = vOrig2.Color; v2.Tu = vOrig2.Tu; v2.Tv = vOrig2.Tv; v2.Normal = normals[i * 3 + 1]; //Nuevo vertice 3 BumpMappingVertex v3 = new BumpMappingVertex(); v3.Position = vOrig3.Position; v3.Color = vOrig3.Color; v3.Tu = vOrig3.Tu; v3.Tv = vOrig3.Tv; v3.Normal = normals[i * 3 + 2]; //Calcular tangente y binormal para todo el triangulo y cargarlas en cada vertice Vector3 tangent; Vector3 binormal; TgcMeshBumpMapping.computeTangentBinormal(v1, v2, v3, out tangent, out binormal); v1.Tangent = tangent; v1.Binormal = binormal; v2.Tangent = tangent; v2.Binormal = binormal; v3.Tangent = tangent; v3.Binormal = binormal; //Cargar VertexBuffer data.Write(v1); data.Write(v2); data.Write(v3); } vb.Unlock(); } //Cargar IndexBuffer en forma plana using (IndexBuffer ib = d3dMesh.IndexBuffer) { short[] indices = new short[origVertexBuffer.Length]; for (int i = 0; i < indices.Length; i++) { indices[i] = (short)i; } ib.SetData(indices, 0, LockFlags.None); } //Clonar texturas y materials TgcTexture[] diffuseMaps = new TgcTexture[mesh.DiffuseMaps.Length]; Material[] materials = new Material[mesh.Materials.Length]; for (int i = 0; i < mesh.DiffuseMaps.Length; i++) { diffuseMaps[i] = mesh.DiffuseMaps[i].clone(); materials[i] = TgcD3dDevice.DEFAULT_MATERIAL; } //Cargar attributeBuffer if (diffuseMaps.Length > 1) { int[] origAttributeBuffer = mesh.D3dMesh.LockAttributeBufferArray(LockFlags.None); int[] newAttributeBuffer = d3dMesh.LockAttributeBufferArray(LockFlags.None); Array.Copy(origAttributeBuffer, newAttributeBuffer, origAttributeBuffer.Length); mesh.D3dMesh.UnlockAttributeBuffer(); d3dMesh.UnlockAttributeBuffer(newAttributeBuffer); } //Crear mesh de BumpMapping Mesh TgcMeshBumpMapping bumpMesh = new TgcMeshBumpMapping(d3dMesh, mesh.Name, mesh.RenderType); bumpMesh.diffuseMaps = diffuseMaps; bumpMesh.materials = materials; bumpMesh.normalMaps = normalMaps; bumpMesh.layer = mesh.Layer; bumpMesh.alphaBlendEnable = mesh.AlphaBlendEnable; bumpMesh.UserProperties = mesh.UserProperties; bumpMesh.boundingBox = mesh.BoundingBox.clone(); bumpMesh.enabled = true; return(bumpMesh); }
/// <summary> /// Crear un TgcMeshBumpMapping en base a un TgcMesh y su normalMap. /// Solo esta soportado un TgcMehs MeshRenderType = DiffuseMap /// </summary> public static TgcMeshBumpMapping fromTgcMesh(TgcMesh mesh, TgcTexture[] normalMaps) { if (mesh.RenderType != MeshRenderType.DIFFUSE_MAP) { throw new Exception("Solo esta soportado MeshRenderType = DiffuseMap"); } //Obtener vertexBuffer original TgcSceneLoader.DiffuseMapVertex[] origVertexBuffer = (TgcSceneLoader.DiffuseMapVertex[])mesh.D3dMesh.LockVertexBuffer( typeof(TgcSceneLoader.DiffuseMapVertex), LockFlags.ReadOnly, mesh.D3dMesh.NumberVertices); mesh.D3dMesh.UnlockVertexBuffer(); //Crear nuevo Mesh de DirectX int triCount = origVertexBuffer.Length / 3; Mesh d3dMesh = new Mesh(triCount, origVertexBuffer.Length, MeshFlags.Managed, BumpMappingVertexElements, GuiController.Instance.D3dDevice); //Calcular normales recorriendo los triangulos Vector3[] normals = new Vector3[origVertexBuffer.Length]; for (int i = 0; i < normals.Length; i++) { normals[i] = new Vector3(0, 0, 0); } for (int i = 0; i < triCount; i++) { //Los 3 vertices del triangulo TgcSceneLoader.DiffuseMapVertex v1 = origVertexBuffer[i * 3]; TgcSceneLoader.DiffuseMapVertex v2 = origVertexBuffer[i * 3 + 1]; TgcSceneLoader.DiffuseMapVertex v3 = origVertexBuffer[i * 3 + 2]; //Face-normal (left-handend) Vector3 a = v2.Position - v1.Position; Vector3 b = v3.Position - v1.Position; Vector3 n = Vector3.Cross(a, b); //Acumular normal del vertice segun todas sus Face-normal normals[i * 3] += n; normals[i * 3 + 1] += n; normals[i * 3 + 2] += n; } //Normalizar normales for (int i = 0; i < normals.Length; i++) { normals[i] = Vector3.Normalize(normals[i]); } //Crear nuevo VertexBuffer using (VertexBuffer vb = d3dMesh.VertexBuffer) { //Iterar sobre triangulos GraphicsStream data = vb.Lock(0, 0, LockFlags.None); for (int i = 0; i < triCount; i++) { //Vertices originales TgcSceneLoader.DiffuseMapVertex vOrig1 = origVertexBuffer[i * 3]; TgcSceneLoader.DiffuseMapVertex vOrig2 = origVertexBuffer[i * 3 + 1]; TgcSceneLoader.DiffuseMapVertex vOrig3 = origVertexBuffer[i * 3 + 2]; //Nuevo vertice 1 BumpMappingVertex v1 = new BumpMappingVertex(); v1.Position = vOrig1.Position; v1.Color = vOrig1.Color; v1.Tu = vOrig1.Tu; v1.Tv = vOrig1.Tv; v1.Normal = normals[i * 3]; //Nuevo vertice 2 BumpMappingVertex v2 = new BumpMappingVertex(); v2.Position = vOrig2.Position; v2.Color = vOrig2.Color; v2.Tu = vOrig2.Tu; v2.Tv = vOrig2.Tv; v2.Normal = normals[i * 3 + 1]; //Nuevo vertice 3 BumpMappingVertex v3 = new BumpMappingVertex(); v3.Position = vOrig3.Position; v3.Color = vOrig3.Color; v3.Tu = vOrig3.Tu; v3.Tv = vOrig3.Tv; v3.Normal = normals[i * 3 + 2]; //Calcular tangente y binormal para todo el triangulo y cargarlas en cada vertice Vector3 tangent; Vector3 binormal; TgcMeshBumpMapping.computeTangentBinormal(v1, v2, v3, out tangent, out binormal); v1.Tangent = tangent; v1.Binormal = binormal; v2.Tangent = tangent; v2.Binormal = binormal; v3.Tangent = tangent; v3.Binormal = binormal; //Cargar VertexBuffer data.Write(v1); data.Write(v2); data.Write(v3); } vb.Unlock(); } //Cargar IndexBuffer en forma plana using (IndexBuffer ib = d3dMesh.IndexBuffer) { short[] indices = new short[origVertexBuffer.Length]; for (int i = 0; i < indices.Length; i++) { indices[i] = (short)i; } ib.SetData(indices, 0, LockFlags.None); } //Clonar texturas y materials TgcTexture[] diffuseMaps = new TgcTexture[mesh.DiffuseMaps.Length]; Material[] materials = new Material[mesh.Materials.Length]; for (int i = 0; i < mesh.DiffuseMaps.Length; i++) { diffuseMaps[i] = mesh.DiffuseMaps[i].clone(); materials[i] = TgcD3dDevice.DEFAULT_MATERIAL; } //Cargar attributeBuffer if (diffuseMaps.Length > 1) { int[] origAttributeBuffer = mesh.D3dMesh.LockAttributeBufferArray(LockFlags.None); int[] newAttributeBuffer = d3dMesh.LockAttributeBufferArray(LockFlags.None); Array.Copy(origAttributeBuffer, newAttributeBuffer, origAttributeBuffer.Length); mesh.D3dMesh.UnlockAttributeBuffer(); d3dMesh.UnlockAttributeBuffer(newAttributeBuffer); } //Crear mesh de BumpMapping Mesh TgcMeshBumpMapping bumpMesh = new TgcMeshBumpMapping(d3dMesh, mesh.Name, mesh.RenderType); bumpMesh.diffuseMaps = diffuseMaps; bumpMesh.materials = materials; bumpMesh.normalMaps = normalMaps; bumpMesh.layer = mesh.Layer; bumpMesh.alphaBlendEnable = mesh.AlphaBlendEnable; bumpMesh.UserProperties = mesh.UserProperties; bumpMesh.boundingBox = mesh.BoundingBox.clone(); bumpMesh.enabled = true; return bumpMesh; }