Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / tangent_space_build.cpp
blobf99a7537b94e65da903dcc589883583cbf59f4d3
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "std3d.h"
20 #include "nel/3d/tangent_space_build.h"
21 #include "nel/3d/index_buffer.h"
22 #include "nel/3d/vertex_buffer.h"
23 #include "nel/misc/triangle.h"
26 #ifdef DEBUG_NEW
27 #define new DEBUG_NEW
28 #endif
30 namespace NL3D
34 //========================================================================================================================
35 /** private func : duplicate a mesh build
36 * It returns the index of the new texture coordinate or 0 if the conversion couldn't occur..
38 static uint DuplicateMBAndAddTexCoord(CMesh::CMeshBuild &outMeshBuild, const CMesh::CMeshBuild &inMeshBuild)
40 outMeshBuild = inMeshBuild;
41 uint numTexCoord = 0;
42 for (uint k = CVertexBuffer::FirstTexCoordValue; k <= CVertexBuffer::FirstTexCoordValue; ++k)
44 if (inMeshBuild.VertexFlags & (1 << k)) numTexCoord = 1 + k - CVertexBuffer::FirstTexCoordValue;
46 if (numTexCoord == CVertexBuffer::MaxStage) return 0;
47 outMeshBuild.VertexFlags = inMeshBuild.VertexFlags | (1 << (numTexCoord + CVertexBuffer::FirstTexCoordValue));
48 outMeshBuild.NumCoords[numTexCoord] = 3;
49 return numTexCoord;
52 //========== Build one tangent space vector ==================
53 static void BuildTGSpaceVect(const NLMISC::CVector &normal, const NLMISC::CVector &sGrad, NLMISC::CUVW &result)
55 // start normalizing the basis
56 NLMISC::CVector res = (sGrad - ((sGrad * normal) * normal)).normed();
57 result.U = res.x;
58 result.V = res.y;
59 result.W = res.z;
62 //========== build one tri from a vb and 3 index in it=================
63 static void BuildTriFromMB(const CMesh::CMeshBuild &mb, const uint index[3], NLMISC::CTriangle &tri)
65 tri.V0 = mb.Vertices[index[0]];
66 tri.V1 = mb.Vertices[index[1]];
67 tri.V2 = mb.Vertices[index[2]];
71 //========================================================================================================================
72 bool BuildTangentSpace(CMesh::CMeshBuild &outMeshBuild, const CMesh::CMeshBuild &inMeshBuild)
74 static const NLMISC::CUVW NullUVW(0, 0, 0); // todo add this directly in the CUVW class (for next compile ...)
76 /// for each face, we must use a previously computed tangent space vector if other vertex datas are similar
77 nlassert(&outMeshBuild != &inMeshBuild);
78 uint tgSpaceStage = DuplicateMBAndAddTexCoord(outMeshBuild, inMeshBuild); // format the resulting vb
79 if (tgSpaceStage == 0) return false; // unable to create the vb
81 uint l, m, n, k; // some loop counters
83 // Tells for each vertex what faces belong to it
84 std::vector<std::vector<uint> > VertToFace(inMeshBuild.Vertices.size());
86 // fill VertToFace
87 for (l = 0; l < inMeshBuild.Faces.size(); ++l)
89 for (m = 0; m < 3; ++m)
91 outMeshBuild.Faces[l].Corner[m].Uvws[tgSpaceStage] = NLMISC::CUVW(0, 0, 0);
92 VertToFace[outMeshBuild.Faces[l].Corner[m].Vertex].push_back(l);
96 /* TODO: debug this version
97 std::vector<NLMISC::CVector> SGradArray(outMeshBuild.Faces.size()); // SGradient for each face
99 // compute sGradient for each face
100 for (k = 0; k < outMeshBuild.Faces.size(); ++k)
102 CMesh::CFace &f = outMeshBuild.Faces[k];
103 NLMISC::CTriangle tri;
104 tri.V0 = outMeshBuild.Vertices[f.Corner[0].Vertex];
105 tri.V1 = outMeshBuild.Vertices[f.Corner[1].Vertex];
106 tri.V2 = outMeshBuild.Vertices[f.Corner[2].Vertex];
107 tri.computeGradient(f.Corner[0].Uvws[0].U,
108 f.Corner[1].Uvws[0].U,
109 f.Corner[2].Uvws[0].U, SGradArray[k]);
110 SGradArray[k].normalize();
114 // for each triangle, add the S gradient contribution to any neighbour vertex for which the owning face has TexCoords that do not mirror with that face
115 for (k = 0; k < outMeshBuild.Faces.size(); ++k)
117 CMesh::CFace &f = outMeshBuild.Faces[k];
118 for (l = 0; l < 3; ++l)
120 const std::vector<uint> &neighbours = VertToFace[f.Corner[l].Vertex];
121 for (m = 0; m < neighbours.size(); ++m)
123 // other face must share smoothgroups with this one
124 if (f.SmoothGroup & outMeshBuild.Faces[neighbours[m]].SmoothGroup)
126 // test if the other face UVs are not mirroring the current ones..
127 float dp = SGradArray[k] * SGradArray[neighbours[m]];
128 if (dp > 0.f)
130 f.Corner[l].Uvws[tgSpaceStage] += NLMISC::CUVW(SGradArray[neighbours[m]].x, SGradArray[neighbours[m]].y, SGradArray[neighbours[m]].z);
137 // normalize each tangent space vector
138 for (k = 0; k < outMeshBuild.Faces.size(); ++k)
140 CMesh::CFace &f = outMeshBuild.Faces[k];
141 for (l = 0; l < 3; ++l)
143 CMesh::CCorner &c = f.Corner[l];
144 CVector tgs(c.Uvws[tgSpaceStage].U, c.Uvws[tgSpaceStage].V, c.Uvws[tgSpaceStage].W);
145 tgs = (tgs - (tgs * c.Normal) * c.Normal).normed();
146 c.Uvws[tgSpaceStage].U = tgs.x;
147 c.Uvws[tgSpaceStage].V = tgs.y;
148 c.Uvws[tgSpaceStage].W = tgs.z;
153 // Old tangent space version (no support for mirrored textures ..)
154 for (l = 0; l < inMeshBuild.Faces.size(); ++l)
157 CMesh::CFace &curF = outMeshBuild.Faces[l];
160 // Build each tangent space vector if needed
161 for (m = 0; m < 3; ++m)
163 uint vertIndex = outMeshBuild.Faces[l].Corner[m].Vertex;
164 bool found = false;
165 NLMISC::CUVW *tsv=0; // a previously computed tangent space vector
166 // Test whether it hasn't been built before, by looking in each corner of each face that share that vertex
167 for (n = 0; n < VertToFace[vertIndex].size() && !found; ++n)
169 CMesh::CFace &f = outMeshBuild.Faces[VertToFace[vertIndex][n]]; // ref to the current face
170 for (k = 0; k < 3; ++k)
172 // can only share with corners that are equal to this one
173 if (f.Corner[k].Vertex == curF.Corner[m].Vertex) // same position
175 if (f.Corner[k].Uvws[tgSpaceStage] != NullUVW) // must have built the tangent space vector previously.
177 if (f.Corner[k].Normal == curF.Corner[m].Normal
178 && f.Corner[k].Uvws[0] == curF.Corner[m].Uvws[0]
181 // no texture and normal discontinuity
182 found = true;
183 tsv = &f.Corner[k].Uvws[tgSpaceStage];
190 if (!found)
192 NLMISC::CVector grad(0, 0, 0);
193 // walk all the triangles around this vertex to sum the gradients
194 // Get the s coordinate gradient over that triangle
195 for (n = 0; n < VertToFace[vertIndex].size(); ++n)
197 CMesh::CFace &f = outMeshBuild.Faces[VertToFace[vertIndex][n]]; // ref to the current face
199 // before to sum this face gradient, make sure there's no normal or mapping discontinuity
200 bool canShare = true;
201 for (k = 0; k < 3; ++k)
203 // can only share with corners that are equal to this one
204 if (f.Corner[k].Vertex == curF.Corner[m].Vertex) // same position
206 if (!(f.Corner[k].Normal == curF.Corner[m].Normal
207 && f.Corner[k].Uvws[0] == curF.Corner[m].Uvws[0]
211 canShare = false;
212 break;
217 if (!canShare) continue;
219 // Get indices of vertices of current tri
220 const uint indices[] = { (uint)f.Corner[0].Vertex,
221 (uint)f.Corner[1].Vertex,
222 (uint)f.Corner[2].Vertex };
223 NLMISC::CTriangle tri;
224 // Build it
225 BuildTriFromMB(outMeshBuild, indices, tri);
226 // Get s coordinates for each corner
227 float s[3];
228 for (k = 0; k < 3; ++k)
230 s[k] = f.Corner[k].Uvws[0].U;
233 NLMISC::CVector sGrad;
234 tri.computeGradient(s[0], s[1], s[2], sGrad);
235 grad += ((tri.V1 - tri.V0) ^ (tri.V2 - tri.V0)).norm() * sGrad; // ponderate by twice the area
237 grad.normalize();
239 // build new tangent space vector
240 BuildTGSpaceVect(inMeshBuild.Faces[l].Corner[m].Normal, grad, curF.Corner[m].Uvws[tgSpaceStage]);
242 else
244 // use previously built vector
245 curF.Corner[m].Uvws[tgSpaceStage] = *tsv;
249 return true;