//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
TEST(GeometryBuilderFaceList, AddVertices)
{
    Vec3fArray inputVertexArr;
    inputVertexArr.reserve(3);
    inputVertexArr.add(Vec3f::X_AXIS);
    inputVertexArr.add(Vec3f::Y_AXIS);
    inputVertexArr.add(Vec3f::Z_AXIS);

    GeometryBuilderFaceList b;
    int indexOfFirstVertex = b.addVertices(inputVertexArr);
    EXPECT_EQ(0, indexOfFirstVertex);
    EXPECT_EQ(3u, b.vertices()->size());

    indexOfFirstVertex = b.addVertices(inputVertexArr);
    EXPECT_EQ(3, indexOfFirstVertex);
    
    ref<Vec3fArray> va = b.vertices();
    EXPECT_EQ(6u, va->size());

    EXPECT_TRUE(va->get(0) == Vec3f::X_AXIS);
    EXPECT_TRUE(va->get(1) == Vec3f::Y_AXIS);
    EXPECT_TRUE(va->get(2) == Vec3f::Z_AXIS);
    EXPECT_TRUE(va->get(3) == Vec3f::X_AXIS);
    EXPECT_TRUE(va->get(4) == Vec3f::Y_AXIS);
    EXPECT_TRUE(va->get(5) == Vec3f::Z_AXIS);
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
TEST(ArrayTest, CopyConvertedData)
{
    // Double array to float
    {
        DoubleArray ad;
        ad.resize(4);
        ad[0] = 0.0;
        ad[1] = 1.0;
        ad[2] = 2.0;
        ad[3] = 3.0;

        // Copy full array
        FloatArray af;
        af.resize(4);
        af.copyConvertedData(ad, 4, 0, 0);
        EXPECT_FLOAT_EQ(0.0f, af[0]);
        EXPECT_FLOAT_EQ(1.0f, af[1]);
        EXPECT_FLOAT_EQ(2.0f, af[2]);
        EXPECT_FLOAT_EQ(3.0f, af[3]);

        // Copy partial array to float array
        af.resize(2);
        af.setAll(0);
        af.copyConvertedData(ad, 2, 0, 1);

        EXPECT_FLOAT_EQ(1.0f, af[0]);
        EXPECT_FLOAT_EQ(2.0f, af[1]);
    }

    // Vec3d to Vec3f and Vec3i
    {
        Vec3dArray ad;
        ad.resize(2);
        ad[0].set(1.1, 2.5, 3.9);
        ad[1].set(11.1, 12.5, 13.9);

        Vec3fArray af;
        af.resize(2);
        af.copyConvertedData(ad, 2, 0, 0);
        EXPECT_FLOAT_EQ(1.1f,  af[0].x());
        EXPECT_FLOAT_EQ(2.5f,  af[0].y());
        EXPECT_FLOAT_EQ(3.9f,  af[0].z());
        EXPECT_FLOAT_EQ(11.1f, af[1].x());
        EXPECT_FLOAT_EQ(12.5f, af[1].y());
        EXPECT_FLOAT_EQ(13.9f, af[1].z());

        Array<Vec3i> ai;
        ai.resize(2);
        ai.copyConvertedData(ad, 2, 0, 0);
        EXPECT_EQ(1,  ai[0].x());
        EXPECT_EQ(2,  ai[0].y());
        EXPECT_EQ(3,  ai[0].z());
        EXPECT_EQ(11, ai[1].x());
        EXPECT_EQ(12, ai[1].y());
        EXPECT_EQ(13, ai[1].z());
    }
}
Beispiel #3
0
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void UniformFloat::setArray(const Vec3fArray& values)
{
    size_t numValues = values.size();
    CVF_ASSERT(numValues > 0);

    m_type = FLOAT_VEC3;
    m_data.resize(3*numValues);
    m_data.copyData(values.ptr()->ptr(), 3*numValues, 0);
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void GeometryBuilder::addTriangleByVertices(const Vec3f& v0, const Vec3f& v1, const Vec3f& v2)
{
    Vec3fArray verts;
    verts.resize(3);
    verts[0] = v0;
    verts[1] = v1;
    verts[2] = v2;

    uint firstVertexIdx = addVertices(verts);

    addTriangle(firstVertexIdx, firstVertexIdx + 1, firstVertexIdx + 2);
}
Beispiel #5
0
//--------------------------------------------------------------------------------------------------
/// Create a disk with a hole in the middle
//--------------------------------------------------------------------------------------------------
void GeometryUtils::createDisc(double outerRadius, double innerRadius, uint numSlices, GeometryBuilder* builder)
{
    CVF_ASSERT(numSlices >= 4); 
    CVF_ASSERT(builder);

    double da = 2*PI_D/numSlices;

    Vec3fArray verts;
    verts.reserve(2*numSlices);

    Vec3f point = Vec3f::ZERO;

    uint i;
    for (i = 0; i < numSlices; i++) 
    {
        // Precompute this one (A = i*da;)
        double sinA = Math::sin(i*da);
        double cosA = Math::cos(i*da);

        point.x() = static_cast<float>(-sinA*innerRadius);
        point.y() = static_cast<float>( cosA*innerRadius);

        verts.add(point);

        point.x() = static_cast<float>(-sinA*outerRadius);
        point.y() = static_cast<float>( cosA*outerRadius);

        verts.add(point);
    }

    uint baseNodeIdx = builder->addVertices(verts);

    uint conn[3] = { baseNodeIdx, 0, 0};

    for (i = 0; i < numSlices - 1; ++i) 
    {
        uint startIdx = baseNodeIdx + 2*i;

        conn[0] = startIdx + 0;
        conn[1] = startIdx + 3;
        conn[2] = startIdx + 1;
        builder->addTriangle(conn[0], conn[1], conn[2]);

        conn[0] = startIdx + 2;
        conn[1] = startIdx + 3;
        conn[2] = startIdx + 0;
        builder->addTriangle(conn[0], conn[1], conn[2]);
    }

    builder->addTriangle(baseNodeIdx + 0, baseNodeIdx + 1, baseNodeIdx + numSlices*2 - 1);
    builder->addTriangle(baseNodeIdx + 0, baseNodeIdx + numSlices*2 - 1, baseNodeIdx + numSlices*2 - 2);
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void GeometryBuilder::addQuadByVertices(const Vec3f& v0, const Vec3f& v1, const Vec3f& v2, const Vec3f& v3)
{
    Vec3fArray verts;
    verts.resize(4);
    verts[0] = v0;
    verts[1] = v1;
    verts[2] = v2;
    verts[3] = v3;

    uint firstVertexIdx = addVertices(verts);

    addQuad(firstVertexIdx, firstVertexIdx + 1, firstVertexIdx + 2, firstVertexIdx + 3);
}
Beispiel #7
0
//--------------------------------------------------------------------------------------------------
/// Create a disc centered at origin with its normal along positive z-axis
/// 
/// \param radius     Outer radius of the disc
/// \param numSlices  The number of subdivisions around the z-axis. Must be >= 4
/// \param builder    Geometry builder to use when creating geometry
///
/// Creates a disc on the z = 0 plane, centered at origin and with its surface normal pointing 
/// along the positive z-axis.
/// 
/// The disk is subdivided around the z axis into numSlices (as in pizza slices).
/// 
/// The sourceNodes that will be produced by this method:
/// <PRE>
///         1
///      /-----\ 8
///    2/\  |  /\            |y		
///    /  \ | /  \           |    
///    |   \|/   |           |    
///   3|----0----|7          |    
///    |   /|\   |           *-----x
///    \  / | \  /          /     
///    4\/  |  \/6         /z     
///      \-----/          
///         5               </PRE>
/// 
/// The following triangle connectivities will be produced:\n
/// <TT> &nbsp; &nbsp; (0,1,2)  (0,2,3)  (0,3,4) ... (0,8,1)</TT>
//--------------------------------------------------------------------------------------------------
void GeometryUtils::createDisc(double radius, uint numSlices, GeometryBuilder* builder)
{
    CVF_ASSERT(numSlices >= 4); 
    CVF_ASSERT(builder);


    double da = 2*PI_D/numSlices;

    Vec3fArray verts;
    verts.reserve(numSlices + 1);

    // Center of disc
    verts.add(Vec3f::ZERO);


    Vec3f point = Vec3f::ZERO;

    uint i;
    for (i = 0; i < numSlices; i++) 
    {
        // Precompute this one (A = i*da;)
        double sinA = Math::sin(i*da);
        double cosA = Math::cos(i*da);

        point.x() = static_cast<float>(-sinA*radius);
        point.y() = static_cast<float>( cosA*radius);

        verts.add(point);
    }

    uint baseNodeIdx = builder->addVertices(verts);


//     Vec3fArray myArray;
//     myArray.resize(10);
//     generatePointsOnCircle(radius, numSlices, &myArray);


    uint conn[3] = { baseNodeIdx, 0, 0};

    for (i = numSlices; i > 0; i--) 
    {
        conn[1] = baseNodeIdx + i + 1;
        conn[2] = baseNodeIdx + i + 0;

        if (i == numSlices) conn[1] = baseNodeIdx + 1;

        builder->addTriangle(conn[0], conn[1], conn[2]);
    }
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void BoundingBox::add(const Vec3fArray& points)
{
    size_t i;
    for (i = 0; i < points.size(); i++)
    {
        add(points[i]);
    }
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
TEST(ArrayTest, SetPtr)
{
    float* f = new float[6];
    f[0] = 1;
    f[1] = 2;
    f[2] = 3;
    f[3] = 10;
    f[4] = 11;
    f[5] = 12;

    Vec3fArray af;
    af.setPtr(reinterpret_cast<Vec3f*> (f), 2);     // Naughty! How to do this differently
    f = 0; // af has owership

    ASSERT_FLOAT_EQ(1.0f, af[0].x());
    ASSERT_FLOAT_EQ(2.0f, af[0].y());
    ASSERT_FLOAT_EQ(3.0f, af[0].z());
    ASSERT_EQ(true, af[1] == Vec3f(10,11,12));
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
TEST(ArrayTest, AssignFromRawArray)
{
    float* f = new float[6];
    f[0] = 1;
    f[1] = 2;
    f[2] = 3;
    f[3] = 10;
    f[4] = 11;
    f[5] = 12;

    Vec3fArray af;
    af.assign(reinterpret_cast<Vec3f*> (f), 2);     // Naughty! How to do this differently
    delete[] f;
    f = 0;

    ASSERT_FLOAT_EQ(1.0f, af[0].x());
    ASSERT_FLOAT_EQ(2.0f, af[0].y());
    ASSERT_FLOAT_EQ(3.0f, af[0].z());
    ASSERT_EQ(true, af[1] == Vec3f(10,11,12));
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(ModelBasicListTest, MergePartsWithTransformation)
{
    Vec3fArray* verts = new Vec3fArray;
    verts->reserve(3);
    verts->add(Vec3f(0, 0, 0));
    verts->add(Vec3f(1, 0, 0));
    verts->add(Vec3f(1, 1, 0));

    Vec3fArray* norms = new Vec3fArray;
    norms->resize(3);
    norms->set(0, Vec3f::Z_AXIS);
    norms->set(1, Vec3f::Z_AXIS);
    norms->set(2, Vec3f::Z_AXIS);

    DrawableGeo* myGeo = new DrawableGeo;
    myGeo->setFromTriangleVertexArray(verts);
    myGeo->setNormalArray(norms);

    Part* myPart = new Part;
    myPart->setDrawable(myGeo);

    Part* myPart2 = new Part;
    myPart2->setDrawable(myGeo);

    ref<ModelBasicList> myModel = new ModelBasicList;
    myModel->addPart(myPart);
    myModel->addPart(myPart2);
    EXPECT_EQ(2, myModel->partCount());

    Mat4d matrix;
    matrix.setTranslation(Vec3d(10, 20, 30));
    Transform* transform = new Transform;
    transform->setLocalTransform(matrix);
    myPart2->setTransform(transform);


    myModel->mergeParts(1000, 1000);
    EXPECT_EQ(1, myModel->partCount());

    Part* mergedPart = myModel->part(0);
    DrawableGeo* mergedGeo = dynamic_cast<DrawableGeo*>(mergedPart->drawable());
    const Vec3fArray* vertices = mergedGeo->vertexArray();
    EXPECT_EQ(6, vertices->size());

    Vec3f v5 = vertices->get(5);
    EXPECT_EQ(11, v5.x());
    EXPECT_EQ(21, v5.y());
    EXPECT_EQ(30, v5.z());
}
Beispiel #12
0
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void DrawableVectors::createUploadBufferObjectsGPU(OpenGLContext* oglContext)
{
    if (!m_renderWithVBO || m_vertexArray->size() == 0 || m_vectorGlyph.isNull() || m_vectorGlyphPrimSet.isNull())
    {
        return;
    }

    if (m_glyphVerticesAndNormalsBO.isNull() || !m_glyphVerticesAndNormalsBO->isUploaded())
    {
        // Build a interleaved VBO for glyphs vertices and normals to get better performance 
        // during the main shader based draw path
        size_t numVertices = m_vectorGlyph->vertexArray()->size();

        Vec3fArray data;
        data.reserve(numVertices*2);
        size_t i;
        for (i = 0; i < numVertices; i++)
        {
            data.add(m_vectorGlyph->vertexArray()->get(i));
            data.add(m_vectorGlyph->normalArray()->get(i));
        }

        GLuint uiSizeInBytes = static_cast<GLuint>(data.size()*3*sizeof(float));
        m_glyphVerticesAndNormalsBO = oglContext->resourceManager()->getOrCreateManagedBufferObject(oglContext, GL_ARRAY_BUFFER, uiSizeInBytes, data.ptr()->ptr());
    }


    if (m_indicesBO.isNull() || !m_indicesBO->isUploaded())
    {
        const UShortArray* indices = m_vectorGlyphPrimSet->indices();
        size_t numIndices = indices->size();
        if (numIndices > 0) 
        {
            GLuint uiSizeInBytes = static_cast<GLuint>(numIndices*sizeof(GLushort));
            m_indicesBO = oglContext->resourceManager()->getOrCreateManagedBufferObject(oglContext, GL_ELEMENT_ARRAY_BUFFER, uiSizeInBytes, indices->ptr());
            CVF_CHECK_OGL(oglContext);
        }
    }
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
TEST(ArrayTest, ptrToIdx)
{
    // Vec3f array
    Vec3fArray vA;
    vA.resize(4);
    ASSERT_EQ(4u, vA.size());

    vA[0] = Vec3f(1,2,3);
    vA[1] = Vec3f(1.1f, 2.2f, 3.3f);
    vA[2] = Vec3f(0,0,0);
    vA[3] = Vec3f(4,5,6);

    Vec3f* p1 = vA.ptr(1);
    ASSERT_FLOAT_EQ(1.1f, p1->x());
    ASSERT_FLOAT_EQ(2.2f, p1->y());
    ASSERT_FLOAT_EQ(3.3f, p1->z());

    Vec3f* p3 = vA.ptr(3);
    ASSERT_FLOAT_EQ(4, p3->x());
    ASSERT_FLOAT_EQ(5, p3->y());
    ASSERT_FLOAT_EQ(6, p3->z());
}
Beispiel #14
0
//--------------------------------------------------------------------------------------------------
/// Create a 2D patch
/// 
/// \param origin       The start point of the patch
/// \param uUnit        Direction vector u. First point 'to the right of' origin is origin + uUnit.
/// \param vUnit        Direction vector v. Coordinates of first point 'above' origin is origin + vunit. 
/// \param uCellCount   The number of cells/quads to generate along the uUnit dimension.
/// \param vCellCount   The number of cells/quads to generate along the vUnit dimension.
/// \param builder      Geometry builder to use when creating geometry
/// 
/// The figure below illustrates how the patch is constructed from the specified parameters.
/// 
/// <PRE>
///         v8-----v9----v10----v11             Parameters:            Resulting vertices:
///         |      |      |      |                origin = (10,20,0)     v0 = (10,20,0)
///  origin |      |      |      |                uUnit  = (2,0,0)       v1 = (12,20,0)
/// + vunit v4-----v5-----v6-----v7   |y          vUnit  = (0,1,0)       v2 = (14,20,0)
///         |      |      |      |    |           uCellCount = 3         v3 = (16,20,0)
///         |      |      |      |    |           vCellCount = 2         v4 = (10,21,0)
///         v0-----v1-----v2-----v3   *----x                             v5 = (12,21,0)
///     origin    origin                                                 :
///              + uUnit   </PRE>
/// 
/// The following quad connectivities will be produced:\n
/// <TT> &nbsp; &nbsp; (v4,v0,v1,v5)  (v5,v1,v2,v6)  (v6,v2,v3,v5) ... (v10,v6,v7,v11)</TT>
//--------------------------------------------------------------------------------------------------
void GeometryUtils::createPatch(const Vec3f& origin, const Vec3f& uUnit, const Vec3f& vUnit, uint uCellCount, uint vCellCount, GeometryBuilder* builder)
{
    CVF_ASSERT(uCellCount > 0);
    CVF_ASSERT(vCellCount > 0);

    uint numVertices = (uCellCount + 1)*(vCellCount + 1);
    uint numQuads = uCellCount*vCellCount;
    
    Vec3fArray vertices;
    vertices.reserve(numVertices);

    uint u, v;
    for (v = 0; v <= vCellCount; v++)
    {
        for (u = 0; u <= uCellCount; u++)
        {
            vertices.add(origin + static_cast<float>(u)*uUnit + static_cast<float>(v)*vUnit);
        }
    }

    uint baseNodeIdx = builder->addVertices(vertices);

    UIntArray conn;
    conn.reserve(4*numQuads);

    for (v = 0; v < vCellCount; v++)
    {
        for (u = 0; u < uCellCount; u++)
        {
            conn.add(baseNodeIdx + u     + (v + 1)*(uCellCount + 1));
            conn.add(baseNodeIdx + u     + v*(uCellCount + 1));
            conn.add(baseNodeIdx + u + 1 + v*(uCellCount + 1));
            conn.add(baseNodeIdx + u + 1 + (v + 1)*(uCellCount + 1));
        }
    }

    builder->addQuads(conn);
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
TEST(ArrayTest, SharedData)
{
    float* f = new float[6];
    f[0] = 1;
    f[1] = 2;
    f[2] = 3;
    f[3] = 10;
    f[4] = 11;
    f[5] = 12;

    {
        Vec3fArray af;
        af.setSharedPtr(reinterpret_cast<Vec3f*> (f), 2);       // Naughty! How to do this differently

        ASSERT_FLOAT_EQ(1.0f, af[0].x());
        ASSERT_FLOAT_EQ(2.0f, af[0].y());
        ASSERT_FLOAT_EQ(3.0f, af[0].z());
        ASSERT_EQ(true, af[1] == Vec3f(10,11,12));
    }

    ASSERT_FLOAT_EQ(1.0f, f[0]);
    ASSERT_FLOAT_EQ(2.0f, f[1]);
    ASSERT_FLOAT_EQ(3.0f, f[2]);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(ModelBasicListTest, MergeParts)
{
    Vec3fArray* verts = new Vec3fArray;
    verts->reserve(3);
    verts->add(Vec3f(0, 0, 0));
    verts->add(Vec3f(1, 0, 0));
    verts->add(Vec3f(1, 1, 0));

    Vec3fArray* norms = new Vec3fArray;
    norms->resize(3);
    norms->set(0, Vec3f::Z_AXIS);
    norms->set(1, Vec3f::Z_AXIS);
    norms->set(2, Vec3f::Z_AXIS);

    DrawableGeo* myGeo = new DrawableGeo;
    myGeo->setFromTriangleVertexArray(verts);
    myGeo->setNormalArray(norms);

    Part* myPart = new Part;
    myPart->setDrawable(myGeo);

    Part* myPart2 = new Part;
    myPart2->setDrawable(myGeo);

    ref<ModelBasicList> myModel = new ModelBasicList;
    myModel->addPart(myPart);
    myModel->addPart(myPart2);
    EXPECT_EQ(2, myModel->partCount());

    myModel->mergeParts(1000, 1000);
    EXPECT_EQ(1, myModel->partCount());

    Part* mergedPart = myModel->part(0);

    DrawableGeo* mergedGeo = dynamic_cast<DrawableGeo*>(mergedPart->drawable());
    const Vec3fArray* vertices = mergedGeo->vertexArray();
    EXPECT_EQ(6, vertices->size());

    Vec3f v5 = vertices->get(5);
    EXPECT_EQ(1, v5.x());
    EXPECT_EQ(1, v5.y());
    EXPECT_EQ(0, v5.z());
}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
TEST(ArrayTest, BasicVec3fArray)
{
    // Vec3f array
    Vec3fArray vA;
    vA.resize(4);
    ASSERT_EQ(4u, vA.size());

    vA[0] = Vec3f(1,2,3);
    vA[1] = Vec3f(1.1f, 2.2f, 3.3f);
    vA[2] = Vec3f(0,0,0);
    vA[3] = Vec3f(4,5,6);

    ASSERT_EQ(true, vA[0] == Vec3f(1, 2, 3));
    ASSERT_EQ(true, vA[1] == Vec3f(1.1f, 2.2f, 3.3f));
    ASSERT_EQ(true, vA[2] == Vec3f(0, 0, 0));
    ASSERT_EQ(true, vA[3] == Vec3f(4, 5, 6));

    const float* pf = vA.ptr()->ptr();

    ASSERT_FLOAT_EQ(1.0f, pf[0]);
    ASSERT_FLOAT_EQ(2.0f, pf[1]);
    ASSERT_FLOAT_EQ(3.0f, pf[2]);
    ASSERT_FLOAT_EQ(1.1f, pf[3]);
    ASSERT_FLOAT_EQ(2.2f, pf[4]);
    ASSERT_FLOAT_EQ(3.3f, pf[5]);
    ASSERT_FLOAT_EQ(0.0f, pf[6]);
    ASSERT_FLOAT_EQ(0.0f, pf[7]);
    ASSERT_FLOAT_EQ(0.0f, pf[8]);
    ASSERT_FLOAT_EQ(4.0f, pf[9]);
    ASSERT_FLOAT_EQ(5.0f, pf[10]);
    ASSERT_FLOAT_EQ(6.0f, pf[11]);

    vA.clear();

    ASSERT_EQ(0u, vA.size());
}
Beispiel #18
0
//--------------------------------------------------------------------------------------------------
/// Create a (possibly oblique) cylinder oriented along the z-axis
///
/// \param	bottomRadius    Bottom radius of cylinder
/// \param	topRadius       Top radius of cylinder
/// \param	height          Height of cylinder
/// \param	topOffsetX      Offset top disc relative to bottom in X direction
/// \param	topOffsetY      Offset top disc relative to bottom in Y direction
/// \param	numSlices       Number of slices
/// \param	normalsOutwards true to generate polygons with outward facing normals. 
/// \param	closedBot       true to close the bottom of the cylinder with a disc
/// \param	closedTop       true to close the top of the cylinder with a disc
/// \param	numPolysZDir    Number of (subdivisions) polygons along the Z axis.
/// \param  builder         Geometry builder to use when creating geometry
///
/// An oblique cylinder is a cylinder with bases that are not aligned one directly above the other
///	The base of the cylinder is placed at z = 0, and the top at z = height. 
///	Cylinder is subdivided around the z-axis into slices.
///	Use the cone functions instead of setting one of the radius params to 0
//--------------------------------------------------------------------------------------------------
void GeometryUtils::createObliqueCylinder(float bottomRadius, float topRadius, float height, float topOffsetX, float topOffsetY, uint numSlices, bool normalsOutwards, bool closedBot, bool closedTop, uint numPolysZDir, GeometryBuilder* builder)
{
    // Create cylinder...
    Vec3f centBot(0, 0, 0);
    Vec3f centTop(topOffsetX, topOffsetY, height);

    // Create vertices
    uint zPoly;
    for (zPoly = 0; zPoly <= numPolysZDir; zPoly++)
    {
        float fT = static_cast<float>((1.0/numPolysZDir)*(zPoly));
        float radius = bottomRadius + fT*(topRadius - bottomRadius);
        Vec3f center(fT*topOffsetX, fT*topOffsetY, fT*height);

        Vec3fArray verts;
        verts.reserve(numSlices);
        Vec3f point = Vec3f::ZERO;

        double da = 2*PI_D/numSlices;
        uint i;
        for (i = 0; i < numSlices; i++) 
        {
            // Precompute this one (A = i*da;)
            double sinA = Math::sin(i*da);
            double cosA = Math::cos(i*da);

            point.x() = static_cast<float>(-sinA*radius);
            point.y() = static_cast<float>( cosA*radius);
            point.z() = 0;

            point += center;

            verts.add(point);
        }

        uint baseNodeIdx = builder->addVertices(verts);

        // First time we only create the sourceNodes
        if (zPoly != 0)
        {
            uint offset = baseNodeIdx - numSlices;
            uint piConn[4] = { 0, 0, 0, 0 };

            // Normals facing outwards
            if (normalsOutwards)
            {
                uint i;
                for (i = 0; i < numSlices; i++) 
                {
                    piConn[0] = offset + i;
                    piConn[1] = offset + i + 1;
                    piConn[2] = offset + i + numSlices + 1;
                    piConn[3] = offset + i + numSlices;

                    if (i == numSlices - 1) 
                    {
                        piConn[1] = offset;
                        piConn[2] = offset + numSlices;
                    }

                    builder->addQuad(piConn[0], piConn[1], piConn[2], piConn[3]);
                }
            }

            // Normals facing inwards
            else
            {
                uint i;
                for (i = 0; i < numSlices; i++) 
                {
                    piConn[0] = offset + i + 1;
                    piConn[1] = offset + i;
                    piConn[2] = offset + i + numSlices;
                    piConn[3] = offset + i + numSlices + 1;

                    if (i == numSlices - 1) 
                    {
                        piConn[0] = offset;
                        piConn[3] = offset + numSlices;
                    }

                    builder->addQuad(piConn[0], piConn[1], piConn[2], piConn[3]);
                }
            }
        }
    }

    if (closedBot)
    {
        createDisc(bottomRadius, numSlices, builder);
    }

    if (closedTop)
    {
        uint startIdx = builder->vertexCount();
        createDisc(topRadius, numSlices, builder);
        uint endIdx = builder->vertexCount() - 1;

        // Translate the top disc sourceNodes, also flip it to get the normals the right way
        Mat4f mat = Mat4f::fromRotation(Vec3f(1.0f, 0.0f, 0.0f), Math::toRadians(180.0f));
        mat.translatePreMultiply(Vec3f(topOffsetX, topOffsetY, height));

        builder->transformVertexRange(startIdx, endIdx, mat);
    }
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST(ModelBasicListTest, MergePartsCheckBB)
{
    Vec3fArray* verts = new Vec3fArray;
    verts->reserve(3);
    verts->add(Vec3f(0, 0, 0));
    verts->add(Vec3f(1, 0, 0));
    verts->add(Vec3f(1, 1, 0));

    Vec3fArray* norms = new Vec3fArray;
    norms->resize(3);
    norms->set(0, Vec3f::Z_AXIS);
    norms->set(1, Vec3f::Z_AXIS);
    norms->set(2, Vec3f::Z_AXIS);

    DrawableGeo* myGeo = new DrawableGeo;
    myGeo->setFromTriangleVertexArray(verts);
    myGeo->setNormalArray(norms);


    ref<ModelBasicList> myModel = new ModelBasicList;

    {
        Part* myPart = new Part;
        myPart->setDrawable(myGeo);
        Mat4d matrix;
        matrix.setTranslation(Vec3d(10, 20, 30));
        Transform* transform = new Transform;
        transform->setLocalTransform(matrix);
        myPart->setTransform(transform);
        myModel->addPart(myPart);
    }

    {
        Part* myPart2 = new Part;
        myPart2->setDrawable(myGeo);
        Mat4d matrix;
        matrix.setTranslation(Vec3d(20, 20, 30));
        Transform* transform2 = new Transform;
        transform2->setLocalTransform(matrix);
        myPart2->setTransform(transform2);
        myModel->addPart(myPart2);
    }

    {
        Part* myPart3 = new Part;
        myPart3->setDrawable(myGeo);
        Mat4d matrix;
        matrix.setTranslation(Vec3d(100, 20, 30));
        Transform* transform3 = new Transform;
        transform3->setLocalTransform(matrix);
        myPart3->setTransform(transform3);
        myModel->addPart(myPart3);
    }

    {
        Part* myPart4 = new Part;
        myPart4->setDrawable(myGeo);
        Mat4d matrix;
        matrix.setTranslation(Vec3d(110, 20, 30));
        Transform* transform4 = new Transform;
        transform4->setLocalTransform(matrix);
        myPart4->setTransform(transform4);
        myModel->addPart(myPart4);
    }

    EXPECT_EQ(4, myModel->partCount());

    myModel->mergeParts(1, 1000);
    EXPECT_EQ(4, myModel->partCount());

    myModel->mergeParts(20, 1000);
    EXPECT_EQ(2, myModel->partCount());

    myModel->mergeParts(200, 1000);
    EXPECT_EQ(1, myModel->partCount());

}
//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
TEST(VertexCompactorTest, Quads)
{
    // Vertices, three possible quads 
    // 4------5------6------7
    // |  Q1  |  Q2  |  Q3  | 
    // |      |      |      | 
    // 0------1------2------3

    Vec3fArray orgVertices;
    orgVertices.reserve(8);
    orgVertices.add(Vec3f(0, 0, 0));
    orgVertices.add(Vec3f(1, 0, 0));
    orgVertices.add(Vec3f(2, 0, 0));
    orgVertices.add(Vec3f(3, 0, 0));
    orgVertices.add(Vec3f(0, 1, 0));
    orgVertices.add(Vec3f(1, 1, 0));
    orgVertices.add(Vec3f(2, 1, 0));
    orgVertices.add(Vec3f(3, 1, 0));

    const cvf::uint connQ1[4] = { 0, 1, 5, 4 };
    const cvf::uint connQ2[4] = { 1, 2, 6, 5 };
    //const cvf::uint connQ3[4] = { 2, 3, 7, 6 };

    // Q2
    {
        UIntArray orgConn(connQ2, 4);
        VertexCompactor vc(orgConn, orgVertices);

        ref<UIntArray> indices = vc.indices();
        ASSERT_EQ(4, indices->size());
        EXPECT_EQ(0, indices->get(0));
        EXPECT_EQ(1, indices->get(1));
        EXPECT_EQ(2, indices->get(2));
        EXPECT_EQ(3, indices->get(3));

        ref<Vec3fArray> va = vc.vertexArray();
        ASSERT_EQ(4, va->size());
        EXPECT_TRUE(Vec3f(1, 0, 0) == va->get(0));
        EXPECT_TRUE(Vec3f(2, 0, 0) == va->get(1));
        EXPECT_TRUE(Vec3f(2, 1, 0) == va->get(2));
        EXPECT_TRUE(Vec3f(1, 1, 0) == va->get(3));

        ref<UIntArray> vertexSourceIndices = vc.perVertexOriginalIndices();
        ASSERT_EQ(4, vertexSourceIndices->size());
        EXPECT_EQ(1, vertexSourceIndices->get(0));
        EXPECT_EQ(2, vertexSourceIndices->get(1));
        EXPECT_EQ(6, vertexSourceIndices->get(2));
        EXPECT_EQ(5, vertexSourceIndices->get(3));
    }

    // Q1 + Q2
    {
        UIntArray orgConn(8);
        orgConn.copyData(connQ1, 4, 0);
        orgConn.copyData(connQ2, 4, 4);

        VertexCompactor vc(orgConn, orgVertices);

        ref<UIntArray> indices = vc.indices();
        ASSERT_EQ(8, indices->size());
        EXPECT_EQ(0, indices->get(0));
        EXPECT_EQ(1, indices->get(1));
        EXPECT_EQ(2, indices->get(2));
        EXPECT_EQ(3, indices->get(3));
        EXPECT_EQ(1, indices->get(4));
        EXPECT_EQ(4, indices->get(5));
        EXPECT_EQ(5, indices->get(6));
        EXPECT_EQ(2, indices->get(7));

        ref<Vec3fArray> va = vc.vertexArray();
        ASSERT_EQ(6, va->size());
        EXPECT_TRUE(Vec3f(0, 0, 0) == va->get(0));
        EXPECT_TRUE(Vec3f(1, 0, 0) == va->get(1));
        EXPECT_TRUE(Vec3f(1, 1, 0) == va->get(2));
        EXPECT_TRUE(Vec3f(0, 1, 0) == va->get(3));
        EXPECT_TRUE(Vec3f(2, 0, 0) == va->get(4));
        EXPECT_TRUE(Vec3f(2, 1, 0) == va->get(5));

        ref<UIntArray> vertexSourceIndices = vc.perVertexOriginalIndices();
        ASSERT_EQ(6, vertexSourceIndices->size());
        EXPECT_EQ(0, vertexSourceIndices->get(0));
        EXPECT_EQ(1, vertexSourceIndices->get(1));
        EXPECT_EQ(5, vertexSourceIndices->get(2));
        EXPECT_EQ(4, vertexSourceIndices->get(3));
        EXPECT_EQ(2, vertexSourceIndices->get(4));
        EXPECT_EQ(6, vertexSourceIndices->get(5));
    }
}
Beispiel #21
0
//--------------------------------------------------------------------------------------------------
/// Create a sphere with center in origin
///
/// \param     radius     Radius of sphere
/// \param     numSlices  The number of subdivisions around the z-axis (similar to lines of longitude).  
/// \param     numStacks  The number of subdivisions along the z-axis (similar to lines of latitude). 
/// \param     builder    Geometry builder to use when creating geometry
//--------------------------------------------------------------------------------------------------
void GeometryUtils::createSphere(double radius, uint numSlices, uint numStacks, GeometryBuilder* builder)
{
    // Code is strongly inspired by mesa.

    // From GLviewAPI:
    // float nsign = bNormalsOutwards ? 1.0f : -1.0f;
    // Could be added as a param if needed (e.g. dome)
    const double nsign = 1.0;

    double rho = PI_D/numStacks;
    double theta = 2.0*PI_D/static_cast<double>(numSlices);

    // Array to receive the node coordinates
    Vec3fArray vertices;
    uint vertexCount = 1 + 2*numSlices + (numStacks - 2)*numSlices;
    vertices.reserve(vertexCount);

    // Create the +Z end as triangles
    Vec3d vTop(0.0, 0.0, nsign*radius);
    vertices.add(Vec3f(vTop));

    ref<UIntArray> triangleFan = new UIntArray;
    triangleFan->reserve(numSlices + 2);
    triangleFan->add(0);

    uint j;
    for (j = 0; j < numSlices; j++) 
    {
        double localTheta = j * theta;

        Vec3d v;
        v.x() = -Math::sin(localTheta) * Math::sin(rho);
        v.y() =  Math::cos(localTheta) * Math::sin(rho);
        v.z() = nsign                  * Math::cos(rho);

        v *= radius;
        vertices.add(Vec3f(v));

        triangleFan->add(j + 1);
    }

    // Close top fan
    triangleFan->add(1);
    builder->addTriangleFan(*triangleFan);

    // Intermediate stacks as quad-strips
    // First and last stacks are handled separately

    ref<UIntArray> quadStrip = new UIntArray;
    quadStrip->reserve(numSlices*2 + 2);

    uint i;
    for (i = 1; i < numStacks - 1; i++) 
    {
        double localRho = i * rho;

        quadStrip->setSizeZero();

        for (j = 0; j < numSlices; j++) 
        {
            double localTheta = j * theta;

            Vec3d v;
            v.x() = -Math::sin(localTheta) * Math::sin(localRho + rho);
            v.y() =  Math::cos(localTheta) * Math::sin(localRho + rho);
            v.z() = nsign                  * Math::cos(localRho + rho);

            v *= radius;
            vertices.add(Vec3f(v));

            uint iC1 = (i*numSlices) + 1 + j;
            uint iC0 = iC1 - numSlices;
            quadStrip->add(iC0);
            quadStrip->add(iC1);
        }

        // Close quad-strip
        uint iStartC1 = (i*numSlices) + 1;
        uint iStartC0 = iStartC1 - numSlices;
        quadStrip->add(iStartC0);
        quadStrip->add(iStartC1);

        builder->addQuadStrip(*quadStrip);
    }

    // Create -Z end as triangles
    Vec3d vBot( 0.0, 0.0, -radius*nsign  );
    vertices.add(Vec3f(vBot));

    uint endNodeIndex = static_cast<uint>(vertices.size()) - 1;

    triangleFan->setSizeZero();
    triangleFan->add(endNodeIndex);

    for (j = 0; j < numSlices; j++) 
    {
        triangleFan->add(endNodeIndex - j - 1);
    }

    // Close bottom fan
    triangleFan->add(endNodeIndex - 1);
    builder->addTriangleFan(*triangleFan);

    builder->addVertices(vertices);
}
Beispiel #22
0
//--------------------------------------------------------------------------------------------------
/// Create a cone oriented along the z-axis
///
/// \param	bottomRadius    Bottom radius of cone
/// \param	height          Height of cone
/// \param	numSlices       Number of slices
/// \param	normalsOutwards true to generate polygons with outward facing normals. 
/// \param	closedBot       true to close the bottom of the cone with a disc
/// \param	singleTopNode   Specify if a single top node should be used, or if each side triangle 
///                         should have its own top node.
/// \param  builder         Geometry builder to use when creating geometry
///
//--------------------------------------------------------------------------------------------------
void GeometryUtils::createCone(float bottomRadius, float height, uint numSlices, bool normalsOutwards, bool closedBot, bool singleTopNode, GeometryBuilder* builder)
{
    Vec3fArray verts;

    if (singleTopNode)
    {
        verts.reserve(numSlices + 1);
    }
    else
    {
        verts.reserve(numSlices*2);
    }

    Vec3f point = Vec3f::ZERO;

    double da = 2*PI_D/numSlices;
    uint i;
    for (i = 0; i < numSlices; i++) 
    {
        // Precompute this one (A = i*da;)
        double sinA = Math::sin(i*da);
        double cosA = Math::cos(i*da);

        point.x() = static_cast<float>(-sinA*bottomRadius);
        point.y() = static_cast<float>( cosA*bottomRadius);

        verts.add(point);
    }

    if (singleTopNode)
    {
        verts.add(Vec3f(0, 0, height));
    }
    else
    {
        // Unique sourceNodes at apex of cone
        Vec3f topNode(0, 0, height);

        uint i;
        for (i = 0; i < numSlices; i++)
        {
            verts.add(topNode);
        }
    }

    uint baseNodeIdx = builder->addVertices(verts);

    uint piConn[3] = { 0, 0, 0 };

    // Normals facing outwards
    if (normalsOutwards)
    {
        uint i;
        for (i = 0; i < numSlices; i++) 
        {
            piConn[0] = baseNodeIdx + i;
            piConn[1] = baseNodeIdx + i + 1;
            piConn[2] = singleTopNode ? baseNodeIdx + numSlices : baseNodeIdx + i + numSlices; 

            if (i == numSlices - 1) 
            {
                piConn[1] = baseNodeIdx;
            }
            
            if (normalsOutwards)
            {
                builder->addTriangle(piConn[0], piConn[1], piConn[2]);
            }
            else
            {
                builder->addTriangle(piConn[1], piConn[0], piConn[2]);
            }
        }
    }

    if (closedBot)
    {
        createDisc(bottomRadius, numSlices, builder);
    }
}