2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
7 #include "BezierPatch.hpp"
10 using namespace cf::math
;
13 #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER<1300)
15 template class cf::math::BezierPatchT
<float>; // VC++ 6 requires the cf::math:: here,
16 template class cf::math::BezierPatchT
<double>; // even though "using" is used above.
19 template<float> void BezierPatchT
<float>::VertexT::Average(const VertexT
& A
, const VertexT
& B
)
21 const float onehalf
=0.5f
;
23 Coord
=(A
.Coord
+B
.Coord
)*onehalf
;
24 TexCoord
=(A
.TexCoord
+B
.TexCoord
)*onehalf
;
25 Normal
=(A
.Normal
+B
.Normal
)*onehalf
;
26 TangentS
=(A
.TangentS
+B
.TangentS
)*onehalf
;
27 TangentT
=(A
.TangentT
+B
.TangentT
)*onehalf
;
31 template<double> void BezierPatchT
<double>::VertexT::Average(const VertexT
& A
, const VertexT
& B
)
33 const double onehalf
=0.5;
35 Coord
=(A
.Coord
+B
.Coord
)*onehalf
;
36 TexCoord
=(A
.TexCoord
+B
.TexCoord
)*onehalf
;
37 Normal
=(A
.Normal
+B
.Normal
)*onehalf
;
38 TangentS
=(A
.TangentS
+B
.TangentS
)*onehalf
;
39 TangentT
=(A
.TangentT
+B
.TangentT
)*onehalf
;
45 /// Returns the unit vector of A if the length of A is greater than Epsilon, the null vector otherwise.
46 template<class T
> static Vector3T
<T
> myNormalize(const Vector3T
<T
>& A
, const T Epsilon
)
48 const T Length
=length(A
);
50 return (Length
>Epsilon
) ? A
/Length
: Vector3T
<T
>();
54 template<class T
> void BezierPatchT
<T
>::VertexT::Average(const VertexT
& A
, const VertexT
& B
)
56 const T onehalf
=T(0.5);
58 Coord
=(A
.Coord
+B
.Coord
)*onehalf
;
59 TexCoord
=(A
.TexCoord
+B
.TexCoord
)*onehalf
;
60 Normal
=(A
.Normal
+B
.Normal
)*onehalf
;
61 TangentS
=(A
.TangentS
+B
.TangentS
)*onehalf
;
62 TangentT
=(A
.TangentT
+B
.TangentT
)*onehalf
;
66 template<class T
> BezierPatchT
<T
>::BezierPatchT()
73 template<class T
> BezierPatchT
<T
>::BezierPatchT(unsigned long Width_
, unsigned long Height_
, const ArrayT
< Vector3T
<T
> >& Coords_
)
77 Mesh
.PushBackEmpty(Width
*Height
);
79 for (unsigned long VertexNr
=0; VertexNr
<Mesh
.Size(); VertexNr
++)
81 Mesh
[VertexNr
].Coord
=Coords_
[VertexNr
];
86 template<class T
> BezierPatchT
<T
>::BezierPatchT(unsigned long Width_
, unsigned long Height_
, const ArrayT
< Vector3T
<T
> >& Coords_
, const ArrayT
< Vector3T
<T
> >& TexCoords_
)
90 Mesh
.PushBackEmpty(Width
*Height
);
92 for (unsigned long VertexNr
=0; VertexNr
<Mesh
.Size(); VertexNr
++)
94 Mesh
[VertexNr
].Coord
=Coords_
[VertexNr
];
95 Mesh
[VertexNr
].TexCoord
=TexCoords_
[VertexNr
];
100 template<class T
> void BezierPatchT
<T
>::ComputeTangentSpace()
104 assert((Width
% 2)==1);
105 assert((Height
% 2)==1);
107 // Make sure that we start with 0-vectors everywhere.
108 for (unsigned long VertexNr
=0; VertexNr
<Mesh
.Size(); VertexNr
++)
110 Mesh
[VertexNr
].Normal
=Vector3T
<T
>();
111 Mesh
[VertexNr
].TangentS
=Vector3T
<T
>();
112 Mesh
[VertexNr
].TangentT
=Vector3T
<T
>();
115 // Loop over all 3x3 sub-patches.
116 // Note that whereever two 3x3 sub-patches share common vertices, we have to average their tangent-space axes!
117 for (unsigned long sp_j
=0; sp_j
+2<Height
; sp_j
+=2)
118 for (unsigned long sp_i
=0; sp_i
+2<Width
; sp_i
+=2)
120 Vector3T
<T
> AverageOfGood
[3];
123 for (unsigned long j
=0; j
<3; j
++)
124 for (unsigned long i
=0; i
<3; i
++)
126 VertexT
& v
=GetVertex(sp_i
+i
, sp_j
+j
);
129 IsGood
[i
][j
]=ComputeTangentSpaceInSubPatch(sp_i
, sp_j
, T(i
)/2, T(j
)/2, Axes
);
130 if (!IsGood
[i
][j
]) continue;
136 AverageOfGood
[0]+=Axes
[0];
137 AverageOfGood
[1]+=Axes
[1];
138 AverageOfGood
[2]+=Axes
[2];
141 AverageOfGood
[0]=myNormalize(AverageOfGood
[0], T(0.0));
142 AverageOfGood
[1]=myNormalize(AverageOfGood
[1], T(0.0));
143 AverageOfGood
[2]=myNormalize(AverageOfGood
[2], T(0.0));
145 // Use the average of the non-degenerate axes whereever the axes were degenerate.
146 for (unsigned long j
=0; j
<3; j
++)
147 for (unsigned long i
=0; i
<3; i
++)
150 VertexT
& v
=GetVertex(sp_i
+i
, sp_j
+j
);
152 v
.Normal
+=AverageOfGood
[0];
153 v
.TangentS
+=AverageOfGood
[1];
154 v
.TangentT
+=AverageOfGood
[2];
162 for (unsigned long j
=0; j
<Height
; j
++)
164 Temp
=GetVertex(0, j
).Normal
+GetVertex(Width
-1, j
).Normal
; GetVertex(0, j
).Normal
=Temp
; GetVertex(Width
-1, j
).Normal
=Temp
;
165 Temp
=GetVertex(0, j
).TangentS
+GetVertex(Width
-1, j
).TangentS
; GetVertex(0, j
).TangentS
=Temp
; GetVertex(Width
-1, j
).TangentS
=Temp
;
166 Temp
=GetVertex(0, j
).TangentT
+GetVertex(Width
-1, j
).TangentT
; GetVertex(0, j
).TangentT
=Temp
; GetVertex(Width
-1, j
).TangentT
=Temp
;
174 for (unsigned long i
=0; i
<Width
; i
++)
176 Temp
=GetVertex(i
, 0).Normal
+GetVertex(i
, Height
-1).Normal
; GetVertex(i
, 0).Normal
=Temp
; GetVertex(i
, Height
-1).Normal
=Temp
;
177 Temp
=GetVertex(i
, 0).TangentS
+GetVertex(i
, Height
-1).TangentS
; GetVertex(i
, 0).TangentS
=Temp
; GetVertex(i
, Height
-1).TangentS
=Temp
;
178 Temp
=GetVertex(i
, 0).TangentT
+GetVertex(i
, Height
-1).TangentT
; GetVertex(i
, 0).TangentT
=Temp
; GetVertex(i
, Height
-1).TangentT
=Temp
;
182 // Renormalize all the interpolated tangent space axes.
183 for (unsigned long VertexNr
=0; VertexNr
<Mesh
.Size(); VertexNr
++)
185 Mesh
[VertexNr
].Normal
=myNormalize(Mesh
[VertexNr
].Normal
, T(0.0));
186 Mesh
[VertexNr
].TangentS
=myNormalize(Mesh
[VertexNr
].TangentS
, T(0.0));
187 Mesh
[VertexNr
].TangentT
=myNormalize(Mesh
[VertexNr
].TangentT
, T(0.0));
192 template<class T
> void BezierPatchT
<T
>::ComputeTangentSpace_Obsolete()
195 // 1) This method does not care whether the mesh has already been subdivided or not - it works either way.
196 // 2) Special cases like wrapping and degeneracies are properly taken into account.
199 // I disabled this section of code because it's not well possible to generalize it to also deal with the tangents.
200 // Moreover, I find it a lot nicer to not treat coplanar patches as a special case anyway.
202 // If all points are coplanar (they all lie in a common plane), set all normals to that plane.
204 const Vector3T
<T
> Extent_Horz
=GetVertex(Width
-1, 0).Coord
- GetVertex(0, 0).Coord
;
205 const Vector3T
<T
> Extent_Diag
=GetVertex(Width
-1, Height
-1).Coord
- GetVertex(0, 0).Coord
;
206 const Vector3T
<T
> Extent_Vert
=GetVertex( 0, Height
-1).Coord
- GetVertex(0, 0).Coord
;
208 Vector3T
<T
> Normal
=cross(Extent_Diag
, Extent_Horz
);
210 if (Normal
.GetLengthSqr()==0.0)
212 Normal
=cross(Extent_Vert
, Extent_Horz
);
214 if (Normal
.GetLengthSqr()==0.0)
216 Normal
=cross(Extent_Vert
, Extent_Diag
);
217 // Note that if the patch is wrapped, the Normal may still be not valid here...
221 const T Length
=length(Normal
);
227 // Distance0 is the distance of vertex (0, 0) to the plane through the origin with normal vector Normal.
228 const T Distance0
=dot(Mesh
[0].Coord
, Normal
);
229 unsigned long VertexNr
;
231 for (VertexNr
=1; VertexNr
<Mesh
.Size(); VertexNr
++)
233 const T Distance
=dot(Mesh
[VertexNr
].Coord
, Normal
);
235 if (fabs(Distance
-Distance0
)>T(1.0) /*COPLANAR_EPSILON*/) break;
238 if (VertexNr
==Mesh
.Size())
240 // All mesh vertices are coplanar (lie in a common plane).
241 for (unsigned long VertexNr
=0; VertexNr
<Mesh
.Size(); VertexNr
++)
242 Mesh
[VertexNr
].Normal
=Normal
;
251 // First check whether the patch mesh "wraps" in any direction.
252 // This is done by checking of the left and right / top and bottom borders are identical.
253 // If so, the tangent space should be averaged ("smoothed") across the wrap seam.
254 const bool wrapWidth
=WrapsHorz();
255 const bool wrapHeight
=WrapsVert();
257 const int iWidth
=int(Width
);
258 const int iHeight
=int(Height
);
260 static const int Neighbours
[8][2]={ {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} };
262 for (int i
=0; i
<iWidth
; i
++)
264 for (int j
=0; j
<iHeight
; j
++)
266 Vector3T
<T
> Around_Coords
[8];
267 Vector3T
<T
> Around_TexCoords
[8];
270 for (unsigned long k
=0; k
<8; k
++)
272 Around_Coords
[k
]=Vector3T
<T
>(0, 0, 0);
273 Around_TexCoords
[k
]=Vector3T
<T
>(0, 0, 0);
276 for (int dist
=1; dist
<=3; dist
++)
278 int x
=i
+Neighbours
[k
][0]*dist
;
279 int y
=j
+Neighbours
[k
][1]*dist
;
283 if (x
< 0) x
=iWidth
-1+x
;
284 else if (x
>=iWidth
) x
=1+x
-iWidth
;
289 if (y
< 0) y
=iHeight
-1+y
;
290 else if (y
>=iHeight
) y
=1+y
-iHeight
;
293 if (x
<0 || x
>=iWidth
|| y
<0 || y
>=iHeight
) break; // edge of patch
296 Around_Coords
[k
]=GetVertex(x
, y
).Coord
-GetVertex(i
, j
).Coord
;
297 Around_TexCoords
[k
]=GetVertex(x
, y
).TexCoord
-GetVertex(i
, j
).TexCoord
;
299 const T acLen
=length(Around_Coords
[k
]);
303 // This is a "good" edge.
304 Around_Coords
[k
]/=acLen
;
305 Around_TexCoords
[k
]=myNormalize(Around_TexCoords
[k
], T(0.0));
310 // Else the edge is degenerate, continue to get more dist.
315 // Finally compute the averages of the neighbourhood around the current vertex.
316 GetVertex(i
, j
).Normal
=Vector3T
<T
>(0, 0, 0);
317 GetVertex(i
, j
).TangentS
=Vector3T
<T
>(0, 0, 0);
318 GetVertex(i
, j
).TangentT
=Vector3T
<T
>(0, 0, 0);
320 for (unsigned long k
=0; k
<8; k
++)
322 if (!good
[ k
]) continue;
323 if (!good
[(k
+1) & 7]) continue;
325 const Vector3T
<T
>& Edge01
=Around_Coords
[(k
+1) & 7];
326 const Vector3T
<T
>& Edge02
=Around_Coords
[ k
];
328 const Vector3T
<T
> Normal
=cross(Edge02
, Edge01
);
329 const T NormalL
=length(Normal
);
331 if (NormalL
==0.0) continue;
333 GetVertex(i
, j
).Normal
+=Normal
/NormalL
;
335 // Understanding what's going on here is easy. The key statement is
336 // "The tangent vector is parallel to the direction of increasing S on a parametric surface."
337 // First, there is a short explanation in "The Cg Tutorial", chapter 8.
338 // Second, I have drawn a simple figure that leads to a simple 2x2 system of Gaussian equations, see my TechArchive.
339 const Vector3T
<T
>& uv01
=Around_TexCoords
[(k
+1) & 7];
340 const Vector3T
<T
>& uv02
=Around_TexCoords
[ k
];
341 const T f
=uv01
.x
*uv02
.y
-uv01
.y
*uv02
.x
>0.0 ? T(1.0f
) : T(-1.0f
);
343 GetVertex(i
, j
).TangentS
+=myNormalize(Edge02
.GetScaled(-uv01
.y
*f
) + Edge01
.GetScaled(uv02
.y
*f
), T(0.0));
344 GetVertex(i
, j
).TangentT
+=myNormalize(Edge02
.GetScaled( uv01
.x
*f
) - Edge01
.GetScaled(uv02
.x
*f
), T(0.0));
347 GetVertex(i
, j
).Normal
=myNormalize(GetVertex(i
, j
).Normal
, T(0.0));
348 GetVertex(i
, j
).TangentS
=myNormalize(GetVertex(i
, j
).TangentS
, T(0.0));
349 GetVertex(i
, j
).TangentT
=myNormalize(GetVertex(i
, j
).TangentT
, T(0.0));
355 // "Automatic" subdivision.
356 template<class T
> void BezierPatchT
<T
>::Subdivide(T MaxError
, T MaxLength
, bool OptimizeFlat
)
360 assert((Width
% 2)==1);
361 assert((Height
% 2)==1);
363 const T maxHorizontalErrorSqr
=MaxError
*MaxError
;
364 const T maxVerticalErrorSqr
=MaxError
*MaxError
;
365 const T maxLengthSqr
=MaxLength
*MaxLength
;
368 // horizontal subdivisions
369 for (unsigned long j
=0; j
+2<Width
; j
+=2)
373 // check subdivided midpoints against control points
374 for (i
=0; i
<Height
; i
++)
376 // Consider a point triple A, B, C, where A and B are the curve endpoints and C is the control point.
377 // Let L=(A+C)/2, R=(B+C)/2, N=(L+R)/2, H=(A+B)/2. Then HN==NC==HC/2==(2C-A-B)/4.
378 // Thus, take HN (the "error" in world units) as a measure for tesselation error,
379 // as is cares both for the world size of the patch (ie. is the diameter of a pipe very big or very small),
380 // as well as the severity of the deformation (almost flat curves get less triangles than very tight ones).
381 const Vector3T
<T
>& A
=GetVertex(j
, i
).Coord
;
382 const Vector3T
<T
>& B
=GetVertex(j
+2, i
).Coord
;
383 const Vector3T
<T
>& C
=GetVertex(j
+1, i
).Coord
;
385 const Vector3T
<T
> AC
=C
-A
;
386 const Vector3T
<T
> CB
=B
-C
;
387 const Vector3T
<T
> N
=(A
+B
+C
*T(2.0))*T(0.25);
389 // if the span length is too long, force a subdivision
390 if (MaxLength
>0 && (AC
.GetLengthSqr()>maxLengthSqr
|| CB
.GetLengthSqr()>maxLengthSqr
)) break;
392 // const T lenHN = length((C * T(2.0) - A - B) * T(0.25));
393 // const T lenAB = length(B - A);
395 // see if this midpoint is off far enough to subdivide
396 if ((C
-N
).GetLengthSqr()>maxHorizontalErrorSqr
) break;
398 // This is an alternative to the above `if` test that seems to work very well,
399 // but is prone to infinite looping if A, B and C are very close to each other, but not quite the same
400 // (e.g. `lenAB > T(0.0001)` in the first half of the test freezes CaWE when loading the TechDemo map).
401 // Maybe we should use something conservative like `lenAB > T(0.1) && lenAC > T(0.1) && lenBC > T(0.1)`?
402 // if (lenAB > T(0.001) &&
403 // lenHN > T(0.2) * lenAB) break;
406 if (i
==Height
) continue; // didn't need subdivision
408 SetMeshSize(Width
+2, Height
);
410 // Insert two columns and replace the peak a la deCasteljau.
411 for (i
=0; i
<Height
; i
++)
413 VertexT Left
, Right
, Center
;
415 Left
.Average(GetVertex(j
, i
), GetVertex(j
+1, i
));
416 Right
.Average(GetVertex(j
+1, i
), GetVertex(j
+2, i
));
417 Center
.Average(Left
, Right
);
419 for (unsigned long k
=Width
-1; k
>j
+3; k
--)
420 GetVertex(k
, i
)=GetVertex(k
-2, i
);
422 GetVertex(j
+1, i
)=Left
;
423 GetVertex(j
+2, i
)=Center
;
424 GetVertex(j
+3, i
)=Right
;
427 // back up and recheck this set again, it may need more subdivision
431 // vertical subdivisions
432 for (unsigned long j
=0; j
+2<Height
; j
+=2)
436 // check subdivided midpoints against control points
437 for (i
=0; i
<Width
; i
++)
439 const Vector3T
<T
>& A
=GetVertex(i
, j
).Coord
;
440 const Vector3T
<T
>& B
=GetVertex(i
, j
+2).Coord
;
441 const Vector3T
<T
>& C
=GetVertex(i
, j
+1).Coord
;
443 const Vector3T
<T
> AC
=C
-A
;
444 const Vector3T
<T
> CB
=B
-C
;
445 const Vector3T
<T
> N
=(A
+B
+C
*T(2.0))*T(0.25);
447 // if the span length is too long, force a subdivision
448 if (MaxLength
>0 && (AC
.GetLengthSqr()>maxLengthSqr
|| CB
.GetLengthSqr()>maxLengthSqr
)) break;
450 // const T lenHN = length((C * T(2.0) - A - B) * T(0.25));
451 // const T lenAB = length(B - A);
453 // see if this midpoint is off far enough to subdivide
454 if ((C
-N
).GetLengthSqr()>maxVerticalErrorSqr
) break;
456 // This is an alternative to the above `if` test that seems to work very well,
457 // but is prone to infinite looping if A, B and C are very close to each other, but not quite the same
458 // (e.g. `lenAB > T(0.0001)` in the first half of the test freezes CaWE when loading the TechDemo map).
459 // Maybe we should use something conservative like `lenAB > T(0.1) && lenAC > T(0.1) && lenBC > T(0.1)`?
460 // if (lenAB > T(0.001) &&
461 // lenHN > T(0.2) * lenAB) break;
464 if (i
==Width
) continue; // didn't need subdivision
466 SetMeshSize(Width
, Height
+2);
468 // insert two columns and replace the peak
469 for (i
=0; i
<Width
; i
++)
471 VertexT Left
, Right
, Center
;
473 Left
.Average(GetVertex(i
, j
), GetVertex(i
, j
+1));
474 Right
.Average(GetVertex(i
, j
+1), GetVertex(i
, j
+2));
475 Center
.Average(Left
, Right
);
477 for (unsigned long k
=Height
-1; k
>j
+3; k
--)
478 GetVertex(i
, k
)=GetVertex(i
, k
-2);
480 GetVertex(i
, j
+1)=Left
;
481 GetVertex(i
, j
+2)=Center
;
482 GetVertex(i
, j
+3)=Right
;
485 // back up and recheck this set again, it may need more subdivision
490 // Move all the "approximation" control vertices onto the true shape of the curve.
491 // Note that the other vertices (the "interpolation" control vertices) already and always are on the curve per definition.
492 for (unsigned long i
=0; i
<Width
; i
++)
494 for (unsigned long j
=1; j
<Height
; j
+=2)
498 Left
.Average(GetVertex(i
, j
), GetVertex(i
, j
+1));
499 Right
.Average(GetVertex(i
, j
), GetVertex(i
, j
-1));
500 GetVertex(i
, j
).Average(Left
, Right
);
504 for (unsigned long j
=0; j
<Height
; j
++)
506 for (unsigned long i
=1; i
<Width
; i
+=2)
510 Left
.Average(GetVertex(i
, j
), GetVertex(i
+1, j
));
511 Right
.Average(GetVertex(i
, j
), GetVertex(i
-1, j
));
512 GetVertex(i
, j
).Average(Left
, Right
);
517 if (OptimizeFlat
) OptimizeFlatRowAndColumnStrips();
519 // Normalize all the lerped tangent space axes.
520 for (unsigned long VertexNr
=0; VertexNr
<Mesh
.Size(); VertexNr
++)
522 Mesh
[VertexNr
].Normal
=myNormalize(Mesh
[VertexNr
].Normal
, T(0.0));
523 Mesh
[VertexNr
].TangentS
=myNormalize(Mesh
[VertexNr
].TangentS
, T(0.0));
524 Mesh
[VertexNr
].TangentT
=myNormalize(Mesh
[VertexNr
].TangentT
, T(0.0));
527 // GenerateIndexes();
531 // "Explicit" subdivision.
532 template<class T
> void BezierPatchT
<T
>::Subdivide(unsigned long SubDivsHorz
, unsigned long SubDivsVert
, bool OptimizeFlat
)
536 assert((Width
% 2)==1);
537 assert((Height
% 2)==1);
539 const unsigned long TargetWidth
=((Width
-1)/2 * SubDivsHorz
)+1;
540 const unsigned long TargetHeight
=((Height
-1)/2 * SubDivsVert
)+1;
541 ArrayT
<VertexT
> TargetMesh
;
543 TargetMesh
.PushBackEmpty(TargetWidth
*TargetHeight
);
545 unsigned long baseCol
=0;
547 for (unsigned long i
=0; i
+2<Width
; i
+=2)
549 unsigned long baseRow
=0;
551 for (unsigned long j
=0; j
+2<Height
; j
+=2)
553 VertexT SubPatch
[3][3];
555 for (unsigned long k
=0; k
<3; k
++)
556 for (unsigned long l
=0; l
<3; l
++)
557 SubPatch
[k
][l
]=GetVertex(i
+k
, j
+l
);
559 SampleSinglePatch(SubPatch
, baseCol
, baseRow
, TargetWidth
, SubDivsHorz
, SubDivsVert
, TargetMesh
);
560 baseRow
+=SubDivsVert
;
563 baseCol
+=SubDivsHorz
;
566 // Copy the target mesh back into our mesh.
571 if (OptimizeFlat
) OptimizeFlatRowAndColumnStrips();
573 // Normalize all the lerped tangent space axes.
574 for (unsigned long VertexNr
=0; VertexNr
<Mesh
.Size(); VertexNr
++)
576 Mesh
[VertexNr
].Normal
=myNormalize(Mesh
[VertexNr
].Normal
, T(0.0));
577 Mesh
[VertexNr
].TangentS
=myNormalize(Mesh
[VertexNr
].TangentS
, T(0.0));
578 Mesh
[VertexNr
].TangentT
=myNormalize(Mesh
[VertexNr
].TangentT
, T(0.0));
581 // GenerateIndexes();
585 template<class T
> void BezierPatchT
<T
>::ForceLinearMaxLength(T MaxLength
)
587 const T MaxLengthSqr
=MaxLength
*MaxLength
;
589 for (unsigned long i
=0; i
+1<Width
; i
++)
593 for (j
=0; j
<Height
; j
++)
594 if ((GetVertex(i
, j
).Coord
-GetVertex(i
+1, j
).Coord
).GetLengthSqr() > MaxLengthSqr
)
597 // If the edges were all short enough, no need to subdivide - just consider the next column.
598 if (j
==Height
) continue;
600 // Make room for another column between i and i+1.
601 SetMeshSize(Width
+1, Height
);
603 for (j
=0; j
<Height
; j
++)
605 for (unsigned long k
=Width
-1; k
>i
+1; k
--)
606 GetVertex(k
, j
)=GetVertex(k
-1, j
);
608 // Compute the contents of the new column.
609 VertexT
& NewV
=GetVertex(i
+1, j
);
611 NewV
.Average(GetVertex(i
, j
), GetVertex(i
+2, j
));
613 NewV
.Normal
=myNormalize(NewV
.Normal
, T(0.0));
614 NewV
.TangentS
=myNormalize(NewV
.TangentS
, T(0.0));
615 NewV
.TangentT
=myNormalize(NewV
.TangentT
, T(0.0));
621 for (unsigned long j
=0; j
+1<Height
; j
++)
625 for (i
=0; i
<Width
; i
++)
626 if ((GetVertex(i
, j
).Coord
-GetVertex(i
, j
+1).Coord
).GetLengthSqr() > MaxLengthSqr
)
629 // If the edges were all short enough, no need to subdivide - just consider the next column.
630 if (i
==Width
) continue;
632 // Make room for another row between j and j+1.
633 SetMeshSize(Width
, Height
+1);
635 for (i
=0; i
<Width
; i
++)
637 for (unsigned long k
=Height
-1; k
>j
+1; k
--)
638 GetVertex(i
, k
)=GetVertex(i
, k
-1);
640 // Compute the contents of the new column.
641 VertexT
& NewV
=GetVertex(i
, j
+1);
643 NewV
.Average(GetVertex(i
, j
), GetVertex(i
, j
+2));
645 NewV
.Normal
=myNormalize(NewV
.Normal
, T(0.0));
646 NewV
.TangentS
=myNormalize(NewV
.TangentS
, T(0.0));
647 NewV
.TangentT
=myNormalize(NewV
.TangentT
, T(0.0));
653 // GenerateIndexes();
657 template<class T
> T BezierPatchT
<T
>::GetSurfaceAreaAtVertex(unsigned long i
, unsigned long j
) const
659 static const int Neighbours
[8][2]={ {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} };
661 const VertexT
& Center
=GetVertex(i
, j
);
664 for (unsigned long NeighbourNr
=0; NeighbourNr
<8; NeighbourNr
++)
666 const int Edge1_i
=int(i
)+Neighbours
[NeighbourNr
][0];
667 const int Edge1_j
=int(j
)+Neighbours
[NeighbourNr
][1];
669 const int Edge2_i
=int(i
)+Neighbours
[(NeighbourNr
+1) & 7][0];
670 const int Edge2_j
=int(j
)+Neighbours
[(NeighbourNr
+1) & 7][1];
672 if (Edge1_i
<0 || Edge1_i
>=int(Width
)) continue;
673 if (Edge1_j
<0 || Edge1_j
>=int(Height
)) continue;
675 if (Edge2_i
<0 || Edge2_i
>=int(Width
)) continue;
676 if (Edge2_j
<0 || Edge2_j
>=int(Height
)) continue;
678 // Note that we only take half of the edges into account - the other half "belongs" to other vertices!
679 Vector3T
<T
> Edge1
=(GetVertex(Edge1_i
, Edge1_j
).Coord
-Center
.Coord
)*0.5;
680 Vector3T
<T
> Edge2
=(GetVertex(Edge2_i
, Edge2_j
).Coord
-Center
.Coord
)*0.5;
682 Area
+=length(cross(Edge1
, Edge2
))/2.0f
;
683 // Area+=dot(Center.Normal, cross(Edge1, Edge2))/2.0f; // Vermutung: Dies ergibt die auf die Ebene (durch Center.Normal definiert) projezierte Fläche! Beweis??
690 /// Changes the size of the mesh to NewWidth times NewHeight.
691 template<class T
> void BezierPatchT
<T
>::SetMeshSize(unsigned long NewWidth
, unsigned long NewHeight
)
693 // Well, the implementation is really simple, stable, and covers all cases
694 // (mesh gets broader/narrower and/or heigher/lower), but it's slow, too...
695 ArrayT
<VertexT
> NewMesh
;
697 NewMesh
.PushBackEmpty(NewWidth
*NewHeight
);
699 for (unsigned long j
=0; j
<NewHeight
&& j
<Height
; j
++)
700 for (unsigned long i
=0; i
<NewWidth
&& i
<Width
; i
++)
701 NewMesh
[j
*NewWidth
+i
]=Mesh
[j
*Width
+i
];
709 template<class T
> typename BezierPatchT
<T
>::VertexT BezierPatchT
<T
>::SampleSinglePatchPoint(const VertexT SubPatch
[3][3], const T u
, const T v
) const
711 VertexT SubColumnAtU
[3];
713 const T u_0
=(1-u
)*(1-u
);
714 const T u_1
=2*u
*(1-u
);
717 // Find the control points in u direction for the v coordinate.
718 for (unsigned long RowNr
=0; RowNr
<3; RowNr
++)
720 SubColumnAtU
[RowNr
].Coord
=SubPatch
[0][RowNr
].Coord
*u_0
+ SubPatch
[1][RowNr
].Coord
*u_1
+ SubPatch
[2][RowNr
].Coord
*u_2
;
721 SubColumnAtU
[RowNr
].TexCoord
=SubPatch
[0][RowNr
].TexCoord
*u_0
+ SubPatch
[1][RowNr
].TexCoord
*u_1
+ SubPatch
[2][RowNr
].TexCoord
*u_2
;
722 SubColumnAtU
[RowNr
].Normal
=SubPatch
[0][RowNr
].Normal
*u_0
+ SubPatch
[1][RowNr
].Normal
*u_1
+ SubPatch
[2][RowNr
].Normal
*u_2
;
723 SubColumnAtU
[RowNr
].TangentS
=SubPatch
[0][RowNr
].TangentS
*u_0
+ SubPatch
[1][RowNr
].TangentS
*u_1
+ SubPatch
[2][RowNr
].TangentS
*u_2
;
724 SubColumnAtU
[RowNr
].TangentT
=SubPatch
[0][RowNr
].TangentT
*u_0
+ SubPatch
[1][RowNr
].TangentT
*u_1
+ SubPatch
[2][RowNr
].TangentT
*u_2
;
729 const T v_0
=(1-v
)*(1-v
);
730 const T v_1
=2*v
*(1-v
);
733 Result
.Coord
=SubColumnAtU
[0].Coord
*v_0
+ SubColumnAtU
[1].Coord
*v_1
+ SubColumnAtU
[2].Coord
*v_2
;
734 Result
.TexCoord
=SubColumnAtU
[0].TexCoord
*v_0
+ SubColumnAtU
[1].TexCoord
*v_1
+ SubColumnAtU
[2].TexCoord
*v_2
;
735 Result
.Normal
=SubColumnAtU
[0].Normal
*v_0
+ SubColumnAtU
[1].Normal
*v_1
+ SubColumnAtU
[2].Normal
*v_2
;
736 Result
.TangentS
=SubColumnAtU
[0].TangentS
*v_0
+ SubColumnAtU
[1].TangentS
*v_1
+ SubColumnAtU
[2].TangentS
*v_2
;
737 Result
.TangentT
=SubColumnAtU
[0].TangentT
*v_0
+ SubColumnAtU
[1].TangentT
*v_1
+ SubColumnAtU
[2].TangentT
*v_2
;
743 template<class T
> void BezierPatchT
<T
>::SampleSinglePatch(const VertexT SubPatch
[3][3], unsigned long baseCol
, unsigned long baseRow
, unsigned long TargetWidth
, unsigned long SubDivsHorz
, unsigned long SubDivsVert
, ArrayT
<VertexT
>& TargetMesh
) const
748 for (unsigned long i
=0; i
<SubDivsHorz
; i
++)
750 for (unsigned long j
=0; j
<SubDivsVert
; j
++)
752 const T u
=T(i
)/(SubDivsHorz
-1);
753 const T v
=T(j
)/(SubDivsVert
-1);
755 TargetMesh
[(baseRow
+j
)*TargetWidth
+ baseCol
+i
]=SampleSinglePatchPoint(SubPatch
, u
, v
);
761 /// Returns the result of Point projected onto the vector from Start to End.
762 template<class T
> Vector3T
<T
> BezierPatchT
<T
>::ProjectPointOntoVector(const Vector3T
<T
>& Point
, const Vector3T
<T
>& Start
, const Vector3T
<T
>& End
) const
764 Vector3T
<T
> Vec
=End
-Start
;
765 const T Len
=length(Vec
);
767 if (Len
!=0) Vec
/=Len
;
769 return Start
+Vec
*dot(Point
-Start
, Vec
);
773 /// This method removes unnecessary vertices of "flat" sub-strips from the mesh.
774 /// That is, if a vertex is in the line through its left and right neighbour vertices,
775 /// and the same is true for all vertices in the same column (or row) of that vertex,
776 /// the entire column (or row) can be removed, because the left and right vertices alone
777 /// are enough to define that flat column (or row).
778 template<class T
> void BezierPatchT
<T
>::OptimizeFlatRowAndColumnStrips()
780 for (unsigned long j
=1; j
<Width
-1; j
++)
784 for (unsigned long i
=0; i
<Height
; i
++)
786 const Vector3T
<T
> Offset
=GetVertex(j
, i
).Coord
-ProjectPointOntoVector(GetVertex(j
, i
).Coord
, GetVertex(j
-1, i
).Coord
, GetVertex(j
+1, i
).Coord
);
787 const T DistSqr
=Offset
.GetLengthSqr();
789 if (DistSqr
>MaxSqrDist
) MaxSqrDist
=DistSqr
;
792 if (MaxSqrDist
<=T(0.2*0.2))
794 for (unsigned long i
=0; i
<Height
; i
++)
795 for (unsigned long k
=j
; k
+1<Width
; k
++)
796 GetVertex(k
, i
)=GetVertex(k
+1, i
);
798 SetMeshSize(Width
-1, Height
);
803 for (unsigned long j
=1; j
<Height
-1; j
++)
807 for (unsigned long i
=0; i
<Width
; i
++)
809 const Vector3T
<T
> Offset
=GetVertex(i
, j
).Coord
-ProjectPointOntoVector(GetVertex(i
, j
).Coord
, GetVertex(i
, j
-1).Coord
, GetVertex(i
, j
+1).Coord
);
810 const T DistSqr
=Offset
.GetLengthSqr();
812 if (DistSqr
>MaxSqrDist
) MaxSqrDist
=DistSqr
;
815 if (MaxSqrDist
<T(0.2*0.2))
817 for (unsigned long i
=0; i
<Width
; i
++)
818 for (unsigned long k
=j
; k
+1<Height
; k
++)
819 GetVertex(i
, k
)=GetVertex(i
, k
+1);
821 SetMeshSize(Width
, Height
-1);
828 /// Computes the three tangent-space axes (the normal and the two tangent vectors)
829 /// of the 3x3 sub-patch whose upper left corner is at (sp_i, sp_j) at (s, t), where s and t are values between 0 and 1.
830 /// The method returns true on success, false on failure (the normal vector (Axes[0]) could not be computed because it is degenerate).
831 template<class T
> bool BezierPatchT
<T
>::ComputeTangentSpaceInSubPatch(unsigned long sp_i
, unsigned long sp_j
, const T s
, const T t
, Vector3T
<T
> Axes
[3]) const
833 const T B_s
[3]={ (1.0f
-s
)*(1.0f
-s
), 2.0f
*s
*(1.0f
-s
), s
*s
};
834 const T B_t
[3]={ (1.0f
-t
)*(1.0f
-t
), 2.0f
*t
*(1.0f
-t
), t
*t
};
836 const T B_s_
[3]={ 2.0f
*(s
-1.0f
), 2.0f
-4.0f
*s
, 2.0f
*s
}; // Derivation along s.
837 const T B_t_
[3]={ 2.0f
*(t
-1.0f
), 2.0f
-4.0f
*t
, 2.0f
*t
}; // Derivation along t.
839 Vector3T
<T
> TangentS
; // Tangents of the spatial patch surface.
840 Vector3T
<T
> TangentT
;
842 Vector3T
<T
> TexTangentS
; // Tangents of the texture image in 2D texture space.
843 Vector3T
<T
> TexTangentT
;
845 for (unsigned long i
=0; i
<3; i
++)
846 for (unsigned long j
=0; j
<3; j
++)
848 const VertexT
& v
=GetVertex(sp_i
+i
, sp_j
+j
);
850 // Origin =Origin +scale(v.Coord, B_s [i]*B_t [j]);
851 // TexCoord =TexCoord +scale(v.TexCoord, B_s [i]*B_t [j]);
853 TangentS
=TangentS
+scale(v
.Coord
, B_s_
[i
]*B_t
[j
]);
854 TangentT
=TangentT
+scale(v
.Coord
, B_s
[i
]*B_t_
[j
]);
856 TexTangentS
=TexTangentS
+scale(v
.TexCoord
, B_s_
[i
]*B_t
[j
]);
857 TexTangentT
=TexTangentT
+scale(v
.TexCoord
, B_s
[i
]*B_t_
[j
]);
861 // This is documented in my Tech Archive, see "Computing the tangent space basis vectors".
862 // Note that only the *sign* of d is really important (less its magnitude).
863 const T d
=TexTangentS
.x
*TexTangentT
.y
-TexTangentS
.y
*TexTangentT
.x
;
865 Axes
[0]=cross(TangentT
, TangentS
); // Normal
866 Axes
[1]=scale(TangentT
, -TexTangentS
.y
) + scale(TangentS
, TexTangentT
.y
); // Tangent
867 Axes
[2]=scale(TangentT
, TexTangentS
.x
) - scale(TangentS
, TexTangentT
.x
); // BiTangent
875 const T a0
=length(Axes
[0]);
877 if (a0
<0.000001f
) return false;
880 myNormalize(Axes
[1], T(0.0));
881 myNormalize(Axes
[2], T(0.0));
887 /// Returns whether the patch mesh wraps "in the width", i.e. if the left and right borders are identical.
888 template<class T
> bool BezierPatchT
<T
>::WrapsHorz() const
890 for (unsigned long j
=0; j
<Height
; j
++)
891 if ((GetVertex(0, j
).Coord
-GetVertex(Width
-1, j
).Coord
).GetLengthSqr() > T(1.0*1.0))
898 /// Returns whether the patch mesh wraps "in the height", i.e. if the top and bottom borders are identical.
899 template<class T
> bool BezierPatchT
<T
>::WrapsVert() const
901 for (unsigned long i
=0; i
<Width
; i
++)
902 if ((GetVertex(i
, 0).Coord
-GetVertex(i
, Height
-1).Coord
).GetLengthSqr() > T(1.0*1.0))
909 template class BezierPatchT
<float>;
910 template class BezierPatchT
<double>;