Procházet zdrojové kódy

Additional Custom Geometry (#2476)

codexhound před 6 roky
rodič
revize
f19fc528ad

+ 6 - 0
Source/Urho3D/Container/Vector.h

@@ -79,6 +79,12 @@ public:
         DoInsertElements(0, vector.Begin(), vector.End(), CopyTag{});
     }
 
+    /// Copy-construct from another vector (iterator version)
+    Vector(ConstIterator start, ConstIterator end)
+    {
+        DoInsertElements(0, start, end, CopyTag{});
+    }
+
     /// Move-construct from another vector.
     Vector(Vector<T> && vector)
     {

+ 285 - 0
Source/Urho3D/Graphics/CustomGeometry.cpp

@@ -204,6 +204,291 @@ bool CustomGeometry::DrawOcclusion(OcclusionBuffer* buffer)
     return success;
 }
 
+Vector<Vector3> CustomGeometry::GetCircleShape(float radius, size_t iterations, float startTheta, float endTheta)
+{
+    float stepSize = (endTheta - startTheta) / (float)iterations;
+    Vector<Vector3> shapeList;
+    for (int i = 0; i < iterations; i++) {
+        float curTheta1 = startTheta + ((float)i * stepSize);
+        float curTheta2 = startTheta + ((float)(i+1) * stepSize);
+        float curX1 = radius * cos(curTheta1);
+        float curY1 = radius * sin(curTheta1);
+        float curX2 = radius * cos(curTheta2);
+        float curY2 = radius * sin(curTheta2);
+
+        shapeList.Push(Urho3D::Vector3(curX1, 0, curY1));
+        shapeList.Push(Urho3D::Vector3(curX2, 0, curY2));
+
+        if (i >= iterations - 1) {
+            float curTheta =0;
+            if (Abs(endTheta - startTheta) < (2*M_PI)) {
+                curTheta = endTheta;
+            }
+            float curX = radius * cos(curTheta);
+            float curY = radius * sin(curTheta);
+            shapeList.Push(Urho3D::Vector3(curX, 0, curY));
+        }
+    }
+    return shapeList;
+}
+
+Vector<Vector3> CustomGeometry::GetSquareShape(float size)
+{
+    Vector<Vector3> mSquareList = { Urho3D::Vector3(-(size / 2.0f),0,(size / 2.0f)),Urho3D::Vector3(-(size / 2.0f),0,-(size / 2.0f)),
+        Urho3D::Vector3((size / 2.0f), 0, -(size / 2.0f)),Urho3D::Vector3((size / 2.0f), 0, (size / 2.0f)) };
+    return mSquareList;
+}
+
+void CustomGeometry::MakeCircle(float radius, size_t iterations, float startTheta,
+    float endTheta, bool clear, int geomNum)
+{
+    Vector<Vector3> mCircleShape = GetCircleShape(radius, iterations, startTheta, endTheta);
+    FillShape(mCircleShape,false,clear,geomNum);
+}
+
+void CustomGeometry::MakeCircleGraph(const Vector<Pair<float, Urho3D::SharedPtr<Urho3D::Material> > >& parts,
+    int radius, int iterations)
+{
+    if (parts.Size() > 0) {
+        float totalWeight = 0;
+        SetNumGeometries(parts.Size());
+        auto it = parts.Begin();
+        while (it != parts.End()) {
+            const auto current = (*it);
+            totalWeight += current.first_;
+            it++;
+        }
+
+        it = parts.Begin();
+        float currentStartTheta = 0;
+        float currentEndTheta = 0;
+        int count = 0;
+        while (it != parts.End()) {
+            const auto current = (*it);
+            currentEndTheta = ((current.first_ / totalWeight)*(2 * M_PI)) + currentStartTheta;
+            MakeCircle(radius, (iterations / parts.Size()), currentStartTheta, currentEndTheta,false,count);
+            if (current.second_.NotNull()) {
+                SetMaterial(count, current.second_);
+            }
+            it++;
+            count++;
+            currentStartTheta = currentEndTheta;
+        }
+    }
+}
+
+void CustomGeometry::MakeShape(const Vector<Vector3>& pointList , bool connectTail)
+{
+    Clear();
+    SetNumGeometries(1);
+    BeginGeometry(0, Urho3D::PrimitiveType::LINE_STRIP);
+    Vector3 current;
+    Vector3 next;
+    for (size_t i = 0; i < pointList.Size(); i++) {
+        if ((connectTail && i >= pointList.Size() - 1) || i < pointList.Size() - 1) {
+            current = pointList[i];
+            next = pointList[0];
+            if (i < pointList.Size() - 1) {
+                next = pointList[i + 1];
+            }
+            DefineVertex(current);
+            DefineVertex(next);
+        }
+        
+    }
+    Commit();
+}
+
+void CustomGeometry::FillShape(const Vector<Vector3>& shapeList, bool connectTail, bool clear, int geomNum)
+{
+    if (shapeList.Size() > 0) {
+        int usedGeomNum = geomNum;
+        if (clear) {
+            Clear();
+            SetNumGeometries(1);
+            usedGeomNum = 0;
+        }
+        BeginGeometry(usedGeomNum, PrimitiveType::TRIANGLE_STRIP);
+        auto centerPoint = Vector3(0, 0, 0);
+        if (connectTail) {
+            auto centerPoint = Urho3D::Average(shapeList);
+        }
+        Vector<Vector3> vertices(3);
+        Vector3 current;
+        Vector3 next;
+        Vector3 normal;
+        auto it = shapeList.Begin();
+        auto nextIt = it;
+        while (it != shapeList.End()) {
+            nextIt = it + 1;
+            if (connectTail && nextIt == shapeList.End() || nextIt != shapeList.End())
+            {
+                current = (*it);
+
+                if (nextIt != shapeList.End()) {
+                    next = (*nextIt);
+                }
+                else {
+                    next = (*shapeList.Begin());
+                }
+
+                vertices = { centerPoint, current, next };
+
+                normal = Urho3D::Average(vertices);
+                normal.Normalize();
+                normal.Orthogonalize(normal);
+                DefineVertex(vertices.At(0));
+                DefineNormal(normal);
+
+                DefineVertex(vertices.At(1));
+                DefineNormal(normal);
+
+                DefineVertex(vertices.At(2));
+                DefineNormal(normal);
+            }
+            it++;
+        }
+        Commit();
+    }
+}
+
+void CustomGeometry::MakeSphere(float radius, size_t iterations)
+{
+    //Create the geometry buffer
+    float angleStepSize = (2.0f * M_PI) / (float)iterations;
+    Vector<Vector3> m_xyPoints;
+    for (int i = 0; i < iterations; i++) {
+        float curTheta = i * angleStepSize;
+        for (int j = 0; j < iterations; j++) {
+            float curPhi = j * angleStepSize;
+            float curX = radius * cos(curTheta) * sin(curPhi);
+            float curY = radius * sin(curTheta) * sin(curPhi);
+            float curZ = radius * cos(curPhi);
+            m_xyPoints.Push(Vector3(curX,curY,curZ));
+        }
+    }
+
+    CreateQuadsFromBuffer(m_xyPoints, iterations, iterations, true);
+}
+
+void CustomGeometry::ProtrudeShape(const Vector<Vector3>& mShapeList, 
+    const Vector<Vector3>& mPointList, bool connectTail)
+{
+    Vector3 centerPoint = Urho3D::Average<Vector3>(mShapeList);
+    Vector3 pointCurrent;
+    Vector3 shapeCurrent;
+    Vector3 shapePointVec;
+    Vector3 shapePointDir;
+    Vector<Vector3> mPointBuffer(mShapeList.Size()*mPointList.Size()+mShapeList.Size());
+
+    Vector<Vector3> mLastShapePos = mShapeList;
+    auto pointIter = mPointList.Begin();
+    auto shapeIter = mLastShapePos.Begin();
+
+    int bufferCount = 0;
+    while (shapeIter != mLastShapePos.End()) {
+        mPointBuffer.At(bufferCount) = (*shapeIter);
+        shapeIter++;
+        bufferCount++;
+    }
+
+    
+    int count = 0;
+    while (pointIter != mPointList.End()) {
+        shapeIter = mLastShapePos.Begin();
+        pointCurrent = (*pointIter);
+        count = 0;
+        while (shapeIter != mLastShapePos.End()) {
+            shapeCurrent = (*shapeIter);
+            if (shapeIter == mLastShapePos.Begin()) { //protrude from first point of the shape and create dir Vector to point
+                shapePointVec = pointCurrent - centerPoint;
+                centerPoint = pointCurrent;
+            }
+            // protrude from the rest of the points on the shape to the next point given a dir and length vector
+            shapePointDir = shapePointVec;
+            shapePointDir.Normalize();
+            mLastShapePos[count] = mLastShapePos[count] + shapePointDir * shapePointVec.Length();
+            mPointBuffer.At(bufferCount) = mLastShapePos[count];
+
+            bufferCount++;
+            shapeIter++;
+            count++;
+        }
+        pointIter++;
+    }
+    CreateQuadsFromBuffer(mPointBuffer, mPointList.Size() + 1, mShapeList.Size(), connectTail);
+}
+
+void CustomGeometry::CreateQuadsFromBuffer(const Vector<Vector3>& pointList, size_t zIterations,
+    size_t thetaIterations, bool connectTail)
+{
+    if (!connectTail) {
+        SetNumGeometries(3);
+    }
+    else {
+        SetNumGeometries(1);
+    }
+    
+    //Create the quads from the buffer
+    BeginGeometry(0, Urho3D::PrimitiveType::TRIANGLE_STRIP);
+    for (size_t i = 0; i < zIterations; i++) {
+        if ((i >= zIterations - 1 && connectTail) || i < zIterations - 1) {
+            for (size_t j = 0; j < thetaIterations; j++) {
+                //if at the end connect to the beginning to complete pass
+                size_t iplus = i + 1;
+                size_t jplus = j + 1;
+                if (i >= zIterations - 1) {
+                    iplus = 0;
+                }
+                if (j >= thetaIterations - 1) {
+                    jplus = 0;
+                }
+                Vector<Vector3> avList;
+                avList = { pointList.At((i*thetaIterations) + j) ,pointList.At((iplus*thetaIterations)+
+                    j) ,pointList.At((i*thetaIterations) + jplus) };
+                Vector3 normal = Urho3D::Average<Vector3>(avList);
+                normal.Normalize();
+                DefineVertex(avList.At(0));
+                DefineVertex(avList.At(1));
+                DefineVertex(avList.At(2));
+                DefineNormal(normal);
+                avList.Clear();
+
+                avList = { pointList.At((i*thetaIterations) + j) ,pointList.At((iplus*thetaIterations)+
+                    j) ,pointList.At((iplus*thetaIterations) + jplus) };
+                normal = Urho3D::Average<Vector3>(avList);
+                normal.Normalize();
+                DefineVertex(avList.At(0));
+                DefineVertex(avList.At(1));
+                DefineVertex(avList.At(2));
+                DefineNormal(normal);
+                avList.Clear();
+            }
+        }
+    }
+    Commit();
+
+    if (!connectTail) {
+        //fill in the head and tail
+        auto tailBegin = pointList.Begin();
+        auto tailEnd = pointList.Begin() + thetaIterations;
+        Vector<Vector3> tail(tailBegin, tailEnd);
+        auto headBegin = pointList.Begin() + pointList.Size() - thetaIterations;
+        auto headEnd = pointList.Begin() + pointList.Size();
+        Vector<Vector3> head(headBegin, headEnd);
+        
+        FillShape(tail, true, false, 1);
+        FillShape(head, true, false, 2);
+    }
+}
+
+void CustomGeometry::MakeSquare(float size)
+{
+    Vector<Vector3> mSquareList = { Urho3D::Vector3(-(size / 2.0f),0,(size / 2.0f)),Urho3D::Vector3(-(size / 2.0f),0,-(size / 2.0f)),
+        Urho3D::Vector3((size / 2.0f), 0, -(size / 2.0f)),Urho3D::Vector3((size / 2.0f), 0, (size / 2.0f)) };
+    FillShape(mSquareList,true);
+}
+
 void CustomGeometry::Clear()
 {
     elementMask_ = MASK_POSITION;

+ 28 - 0
Source/Urho3D/Graphics/CustomGeometry.h

@@ -66,6 +66,34 @@ public:
     /// Draw to occlusion buffer. Return true if did not run out of triangles.
     bool DrawOcclusion(OcclusionBuffer* buffer) override;
 
+    /// Geometry Shape Helper Functions (normal defaults to (0,1,0)
+
+    /// Shape Creation Functions, can be used as input into protrude shape
+    static Vector<Vector3> GetCircleShape(float radius = 1, size_t iterations = 100, float startTheta = 0, float endTheta = 2 * M_PI);
+    static Vector<Vector3> GetSquareShape(float size);
+
+    /// Make the custom geometry into a circle, change start and stop to make a segment instead
+    /// Set clear=false and geomNum for making multiple circle segments
+    void MakeCircle(float radius = 1, size_t iterations = 300, float startTheta = 0, float endTheta = 2 * M_PI,
+        bool clear = true, int geomNum = 0);
+    /// Draws a shape generated by connecting the points in the input vector (the end point will connect to the start)
+    void MakeShape(const Vector<Urho3D::Vector3>& pointList, bool connectTail = true);
+    /// Makes this custom geometry into a square shape
+    void MakeSquare(float size);
+    /// Produces a circle graph given a vector of paired (weights and materials)
+    void MakeCircleGraph(const Vector<Pair<float, Urho3D::SharedPtr<Urho3D::Material> > >& parts, 
+        int radius = 1, int iterations = 300);
+    void MakeSphere(float radius = 1, size_t iterations = 200);
+    /// Protrode a shape along a line (note: the input vector must be a complete shape with the tail connecting to head)
+    /// This function makes this object into the newly generated 3D mesh (works best if the line (point list) is also complete)
+    void ProtrudeShape(const Vector<Vector3> &mShapeList, const Vector<Vector3> &mPointList, bool connectTail = false);
+
+    /// Helper Function Creating 3D Meshes (connectTail = true), connect the endLinePoint to the start)
+    void CreateQuadsFromBuffer(const Vector<Vector3>& pointList, size_t zIterations, size_t thetaIterations, bool connectTail = false);
+    /// Fills a point List shape with triangles
+    void FillShape(const Vector<Vector3>& shapeList, bool connectTail = true, bool clear = true, int geomNum = 0);
+    /// End Shape Generation Helper Functions
+
     /// Clear all geometries.
     void Clear();
     /// Set number of geometries.

+ 25 - 0
Source/Urho3D/Math/MathDefs.h

@@ -37,6 +37,12 @@
 namespace Urho3D
 {
 
+// Parameter Angle Type Def
+enum AngleType {
+    DEGREES,
+    RADIANS
+} typedef AngleTypeEnum;
+
 #undef M_PI
 static const float M_PI = 3.14159265358979323846264338327950288f;
 static const float M_HALF_PI = M_PI * 0.5f;
@@ -91,6 +97,14 @@ inline T Abs(T value) { return value >= 0.0 ? value : -value; }
 template <class T>
 inline T Sign(T value) { return value > 0.0 ? 1.0 : (value < 0.0 ? -1.0 : 0.0); }
 
+/// Returns radians from degrees
+template <class T>
+inline T toRadians(const T degrees) { return M_DEGTORAD * degrees; }
+
+/// Returns degrees from radians
+template <class T>
+inline T toDegrees(const T radians) { return M_RADTODEG * radians; }
+
 /// Return a representation of the specified floating-point value as a single format bit layout.
 inline unsigned FloatToRawIntBits(float value)
 {
@@ -166,6 +180,17 @@ template <class T> inline int FloorToInt(T x) { return static_cast<int>(floor(x)
 /// Round value to nearest integer.
 template <class T> inline T Round(T x) { return round(x); }
 
+///Compute Average
+template <class T> inline T Average(const Vector<T> &list){
+    T average = T();
+    auto it = list.Begin();
+    while (it != list.End()) {
+        average += (*it);
+        it++;
+    }
+    return average / list.Size();
+}
+
 /// Round value to nearest integer.
 template <class T> inline int RoundToInt(T x) { return static_cast<int>(round(x)); }
 

+ 21 - 0
Source/Urho3D/Math/Matrix3.cpp

@@ -69,4 +69,25 @@ String Matrix3::ToString() const
     return String(tempBuffer);
 }
 
+void Matrix3::MakeRotationMatrix(float xAxis,float yAxis, float zAxis, Urho3D::AngleTypeEnum angleType)
+{
+    float xAxisInput = xAxis;
+    float yAxisInput = yAxis;
+    float zAxisInput = zAxis;
+    if(angleType == RADIANS){
+        xAxisInput = Urho3D::toDegrees(xAxisInput);
+        yAxisInput = Urho3D::toDegrees(yAxisInput);
+        zAxisInput = Urho3D::toDegrees(zAxisInput);
+    }
+    m00_ = Urho3D::Cos(yAxisInput) * Urho3D::Cos(zAxisInput);
+    m01_ = Urho3D::Cos(xAxisInput) * Urho3D::Sin(zAxisInput) + Urho3D::Sin(xAxisInput) * Urho3D::Sin(yAxisInput) * Urho3D::Cos(zAxisInput);
+    m02_ = Urho3D::Sin(xAxisInput) * Urho3D::Sin(zAxisInput) - Urho3D::Cos(xAxisInput) * Urho3D::Sin(yAxisInput) * Urho3D::Cos(zAxisInput);
+    m10_ = -Urho3D::Cos(yAxisInput) * Urho3D::Sin(zAxisInput);
+    m11_ = Urho3D::Cos(xAxisInput) * Urho3D::Cos(zAxisInput) - Urho3D::Sin(xAxisInput) * Urho3D::Sin(yAxisInput) * Urho3D::Sin(zAxisInput);
+    m12_ = Urho3D::Sin(xAxisInput) * Urho3D::Cos(zAxisInput) + Urho3D::Cos(xAxisInput) * Urho3D::Sin(yAxisInput) * Urho3D::Sin(zAxisInput);
+    m20_ = Urho3D::Sin(yAxisInput);
+    m21_ = -Urho3D::Sin(xAxisInput) * Urho3D::Cos(yAxisInput);
+    m22_ = Urho3D::Cos(xAxisInput) * Urho3D::Cos(yAxisInput);
+}
+
 }

+ 11 - 0
Source/Urho3D/Math/Matrix3.h

@@ -241,6 +241,17 @@ public:
         );
     }
 
+    /// Make a rotation matrix given an axis of rotation
+    /*
+        Useful for standalone rotation of a node around an arbtitrary position,
+        by the given axis of rotation (can be used for orbital mechanics).
+        rotating the parent node doesn't do the same as it's children orbit around its axis
+        of rotation, not the center point
+        To get the next point multiply this by a starting Vector3 pos with it's origin (0,0,0)
+        being the center of rotation
+    */
+    void MakeRotationMatrix(float xAxis,float yAxis, float zAxis, Urho3D::AngleTypeEnum angleType = DEGREES);
+
     /// Test for equality with another matrix with epsilon.
     bool Equals(const Matrix3& rhs) const
     {

+ 9 - 2
Source/Urho3D/Math/Vector3.h

@@ -410,8 +410,15 @@ public:
         return Urho3D::Equals(x_, rhs.x_) && Urho3D::Equals(y_, rhs.y_) && Urho3D::Equals(z_, rhs.z_);
     }
 
-    /// Returns the angle between this vector and another vector in degrees.
-    float Angle(const Vector3& rhs) const { return Urho3D::Acos(DotProduct(rhs) / (Length() * rhs.Length())); }
+    /// Returns the angle between this vector and another vector in degrees or radians.
+    float Angle(const Vector3& rhs, Urho3D::AngleTypeEnum angleType = DEGREES) const 
+    { 
+        float angle = Urho3D::Acos(DotProduct(rhs) / (Length() * rhs.Length()));
+        if (angleType == RADIANS) {
+            angle = Urho3D::toRadians(angle);
+        }
+        return angle; 
+    }
 
     /// Return whether is NaN.
     bool IsNaN() const { return Urho3D::IsNaN(x_) || Urho3D::IsNaN(y_) || Urho3D::IsNaN(z_); }