//##################################################################################################
//
//   Custom Visualization Core library
//   Copyright (C) 2011-2012 Ceetron AS
//    
//   This library is free software: you can redistribute it and/or modify 
//   it under the terms of the GNU General Public License as published by 
//   the Free Software Foundation, either version 3 of the License, or 
//   (at your option) any later version. 
//    
//   This library is distributed in the hope that it will be useful, but WITHOUT ANY 
//   WARRANTY; without even the implied warranty of MERCHANTABILITY or 
//   FITNESS FOR A PARTICULAR PURPOSE.   
//    
//   See the GNU General Public License at <<http://www.gnu.org/licenses/gpl.html>> 
//   for more details. 
//
//##################################################################################################

#include "cvfBase.h"
#include "cvfDrawableGeo.h"
#include "cvfOpenGL.h"
#include "cvfPrimitiveSetIndexedUInt.h"
#include "cvfPrimitiveSetIndexedUShort.h"
#include "cvfPrimitiveSetIndexedUIntScoped.h"
#include "cvfPrimitiveSetIndexedUShortScoped.h"
#include "cvfVertexWelder.h"
#include "cvfOpenGLResourceManager.h"
#include "cvfShaderProgram.h"
#include "cvfRay.h"
#include "cvfBufferObjectManaged.h"
#include "cvfVertexBundle.h"

