File: PatchRenderables.h

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (254 lines) | stat: -rw-r--r-- 7,828 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/**
 * \file
 * These are the renderables that are used in the PatchNode/Patch class to draw
 * the patch onto the screen.
 */
#pragma once

#include <iterator>
#include "PatchTesselation.h"
#include "PatchControlInstance.h"

#include "render/RenderableGeometry.h"

class ITesselationIndexer
{
public:
    virtual ~ITesselationIndexer() {}

    virtual render::GeometryType getType() const = 0;

    // The number of indices generated by this indexer for the given tesselation
    virtual std::size_t getNumIndices(const PatchTesselation& tess) const = 0;

    // Generate the indices for the given tesselation, assigning them to the given insert iterator
    virtual void generateIndices(const PatchTesselation& tess, std::back_insert_iterator<std::vector<unsigned int>> outputIt) const = 0;
};

class TesselationIndexer_Triangles :
    public ITesselationIndexer
{
public:
    render::GeometryType getType() const override
    {
        return render::GeometryType::Triangles;
    }

    std::size_t getNumIndices(const PatchTesselation& tess) const override
    {
        return (tess.height - 1) * (tess.width - 1) * 6; // 6 => 2 triangles per quad
    }

    void generateIndices(const PatchTesselation& tess, std::back_insert_iterator<std::vector<unsigned int>> outputIt) const override
    {
        // Generate the indices to define the triangles in clockwise order
        for (std::size_t h = 0; h < tess.height - 1; ++h)
        {
            auto rowOffset = h * tess.width;

            for (std::size_t w = 0; w < tess.width - 1; ++w)
            {
                outputIt = static_cast<unsigned int>(rowOffset + w + tess.width);
                outputIt = static_cast<unsigned int>(rowOffset + w + 1);
                outputIt = static_cast<unsigned int>(rowOffset + w);

                outputIt = static_cast<unsigned int>(rowOffset + w + tess.width);
                outputIt = static_cast<unsigned int>(rowOffset + w + tess.width + 1);
                outputIt = static_cast<unsigned int>(rowOffset + w + 1);
            }
        }
    }
};

class TesselationIndexer_Quads :
    public ITesselationIndexer
{
public:
    render::GeometryType getType() const override
    {
        return render::GeometryType::Quads;
    }

    std::size_t getNumIndices(const PatchTesselation& tess) const override
    {
        return (tess.height - 1) * (tess.width - 1) * 4; // 4 indices per quad
    }

    void generateIndices(const PatchTesselation& tess, std::back_insert_iterator<std::vector<unsigned int>> outputIt) const override
    {
        for (std::size_t h = 0; h < tess.height - 1; ++h)
        {
            auto rowOffset = h * tess.width;

            for (std::size_t w = 0; w < tess.width - 1; ++w)
            {
                outputIt = static_cast<unsigned int>(rowOffset + w);
                outputIt = static_cast<unsigned int>(rowOffset + w + tess.width);
                outputIt = static_cast<unsigned int>(rowOffset + w + tess.width + 1);
                outputIt = static_cast<unsigned int>(rowOffset + w + 1);
            }
        }
    }
};

