/src/ogre/OgreMain/src/OgreTangentSpaceCalc.cpp
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #include "OgreStableHeaders.h" |
29 | | #include "OgreTangentSpaceCalc.h" |
30 | | |
31 | | namespace Ogre |
32 | | { |
33 | | //--------------------------------------------------------------------- |
34 | | TangentSpaceCalc::TangentSpaceCalc() |
35 | 0 | : mVData(0) |
36 | 0 | , mSplitMirrored(false) |
37 | 0 | , mSplitRotated(false) |
38 | 0 | , mStoreParityInW(false) |
39 | 0 | { |
40 | 0 | } |
41 | | |
42 | | //--------------------------------------------------------------------- |
43 | | void TangentSpaceCalc::clear() |
44 | 0 | { |
45 | 0 | mIDataList.clear(); |
46 | 0 | mOpTypes.clear(); |
47 | 0 | mVData = 0; |
48 | 0 | } |
49 | | //--------------------------------------------------------------------- |
50 | | void TangentSpaceCalc::setVertexData(VertexData* v_in) |
51 | 0 | { |
52 | 0 | mVData = v_in; |
53 | 0 | } |
54 | | //--------------------------------------------------------------------- |
55 | | void TangentSpaceCalc::addIndexData(IndexData* i_in, RenderOperation::OperationType op) |
56 | 0 | { |
57 | 0 | if (op != RenderOperation::OT_TRIANGLE_FAN && |
58 | 0 | op != RenderOperation::OT_TRIANGLE_LIST && |
59 | 0 | op != RenderOperation::OT_TRIANGLE_STRIP) |
60 | 0 | { |
61 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, |
62 | 0 | "Only indexed triangle (list, strip, fan) render operations are supported.", |
63 | 0 | "TangentSpaceCalc::addIndexData"); |
64 | |
|
65 | 0 | } |
66 | 0 | mIDataList.push_back(i_in); |
67 | 0 | mOpTypes.push_back(op); |
68 | 0 | } |
69 | | //--------------------------------------------------------------------- |
70 | | TangentSpaceCalc::Result TangentSpaceCalc::build(unsigned short sourceTexCoordSet) |
71 | 0 | { |
72 | 0 | Result res; |
73 | | |
74 | | // Pull out all the vertex components we'll need |
75 | 0 | populateVertexArray(sourceTexCoordSet); |
76 | | |
77 | | // Now process the faces and calculate / add their contributions |
78 | 0 | processFaces(res); |
79 | | |
80 | | // Now normalise & orthogonalise |
81 | 0 | normaliseVertices(); |
82 | | |
83 | | // Create new final geometry |
84 | | // First extend existing buffers to cope with new vertices |
85 | 0 | extendBuffers(res.vertexSplits); |
86 | | |
87 | | // Alter indexes |
88 | 0 | remapIndexes(res); |
89 | | |
90 | | // Create / identify target & write tangents |
91 | 0 | insertTangents(res, VES_TANGENT, sourceTexCoordSet, 0); |
92 | |
|
93 | 0 | return res; |
94 | | |
95 | |
|
96 | 0 | } |
97 | | //--------------------------------------------------------------------- |
98 | | void TangentSpaceCalc::extendBuffers(VertexSplits& vertexSplits) |
99 | 0 | { |
100 | 0 | if (!vertexSplits.empty()) |
101 | 0 | { |
102 | | // ok, need to increase the vertex buffer size, and alter some indexes |
103 | | |
104 | | // vertex buffers first |
105 | 0 | VertexBufferBinding* newBindings = HardwareBufferManager::getSingleton().createVertexBufferBinding(); |
106 | 0 | const VertexBufferBinding::VertexBufferBindingMap& bindmap = |
107 | 0 | mVData->vertexBufferBinding->getBindings(); |
108 | 0 | for (const auto & i : bindmap) |
109 | 0 | { |
110 | 0 | HardwareVertexBufferSharedPtr srcbuf = i.second; |
111 | | // Derive vertex count from buffer not vertex data, in case using |
112 | | // the vertexStart option in vertex data |
113 | 0 | size_t newVertexCount = srcbuf->getNumVertices() + vertexSplits.size(); |
114 | | // Create new buffer & bind |
115 | 0 | HardwareVertexBufferSharedPtr newBuf = |
116 | 0 | HardwareBufferManager::getSingleton().createVertexBuffer( |
117 | 0 | srcbuf->getVertexSize(), newVertexCount, srcbuf->getUsage(), |
118 | 0 | srcbuf->hasShadowBuffer()); |
119 | 0 | newBindings->setBinding(i.first, newBuf); |
120 | | |
121 | | // Copy existing contents (again, entire buffer, not just elements referenced) |
122 | 0 | newBuf->copyData(*(srcbuf.get()), 0, 0, srcbuf->getNumVertices() * srcbuf->getVertexSize(), true); |
123 | | |
124 | | // Split vertices, read / write from new buffer |
125 | 0 | HardwareBufferLockGuard newBufLock(newBuf, HardwareBuffer::HBL_NORMAL); |
126 | 0 | char* pBase = static_cast<char*>(newBufLock.pData); |
127 | 0 | for (auto & vertexSplit : vertexSplits) |
128 | 0 | { |
129 | 0 | const char* pSrcBase = pBase + vertexSplit.first * newBuf->getVertexSize(); |
130 | 0 | char* pDstBase = pBase + vertexSplit.second * newBuf->getVertexSize(); |
131 | 0 | memcpy(pDstBase, pSrcBase, newBuf->getVertexSize()); |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | | // Update vertex data |
136 | | // Increase vertex count according to num splits |
137 | 0 | mVData->vertexCount += vertexSplits.size(); |
138 | | // Flip bindings over to new buffers (old buffers released) |
139 | 0 | HardwareBufferManager::getSingleton().destroyVertexBufferBinding(mVData->vertexBufferBinding); |
140 | 0 | mVData->vertexBufferBinding = newBindings; |
141 | | |
142 | | // If vertex size requires 32bit index buffer |
143 | 0 | if (mVData->vertexCount > 65536) |
144 | 0 | { |
145 | 0 | for (auto idata : mIDataList) |
146 | 0 | { |
147 | | // check index size |
148 | 0 | HardwareIndexBufferSharedPtr srcbuf = idata->indexBuffer; |
149 | 0 | if (srcbuf->getType() == HardwareIndexBuffer::IT_16BIT) |
150 | 0 | { |
151 | 0 | size_t indexCount = srcbuf->getNumIndexes(); |
152 | | |
153 | | // convert index buffer to 32bit. |
154 | 0 | HardwareIndexBufferSharedPtr newBuf = |
155 | 0 | HardwareBufferManager::getSingleton().createIndexBuffer( |
156 | 0 | HardwareIndexBuffer::IT_32BIT, indexCount, |
157 | 0 | srcbuf->getUsage(), srcbuf->hasShadowBuffer()); |
158 | |
|
159 | 0 | HardwareBufferLockGuard srcBufLock(srcbuf, HardwareBuffer::HBL_NORMAL); |
160 | 0 | HardwareBufferLockGuard newBufLock(newBuf, HardwareBuffer::HBL_NORMAL); |
161 | 0 | uint16* pSrcBase = static_cast<uint16*>(srcBufLock.pData); |
162 | 0 | uint32* pBase = static_cast<uint32*>(newBufLock.pData); |
163 | |
|
164 | 0 | size_t j = 0; |
165 | 0 | while (j < indexCount) |
166 | 0 | { |
167 | 0 | *pBase++ = *pSrcBase++; |
168 | 0 | ++j; |
169 | 0 | } |
170 | | |
171 | | // assign new index buffer. |
172 | 0 | idata->indexBuffer = newBuf; |
173 | 0 | } |
174 | 0 | } |
175 | 0 | } |
176 | 0 | } |
177 | |
|
178 | 0 | } |
179 | | //--------------------------------------------------------------------- |
180 | | void TangentSpaceCalc::remapIndexes(Result& res) |
181 | 0 | { |
182 | 0 | for (size_t i = 0; i < mIDataList.size(); ++i) |
183 | 0 | { |
184 | |
|
185 | 0 | IndexData* idata = mIDataList[i]; |
186 | | // Now do index data |
187 | | // no new buffer required, same size but some triangles remapped |
188 | 0 | HardwareBufferLockGuard indexLock(idata->indexBuffer, HardwareBuffer::HBL_NORMAL); |
189 | 0 | if (idata->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT) |
190 | 0 | { |
191 | 0 | remapIndexes(static_cast<uint32*>(indexLock.pData), i, res); |
192 | 0 | } |
193 | 0 | else |
194 | 0 | { |
195 | 0 | remapIndexes(static_cast<uint16*>(indexLock.pData), i, res); |
196 | 0 | } |
197 | 0 | } |
198 | |
|
199 | 0 | } |
200 | | //--------------------------------------------------------------------- |
201 | | void TangentSpaceCalc::normaliseVertices() |
202 | 0 | { |
203 | | // Just run through our complete (possibly augmented) list of vertices |
204 | | // Normalise the tangents & binormals |
205 | 0 | for (auto & v : mVertexArray) |
206 | 0 | { |
207 | 0 | if (v.parity == 0) |
208 | 0 | v.parity = -1; // never leave it unset (orphan / degenerate-only verts) |
209 | | |
210 | | // Vertices only touched by degenerate-UV (e.g. tiny) triangles |
211 | | // can end up with a zero-length tangent/binormal. Exporting that |
212 | | // yields NaN later on (cross/normalize of a zero vector in the |
213 | | // shader), so replace it with an arbitrary vector orthogonal to |
214 | | // the normal before normalising. |
215 | 0 | if (v.tangent.squaredLength() < 1e-12f) |
216 | 0 | v.tangent = v.norm.perpendicular(); |
217 | 0 | if (v.binormal.squaredLength() < 1e-12f) |
218 | 0 | v.binormal = v.norm.crossProduct(v.tangent); |
219 | |
|
220 | 0 | v.tangent.normalise(); |
221 | 0 | v.binormal.normalise(); |
222 | | |
223 | | // Orthogonalise with the vertex normal since it's currently |
224 | | // orthogonal with the face normals, but will be close to ortho |
225 | | // Apply Gram-Schmidt orthogonalise |
226 | 0 | Vector3 temp = v.tangent; |
227 | 0 | v.tangent = temp - (v.norm * v.norm.dotProduct(temp)); |
228 | |
|
229 | 0 | temp = v.binormal; |
230 | 0 | v.binormal = temp - (v.norm * v.norm.dotProduct(temp)); |
231 | | |
232 | | // renormalize |
233 | 0 | v.tangent.normalise(); |
234 | 0 | v.binormal.normalise(); |
235 | |
|
236 | 0 | } |
237 | 0 | } |
238 | | //--------------------------------------------------------------------- |
239 | | void TangentSpaceCalc::processFaces(Result& result) |
240 | 0 | { |
241 | | // Quick pre-check for triangle strips / fans |
242 | 0 | for (auto & opType : mOpTypes) |
243 | 0 | { |
244 | 0 | if (opType != RenderOperation::OT_TRIANGLE_LIST) |
245 | 0 | { |
246 | | // Can't split strips / fans |
247 | 0 | setSplitMirrored(false); |
248 | 0 | setSplitRotated(false); |
249 | 0 | } |
250 | 0 | } |
251 | |
|
252 | 0 | for (size_t i = 0; i < mIDataList.size(); ++i) |
253 | 0 | { |
254 | 0 | IndexData* i_in = mIDataList[i]; |
255 | 0 | RenderOperation::OperationType opType = mOpTypes[i]; |
256 | | |
257 | | // Read data from buffers |
258 | 0 | HardwareIndexBufferSharedPtr ibuf = i_in->indexBuffer; |
259 | 0 | HardwareBufferLockGuard ibufLock(ibuf, HardwareBuffer::HBL_READ_ONLY); |
260 | 0 | uint16 *p16 = static_cast<uint16*>(ibufLock.pData) + i_in->indexStart; |
261 | 0 | uint32 *p32 = static_cast<uint32*>(ibufLock.pData) + i_in->indexStart; |
262 | 0 | bool isIT32 = ibuf->getType() == HardwareIndexBuffer::IT_32BIT; |
263 | | |
264 | | // current triangle |
265 | 0 | size_t vertInd[3] = { 0, 0, 0 }; |
266 | | // loop through all faces to calculate the tangents and normals |
267 | 0 | size_t faceCount = opType == RenderOperation::OT_TRIANGLE_LIST ? |
268 | 0 | i_in->indexCount / 3 : i_in->indexCount - 2; |
269 | 0 | for (size_t f = 0; f < faceCount; ++f) |
270 | 0 | { |
271 | 0 | bool invertOrdering = false; |
272 | | // Read 1 or 3 indexes depending on type |
273 | 0 | if (f == 0 || opType == RenderOperation::OT_TRIANGLE_LIST) |
274 | 0 | { |
275 | 0 | vertInd[0] = isIT32? *p32++ : *p16++; |
276 | 0 | vertInd[1] = isIT32? *p32++ : *p16++; |
277 | 0 | vertInd[2] = isIT32? *p32++ : *p16++; |
278 | 0 | } |
279 | 0 | else if (opType == RenderOperation::OT_TRIANGLE_FAN) |
280 | 0 | { |
281 | | // Element 0 always remains the same |
282 | | // Element 2 becomes element 1 |
283 | 0 | vertInd[1] = vertInd[2]; |
284 | | // read new into element 2 |
285 | 0 | vertInd[2] = isIT32? *p32++ : *p16++; |
286 | 0 | } |
287 | 0 | else if (opType == RenderOperation::OT_TRIANGLE_STRIP) |
288 | 0 | { |
289 | | // Shunt everything down one, but also invert the ordering on |
290 | | // odd numbered triangles (== even numbered i's) |
291 | | // we interpret front as anticlockwise all the time but strips alternate |
292 | 0 | if (f & 0x1) |
293 | 0 | { |
294 | | // odd tris (index starts at 3, 5, 7) |
295 | 0 | invertOrdering = true; |
296 | 0 | } |
297 | 0 | vertInd[0] = vertInd[1]; |
298 | 0 | vertInd[1] = vertInd[2]; |
299 | 0 | vertInd[2] = isIT32? *p32++ : *p16++; |
300 | 0 | } |
301 | | |
302 | | // deal with strip inversion of winding |
303 | 0 | size_t localVertInd[3]; |
304 | 0 | localVertInd[0] = vertInd[0]; |
305 | 0 | if (invertOrdering) |
306 | 0 | { |
307 | 0 | localVertInd[1] = vertInd[2]; |
308 | 0 | localVertInd[2] = vertInd[1]; |
309 | 0 | } |
310 | 0 | else |
311 | 0 | { |
312 | 0 | localVertInd[1] = vertInd[1]; |
313 | 0 | localVertInd[2] = vertInd[2]; |
314 | 0 | } |
315 | | |
316 | | |
317 | | // For each triangle |
318 | | // Calculate tangent & binormal per triangle |
319 | | // Note these are not normalised, are weighted by UV area |
320 | 0 | Vector3 faceTsU, faceTsV, faceNorm; |
321 | 0 | calculateFaceTangentSpace(localVertInd, faceTsU, faceTsV, faceNorm); |
322 | | |
323 | | // Skip invalid UV space triangles |
324 | 0 | if (faceTsU.isZeroLength() || faceTsV.isZeroLength()) |
325 | 0 | continue; |
326 | | |
327 | 0 | addFaceTangentSpaceToVertices(i, f, localVertInd, faceTsU, faceTsV, faceNorm, result); |
328 | |
|
329 | 0 | } |
330 | 0 | } |
331 | |
|
332 | 0 | } |
333 | | //--------------------------------------------------------------------- |
334 | | void TangentSpaceCalc::addFaceTangentSpaceToVertices( |
335 | | size_t indexSet, size_t faceIndex, size_t *localVertInd, |
336 | | const Vector3& faceTsU, const Vector3& faceTsV, const Vector3& faceNorm, |
337 | | Result& result) |
338 | 0 | { |
339 | | // Calculate parity for this triangle |
340 | 0 | int faceParity = calculateParity(faceTsU, faceTsV, faceNorm); |
341 | | // Now add these to each vertex referenced by the face |
342 | 0 | for (int v = 0; v < 3; ++v) |
343 | 0 | { |
344 | | // index 0 is vertex we're calculating, 1 and 2 are the others |
345 | | |
346 | | // We want to re-weight these by the angle the face makes with the vertex |
347 | | // in order to obtain tessellation-independent results |
348 | 0 | Real angleWeight = calculateAngleWeight(localVertInd[v], |
349 | 0 | localVertInd[(v+1)%3], localVertInd[(v+2)%3]); |
350 | | |
351 | |
|
352 | 0 | VertexInfo* vertex = &(mVertexArray[localVertInd[v]]); |
353 | | |
354 | | // check parity (0 means not set) |
355 | | // Locate parity-version of vertex index, or create if doesn't exist |
356 | | // If parity-version of vertex index was different, record alteration |
357 | | // in triangle remap |
358 | | // in vertex split list |
359 | 0 | bool splitVertex = false; |
360 | 0 | size_t reusedOppositeParity = 0; |
361 | 0 | bool splitBecauseOfParity = false; |
362 | 0 | bool newVertex = false; |
363 | 0 | if (!vertex->parity) |
364 | 0 | { |
365 | | // init |
366 | 0 | vertex->parity = faceParity; |
367 | 0 | newVertex = true; |
368 | 0 | } |
369 | 0 | if (mSplitMirrored) |
370 | 0 | { |
371 | 0 | if (!newVertex && faceParity != calculateParity(vertex->tangent, vertex->binormal, vertex->norm))//vertex->parity != faceParity) |
372 | 0 | { |
373 | | // Check for existing alternative parity |
374 | 0 | if (vertex->oppositeParityIndex) |
375 | 0 | { |
376 | | // Ok, have already split this vertex because of parity |
377 | | // Use the same one again |
378 | 0 | reusedOppositeParity = vertex->oppositeParityIndex; |
379 | 0 | vertex = &(mVertexArray[reusedOppositeParity]); |
380 | 0 | } |
381 | 0 | else |
382 | 0 | { |
383 | 0 | splitVertex = true; |
384 | 0 | splitBecauseOfParity = true; |
385 | |
|
386 | 0 | LogManager::getSingleton().stream(LML_TRIVIAL) |
387 | 0 | << "TSC parity split - Vpar: " << vertex->parity |
388 | 0 | << " Fpar: " << faceParity |
389 | 0 | << " faceTsU: " << faceTsU |
390 | 0 | << " faceTsV: " << faceTsV |
391 | 0 | << " faceNorm: " << faceNorm |
392 | 0 | << " vertTsU:" << vertex->tangent |
393 | 0 | << " vertTsV:" << vertex->binormal |
394 | 0 | << " vertNorm:" << vertex->norm; |
395 | |
|
396 | 0 | } |
397 | 0 | } |
398 | 0 | } |
399 | |
|
400 | 0 | if (mSplitRotated) |
401 | 0 | { |
402 | | |
403 | | // deal with excessive tangent space rotations as well as mirroring |
404 | | // same kind of split behaviour appropriate |
405 | 0 | if (!newVertex && !splitVertex) |
406 | 0 | { |
407 | | // If more than 90 degrees, split |
408 | 0 | Vector3 uvCurrent = vertex->tangent + vertex->binormal; |
409 | | |
410 | | // project down to the plane (plane normal = face normal) |
411 | 0 | Vector3 vRotHalf = uvCurrent - faceNorm; |
412 | 0 | vRotHalf *= faceNorm.dotProduct(uvCurrent); |
413 | |
|
414 | 0 | if ((faceTsU + faceTsV).dotProduct(vRotHalf) < 0.0f) |
415 | 0 | { |
416 | 0 | splitVertex = true; |
417 | 0 | } |
418 | 0 | } |
419 | 0 | } |
420 | |
|
421 | 0 | if (splitVertex) |
422 | 0 | { |
423 | 0 | size_t newVertexIndex = mVertexArray.size(); |
424 | 0 | VertexSplit splitInfo(localVertInd[v], newVertexIndex); |
425 | 0 | result.vertexSplits.push_back(splitInfo); |
426 | | // re-point opposite parity |
427 | 0 | if (splitBecauseOfParity) |
428 | 0 | { |
429 | 0 | vertex->oppositeParityIndex = newVertexIndex; |
430 | 0 | } |
431 | | // copy old values but reset tangent space |
432 | 0 | VertexInfo locVertex = *vertex; |
433 | 0 | locVertex.tangent = Vector3::ZERO; |
434 | 0 | locVertex.binormal = Vector3::ZERO; |
435 | 0 | locVertex.parity = faceParity; |
436 | 0 | mVertexArray.push_back(locVertex); |
437 | 0 | result.indexesRemapped.push_back(IndexRemap(indexSet, faceIndex, splitInfo)); |
438 | |
|
439 | 0 | vertex = &(mVertexArray[newVertexIndex]); |
440 | |
|
441 | 0 | } |
442 | 0 | else if (reusedOppositeParity) |
443 | 0 | { |
444 | | // didn't split again, but we do need to record the re-used remapping |
445 | 0 | VertexSplit splitInfo(localVertInd[v], reusedOppositeParity); |
446 | 0 | result.indexesRemapped.push_back(IndexRemap(indexSet, faceIndex, splitInfo)); |
447 | |
|
448 | 0 | } |
449 | | |
450 | | // Add weighted tangent & binormal |
451 | 0 | vertex->tangent += (faceTsU * angleWeight); |
452 | 0 | vertex->binormal += (faceTsV * angleWeight); |
453 | | |
454 | |
|
455 | 0 | } |
456 | |
|
457 | 0 | } |
458 | | //--------------------------------------------------------------------- |
459 | | int TangentSpaceCalc::calculateParity(const Vector3& u, const Vector3& v, const Vector3& n) |
460 | 0 | { |
461 | | // Note that this parity is the reverse of what you'd expect - this is |
462 | | // because the 'V' texture coordinate is actually left handed |
463 | 0 | if (u.crossProduct(v).dotProduct(n) >= 0.0f) |
464 | 0 | return -1; |
465 | 0 | else |
466 | 0 | return 1; |
467 | |
|
468 | 0 | } |
469 | | //--------------------------------------------------------------------- |
470 | | void TangentSpaceCalc::calculateFaceTangentSpace(const size_t* vertInd, |
471 | | Vector3& tsU, Vector3& tsV, Vector3& tsN) |
472 | 0 | { |
473 | 0 | const VertexInfo& v0 = mVertexArray[vertInd[0]]; |
474 | 0 | const VertexInfo& v1 = mVertexArray[vertInd[1]]; |
475 | 0 | const VertexInfo& v2 = mVertexArray[vertInd[2]]; |
476 | 0 | Vector2 deltaUV1 = v1.uv - v0.uv; |
477 | 0 | Vector2 deltaUV2 = v2.uv - v0.uv; |
478 | 0 | Vector3 deltaPos1 = v1.pos - v0.pos; |
479 | 0 | Vector3 deltaPos2 = v2.pos - v0.pos; |
480 | | |
481 | | // face normal |
482 | 0 | tsN = deltaPos1.crossProduct(deltaPos2); |
483 | 0 | tsN.normalise(); |
484 | | |
485 | |
|
486 | 0 | Real uvarea = deltaUV1.crossProduct(deltaUV2) * 0.5f; |
487 | 0 | if (Math::RealEqual(uvarea, 0.0f)) |
488 | 0 | { |
489 | | // no tangent, null uv area |
490 | 0 | tsU = tsV = Vector3::ZERO; |
491 | 0 | } |
492 | 0 | else |
493 | 0 | { |
494 | | |
495 | | // Normalise by uvarea |
496 | 0 | Real a = deltaUV2.y / uvarea; |
497 | 0 | Real b = -deltaUV1.y / uvarea; |
498 | 0 | Real c = -deltaUV2.x / uvarea; |
499 | 0 | Real d = deltaUV1.x / uvarea; |
500 | |
|
501 | 0 | tsU = (deltaPos1 * a) + (deltaPos2 * b); |
502 | 0 | tsU.normalise(); |
503 | |
|
504 | 0 | tsV = (deltaPos1 * c) + (deltaPos2 * d); |
505 | 0 | tsV.normalise(); |
506 | |
|
507 | 0 | Real abs_uvarea = Math::Abs(uvarea); |
508 | 0 | tsU *= abs_uvarea; |
509 | 0 | tsV *= abs_uvarea; |
510 | | |
511 | | // tangent (tsU) and binormal (tsV) are now weighted by uv area |
512 | | |
513 | |
|
514 | 0 | } |
515 | |
|
516 | 0 | } |
517 | | //--------------------------------------------------------------------- |
518 | | Real TangentSpaceCalc::calculateAngleWeight(size_t vidx0, size_t vidx1, size_t vidx2) |
519 | 0 | { |
520 | 0 | const VertexInfo& v0 = mVertexArray[vidx0]; |
521 | 0 | const VertexInfo& v1 = mVertexArray[vidx1]; |
522 | 0 | const VertexInfo& v2 = mVertexArray[vidx2]; |
523 | |
|
524 | 0 | Vector3 diff0 = v1.pos - v0.pos; |
525 | 0 | Vector3 diff1 = v2.pos - v1.pos; |
526 | | |
527 | | // Weight is just the angle - larger == better |
528 | 0 | return diff0.angleBetween(diff1).valueRadians(); |
529 | |
|
530 | 0 | } |
531 | | //--------------------------------------------------------------------- |
532 | | void TangentSpaceCalc::populateVertexArray(unsigned short sourceTexCoordSet) |
533 | 0 | { |
534 | | // Just pull data out into more friendly structures |
535 | 0 | VertexDeclaration *dcl = mVData->vertexDeclaration; |
536 | 0 | VertexBufferBinding *bind = mVData->vertexBufferBinding; |
537 | | |
538 | | // Get the incoming UV element |
539 | 0 | const auto* uvElem = dcl->findElementBySemantic(VES_TEXTURE_COORDINATES, sourceTexCoordSet); |
540 | |
|
541 | 0 | if (!uvElem || uvElem->getType() != VET_FLOAT2) |
542 | 0 | { |
543 | 0 | OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, |
544 | 0 | "No 2D texture coordinates with selected index, cannot calculate tangents"); |
545 | 0 | } |
546 | | |
547 | | // find a normal buffer |
548 | 0 | const auto* normElem = dcl->findElementBySemantic(VES_NORMAL); |
549 | 0 | if (!normElem) |
550 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, |
551 | 0 | "No vertex normals found, cannot calculate tangents"); |
552 | | |
553 | 0 | HardwareVertexBufferSharedPtr uvBuf, posBuf, normBuf; |
554 | 0 | unsigned char *pUvBase, *pPosBase, *pNormBase; |
555 | 0 | size_t uvInc, posInc, normInc; |
556 | |
|
557 | 0 | uvBuf = bind->getBuffer(uvElem->getSource()); |
558 | 0 | pUvBase = static_cast<unsigned char*>( |
559 | 0 | uvBuf->lock(HardwareBuffer::HBL_READ_ONLY)); |
560 | 0 | uvInc = uvBuf->getVertexSize(); |
561 | | // offset for vertex start |
562 | 0 | pUvBase += mVData->vertexStart * uvInc; |
563 | | |
564 | | // find position |
565 | 0 | const VertexElement *posElem = dcl->findElementBySemantic(VES_POSITION); |
566 | 0 | if (posElem->getSource() == uvElem->getSource()) |
567 | 0 | { |
568 | 0 | pPosBase = pUvBase; |
569 | 0 | posInc = uvInc; |
570 | 0 | } |
571 | 0 | else |
572 | 0 | { |
573 | | // A different buffer |
574 | 0 | posBuf = bind->getBuffer(posElem->getSource()); |
575 | 0 | pPosBase = static_cast<unsigned char*>( |
576 | 0 | posBuf->lock(HardwareBuffer::HBL_READ_ONLY)); |
577 | 0 | posInc = posBuf->getVertexSize(); |
578 | | // offset for vertex start |
579 | 0 | pPosBase += mVData->vertexStart * posInc; |
580 | 0 | } |
581 | |
|
582 | 0 | if (normElem->getSource() == uvElem->getSource()) |
583 | 0 | { |
584 | 0 | pNormBase = pUvBase; |
585 | 0 | normInc = uvInc; |
586 | 0 | } |
587 | 0 | else if (normElem->getSource() == posElem->getSource()) |
588 | 0 | { |
589 | | // normals are in the same buffer as position |
590 | | // this condition arises when an animated(skeleton) mesh is not built with |
591 | | // an edge list buffer ie no shadows being used. |
592 | 0 | pNormBase = pPosBase; |
593 | 0 | normInc = posInc; |
594 | 0 | } |
595 | 0 | else |
596 | 0 | { |
597 | | // A different buffer |
598 | 0 | normBuf = bind->getBuffer(normElem->getSource()); |
599 | 0 | pNormBase = static_cast<unsigned char*>( |
600 | 0 | normBuf->lock(HardwareBuffer::HBL_READ_ONLY)); |
601 | 0 | normInc = normBuf->getVertexSize(); |
602 | | // offset for vertex start |
603 | 0 | pNormBase += mVData->vertexStart * normInc; |
604 | 0 | } |
605 | | |
606 | | // Preinitialise vertex info |
607 | 0 | mVertexArray.clear(); |
608 | 0 | mVertexArray.resize(mVData->vertexCount); |
609 | |
|
610 | 0 | float* pFloat; |
611 | 0 | VertexInfo* vInfo = &(mVertexArray[0]); |
612 | 0 | for (size_t v = 0; v < mVData->vertexCount; ++v, ++vInfo) |
613 | 0 | { |
614 | 0 | posElem->baseVertexPointerToElement(pPosBase, &pFloat); |
615 | 0 | vInfo->pos.x = *pFloat++; |
616 | 0 | vInfo->pos.y = *pFloat++; |
617 | 0 | vInfo->pos.z = *pFloat++; |
618 | 0 | pPosBase += posInc; |
619 | |
|
620 | 0 | normElem->baseVertexPointerToElement(pNormBase, &pFloat); |
621 | 0 | vInfo->norm.x = *pFloat++; |
622 | 0 | vInfo->norm.y = *pFloat++; |
623 | 0 | vInfo->norm.z = *pFloat++; |
624 | 0 | pNormBase += normInc; |
625 | |
|
626 | 0 | uvElem->baseVertexPointerToElement(pUvBase, &pFloat); |
627 | 0 | vInfo->uv.x = *pFloat++; |
628 | 0 | vInfo->uv.y = *pFloat++; |
629 | 0 | pUvBase += uvInc; |
630 | | |
631 | |
|
632 | 0 | } |
633 | | |
634 | | // unlock buffers |
635 | 0 | uvBuf->unlock(); |
636 | 0 | if (posBuf) |
637 | 0 | { |
638 | 0 | posBuf->unlock(); |
639 | 0 | } |
640 | 0 | if (normBuf) |
641 | 0 | { |
642 | 0 | normBuf->unlock(); |
643 | 0 | } |
644 | |
|
645 | 0 | } |
646 | | //--------------------------------------------------------------------- |
647 | | void TangentSpaceCalc::insertTangents(Result& res, |
648 | | VertexElementSemantic targetSemantic, unsigned short sourceTexCoordSet, |
649 | | unsigned short index) |
650 | 0 | { |
651 | | // Make a new tangents semantic or find an existing one |
652 | 0 | VertexDeclaration *vDecl = mVData->vertexDeclaration ; |
653 | 0 | VertexBufferBinding *vBind = mVData->vertexBufferBinding ; |
654 | |
|
655 | 0 | const VertexElement *tangentsElem = vDecl->findElementBySemantic(targetSemantic, index); |
656 | 0 | VertexElementType tangentsType = mStoreParityInW ? VET_FLOAT4 : VET_FLOAT3; |
657 | |
|
658 | 0 | OgreAssert(!tangentsElem || tangentsElem->getType() == tangentsType, |
659 | 0 | "Target semantic set already exists but is not of the right size, therefore cannot contain " |
660 | 0 | "tangents. You should delete this existing entry first"); |
661 | | |
662 | 0 | HardwareVertexBufferSharedPtr targetBuffer, origBuffer; |
663 | 0 | unsigned char* pSrc = NULL; |
664 | |
|
665 | 0 | if (!tangentsElem) |
666 | 0 | { |
667 | | // To be most efficient with our vertex streams, |
668 | | // tack the new tangents onto the same buffer as the |
669 | | // source texture coord set |
670 | 0 | const VertexElement* prevTexCoordElem = |
671 | 0 | mVData->vertexDeclaration->findElementBySemantic( |
672 | 0 | VES_TEXTURE_COORDINATES, sourceTexCoordSet); |
673 | 0 | if (!prevTexCoordElem) |
674 | 0 | { |
675 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, |
676 | 0 | "Cannot locate the first texture coordinate element to " |
677 | 0 | "which to append the new tangents.", |
678 | 0 | "Mesh::orgagniseTangentsBuffer"); |
679 | 0 | } |
680 | | // Find the buffer associated with this element |
681 | 0 | origBuffer = mVData->vertexBufferBinding->getBuffer( |
682 | 0 | prevTexCoordElem->getSource()); |
683 | | // Now create a new buffer, which includes the previous contents |
684 | | // plus extra space for the 3D coords |
685 | 0 | targetBuffer = HardwareBufferManager::getSingleton().createVertexBuffer( |
686 | 0 | origBuffer->getVertexSize() + VertexElement::getTypeSize(tangentsType), |
687 | 0 | origBuffer->getNumVertices(), |
688 | 0 | origBuffer->getUsage(), |
689 | 0 | origBuffer->hasShadowBuffer() ); |
690 | | // Add the new element |
691 | 0 | tangentsElem = &(vDecl->addElement( |
692 | 0 | prevTexCoordElem->getSource(), |
693 | 0 | origBuffer->getVertexSize(), |
694 | 0 | tangentsType, |
695 | 0 | targetSemantic, |
696 | 0 | index)); |
697 | | // Set up the source pointer |
698 | 0 | pSrc = static_cast<unsigned char*>( |
699 | 0 | origBuffer->lock(HardwareBuffer::HBL_READ_ONLY)); |
700 | | // Rebind the new buffer |
701 | 0 | vBind->setBinding(prevTexCoordElem->getSource(), targetBuffer); |
702 | 0 | } |
703 | 0 | else |
704 | 0 | { |
705 | | // space already there |
706 | 0 | origBuffer = mVData->vertexBufferBinding->getBuffer( |
707 | 0 | tangentsElem->getSource()); |
708 | 0 | targetBuffer = origBuffer; |
709 | 0 | } |
710 | | |
711 | 0 | auto pDest = static_cast<uint8*>( |
712 | 0 | targetBuffer->lock(pSrc ? HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_WRITE_ONLY)); |
713 | 0 | size_t origVertSize = origBuffer->getVertexSize(); |
714 | 0 | size_t newVertSize = targetBuffer->getVertexSize(); |
715 | 0 | for (size_t v = 0; v < origBuffer->getNumVertices(); ++v) |
716 | 0 | { |
717 | 0 | if (pSrc) |
718 | 0 | { |
719 | | // Copy original vertex data as well |
720 | 0 | memcpy(pDest, pSrc, origVertSize); |
721 | 0 | pSrc += origVertSize; |
722 | 0 | } |
723 | | // Write in the tangent |
724 | 0 | float* pTangent; |
725 | 0 | tangentsElem->baseVertexPointerToElement(pDest, &pTangent); |
726 | 0 | VertexInfo& vertInfo = mVertexArray[v]; |
727 | 0 | *pTangent++ = vertInfo.tangent.x; |
728 | 0 | *pTangent++ = vertInfo.tangent.y; |
729 | 0 | *pTangent++ = vertInfo.tangent.z; |
730 | 0 | if (mStoreParityInW) |
731 | 0 | *pTangent++ = (float)vertInfo.parity; |
732 | | |
733 | | // Next target vertex |
734 | 0 | pDest += newVertSize; |
735 | |
|
736 | 0 | } |
737 | 0 | targetBuffer->unlock(); |
738 | |
|
739 | 0 | if (pSrc) |
740 | 0 | { |
741 | 0 | origBuffer->unlock(); |
742 | 0 | } |
743 | 0 | } |
744 | | |
745 | | } |