Fix issue in Rocket.lua script.
[Cafu-Engine.git] / Libs / Math3D / BezierPatch.cpp
blob55946f3858e6d120e6a15d0716ddd204b386e35a
1 /*
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.
5 */
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;
42 #endif
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()
67 : Width(0),
68 Height(0)
73 template<class T> BezierPatchT<T>::BezierPatchT(unsigned long Width_, unsigned long Height_, const ArrayT< Vector3T<T> >& Coords_)
74 : Width(Width_),
75 Height(Height_)
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_)
87 : Width(Width_),
88 Height(Height_)
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()
102 assert(Width >=3);
103 assert(Height>=3);
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];
121 bool IsGood[3][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);
127 Vector3T<T> Axes[3];
129 IsGood[i][j]=ComputeTangentSpaceInSubPatch(sp_i, sp_j, T(i)/2, T(j)/2, Axes);
130 if (!IsGood[i][j]) continue;
132 v.Normal +=Axes[0];
133 v.TangentS+=Axes[1];
134 v.TangentT+=Axes[2];
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++)
148 if (!IsGood[i][j])
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];
158 if (WrapsHorz())
160 Vector3T<T> Temp;
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;
170 if (WrapsVert())
172 Vector3T<T> 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()
194 // NOTES:
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.
198 #if 0
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);
223 if (Length!=0.0)
225 Normal/=Length;
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;
244 return;
248 #endif
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];
268 bool good[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);
274 good [k]=false;
276 for (int dist=1; dist<=3; dist++)
278 int x=i+Neighbours[k][0]*dist;
279 int y=j+Neighbours[k][1]*dist;
281 if (wrapWidth)
283 if (x< 0) x=iWidth-1+x;
284 else if (x>=iWidth) x=1+x-iWidth;
287 if (wrapHeight)
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]);
301 if (acLen>T(0.2))
303 // This is a "good" edge.
304 Around_Coords [k]/=acLen;
305 Around_TexCoords[k]=myNormalize(Around_TexCoords[k], T(0.0));
306 good [k]=true;
307 break;
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)
358 assert(Width >=3);
359 assert(Height>=3);
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)
371 unsigned long i;
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
428 j-=2;
431 // vertical subdivisions
432 for (unsigned long j=0; j+2<Height; j+=2)
434 unsigned long i;
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
486 j-=2;
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)
496 VertexT Left, Right;
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)
508 VertexT Left, Right;
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)
534 assert(Width >=3);
535 assert(Height>=3);
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.
567 Width =TargetWidth;
568 Height=TargetHeight;
569 Mesh =TargetMesh;
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++)
591 unsigned long j;
593 for (j=0; j<Height; j++)
594 if ((GetVertex(i, j).Coord-GetVertex(i+1, j).Coord).GetLengthSqr() > MaxLengthSqr)
595 break;
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));
618 i--;
621 for (unsigned long j=0; j+1<Height; j++)
623 unsigned long i;
625 for (i=0; i<Width; i++)
626 if ((GetVertex(i, j).Coord-GetVertex(i, j+1).Coord).GetLengthSqr() > MaxLengthSqr)
627 break;
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));
650 j--;
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);
662 T Area =0;
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??
686 return Area;
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];
703 Mesh =NewMesh;
704 Width =NewWidth;
705 Height=NewHeight;
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);
715 const T u_2=u*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;
727 VertexT Result;
729 const T v_0=(1-v)*(1-v);
730 const T v_1=2*v*(1-v);
731 const T v_2=v*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;
739 return Result;
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
745 SubDivsHorz+=1;
746 SubDivsVert+=1;
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++)
782 T MaxSqrDist=0;
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);
799 j--;
803 for (unsigned long j=1; j<Height-1; j++)
805 T MaxSqrDist=0;
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);
822 j--;
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
869 if (d<0)
871 Axes[1]=-Axes[1];
872 Axes[2]=-Axes[2];
875 const T a0=length(Axes[0]);
877 if (a0<0.000001f) return false;
879 Axes[0]/=a0;
880 myNormalize(Axes[1], T(0.0));
881 myNormalize(Axes[2], T(0.0));
883 return true;
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))
892 return false;
894 return true;
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))
903 return false;
905 return true;
909 template class BezierPatchT<float>;
910 template class BezierPatchT<double>;