Coverage Report

Created: 2026-10-28 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}