1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
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
;
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;
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();
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());
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]];
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
;
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
183 tsv
= &f
.Corner
[k
].Uvws
[tgSpaceStage
];
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]
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
;
225 BuildTriFromMB(outMeshBuild
, indices
, tri
);
226 // Get s coordinates for each corner
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
239 // build new tangent space vector
240 BuildTGSpaceVect(inMeshBuild
.Faces
[l
].Corner
[m
].Normal
, grad
, curF
.Corner
[m
].Uvws
[tgSpaceStage
]);
244 // use previously built vector
245 curF
.Corner
[m
].Uvws
[tgSpaceStage
] = *tsv
;