namespace cvf {



//==================================================================================================
///
/// \class cvf::DrawableGeo
/// \ingroup Render
///
/// A polygon based drawable.
/// 
/// This class stores vertices (nodes, coordinates), normals and a collection of PrimitiveSet to
/// describe the geometry. Each PrimitiveSet describes an array of equal primitives (POINTS, LINES or TRIS).
/// 
/// The geometry can be rendered in immediate mode (glBegin..), with vertex arrays or using VBO.  
///
//==================================================================================================

//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
DrawableGeo::DrawableGeo()
:   m_vertexBundle(new VertexBundle),
    m_renderMode(VERTEX_ARRAY)
{
}


//--------------------------------------------------------------------------------------------------
/// Deletes all OpenGL resources created by this drawable geo
//--------------------------------------------------------------------------------------------------
DrawableGeo::~DrawableGeo()
{
    releaseBufferObjectsGPU();
}


//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
ref<DrawableGeo> DrawableGeo::shallowCopy() const
{
    ref<DrawableGeo> newGeo = new DrawableGeo;

    // Replace bundle in new drawable with a copy of our own
    newGeo->m_vertexBundle = m_vertexBundle->shallowCopy();

    size_t numPrimSets = m_primitiveSets.size();
    size_t i;
    for (i = 0; i < numPrimSets; i++)
    {
        newGeo->m_primitiveSets.push_back(const_cast<PrimitiveSet*>(m_primitiveSets.at(i)));
    }

    newGeo->m_boundingBox = m_boundingBox;
    newGeo->m_renderMode = m_renderMode;

    return newGeo;
}


//--------------------------------------------------------------------------------------------------
/// Main shader based rendering path for the geometry 
/// 
/// Will render from either client vertex arrays or buffer objects (VBOs). 
/// 
/// \note    In order to get rendering from buffer objects, the buffer objects must already be created
///          and uploaded through a previous call to createUploadBufferObjectsGPU().
/// \warning Requires at least OpenGL 2.0
//--------------------------------------------------------------------------------------------------
void DrawableGeo::render(OpenGLContext* oglContext, ShaderProgram* shaderProgram, const MatrixState&)
{
    // This is shader based path so...
    CVF_TIGHT_ASSERT(ShaderProgram::supportedOpenGL(oglContext));

    size_t numPrimitiveSets = m_primitiveSets.size();
    if (numPrimitiveSets == 0 || m_vertexBundle->vertexCount() == 0)
    {
        return;
    }

    VertexBundleUsage bundleUsage;
    m_vertexBundle->useBundle(oglContext, &bundleUsage, shaderProgram);

    size_t i;
    for (i = 0; i < numPrimitiveSets; i++)
    {
        PrimitiveSet* primSet = m_primitiveSets[i].p();
        CVF_TIGHT_ASSERT(primSet);
        primSet->render(oglContext);
    }

    CVF_CHECK_OGL(oglContext);

    m_vertexBundle->finishUseBundle(oglContext, &bundleUsage);
}


//--------------------------------------------------------------------------------------------------
/// Render the geometry using fixed function style of specifying the arrays. 
///
/// The main difference between this render path and that of render() is that this path will
/// specify all client arrays or buffer objects using 'old style' glVertexArray(), glNormalArray() etc
/// 
/// \warning Requires at least OpenGL 1.5 since it uses buffer objects.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::renderFixedFunction(OpenGLContext* oglContext, const MatrixState&)
{
    CVF_ASSERT(BufferObjectManaged::supportedOpenGL(oglContext));

#ifdef CVF_OPENGL_ES
    CVF_FAIL_MSG("Not supported on OpenGL ES");
#else

    size_t numPrimitiveSets = m_primitiveSets.size();
    if (numPrimitiveSets == 0 || m_vertexBundle->vertexCount() == 0)
    {
        return;
    }

    // Setup good old 'vertex arrays' for use with fixed function drawing
    VertexBundleUsage bundleUsage;
    m_vertexBundle->useBundleFixedFunction(oglContext, &bundleUsage);

    size_t i;
    for (i = 0; i < numPrimitiveSets; i++)
    {
        PrimitiveSet* primSet = m_primitiveSets[i].p();
        CVF_TIGHT_ASSERT(primSet);
        primSet->render(oglContext);
    }

    CVF_CHECK_OGL(oglContext);

    m_vertexBundle->finishUseBundle(oglContext, &bundleUsage);

#endif // CVF_OPENGL_ES
    }


//--------------------------------------------------------------------------------------------------
/// Create any needed buffer objects and upload data to the GPU
///
/// Buffer objects are created only if renderMode is set to VBO.
/// 
/// \warning Requires at least OpenGL 1.5
//--------------------------------------------------------------------------------------------------
void DrawableGeo::createUploadBufferObjectsGPU(OpenGLContext* oglContext)
{
    CVF_TIGHT_ASSERT(oglContext);
    CVF_TIGHT_ASSERT(BufferObjectManaged::supportedOpenGL(oglContext));

    if (m_renderMode != BUFFER_OBJECT)
    {
        return;
    }

    // Upload all data in the bundle
    m_vertexBundle->createUploadBufferObjectsGPU(oglContext);

    // Do the primitive sets separately
    size_t numPrimitiveSets = m_primitiveSets.size();
    for (size_t i = 0; i < numPrimitiveSets; i++)
    {
        PrimitiveSet* prim = m_primitiveSets[i].p();
        prim->createUploadBufferObjectsGPU(oglContext);
    }
}


//--------------------------------------------------------------------------------------------------
/// Releases all buffer objects (BOs) held by this drawable
///
/// \warning The OpenGL context in which the resources were created or a context that is being
///          shared must be current in the calling thread.
/// \warning In order to assure that the actual OpenGL resources get deleted, you must call 
///          OpenGLResourceManager::deleteOrphanedManagedBufferObjects() afterwards.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::releaseBufferObjectsGPU()
{
    m_vertexBundle->releaseBufferObjectsGPU();

    // Release all buffer objects in primitive sets
    size_t numPrimitiveSets = m_primitiveSets.size();
    for (size_t i = 0; i < numPrimitiveSets; i++)
    {
        PrimitiveSet* prim = m_primitiveSets[i].p();
        CVF_TIGHT_ASSERT(prim);
        prim->releaseBufferObjectsGPU();
    }
}


//--------------------------------------------------------------------------------------------------
/// Do immediate mode rendering
//--------------------------------------------------------------------------------------------------
void DrawableGeo::renderImmediateMode(OpenGLContext* oglContext, const MatrixState&)
{
#ifdef CVF_OPENGL_ES
    CVF_FAIL_MSG("Not supported on OpenGL ES");
#else
    CVF_ASSERT(oglContext);

    const Vec3fArray* vertexArr = m_vertexBundle->vertexArray();
    const Vec3fArray* normalArr = m_vertexBundle->normalArray();
    const Vec2fArray* texCoordArr = m_vertexBundle->textureCoordArray();
    const Color3ubArray* colorArr = m_vertexBundle->colorArray();

    size_t numPrimitiveSets = m_primitiveSets.size();
    size_t ip;
    for (ip = 0; ip < numPrimitiveSets; ip++)
    {
        const PrimitiveSet* primitiveSet = m_primitiveSets.at(ip);
        CVF_TIGHT_ASSERT(primitiveSet);

        glBegin(primitiveSet->primitiveTypeOpenGL());

        size_t numIndices = primitiveSet->indexCount();
        size_t i;
        for (i = 0; i < numIndices; i++)
        {
            uint index = primitiveSet->index(i);

            if (normalArr)
            {
                glNormal3fv((const float*)&normalArr->get(index));
            }

            if (colorArr)
            {
                glColor3ubv((const ubyte*)&colorArr->get(index));
            }

            if (texCoordArr)
            {
                glTexCoord2fv((const float*)&texCoordArr->get(index));
            }

            glVertex3fv((float*)&vertexArr->get(index));
        }

        glEnd();
    }

#endif // CVF_OPENGL_ES
}


//--------------------------------------------------------------------------------------------------
/// Get the number of vertices (nodes, points) in the drawable
//--------------------------------------------------------------------------------------------------
size_t DrawableGeo::vertexCount() const
{
    return m_vertexBundle->vertexCount();
}


//--------------------------------------------------------------------------------------------------
/// Get the number of triangles in the drawable. A quad is not 2 triangles.
//--------------------------------------------------------------------------------------------------
size_t DrawableGeo::triangleCount() const
{
    size_t count = 0;
    size_t numPrimitiveObjects = m_primitiveSets.size();

    size_t i;
    for (i = 0; i < numPrimitiveObjects; i++)
    {
        const PrimitiveSet* prim = m_primitiveSets[i].p();
        CVF_ASSERT(prim);

        count += prim->triangleCount();
    }

    return count;
}


//--------------------------------------------------------------------------------------------------
/// Get the total number of OpenGL primitives (sum of lines, points, quads, etc.) 
//--------------------------------------------------------------------------------------------------
size_t DrawableGeo::faceCount() const
{
    size_t count = 0;
    size_t numPrimitiveObjects = m_primitiveSets.size();

    size_t i;
    for (i = 0; i < numPrimitiveObjects; i++)
    {
        const PrimitiveSet* prim = m_primitiveSets[i].p();
        CVF_ASSERT(prim);

        count += prim->faceCount();
    }

    return count;
}


//--------------------------------------------------------------------------------------------------
/// Set the render mode (immediate, vertex array or VBO) to use when rendering the geometry
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setRenderMode(RenderMode renderMode)
{
    if (m_renderMode != renderMode)
    {
        m_renderMode = renderMode;
        releaseBufferObjectsGPU();
    }
}


//--------------------------------------------------------------------------------------------------
/// Returns the current render mode used to draw the geometry.
//--------------------------------------------------------------------------------------------------
DrawableGeo::RenderMode DrawableGeo::renderMode() const
{
    return m_renderMode;
}


//--------------------------------------------------------------------------------------------------
/// Set the vertices (node coordinates) of this geometry. 
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setVertexArray(Vec3fArray* vertexArray)
{
    m_vertexBundle->setVertexArray(vertexArray);
    recomputeBoundingBox();
}


//--------------------------------------------------------------------------------------------------
/// Set the per node normals of this geometry
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setNormalArray(Vec3fArray* normalArray)
{
    m_vertexBundle->setNormalArray(normalArray);
}


//--------------------------------------------------------------------------------------------------
/// Set the per node colors of this geometry
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setColorArray(Color3ubArray* colorArray)
{
    m_vertexBundle->setColorArray(colorArray);
}


//--------------------------------------------------------------------------------------------------
/// Returns the vertices (node coordinates) of this geometry
//--------------------------------------------------------------------------------------------------
const Vec3fArray* DrawableGeo::vertexArray() const
{
    return m_vertexBundle->vertexArray();
}


//--------------------------------------------------------------------------------------------------
/// Returns pointer to the normal array for this geometry
//--------------------------------------------------------------------------------------------------
const Vec3fArray* DrawableGeo::normalArray() const
{
    return m_vertexBundle->normalArray();
}


//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setTextureCoordArray(Vec2fArray* textureCoordArray)
{
    m_vertexBundle->setTextureCoordArray(textureCoordArray);
}


//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
const Vec2fArray* DrawableGeo::textureCoordArray() const
{
    return m_vertexBundle->textureCoordArray();
}


//--------------------------------------------------------------------------------------------------
/// Replace or add a vertex attribute to this DrawableGeo's vertex attribute set
/// 
/// If a vertex attribute with the same name as the incoming attribute is already present, the 
/// existing attribute will be replaced.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setVertexAttribute(VertexAttribute* vertexAttribute)
{
    m_vertexBundle->setGenericAttribute(vertexAttribute);
}


//--------------------------------------------------------------------------------------------------
/// Returns the number of primitive sets in this geometry
//--------------------------------------------------------------------------------------------------
size_t DrawableGeo::primitiveSetCount() const
{
    return m_primitiveSets.size();
}


//--------------------------------------------------------------------------------------------------
/// Add the given primitive set to the drawable. primitives cannot be NULL.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::addPrimitiveSet(PrimitiveSet* primitiveSet)
{
    CVF_ASSERT(primitiveSet);

    m_primitiveSets.push_back(primitiveSet);
}


//--------------------------------------------------------------------------------------------------
/// Returns the primitive set at the given index
//--------------------------------------------------------------------------------------------------
const PrimitiveSet* DrawableGeo::primitiveSet(size_t index) const
{
    CVF_ASSERT(index < primitiveSetCount());
    return m_primitiveSets.at(index);
}


//--------------------------------------------------------------------------------------------------
/// Returns the primitive set at the given index
//--------------------------------------------------------------------------------------------------
PrimitiveSet* DrawableGeo::primitiveSet(size_t index) 
{
    CVF_ASSERT(index < primitiveSetCount());
    return m_primitiveSets.at(index);
}


//--------------------------------------------------------------------------------------------------
/// Get connectivity table for the specified face
/// 
/// \param indexOfFace  Index of the face being queried.
/// \param indices      Will receive the connectivity table for the specified face
//--------------------------------------------------------------------------------------------------
void DrawableGeo::getFaceIndices(size_t indexOfFace, UIntArray* indices) const
{
    CVF_ASSERT(indices);
    indices->setSizeZero();

    size_t idxFirstPolyInPrimSet = 0;

    size_t numPrimSets = m_primitiveSets.size();
    size_t ip;
    for (ip = 0; ip < numPrimSets; ip++)
    {
        const PrimitiveSet* primSet = m_primitiveSets.at(ip);

        size_t primitiveFaceCount = primSet->faceCount();
        if (indexOfFace >= idxFirstPolyInPrimSet && indexOfFace < idxFirstPolyInPrimSet + primitiveFaceCount)
        {
            size_t localPolygonIdx = indexOfFace - idxFirstPolyInPrimSet;
            primSet->getFaceIndices(localPolygonIdx, indices);

            return;
        }

        idxFirstPolyInPrimSet += primitiveFaceCount;
    }
}


//--------------------------------------------------------------------------------------------------
/// Sets the DrawableGeo object's geometry representation from a face list
/// 
/// \param faceList Face list
///
/// faceList contains number of items before each face connectivities. E.g. 3 0 1 2   3 2 3 1   3 2 1 3
///
/// \note This method will use more temporary memory than strictly needed in order to optimize 
///       performance.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setFromFaceList(const UIntArray& faceList)
{
    m_primitiveSets.clear();

    size_t numFaceListEntries = faceList.size();

    ref<UIntArray> triangleConnects = new UIntArray;
	triangleConnects->reserve(numFaceListEntries*3);		// Usually too much, but temporary and will be squeezed if kept.

    size_t i = 0;
    while (i < numFaceListEntries)
    {
        uint numConnects = faceList[i++];
        CVF_ASSERT(numConnects >= 3);

        if (numConnects == 3)
        {
			triangleConnects->add(faceList[i++]);
			triangleConnects->add(faceList[i++]);
			triangleConnects->add(faceList[i++]);
		}
		else 
		{
            size_t j;
            for (j = 0; j < numConnects - 2;  j++)
            {
                triangleConnects->add(faceList[i]);
                triangleConnects->add(faceList[i + 1 + j]);
                triangleConnects->add(faceList[i + 2 + j]);
            }

            i += numConnects;
		}
    }

    // Check if the largest index used in the triangle connects exceeds short representation
	if (triangleConnects->max() < std::numeric_limits<ushort>::max())
	{
		// Create an USHORT primitive set
		size_t arraySize = triangleConnects->size();

		ref<UShortArray> shortIndices = new UShortArray;
		shortIndices->resize(arraySize);

		size_t j;
		for (j = 0; j < arraySize; j++)
		{
			shortIndices->set(j, static_cast<ushort>(triangleConnects->get(j)));
		}

		ref<PrimitiveSetIndexedUShort> prim = new PrimitiveSetIndexedUShort(PT_TRIANGLES);
		prim->setIndices(shortIndices.p());

		m_primitiveSets.push_back(prim.p());
	}
	else
	{
		// Create a UINT primitive set
		ref<PrimitiveSetIndexedUInt> prim = new PrimitiveSetIndexedUInt(PT_TRIANGLES);

		triangleConnects->squeeze();
		prim->setIndices(triangleConnects.p());
		m_primitiveSets.push_back(prim.p());
	}
}


//--------------------------------------------------------------------------------------------------
/// Setup a geometry with triangles from an array of vertices (3 vertices per triangle).
/// 
/// This method sets the vertices to the passed vertexArray, and then creates one PrimitiveSet with 
/// one triangle for every three vertices in vertexArray.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setFromTriangleVertexArray(Vec3fArray* vertexArray)
{
    CVF_ASSERT(vertexArray);

    m_vertexBundle->setVertexArray(vertexArray);
    m_primitiveSets.clear();

    size_t numVertices = vertexArray->size();

    ref<UIntArray> indices = new UIntArray;
    indices->resize(numVertices);

    size_t i;
    for (i = 0; i < numVertices; i++)
    {
        indices->set(i, static_cast<uint>(i));
    }

    ref<PrimitiveSetIndexedUInt> prim = new PrimitiveSetIndexedUInt(PT_TRIANGLES);
    prim->setIndices(indices.p());

    m_primitiveSets.push_back(prim.p());

    recomputeBoundingBox();
}


//--------------------------------------------------------------------------------------------------
/// Setup geometry with quads from an array of vertices (4 vertices per quad).
/// 
/// This method sets the vertices to the passed vertexArray, and then creates one PrimitiveSet with 
/// two triangles for every four vertices in vertexArray.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::setFromQuadVertexArray(Vec3fArray* vertexArray)
{
    CVF_ASSERT(vertexArray);

    m_vertexBundle->setVertexArray(vertexArray);
    m_primitiveSets.clear();

    size_t numVertices = vertexArray->size();
	size_t numQuads = numVertices/4;
	CVF_ASSERT(numVertices%4 == 0);

    // Two triangles per quad
    ref<UIntArray> indices = new UIntArray;
    indices->resize(numQuads*2*3);				

	size_t index = 0;
    uint i;
    for (i = 0; i < numQuads; i++)
    {
        indices->set(index++, i*4);
		indices->set(index++, i*4 + 1);
		indices->set(index++, i*4 + 2);

		indices->set(index++, i*4);
		indices->set(index++, i*4 + 2);
		indices->set(index++, i*4 + 3);
    }

    ref<PrimitiveSetIndexedUInt> prim = new PrimitiveSetIndexedUInt(PT_TRIANGLES);
    prim->setIndices(indices.p());

    m_primitiveSets.push_back(prim.p());

    recomputeBoundingBox();
}


//--------------------------------------------------------------------------------------------------
/// Weld vertices based on vertex distance
///
/// \warning Calling this function will delete all vertex related data except the vertex positions
//--------------------------------------------------------------------------------------------------
void DrawableGeo::weldVertices(double weldDistance)
{
    size_t numVertices = m_vertexBundle->vertexCount();
    size_t numPrimSets = m_primitiveSets.size();
    if (numVertices == 0 || numPrimSets == 0)
    {
        return;
    }

    cref<Vec3fArray> sourceVertexArray = m_vertexBundle->vertexArray();
    CVF_ASSERT(sourceVertexArray.notNull());

    // Try and use bounding box to guess cell size
    // Probably needs more experimenting here
    double cellSize = m_boundingBox.radius()/100;

    VertexWelder welder;
    welder.initialize(weldDistance, cellSize, static_cast<uint>(numVertices));
    welder.reserveVertices(static_cast<uint>(numVertices));

    Collection<PrimitiveSet> newPrimSets;

    size_t ip;
    for (ip = 0; ip < numPrimSets; ip++)
    {
        const PrimitiveSet* srcPrimSet = m_primitiveSets.at(ip);
        PrimitiveType primType = srcPrimSet->primitiveType();
        size_t numIndices = srcPrimSet->indexCount();
        
        ref<UIntArray> newIndices = new UIntArray;
        newIndices->reserve(numIndices);

        size_t i;
        for (i = 0; i < numIndices; i++)
        {
            uint idx = srcPrimSet->index(i);
            Vec3f v = sourceVertexArray->get(idx);
            uint newIndexOfVertex = welder.weldVertex(v, NULL);
            newIndices->add(newIndexOfVertex);
        }

        ref<PrimitiveSetIndexedUInt> primSet = new PrimitiveSetIndexedUInt(primType);
        primSet->setIndices(newIndices.p());

        newPrimSets.push_back(primSet.p());
    }

    releaseBufferObjectsGPU();

    m_vertexBundle->clear();
    m_vertexBundle->setVertexArray(welder.createVertexArray().p());
    m_primitiveSets = newPrimSets;

    recomputeBoundingBox();
}


//--------------------------------------------------------------------------------------------------
/// Merge a collection of drawable geometry objects into this drawable
/// 
/// \param drawableGeos Collection of drawable geometries to be merged
/// 
/// A new vertex array is created with the incoming vertex arrays appended to the existing contents. 
/// Primitives are copied and indices updated.
/// 
/// \warning All other vertex attribute data such as normals, texture coordinates etc will be set to NULL
//--------------------------------------------------------------------------------------------------
void DrawableGeo::mergeInto(const Collection<DrawableGeo>& drawableGeos)
{
    size_t totalVertexCount = m_vertexBundle->vertexCount();
    size_t i;
    for (i = 0; i < drawableGeos.size(); i++)
    {
        const DrawableGeo* geo = drawableGeos[i].p();
        totalVertexCount += geo->vertexCount();
    }

    // Nothing to do if no existing vertices and no new vertices
    if (totalVertexCount == 0)
    {
        return;
    }

    // Create a new vertex array and copy data from our array
    cref<Vec3fArray> oldVertexArray = m_vertexBundle->vertexArray();
    ref<Vec3fArray> newVertexArr = new Vec3fArray(totalVertexCount);
    size_t currentVertexIndex = 0;
    if (oldVertexArray.notNull() && oldVertexArray->size() > 0)
    {
        newVertexArr->copyData(*oldVertexArray, oldVertexArray->size(), 0, 0);
        currentVertexIndex = oldVertexArray->size();
    }

    // Then copy from the other drawable geos
    for (i = 0; i < drawableGeos.size(); i++)
    {
        const DrawableGeo* otherDrawable = drawableGeos[i].p();
        size_t j = 0;
        for (j = 0; j < otherDrawable->primitiveSetCount(); j++)
        {
			const PrimitiveSet* primSet = otherDrawable->primitiveSet(j);
			CVF_ASSERT(primSet);

            ref<UIntArray> indices = new UIntArray;
            indices->resize(primSet->indexCount());

            uint k;
            for (k = 0; k < primSet->indexCount(); k++)
            {
                uint val = primSet->index(k);

                val += static_cast<uint>(currentVertexIndex);
                indices->set(k, val);
            }

            ref<PrimitiveSetIndexedUInt> prim = new PrimitiveSetIndexedUInt(primSet->primitiveType());
            prim->setIndices(indices.p());
            m_primitiveSets.push_back(prim.p());
        }

        const Vec3fArray* otherVertices = otherDrawable->vertexArray();
        CVF_ASSERT(otherVertices);

        // Append other drawable vertices vertex array and update vertex index
        newVertexArr->copyData(otherVertices->ptr(), otherVertices->size(), currentVertexIndex);
        currentVertexIndex += otherVertices->size();
    }

    // Clear all vertex attributes and set new vertex array
    m_vertexBundle->clear();
    m_vertexBundle->setVertexArray(newVertexArr.p());

    recomputeBoundingBox();
}


//--------------------------------------------------------------------------------------------------
/// Merge a drawable geometry object with this drawable possibly with transformation.
/// 
/// \param drawableGeo Drawable geometries to be merged
/// \param transformation Transformation matrix used to modify vertices
/// 
/// Vertices are converted if a transformation matrix is given.
/// Vertex arrays are appended to the merged vertex array. Primitives are copied and indices updated.
/// 
/// \warning All other vertex attribute data such as normals, texture coordinates etc will be set to NULL
//--------------------------------------------------------------------------------------------------
void DrawableGeo::mergeInto(const DrawableGeo& drawableGeo, const Mat4d* transformation)
{
    size_t totalVertexCount = m_vertexBundle->vertexCount();
    totalVertexCount += drawableGeo.vertexCount();

    // Nothing to do if no existing vertices and no new vertices
    if (totalVertexCount == 0)
    {
        return;
    }

    // Create a new vertex array and copy data from our array
    cref<Vec3fArray> oldVertexArray = m_vertexBundle->vertexArray();
    ref<Vec3fArray> newVertexArr = new Vec3fArray(totalVertexCount);
    size_t currentVertexIndex = 0;
    if (oldVertexArray.notNull() && oldVertexArray->size() > 0)
    {
        newVertexArr->copyData(*oldVertexArray, oldVertexArray->size(), 0, 0);
        currentVertexIndex = oldVertexArray->size();
    }

    // Do the primitive set
    size_t i = 0;
    for (i = 0; i < drawableGeo.primitiveSetCount(); i++)
    {
        const PrimitiveSet* primSet = drawableGeo.primitiveSet(i);
        CVF_ASSERT(primSet);

        ref<UIntArray> indices = new UIntArray;
        indices->resize(primSet->indexCount());

        uint k;
        for (k = 0; k < primSet->indexCount(); k++)
        {
            uint val = primSet->index(k);

            val += static_cast<uint>(currentVertexIndex);
            indices->set(k, val);
        }

        ref<PrimitiveSetIndexedUInt> prim = new PrimitiveSetIndexedUInt(primSet->primitiveType());
        prim->setIndices(indices.p());
        m_primitiveSets.push_back(prim.p());
    }


    const Vec3fArray* srcVertices = drawableGeo.vertexArray();
    CVF_ASSERT(srcVertices);

    if (transformation)
    {
        size_t j;
        for (j = 0; j < srcVertices->size(); j++)
        {
            // Transform to double vector to be able to do a transform using a double matrix
            Vec3d tmpDoubleVec(srcVertices->get(j));
            tmpDoubleVec.transformPoint(*transformation);
            newVertexArr->set(currentVertexIndex, Vec3f(tmpDoubleVec));
            currentVertexIndex++;
        }
    }
    else
    {
        // Append other drawable vertices vertex array and update vertex index
        newVertexArr->copyData(*srcVertices, srcVertices->size(), currentVertexIndex, 0);
    }

    // Clear all vertex attributes and set new vertex array
    m_vertexBundle->clear();
    m_vertexBundle->setVertexArray(newVertexArr.p());

    recomputeBoundingBox();
}


//--------------------------------------------------------------------------------------------------
/// Convert indexed primitive set to unsigned short if possible
/// 
/// \return  The number of primitive sets that was converted.
//--------------------------------------------------------------------------------------------------
int DrawableGeo::convertFromUIntToUShort()
{
    int numConverted = 0;

    Collection<PrimitiveSet> myCollection;

    size_t numPrimitiveObjects = m_primitiveSets.size();
    size_t iPrim;
    for (iPrim = 0; iPrim < numPrimitiveObjects; iPrim++)
    {
        PrimitiveSet* primitive = m_primitiveSets[iPrim].p();

        PrimitiveSetIndexedUInt* primitiveSetUInt = dynamic_cast<PrimitiveSetIndexedUInt*>(primitive);
        PrimitiveSetIndexedUIntScoped* primitiveSetUIntScoped = dynamic_cast<PrimitiveSetIndexedUIntScoped*>(primitive);
		if (vertexCount() < std::numeric_limits<ushort>::max() && primitiveSetUInt)
        {
            const UIntArray* uiIndices = primitiveSetUInt->indices();
            
            ref<UShortArray> indices = new UShortArray;

            if (uiIndices)
            {
                size_t uiArraySize = uiIndices->size();
                
                indices->resize(uiArraySize);
                
                size_t j;
                for (j = 0; j < uiArraySize; j++)
                {
                    indices->set(j, static_cast<ushort>(uiIndices->get(j)));
                }                
            }
            
            ref<PrimitiveSetIndexedUShort> prim = new PrimitiveSetIndexedUShort(primitive->primitiveType());
            prim->setIndices(indices.p());

            myCollection.push_back(prim.p());
            numConverted++;
        }
        else if (vertexCount() < std::numeric_limits<ushort>::max() && primitiveSetUIntScoped)
        {
            const UIntArray* uiIndices = primitiveSetUIntScoped->indices();
            size_t uiArraySize = uiIndices->size();

            ref<UShortArray> indices = new UShortArray;
            indices->resize(uiArraySize);

            size_t j;
            for (j = 0; j < uiArraySize; j++)
            {
                indices->set(j, static_cast<ushort>(uiIndices->get(j)));
            }

            ref<PrimitiveSetIndexedUShortScoped> prim = new PrimitiveSetIndexedUShortScoped(primitive->primitiveType());
            prim->setIndices(indices.p(), primitiveSetUIntScoped->scopeFirstElement(), primitiveSetUIntScoped->scopeElementCount());

            myCollection.push_back(prim.p());
            numConverted++;
        }
        else
        {
            myCollection.push_back(primitive);
        }
    }

    m_primitiveSets.clear();
    m_primitiveSets = myCollection;

    return numConverted;
}

//--------------------------------------------------------------------------------------------------
/// Transforms all vertices in this drawable geometry using the specified matrix
/// 
/// \warning Calling this function will create a new internal vertex array.
/// \warning Normals may have to be recomputed after this function has been called.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::transform(const Mat4d& transformation)
{
    size_t numVertices = m_vertexBundle->vertexCount();
    if (numVertices == 0)
    {
        return;
    }

    cref<Vec3fArray> sourceVertexArray = m_vertexBundle->vertexArray();
    CVF_ASSERT(sourceVertexArray.notNull());

    ref<Vec3fArray> newVertexArr = new Vec3fArray(numVertices);

    size_t i;
    for (i = 0; i < numVertices; i++)
    {
        // Transform to double vector to be able to do a transform using a double matrix
        Vec3d tmpVertex(sourceVertexArray->get(i));
        tmpVertex.transformPoint(transformation);

        newVertexArr->set(i, Vec3f(tmpVertex));
    }

    m_vertexBundle->setVertexArray(newVertexArr.p());

	recomputeBoundingBox();
}


//--------------------------------------------------------------------------------------------------
/// Compute per node normals based on all primitive sets.
//--------------------------------------------------------------------------------------------------
void DrawableGeo::computeNormals()
{
    size_t numVertices = m_vertexBundle->vertexCount();
    if (numVertices == 0)
    {
        m_vertexBundle->setNormalArray(NULL);
        return;
    }

    cref<Vec3fArray> vertexArr = m_vertexBundle->vertexArray();
    CVF_ASSERT(vertexArr.notNull());

    ref<Vec3fArray> normalArr = new Vec3fArray(numVertices);
    normalArr->setAll(Vec3f::ZERO);

    size_t numPrimitiveObjects = m_primitiveSets.size();
    size_t iPrim;
    for (iPrim = 0; iPrim < numPrimitiveObjects; iPrim++)
    {
        const PrimitiveSet* prim = m_primitiveSets[iPrim].p();
        CVF_ASSERT(prim);

        PrimitiveType primType = prim->primitiveType();

        switch (primType)
        {
            case PT_TRIANGLES:
            {
                size_t indexCount = prim->indexCount();
                CVF_ASSERT(indexCount%3 == 0);

                size_t i;
                for (i = 0; i < indexCount; i += 3)
                {
                    uint a = prim->index(i + 0);
                    uint b = prim->index(i + 1);
                    uint c = prim->index(i + 2);

                    const Vec3f& v0 = vertexArr->get(a);
                    Vec3f v1 = vertexArr->get(b) - v0;
                    Vec3f v2 = vertexArr->get(c) - v0;

                    Vec3f n = v1 ^ v2;
                    n.normalize();

                    (*normalArr)[a] += n;
                    (*normalArr)[b] += n;
                    (*normalArr)[c] += n;
                }
            }
            break;

            // Normals not applicable for the following primitives
            case PT_POINTS:
            case PT_LINES:
            case PT_LINE_LOOP:
            case PT_LINE_STRIP:
            {
                break;
            }

            case PT_TRIANGLE_STRIP:
            {
                size_t indexCount = prim->indexCount();
                size_t i;
                for (i = 2; i < indexCount; i++)
                {
                    // In Tri strips, the winding flips every other triangle
                    // eg: v0,v1,v2 then v1,v3,v2, v2,v3,v4 etc.
                    // See OpenGL redbook for more info.
                    bool ccw = (i % 2 == 0);

                    uint a = prim->index(i - 2);
                    uint b = ccw ? prim->index(i - 1) : prim->index(i);
                    uint c = ccw ? prim->index(i)     : prim->index(i - 1);

                    const Vec3f& v0 = vertexArr->get(a);
                    Vec3f v1 = vertexArr->get(b) - v0;
                    Vec3f v2 = vertexArr->get(c) - v0;

                    Vec3f n = v1 ^ v2;
                    n.normalize();

                    (*normalArr)[a] += n;
                    (*normalArr)[b] += n;
                    (*normalArr)[c] += n;
                }

                break;
            }

            case PT_TRIANGLE_FAN:
            {
                size_t indexCount = prim->indexCount();
                size_t i;
                for (i = 2; i < indexCount; i++)
                {
                    uint a = prim->index(0);
                    uint b = prim->index(i - 1);
                    uint c = prim->index(i);

                    const Vec3f& v0 = vertexArr->get(a);
                    Vec3f v1 = vertexArr->get(b) - v0;
                    Vec3f v2 = vertexArr->get(c) - v0;

                    Vec3f n = v1 ^ v2;
                    n.normalize();

                    (*normalArr)[a] += n;
                    (*normalArr)[b] += n;
                    (*normalArr)[c] += n;
                }

                break;
            }

            default:
            {
                CVF_FAIL_MSG("Unhandled primitive for normal calculation");
                break;
            }
        }
    }

    size_t i;
    for (i = 0; i < normalArr->size(); i++)
    {
        Vec3f v = normalArr->get(i);
        v.normalize();
        normalArr->set(i, v);
    }

    m_vertexBundle->setNormalArray(normalArr.p());
}


//--------------------------------------------------------------------------------------------------
/// Updated the cached bounding box in this drawable geo.
/// 
/// Normally you will not need to call this function directly. Member functions that change the
/// vertex coordinates will automatically update the bounding box. The exception is if you manually
/// modify the vertices by directly manipulating the array returned by vertexArray().
//--------------------------------------------------------------------------------------------------
void DrawableGeo::recomputeBoundingBox()
{
    m_boundingBox.reset();
    if (m_vertexBundle->vertexCount() > 0)
    {
        cref<Vec3fArray> vertexArr = m_vertexBundle->vertexArray();
        CVF_ASSERT(vertexArr.notNull());
        m_boundingBox.add(*vertexArr);
    }
}


//--------------------------------------------------------------------------------------------------
/// Returns the content of this geometry as a face list.
//--------------------------------------------------------------------------------------------------
ref<UIntArray> DrawableGeo::toFaceList() const
{
    ref<UIntArray> faceList = new UIntArray;

    size_t i;
    for (i = 0; i < m_primitiveSets.size(); i++)
    {
        const PrimitiveSet* prim = m_primitiveSets[i].p();
        CVF_ASSERT(prim);

        PrimitiveType primType = prim->primitiveType();
        switch (primType)
        {
            case PT_TRIANGLES:
            {
                size_t indexCount = prim->indexCount();
                size_t newSize = faceList->size();
                newSize += (indexCount / 3) * 4;
                faceList->reserve(newSize);

                size_t i;
                for (i = 0; i < indexCount; i += 3)
                {
                    faceList->add(3);
                    faceList->add(prim->index(i + 0));
                    faceList->add(prim->index(i + 1));
                    faceList->add(prim->index(i + 2));
                }
            }
            break;

            case PT_POINTS:
            case PT_LINES:
            case PT_LINE_LOOP:
            case PT_LINE_STRIP:
            {
                break;
            }

            case PT_TRIANGLE_STRIP:
            {
                size_t indexCount = prim->indexCount();

                size_t newSize = faceList->size();
                newSize += (indexCount - 2) * 4;
                faceList->reserve(newSize);

                size_t i;
                for (i = 2; i < indexCount; i++)
                {
                    // In Tri strips, the winding flips every other triangle
                    // eg: v0,v1,v2 then v1,v3,v2, v2,v3,v4 etc.
                    // See OpenGL redbook for more info.
                    bool ccw = (i % 2 == 0);

                    uint a = prim->index(i - 2);
                    uint b = ccw ? prim->index(i - 1) : prim->index(i);
                    uint c = ccw ? prim->index(i)     : prim->index(i - 1);

                    faceList->add(3);
                    faceList->add(a);
                    faceList->add(b);
                    faceList->add(c);
                }

                break;
            }

            case PT_TRIANGLE_FAN:
            {
                size_t indexCount = prim->indexCount();

                size_t newSize = faceList->size();
                newSize += (indexCount - 2) * 4;
                faceList->reserve(newSize);

                size_t i;
                for (i = 2; i < indexCount; i++)
                {
                    uint a = prim->index(0);
                    uint b = prim->index(i - 1);
                    uint c = prim->index(i);

                    faceList->add(3);
                    faceList->add(a);
                    faceList->add(b);
                    faceList->add(c);
                }

                break;
            }

            default:
            {
                CVF_FAIL_MSG("Unhandled primitive for normal calculation");
                break;
            }
        }
    }

    return faceList;
}


//--------------------------------------------------------------------------------------------------
/// Get the bounding box
//--------------------------------------------------------------------------------------------------
BoundingBox DrawableGeo::boundingBox() const
{
    return m_boundingBox;
}


//--------------------------------------------------------------------------------------------------
/// Intersect the drawable geo with the ray and return the closest intersection point and the face hit
///
/// Returns true if anything was hit.
//--------------------------------------------------------------------------------------------------
bool DrawableGeo::rayIntersect(const Ray& ray, Vec3d* intersectionPoint, uint* faceHit) const
{
    bool anyHits = false;
    double minDistSquared = 1.0e300;

    cref<Vec3fArray> vertexArr = m_vertexBundle->vertexArray();

    size_t numPrimitiveSets = m_primitiveSets.size();
    size_t iPrimSet;
    int accumulatedFaceCount = 0;

    for (iPrimSet = 0; iPrimSet < numPrimitiveSets; iPrimSet++)
    {
        const PrimitiveSet* primSet = m_primitiveSets.at(iPrimSet);
        CVF_TIGHT_ASSERT(primSet);

        UIntArray conn;
        int numPrimFaces = static_cast<int>(primSet->faceCount());

#pragma omp parallel for private (conn)
        for (int i = 0; i < numPrimFaces; i++)
        {
            bool hitThisFace = false;
            Vec3d localIntersect;

            primSet->getFaceIndices(static_cast<size_t>(i), &conn);
            int numconn = static_cast<int>(conn.size());
            CVF_TIGHT_ASSERT(numconn <= 3);
            if (numconn == 3)
            {
                hitThisFace = ray.triangleIntersect(Vec3d(vertexArr->get(conn[0])), 
                                                    Vec3d(vertexArr->get(conn[1])), 
                                                    Vec3d(vertexArr->get(conn[2])), 
                                                    &localIntersect);
            }

            if (hitThisFace)
            {
                double distSquared = (ray.origin() - localIntersect).lengthSquared();
                #pragma omp critical
		{
                if (distSquared < minDistSquared)
                {
                    if (intersectionPoint)
                    {
                    *intersectionPoint = localIntersect;
                    }
                    
                    minDistSquared = distSquared;

                    if (faceHit)
                    {
                            *faceHit = i + accumulatedFaceCount;
                    }
                }
                anyHits = true;
            }
        }
        } // End omp parallel for
        accumulatedFaceCount += numPrimFaces;
    }

    return anyHits;
}


//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
bool DrawableGeo::rayIntersect(const Ray& ray, Vec3dArray* intersectionPoints, UIntArray* facesHit) const
{
    if (intersectionPoints) intersectionPoints->setSizeZero();
    if (facesHit) facesHit->setSizeZero();

    std::vector<Vec3d> isectPts;
    std::vector<uint> faceIndices;

    cref<Vec3fArray> vertexArr = m_vertexBundle->vertexArray();
    int accumulatedFaceCount = 0;

    size_t numPrimitiveSets = m_primitiveSets.size();
    size_t iPrimSet;
    for (iPrimSet = 0; iPrimSet < numPrimitiveSets; iPrimSet++)
    {
        const PrimitiveSet* primSet = m_primitiveSets.at(iPrimSet);
        CVF_TIGHT_ASSERT(primSet);

        UIntArray conn;
        int numPrimFaces = static_cast<int>(primSet->faceCount());
#pragma omp parallel for private(conn)
        for (int i = 0; i < numPrimFaces; i++)
        {
            bool hitThisFace = false;
            Vec3d localIntersect;

            primSet->getFaceIndices(static_cast<size_t>(i), &conn);
            int numconn = static_cast<int>(conn.size());
            if (numconn == 3)
            {
                hitThisFace = ray.triangleIntersect(Vec3d(vertexArr->get(conn[0])), 
                                                    Vec3d(vertexArr->get(conn[1])), 
                                                    Vec3d(vertexArr->get(conn[2])), 
                                                    &localIntersect);
            }

            if (hitThisFace)
            {
              #pragma omp critical
	      {
                isectPts.push_back(localIntersect);
                faceIndices.push_back(i + accumulatedFaceCount);
	      }
            }
        }
        accumulatedFaceCount += numPrimFaces;
    }

    if (isectPts.size() > 0)
    {
        if (intersectionPoints) intersectionPoints->assign(isectPts);
        if (facesHit) facesHit->assign(faceIndices);

        return true;
    }
    else
    {
        return false;
    }
}


//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
bool DrawableGeo::rayIntersectCreateDetail(const Ray& ray, Vec3d* intersectionPoint, ref<HitDetail>* hitDetail) const
{
    uint faceIdx = 0;
    if (rayIntersect(ray, intersectionPoint, &faceIdx))
    {
        if (hitDetail)
        {
            *hitDetail = new HitDetailDrawableGeo(faceIdx);
        }
        return true;
    }
    else
    {
        return false;
    }
}



//==================================================================================================
///
/// \class cvf::HitDetailDrawableGeo
/// \ingroup Render
///
/// 
///
//==================================================================================================

//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
HitDetailDrawableGeo::HitDetailDrawableGeo(uint faceIndex)
:   m_faceIndex(faceIndex)
{
}


//--------------------------------------------------------------------------------------------------
/// 
//--------------------------------------------------------------------------------------------------
uint HitDetailDrawableGeo::faceIndex() const
{
    return m_faceIndex;
}


} // namespace cvf
