Coverage Report

Created: 2025-07-11 06:36

/src/ogre/OgreMain/include/OgreTangentSpaceCalc.h
Line
Count
Source (jump to first uncovered line)
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
#ifndef _OgreTangentSpaceCalc_H_
29
#define _OgreTangentSpaceCalc_H_
30
31
#include "OgrePrerequisites.h"
32
#include "OgreRenderOperation.h"
33
#include "OgreVector.h"
34
#include "OgreHeaderPrefix.h"
35
36
namespace Ogre
37
{
38
39
    /** \addtogroup Core
40
    *  @{
41
    */
42
    /** \addtogroup Math
43
    *  @{
44
    */
45
    /** Class for calculating a tangent space basis.
46
    */
47
    class _OgreExport TangentSpaceCalc
48
    {
49
    public:
50
        TangentSpaceCalc();
51
52
        typedef std::pair<size_t, size_t> VertexSplit;
53
54
        /// Information about a remapped index
55
        struct IndexRemap
56
        {
57
            /// Index data set (can be >0 if more than one index data was added)
58
            size_t indexSet;
59
            /// The position in the index buffer that's affected
60
            size_t faceIndex;
61
            /// The old and new vertex index
62
            VertexSplit splitVertex;
63
        
64
0
            IndexRemap() {} // to keep container happy
65
0
            IndexRemap(size_t i, size_t f, const VertexSplit& s) : indexSet(i), faceIndex(f), splitVertex(s) {}
66
        };
67
        /** List of indexes that were remapped (split vertices).
68
        */
69
        typedef std::list<IndexRemap> IndexRemapList;
70
71
        typedef std::list<VertexSplit> VertexSplits;
72
73
        /// The result of having built a tangent space basis
74
        struct Result
75
        {
76
            /** A list of vertex indices which were split off into new vertices
77
                because of mirroring. First item in each pair is the source vertex 
78
                index, the second value is the split vertex index.
79
            */
80
            VertexSplits vertexSplits;
81
            /** A list of indexes which were affected by splits. You can use this if you have other
82
                triangle-based data which you will need to alter to match. */
83
            IndexRemapList indexesRemapped;
84
        };
85
86
        /// Reset the calculation object
87
        void clear();
88
89
        /** Set the incoming vertex data (which will be modified) */
90
        void setVertexData(VertexData* v_in);
91
92
        /** Add a set of index data that references the vertex data.
93
            This might be modified if there are vertex splits.
94
        */
95
        void addIndexData(IndexData* i_in, RenderOperation::OperationType opType = RenderOperation::OT_TRIANGLE_LIST);
96
97
        /** Sets whether to store tangent space parity in the W of a 4-component tangent or not.
98
99
            The default element format to use is VET_FLOAT3 which is enough to accurately 
100
            deal with tangents that do not involve any texture coordinate mirroring. 
101
            If you wish to allow UV mirroring in your model, you must enable 4-component
102
            tangents using this method, and the 'w' coordinate will be populated
103
            with the parity of the triangle (+1 or -1), which will allow you to generate
104
            the bitangent properly.
105
        @param enabled true to enable 4-component tangents (default false). If you enable
106
            this, you will probably also want to enable mirror splitting (see setSplitMirrored), 
107
            and your shader must understand how to deal with the parity.
108
        */
109
0
        void setStoreParityInW(bool enabled) { mStoreParityInW = enabled; }
110
111
        /**  Gets whether to store tangent space parity in the W of a 4-component tangent or not. */
112
0
        bool getStoreParityInW() const { return mStoreParityInW; }
113
114
        /** Sets whether or not to split vertices when a mirrored tangent space
115
            transition is detected (matrix parity differs).
116
117
            This defaults to 'off' because it's the safest option; tangents will be
118
            interpolated in all cases even if they don't agree around a vertex, so
119
            artefacts will be smoothed out. When you're using art assets of 
120
            unknown quality this can avoid extra seams on the visible surface. 
121
            However, if your artists are good, they will be hiding texture seams
122
            in folds of the model and thus you can turn this option on, which will
123
            prevent the results of those seams from getting smoothed into other
124
            areas, which is exactly what you want.
125
        @note This option is automatically disabled if you provide any strip or
126
            fan based geometry.
127
        */
128
0
        void setSplitMirrored(bool split) { mSplitMirrored = split; }
129
        
130
        /** Gets whether or not to split vertices when a mirrored tangent space
131
            transition is detected.
132
        */
133
0
        bool getSplitMirrored() const { return mSplitMirrored; }
134
135
        /** Sets whether or not to split vertices when tangent space rotates
136
            more than 90 degrees around a vertex.
137
138
            This defaults to 'off' because it's the safest option; tangents will be
139
            interpolated in all cases even if they don't agree around a vertex, so
140
            artefacts will be smoothed out. When you're using art assets of 
141
            unknown quality this can avoid extra seams on the visible surface. 
142
            However, if your artists are good, they will be hiding texture inconsistencies
143
            in folds of the model and thus you can turn this option on, which will
144
            prevent the results of those seams from getting smoothed into other
145
            areas, which is exactly what you want.
146
        @note This option is automatically disabled if you provide any strip or
147
            fan based geometry.
148
        */
149
0
        void setSplitRotated(bool split) { mSplitRotated = split; }
150
        /** Sets whether or not to split vertices when tangent space rotates
151
        more than 90 degrees around a vertex.
152
        */
153
0
        bool getSplitRotated() const { return mSplitRotated; }
154
155
        /** Build a tangent space basis from the provided data.
156
157
            Only indexed triangle lists are allowed. Strips and fans cannot be
158
            supported because it may be necessary to split the geometry up to 
159
            respect deviances in the tangent space basis better.
160
        @param sourceTexCoordSet The texture coordinate index which should be used as the source
161
            of 2D texture coordinates, with which to calculate the tangents.
162
        @return
163
            A structure containing the results of the tangent space build. Vertex data
164
            will always be modified but it's also possible that the index data
165
            could be adjusted. This happens when mirroring is used on a mesh, which
166
            causes the tangent space to be inverted on opposite sides of an edge.
167
            This is discontinuous, therefore the vertices have to be split along
168
            this edge, resulting in new vertices.
169
        */
170
        Result build(unsigned short sourceTexCoordSet = 0);
171
172
        OGRE_DEPRECATED Result build(VertexElementSemantic targetSemantic, unsigned short sourceTexCoordSet = 0,
173
                                     unsigned short index = 0)
174
0
        {
175
0
            OgreAssert(targetSemantic == VES_TANGENT && index == 0, "Invalid Parameters");
176
0
            return build(sourceTexCoordSet);
177
0
        }
178
179
    private:
180
181
        VertexData* mVData;
182
        typedef std::vector<IndexData*> IndexDataList;
183
        typedef std::vector<RenderOperation::OperationType> OpTypeList;
184
        IndexDataList mIDataList;
185
        OpTypeList mOpTypes;
186
        bool mSplitMirrored;
187
        bool mSplitRotated;
188
        bool mStoreParityInW;
189
190
191
        struct VertexInfo
192
        {
193
            Vector3 pos;
194
            Vector3 norm;
195
            Vector2 uv;
196
            Vector3 tangent;
197
            Vector3 binormal;
198
            // Which way the tangent space is oriented (+1 / -1) (set on first time found)
199
            int parity;
200
            // What index the opposite parity vertex copy is at (0 if not created yet)
201
            size_t oppositeParityIndex;
202
203
0
            VertexInfo() : tangent(Vector3::ZERO), binormal(Vector3::ZERO), 
204
0
                parity(0), oppositeParityIndex(0) {}
205
        };
206
        typedef std::vector<VertexInfo> VertexInfoArray;
207
        VertexInfoArray mVertexArray;
208
209
        void extendBuffers(VertexSplits& splits);
210
        void insertTangents(Result& res,
211
            VertexElementSemantic targetSemantic, 
212
            unsigned short sourceTexCoordSet, unsigned short index);
213
214
        void populateVertexArray(unsigned short sourceTexCoordSet);
215
        void processFaces(Result& result);
216
        /// Calculate face tangent space, U and V are weighted by UV area, N is normalised
217
        void calculateFaceTangentSpace(const size_t* vertInd, Vector3& tsU, Vector3& tsV, Vector3& tsN);
218
        Real calculateAngleWeight(size_t v0, size_t v1, size_t v2);
219
        int calculateParity(const Vector3& u, const Vector3& v, const Vector3& n);
220
        void addFaceTangentSpaceToVertices(size_t indexSet, size_t faceIndex, size_t *localVertInd, 
221
            const Vector3& faceTsU, const Vector3& faceTsV, const Vector3& faceNorm, Result& result);
222
        void normaliseVertices();
223
        void remapIndexes(Result& res);
224
        template <typename T>
225
        void remapIndexes(T* ibuf, size_t indexSet, Result& res)
226
0
        {
227
0
            for (auto & remap : res.indexesRemapped)
228
0
            {
229
                // Note that because this is a vertex split situation, and vertex
230
                // split is only for some faces, it's not a case of replacing all
231
                // instances of vertex index A with vertex index B
232
                // It actually matters which triangle we're talking about, so drive
233
                // the update from the face index
234
235
0
                if (remap.indexSet == indexSet)
236
0
                {
237
0
                    T* pBuf;
238
0
                    pBuf = ibuf + remap.faceIndex * 3;
239
240
0
                    for (int v = 0; v < 3; ++v, ++pBuf)
241
0
                    {
242
0
                        if (*pBuf == remap.splitVertex.first)
243
0
                        {
244
0
                            *pBuf = (T)remap.splitVertex.second;
245
0
                        }
246
0
                    }
247
0
                }
248
249
250
0
            }
251
0
        }
Unexecuted instantiation: void Ogre::TangentSpaceCalc::remapIndexes<unsigned int>(unsigned int*, unsigned long, Ogre::TangentSpaceCalc::Result&)
Unexecuted instantiation: void Ogre::TangentSpaceCalc::remapIndexes<unsigned short>(unsigned short*, unsigned long, Ogre::TangentSpaceCalc::Result&)
252
        
253
254
    };
255
    /** @} */
256
    /** @} */
257
258
}
259
260
#include "OgreHeaderSuffix.h"
261
262
#endif