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 #ifndef CAFU_MATH_BEZIERPATCH_HPP_INCLUDED
8 #define CAFU_MATH_BEZIERPATCH_HPP_INCLUDED
10 #include "Math3D/Vector3.hpp"
11 #include "Templates/Array.hpp"
27 ArrayT< Vector3T<T> > Coords;
28 ArrayT< Vector3T<T> > TexCoords;
32 /// This class represents a mesh that approximates a Bezier patch.
33 /// Two invariants are possible:
34 /// a) Before one of the Subdivide() methods has been called, the mesh is considered to be a control mesh.
35 /// A control mesh consists alternatingly of interpolation and approximation vertices which are quasi a
36 /// composition of 3x3 sub-patches with shared borders. The width and height therefore must be odd numbers >= 3.
37 /// b) After one of the Subdivide() methods has been called, the mesh is just an approximation of the Bezier patch.
38 /// Contrary to a), all it's vertices lie on the patch curve.
44 /// Represents a single vertex.
47 Vector3T
<T
> Coord
; ///< Vertex coordinate.
48 Vector3T
<T
> TexCoord
; ///< Vertex texture coordinates.
49 Vector3T
<T
> Normal
; ///< Vertex normal.
50 Vector3T
<T
> TangentS
; ///< DOCTODO.
51 Vector3T
<T
> TangentT
; ///< DOCTODO.
53 /// Computes the values of this vertex as the average of the given vertices A and B.
54 void Average(const VertexT
& A
, const VertexT
& B
);
58 /// Constructor for creating an empty Bezier patch.
61 /// Constructor for creating a patch from given coordinates.
62 BezierPatchT(unsigned long Width_
, unsigned long Height_
, const ArrayT
< Vector3T
<T
> >& Coords_
);
64 /// Constructor for creating a patch from given coordinates and texture-coordinates.
65 BezierPatchT(unsigned long Width_
, unsigned long Height_
, const ArrayT
< Vector3T
<T
> >& Coords_
, const ArrayT
< Vector3T
<T
> >& TexCoords_
);
67 /// (Re-)computes the tangent space vectors (normal and tangents) for each vertex of the mesh.
68 /// This method must only be called *before* any of the Subdivide() methods is called, it does not work thereafter.
69 void ComputeTangentSpace();
71 /// (Re-)computes the tangent space vectors (normal and tangents) for each vertex of the mesh.
72 /// This method is supposed to be called before one of the Subdivide() methods is called, but it is actually independent of it.
73 /// That is, it does not matter whether this method is called first and Subdivide() second, or vice versa - both orders are
74 /// possible and valid, and they should even yield the same result.
75 /// OBSOLETE NOTE: This method does not really work reliable, the tangent space axes that it generates are frequently questionable...
76 /// Instead of debugging its code, I rather provide a completely new implementation in a seperate method.
77 void ComputeTangentSpace_Obsolete();
79 /// This method subdivides the patch "automatically", that is, by the given maximal error and length bounds.
80 /// @param MaxError The maximum allowed spatial distance between the computed approximation mesh and the true mathematical curve.
81 /// @param MaxLength The maximum side length that one mesh element may have.
82 /// @param OptimizeFlat If true unnecessary vertices from flat substrips are removed.
83 void Subdivide(T MaxError
, T MaxLength
, bool OptimizeFlat
=true);
85 /// Subdivides the patch "manually", that is, as often as explicitly stated by the parameter values.
86 /// @param SubDivsHorz The number of horizontal subdivisions that each 3x3 sub-patch is subdivided into.
87 /// @param SubDivsVert The number of vertical subdivisions that each 3x3 sub-patch is subdivided into.
88 /// @param OptimizeFlat If true unnecessary vertices from flat substrips are removed.
89 void Subdivide(unsigned long SubDivsHorz
, unsigned long SubDivsVert
, bool OptimizeFlat
=true);
91 /// Intended to be called *after* one of the Subdivide() methods has been called,
92 /// this methods linearly subdivides the rows and columns of the mesh so that the MaxLength is never exceeded.
93 /// This is useful/intended for obtaining meshes for lightmap computation purposes.
94 /// Note that this method quasi does the very opposite from what OptimizeFlatRowAndColumnStrips() does:
95 /// it inserts "flat" (linear) rows and columns of vertices.
96 /// Q: Couldn't we just call Subdivide(LargeNum, MaxLength, false); to achieve the same result?
97 /// A: No!! Two counter examples, both observed with the handrails in the TechDemo map:
98 /// a) The end pads are small 3x3 patches with sides shorter than MaxLength. However they are not
99 /// reduced to 2x2 meshes when Subdivide() is called with OptimizeFlat being set to false.
100 /// b) The rails themselves suffer from the same problem (at each of the four cylindrical sides),
101 /// *plus* they rely on their explicitly stated number of subdivisions for ignoring their central
102 /// interpolating control vertex, whose consideration would deform the handrail!
103 /// @param MaxLength Maximum length of the subdivisions.
104 void ForceLinearMaxLength(T MaxLength
);
106 /// Returns the area of the Bezier patch surface around vertex (i, j).
107 T
GetSurfaceAreaAtVertex(unsigned long i
, unsigned long j
) const;
109 /// Returns the const mesh vertex at (i, j).
110 const VertexT
& GetVertex(unsigned long i
, unsigned long j
) const { assert(i
<Width
&& j
<Height
); return Mesh
[j
*Width
+i
]; }
112 /// Returns the (non-const) mesh vertex at (i, j).
113 VertexT
& GetVertex(unsigned long i
, unsigned long j
) { assert(i
<Width
&& j
<Height
); return Mesh
[j
*Width
+i
]; }
115 bool WrapsHorz() const; ///< Returns whether the left and right borders are identical.
116 bool WrapsVert() const; ///< Returns whether the top and bottom borders are identical.
119 // The mesh is a regular array of Width*Height components, stored as a series of rows.
120 // The GetMesh() methods are provided for convenient access.
121 // TODO: Make private and provide const get methods instead?
122 unsigned long Width
; ///< Number of vertices in width direction.
123 unsigned long Height
; ///< Number of vertices in height direction.
124 ArrayT
<VertexT
> Mesh
; ///< Array of Width*Heights vertices that build up the bezier patch.
129 // Helper methods for the Subdivide() methods.
130 void SetMeshSize(unsigned long NewWidth
, unsigned long NewHeight
);
131 VertexT
SampleSinglePatchPoint(const VertexT SubPatch
[3][3], const T u
, const T v
) const;
132 void 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;
133 Vector3T
<T
> ProjectPointOntoVector(const Vector3T
<T
>& Point
, const Vector3T
<T
>& Start
, const Vector3T
<T
>& End
) const;
134 void OptimizeFlatRowAndColumnStrips();
135 bool ComputeTangentSpaceInSubPatch(unsigned long sp_i
, unsigned long sp_j
, const T s
, const T t
, Vector3T
<T
> Axes
[3]) const;