Repository navigation
Expand file tree
/
Copy pathBezier.h
More file actions
191 lines (160 loc) · 8.38 KB
/
Copy pathBezier.h
File metadata and controls
191 lines (160 loc) · 8.38 KB
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
//--------------------------------------------------------------------------------------
// File: Bezier.h
//
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// https://go.microsoft.com/fwlink/?LinkId=248929
// https://go.microsoft.com/fwlink/?LinkID=615561
//--------------------------------------------------------------------------------------
#pragma once
#include <array>
#include <algorithm>
#include <DirectXMath.h>
namespace Bezier
{
// Performs a cubic bezier interpolation between four control points,
// returning the value at the specified time (t ranges 0 to 1).
template<typename T>
inline T CubicInterpolate(T const& p1, T const& p2, T const& p3, T const& p4, float t) noexcept
{
return p1 * (1 - t) * (1 - t) * (1 - t) + p2 * 3 * t * (1 - t) * (1 - t) + p3 * 3 * t * t * (1 - t) + p4 * t * t * t;
}
template<>
inline DirectX::XMVECTOR CubicInterpolate(DirectX::XMVECTOR const& p1,
DirectX::XMVECTOR const& p2,
DirectX::XMVECTOR const& p3,
DirectX::XMVECTOR const& p4,
float t) noexcept
{
using namespace DirectX;
const XMVECTOR T0 = XMVectorReplicate((1 - t) * (1 - t) * (1 - t));
const XMVECTOR T1 = XMVectorReplicate(3 * t * (1 - t) * (1 - t));
const XMVECTOR T2 = XMVectorReplicate(3 * t * t * (1 - t));
const XMVECTOR T3 = XMVectorReplicate(t * t * t);
XMVECTOR Result = XMVectorMultiply(p1, T0);
Result = XMVectorMultiplyAdd(p2, T1, Result);
Result = XMVectorMultiplyAdd(p3, T2, Result);
Result = XMVectorMultiplyAdd(p4, T3, Result);
return Result;
}
// Computes the tangent of a cubic bezier curve at the specified time.
template<typename T>
inline T CubicTangent(T const& p1, T const& p2, T const& p3, T const& p4, float t) noexcept
{
return p1 * (-1 + 2 * t - t * t) + p2 * (1 - 4 * t + 3 * t * t) + p3 * (2 * t - 3 * t * t) + p4 * (t * t);
}
template<>
inline DirectX::XMVECTOR CubicTangent(DirectX::XMVECTOR const& p1,
DirectX::XMVECTOR const& p2,
DirectX::XMVECTOR const& p3,
DirectX::XMVECTOR const& p4,
float t) noexcept
{
using namespace DirectX;
const XMVECTOR T0 = XMVectorReplicate(-1 + 2 * t - t * t);
const XMVECTOR T1 = XMVectorReplicate(1 - 4 * t + 3 * t * t);
const XMVECTOR T2 = XMVectorReplicate(2 * t - 3 * t * t);
const XMVECTOR T3 = XMVectorReplicate(t * t);
XMVECTOR Result = XMVectorMultiply(p1, T0);
Result = XMVectorMultiplyAdd(p2, T1, Result);
Result = XMVectorMultiplyAdd(p3, T2, Result);
Result = XMVectorMultiplyAdd(p4, T3, Result);
return Result;
}
// Creates vertices for a patch that is tessellated at the specified level.
// Calls the specified outputVertex function for each generated vertex,
// passing the position, normal, and texture coordinate as parameters.
template<typename TOutputFunc>
void
CreatePatchVertices(_In_reads_(16) const DirectX::XMVECTOR patch[16], size_t tessellation, bool isMirrored, TOutputFunc outputVertex)
{
using namespace DirectX;
for (size_t i = 0; i <= tessellation; i++)
{
const float u = float(i) / float(tessellation);
for (size_t j = 0; j <= tessellation; j++)
{
const float v = float(j) / float(tessellation);
// Perform four horizontal bezier interpolations
// between the control points of this patch.
const XMVECTOR p1 = CubicInterpolate(patch[0], patch[1], patch[2], patch[3], u);
const XMVECTOR p2 = CubicInterpolate(patch[4], patch[5], patch[6], patch[7], u);
const XMVECTOR p3 = CubicInterpolate(patch[8], patch[9], patch[10], patch[11], u);
const XMVECTOR p4 = CubicInterpolate(patch[12], patch[13], patch[14], patch[15], u);
// Perform a vertical interpolation between the results of the
// previous horizontal interpolations, to compute the position.
const XMVECTOR position = CubicInterpolate(p1, p2, p3, p4, v);
// Perform another four bezier interpolations between the control
// points, but this time vertically rather than horizontally.
const XMVECTOR q1 = CubicInterpolate(patch[0], patch[4], patch[8], patch[12], v);
const XMVECTOR q2 = CubicInterpolate(patch[1], patch[5], patch[9], patch[13], v);
const XMVECTOR q3 = CubicInterpolate(patch[2], patch[6], patch[10], patch[14], v);
const XMVECTOR q4 = CubicInterpolate(patch[3], patch[7], patch[11], patch[15], v);
// Compute vertical and horizontal tangent vectors.
const XMVECTOR tangent1 = CubicTangent(p1, p2, p3, p4, v);
const XMVECTOR tangent2 = CubicTangent(q1, q2, q3, q4, u);
// Cross the two tangent vectors to compute the normal.
XMVECTOR normal = XMVector3Cross(tangent1, tangent2);
if (!XMVector3NearEqual(normal, XMVectorZero(), g_XMEpsilon))
{
normal = XMVector3Normalize(normal);
// If this patch is mirrored, we must invert the normal.
if (isMirrored)
{
normal = XMVectorNegate(normal);
}
}
else
{
// In a tidy and well constructed bezier patch, the preceding
// normal computation will always work. But the classic teapot
// model is not tidy or well constructed! At the top and bottom
// of the teapot, it contains degenerate geometry where a patch
// has several control points in the same place, which causes
// the tangent computation to fail and produce a zero normal.
// We 'fix' these cases by just hard-coding a normal that points
// either straight up or straight down, depending on whether we
// are on the top or bottom of the teapot. This is not a robust
// solution for all possible degenerate bezier patches, but hey,
// it's good enough to make the teapot work correctly!
normal = XMVectorSelect(g_XMIdentityR1, g_XMNegIdentityR1, XMVectorLess(position, XMVectorZero()));
}
// Compute the texture coordinate.
const float mirroredU = isMirrored ? 1 - u : u;
const XMVECTOR textureCoordinate = XMVectorSet(mirroredU, v, 0, 0);
// Output this vertex.
outputVertex(position, normal, textureCoordinate);
}
}
}
// Creates indices for a patch that is tessellated at the specified level.
// Calls the specified outputIndex function for each generated index value.
template<typename TOutputFunc>
void CreatePatchIndices(size_t tessellation, bool isMirrored, TOutputFunc outputIndex)
{
size_t stride = tessellation + 1;
for (size_t i = 0; i < tessellation; i++)
{
for (size_t j = 0; j < tessellation; j++)
{
// Make a list of six index values (two triangles).
std::array<size_t, 6> indices = {
i * stride + j,
(i + 1) * stride + j,
(i + 1) * stride + j + 1,
i * stride + j,
(i + 1) * stride + j + 1,
i * stride + j + 1,
};
// If this patch is mirrored, reverse indices to fix the winding order.
if (isMirrored)
{
std::reverse(indices.begin(), indices.end());
}
// Output these index values.
std::for_each(indices.begin(), indices.end(), outputIndex);
}
}
}
} // namespace Bezier