template<typename TesselationIndexerT>
class RenderablePatchTesselation :
    public render::RenderableGeometry
{
private:
    static_assert(std::is_base_of_v<ITesselationIndexer, TesselationIndexerT>, "Indexer must implement ITesselationIndexer");
    TesselationIndexerT _indexer;

    const PatchTesselation& _tess;
    bool _needsUpdate;

    bool _whiteVertexColour;

public:
    // When whiteVertexColour is set to true, all colour vertex attributes will be set to 1,1,1,1
    RenderablePatchTesselation(const PatchTesselation& tess, bool whiteVertexColour) :
        _tess(tess),
        _needsUpdate(true),
        _whiteVertexColour(whiteVertexColour)
    {}

    void queueUpdate()
    {
        _needsUpdate = true;
    }

protected:
    void updateGeometry() override
    {
        if (!_needsUpdate) return;

        _needsUpdate = false;

        if (_tess.height == 0 || _tess.width == 0)
        {
            clear();
            return;
        }

        // Generate the new index array
        std::vector<unsigned int> indices;
        indices.reserve(_indexer.getNumIndices(_tess));

        _indexer.generateIndices(_tess, std::back_inserter(indices));

        updateGeometryWithData(_indexer.getType(), getColouredVertices(), indices);
    }

    std::vector<render::RenderVertex> getColouredVertices()
    {
        std::vector<render::RenderVertex> vertices;
        vertices.reserve(_tess.vertices.size());

        for (const auto& vertex : _tess.vertices)
        {
            // Copy vertex data, but set the colour to 1,1,1,1
            vertices.push_back(render::RenderVertex(vertex.vertex, vertex.normal,
                vertex.texcoord, _whiteVertexColour ? Vector4{ 1, 1, 1, 1 } : vertex.colour, 
                vertex.tangent, vertex.bitangent));
        }

        return vertices;
    }
};

// Renders the wireframe lines between a patch's control points
class RenderablePatchLattice :
    public render::RenderableGeometry
{
private:
    const IPatch& _patch;
    const std::vector<PatchControlInstance>& _controlPoints;
    bool _needsUpdate;

public:
    RenderablePatchLattice(const IPatch& patch, const std::vector<PatchControlInstance>& controlPoints) :
        _patch(patch),
        _controlPoints(controlPoints),
        _needsUpdate(true)
    {}

    void queueUpdate()
    {
        _needsUpdate = true;
    }

protected:
    void updateGeometry() override
    {
        if (!_needsUpdate) return;

        _needsUpdate = false;

        auto width = _patch.getWidth();
        auto height = _patch.getHeight();
        assert(width * height == _controlPoints.size());

        std::vector<render::RenderVertex> vertices;
        vertices.reserve(_controlPoints.size());

        for (const auto& ctrl : _controlPoints)
        {
            vertices.push_back(render::RenderVertex(ctrl.control.vertex, { 0, 0, 1 }, ctrl.control.texcoord, { 1, 0.5, 0, 1 }));
        }

        // Generate the index array
        std::vector<unsigned int> indices;
        indices.reserve(((width * (height - 1)) + (height * (width - 1))) << 1);

        for (std::size_t h = 0; h < height - 1; ++h)
        {
            auto rowOffset = h * width;

            for (std::size_t w = 0; w < width - 1; ++w)
            {
                indices.push_back(static_cast<unsigned int>(rowOffset + w));
                indices.push_back(static_cast<unsigned int>(rowOffset + w + 1));

                indices.push_back(static_cast<unsigned int>(rowOffset + w));
                indices.push_back(static_cast<unsigned int>(rowOffset + w + width));
            }

            indices.push_back(static_cast<unsigned int>(rowOffset + width - 1));
            indices.push_back(static_cast<unsigned int>(rowOffset + width - 1 + width));
        }

        auto rowOffset = (height - 1) * width;

        for (std::size_t w = 0; w < width - 1; ++w)
        {
            indices.push_back(static_cast<unsigned int>(rowOffset + w));
            indices.push_back(static_cast<unsigned int>(rowOffset + w + 1));
        }

        assert(indices.size() == ((width * (height - 1)) + (height * (width - 1))) << 1);

        updateGeometryWithData(render::GeometryType::Lines, vertices, indices);
    }
};

// Represents the set of coloured points used to manipulate the control point matrix
class RenderablePatchControlPoints :
    public render::RenderableGeometry
{
private:
    bool _needsUpdate;

    const IPatch& _patch;
    const std::vector<PatchControlInstance>& _controlPoints;

public:
    RenderablePatchControlPoints(const IPatch& patch, const std::vector<PatchControlInstance>& controlPoints);

    void queueUpdate()
    {
        _needsUpdate = true;
    }

protected:
    void updateGeometry() override;
};