m3g/m3gcore11/src/m3g_indexbuffer.c
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 16 Apr 2010 16:21:04 +0300
changeset 36 01a6848ebfd7
parent 0 5d03bc08d59c
permissions -rw-r--r--
Revision: 201009 Kit: 201015

/*
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: IndexBuffer implementation
*
*/


/*!
 * \internal
 * \file
 * \brief IndexBuffer implementation
 */

#ifndef M3G_CORE_INCLUDE
#   error included by m3g_core.c; do not compile separately.
#endif

#include "m3g_indexbuffer.h"

/*----------------------------------------------------------------------
 * Private functions
 *--------------------------------------------------------------------*/


/*----------------------------------------------------------------------
 * Internal functions
 *--------------------------------------------------------------------*/

/*!
 * \internal
 * \brief IndexBuffer destructor
 *
 * \param obj IndexBuffer object
 */
static void m3gDestroyIndexBuffer(Object *obj)
{
    IndexBuffer *ib = (IndexBuffer *) obj;
    M3G_VALIDATE_OBJECT(ib);

	{
		Interface *m3g = M3G_INTERFACE(ib);
		m3gFree(m3g, ib->indices);
		m3gFree(m3g, ib->lengths);
	}

    m3gDestroyObject(obj);
}

/*!
 * \internal
 * \brief Sends the contents of an IndexBuffer to the current GL
 * instance for processing
 *
 * \param buf IndexBuffer object
 */
static void m3gSendIndexBuffer(const IndexBuffer *buf)
{
    M3G_VALIDATE_OBJECT(buf);
    
    M3G_ASSERT(buf->indices);
    M3G_ASSERT(buf->glPrimitive == GL_TRIANGLE_STRIP);
    
    M3G_BEGIN_PROFILE(M3G_INTERFACE(buf), M3G_PROFILE_NGL_DRAW);
    glDrawElements(buf->glPrimitive, buf->indexCount, buf->glType, buf->indices);
    M3G_END_PROFILE(M3G_INTERFACE(buf), M3G_PROFILE_NGL_DRAW);

    M3G_ASSERT_GL;
}

/*!
 * \internal
 * \brief Gets triangle indices, used by mesh pick routine.
 *
 * \param buf IndexBuffer object
 * \param triangle triangle index
 * \param indices triangle indices
 * \retval M3G_TRUE  indices fetched
 * \retval M3G_FALSE no such triangle
 */
static M3Gbool m3gGetIndices(const IndexBuffer *buf,
                             M3Gint triangle,
                             M3Gint *indices) {
    M3Gubyte *bptr;
    M3Gushort *sptr;

    if(triangle + 2 >= buf->indexCount) return M3G_FALSE;

    switch(buf->glType) {
        case GL_UNSIGNED_BYTE:
            bptr = (M3Gubyte *)buf->indices;
            bptr += triangle;
            indices[0] = *bptr++;
            indices[1] = *bptr++;
            indices[2] = *bptr;
            break;

        case GL_UNSIGNED_SHORT:
            sptr = (M3Gushort *)buf->indices;
            sptr += triangle;
            indices[0] = (M3Gint) *sptr++;
            indices[1] = (M3Gint) *sptr++;
            indices[2] = (M3Gint) *sptr;
            break;
    }

    /* Winding */
    indices[3] = triangle & 1;

    return M3G_TRUE;
}

/*!
 * \internal
 * \brief Overloaded Object3D method.
 *
 * \param originalObj original IndexBuffer object
 * \param cloneObj pointer to cloned IndexBuffer object
 * \param pairs array for all object-duplicate pairs
 * \param numPairs number of pairs
 */
static M3Gbool m3gIndexBufferDuplicate(const Object *originalObj,
                                       Object **cloneObj,
                                       Object **pairs,
                                       M3Gint *numPairs)
{
    M3Gint size;
    IndexBuffer *original = (IndexBuffer *)originalObj;
    IndexBuffer *clone = (IndexBuffer *)m3gAllocZ(originalObj->interface, sizeof(IndexBuffer));

    if (clone == NULL) {
        return M3G_FALSE;
    }

    *cloneObj = (Object *)clone;

    /* Call init since this object is 'manually' created */
    m3gInitObject((Object*) clone, originalObj->interface, M3G_CLASS_INDEX_BUFFER);

    if(!m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
        return M3G_FALSE;
    }

    clone->indexCount = original->indexCount;
    clone->glType = original->glType;
    clone->glPrimitive = original->glPrimitive;
    clone->stripCount = original->stripCount;

    if (clone->glType == GL_UNSIGNED_BYTE) {
        size = clone->indexCount;
        clone->indices = m3gAlloc(originalObj->interface, size);
    }
    else {
        size = clone->indexCount * sizeof(M3Gshort);
        clone->indices = m3gAlloc(originalObj->interface, size);
    }

    clone->lengths = (M3Gushort *) m3gAlloc(originalObj->interface, (M3Gsize) clone->stripCount*2);

    if(clone->indices == NULL || clone->lengths == NULL) {
        /* Duplicate will call m3gDeleteObject */
        return M3G_FALSE;
    }

    m3gCopy(clone->lengths, original->lengths, clone->stripCount*2);
    m3gCopy(clone->indices, original->indices, size);

    return M3G_TRUE;
}

/*!
 * \internal
 * \brief Gets maximum index of this index buffer.
 *
 * \param buf IndexBuffer object
 * \return maximum index used
 */
static M3Gint m3gGetMaxIndex(const IndexBuffer *buf)
{
    return buf->maxIndex;
}

/*----------------------------------------------------------------------
 * Virtual function table
 *--------------------------------------------------------------------*/

static const ObjectVFTable m3gvf_IndexBuffer = {
    m3gObjectApplyAnimation,
    m3gObjectIsCompatible,
    m3gObjectUpdateProperty,
    m3gObjectDoGetReferences,
    m3gObjectFindID,
    m3gIndexBufferDuplicate,
    m3gDestroyIndexBuffer
};

/*----------------------------------------------------------------------
 * Public functions
 *--------------------------------------------------------------------*/

/*!
 * \brief Creates an implicit strip buffer
 *
 * \param interface     M3G interface
 * \param stripCount    number of triangle strips
 * \param stripLengths  array of strip lengths
 * \param firstIndex    first index
 * \retval IndexBuffer new IndexBuffer object
 * \retval NULL IndexBuffer creating failed
 */
M3G_API M3GIndexBuffer m3gCreateImplicitStripBuffer(
    M3GInterface interface,
    M3Gsizei stripCount,
    const M3Gsizei *stripLengths,
    M3Gint firstIndex)
{
	M3GIndexBuffer ib;
	M3Gint *stripIndices, i, indexCount = 0;
    Interface *m3g = (Interface *) interface;
    M3G_VALIDATE_INTERFACE(m3g);
	
	if (stripLengths == NULL) {
		m3gRaiseError(m3g, M3G_NULL_POINTER);
        return 0;
	}

    if (stripCount == 0) {
		m3gRaiseError(m3g, M3G_INVALID_VALUE);
        return 0;
    }

	for (i = 0; i < stripCount; i++) {
		if(stripLengths[i] < 3) {
			m3gRaiseError(m3g, M3G_INVALID_VALUE);
            return 0;
		}
		indexCount += stripLengths[i];
	}

	if (firstIndex < 0 ||
		(firstIndex + indexCount) > 65535) {
		m3gRaiseError(m3g, M3G_INVALID_INDEX);
        return 0;
	}

	stripIndices = m3gAlloc(m3g, indexCount * sizeof(M3Gint));

	if (stripIndices == NULL) {
		return 0;	/* automatic out of memory from m3gAlloc */
	}

	/* Generate explict arrays */
    
	for (i = 0; i < indexCount; i++) {
		stripIndices[i] = firstIndex + i;
	}

	ib = m3gCreateStripBuffer(interface,
                              M3G_TRIANGLE_STRIPS,
                              stripCount, stripLengths,
                              M3G_INT, indexCount, stripIndices);
	m3gFree(m3g, stripIndices);
	return ib;
}

/*!
 * \brief Creates an indexed triangle strip buffer
 *
 * \note Optimizes rendering by joining the indices. Also scans the
 * array for the maximum value, and allocates the storage using the
 * smallest possible data type.
 *
 * \param interface     M3G interface
 * \param primitive     primitive type, always M3G_TRIANGLE_STRIPS
 * \param stripCount    number of triangle strips
 * \param stripLengths  array of strip lengths
 * \param type          data type of indices
 * \param numIndices    number of indices
 * \param stripIndices  array of indices
 * \retval IndexBuffer new IndexBuffer object
 * \retval NULL IndexBuffer creating failed
 */
M3G_API M3GIndexBuffer m3gCreateStripBuffer(M3GInterface interface,
                                            M3Gprimitive primitive,
                                            M3Gsizei stripCount,
                                            const M3Gsizei *stripLengths,
                                            M3Gdatatype type,
                                            M3Gsizei numIndices,
                                            const void *stripIndices)
{
    M3Gsizei joinedIndexCount = 0;
    M3Gsizei originalIndexCount = 0;
    M3Gint maxIndex = 0;

    Interface *m3g = (Interface *) interface;
    M3G_VALIDATE_INTERFACE(m3g);

    if (primitive != M3G_TRIANGLE_STRIPS
        || (type != M3G_INT && type != M3G_UINT)) {
        m3gRaiseError(m3g, M3G_INVALID_ENUM);
        return 0;
    }
    if (stripIndices == NULL ||
    	stripLengths == NULL) {
        m3gRaiseError(m3g, M3G_NULL_POINTER);
        return 0;
    }

	if (stripCount == 0 || numIndices == 0) {
        m3gRaiseError(m3g, M3G_INVALID_VALUE);
        return 0;
	}

	{
        /* Find the maximum index and count the actual number of indices
         * required for joining the strips */
        
        int strip;
        M3Gint *idx = (M3Gint *) stripIndices;
        for (strip = 0; strip < stripCount; ++strip) {
            if(stripLengths[strip] < 3) {
                m3gRaiseError(m3g, M3G_INVALID_VALUE);
                return 0;
            }
            if (strip != 0) {
                joinedIndexCount += (M3Guint)((joinedIndexCount & 1) ? 3 : 2);
            }

            joinedIndexCount += (M3Guint) stripLengths[strip];
            originalIndexCount += (M3Guint) stripLengths[strip];

        	if (numIndices < originalIndexCount) {
        		m3gRaiseError(m3g, M3G_INVALID_VALUE);
                return 0;
        	}

            M3G_ASSERT(stripLengths[strip] > 0);
            {
                int i;
                for (i = 0; i < stripLengths[strip]; ++i, ++idx) {
                    if ((*idx & 0xFFFF0000u) != 0) { /* > 65535? */
                    	m3gRaiseError(m3g, M3G_INVALID_INDEX);
                        return 0;
                    }
                    if (*idx > maxIndex)
                        maxIndex = *idx;
                }
            }
        }
    }

    {
        /* Allocate the buffer object */
    
        IndexBuffer *buf = m3gAllocZ(m3g, sizeof(IndexBuffer));
        if (buf == NULL) {
            return 0;
        }
		/* IndexBuffer is derived from Object */
		m3gInitObject(&buf->object, m3g, M3G_CLASS_INDEX_BUFFER);

        buf->glPrimitive = GL_TRIANGLE_STRIP;
        buf->indexCount = joinedIndexCount;

        /* Allocate the index elements as either bytes or shorts,
         * depending on the maximum value we need to store. Note that
         * OpenGL ES does not support 32-bit indices */
        
        if (maxIndex <= 0xFF) {
            buf->indices = m3gAlloc(m3g, (M3Gsize) joinedIndexCount);
            buf->glType = GL_UNSIGNED_BYTE;
        }
        else {
            M3G_ASSERT(maxIndex <= 0xFFFF);
            buf->indices = m3gAlloc(m3g, (M3Gsize) joinedIndexCount*2);
            buf->glType = GL_UNSIGNED_SHORT;
        }
        
        /* Allocate space for original strip lengths */
        buf->lengths = (M3Gushort *) m3gAlloc(m3g, (M3Gsize) stripCount*2);

        if (buf->indices == NULL || buf->lengths == NULL) {
            m3gDeleteObject((M3GObject) buf);
            return 0;
        }

        buf->stripCount = stripCount;

        {
            /* Copy the indices, converting to the chosen type and
             * joining the strips as we go */
            
            M3Guint *src = (M3Guint*) stripIndices;     /* type asserted above */
            void *dstStrip = buf->indices;
            int strip;
            
            for (strip = 0; strip < stripCount; ++strip) {
                int i;

                buf->lengths[strip] = (M3Gushort) stripLengths[strip];

                /*@notfunction@*/
                #define COPY_STRIP(indexType) do {              \
                    indexType *dst = (indexType *) dstStrip;    \
                    if (strip != 0) {                           \
                        *dst++ = (indexType) *(src-1);          \
                        *dst++ = (indexType) *src;              \
                        if (stripLengths[strip-1] & 1) {        \
                            *dst++ = (indexType) *src;          \
                        }                                       \
                    }                                           \
                    for (i = 0; i < stripLengths[strip]; ++i) { \
                        *dst++ = (indexType) *src++;            \
                    }                                           \
                    dstStrip = (void *) dst;                    \
                    M3G_ASSERT(dst <= (indexType *)(buf->indices) + buf->indexCount); \
                } while (0)
                    
                switch (buf->glType) {
                    
                case GL_UNSIGNED_BYTE:
                    COPY_STRIP(GLubyte);
                    break;
                    
                case GL_UNSIGNED_SHORT:
                    COPY_STRIP(GLushort);
                    break;
                    
                default:
                    M3G_ASSERT(0);
                }
                
                #undef COPY_STRIP
            }
        }

        /* Store maximum index */
        buf->maxIndex = maxIndex;

        /* All done! */
        return (M3GIndexBuffer) buf;
    }
}

/*!
 * \brief Gets the number of index batches in an index buffer
 *
 * An index batch usually corresponds to a single OpenGL rendering
 * call.
 *
 * \param buffer  index buffer handle
 * \return number of rendering batches
 */
M3G_API M3Gint m3gGetBatchCount(M3GIndexBuffer buffer)
{
    M3G_VALIDATE_OBJECT(buffer);
    M3G_UNREF(buffer);
    return 1;
}

/*!
 * \brief Returns the indices in an index batch
 *
 * \param buffer      index buffer handle
 * \param batchIndex  batch index
 * \param indices     pointer to a buffer to hold the indices
 * \retval M3G_TRUE   buffer has explicit indices
 * \retval M3G_FALSE  buffer has implicit indices; only the first index
 *                    is stored in \c indices
 */
M3G_API M3Gbool m3gGetBatchIndices(M3GIndexBuffer buffer,
                                   M3Gint batchIndex,
                                   M3Gint *indices)
{
    M3Gint i, j, tri = 0;
    M3Gint triIndices[4] = {0, 0, 0, 0};
    M3G_VALIDATE_OBJECT(buffer);
    M3G_UNREF(batchIndex);

    for (i = 0; i < buffer->stripCount; i++) {
        for (j = 0; j < buffer->lengths[i] - 2; j++) {
            m3gGetIndices(buffer, tri, triIndices); 

            *indices++ = triIndices[0];
            if (triIndices[3] == 0) {
                *indices++ = triIndices[1];
                *indices++ = triIndices[2];
            }
            else {
                *indices++ = triIndices[2];
                *indices++ = triIndices[1];
            }

            ++tri;
        }

        /* Eliminate degenerate triangles */
        if (buffer->lengths[i] & 1) {
            tri += 5;
        }
        else {
            tri += 4;
        }
    }

    return M3G_TRUE;
}

/*!
 * \brief Returns the size of an index batch
 *
 * \param buffer      index buffer handle
 * \param batchIndex  batch index
 * \return number of indices in the batch
 */
M3G_API M3Gint m3gGetBatchSize(M3GIndexBuffer buffer, M3Gint batchIndex)
{
    M3Gint i, count = 0;
    M3G_VALIDATE_OBJECT(buffer);
    
    if (batchIndex != 0) {
        return 0;
    }

    for (i = 0; i < buffer->stripCount; i++) {
        count += buffer->lengths[i] - 2;
    }

    return count * 3;
}

/*!
 * \brief Returns the primitive type of an index buffer
 *
 * \param buffer  index buffer handle
 * \return type of primitives stored in the buffer
 */
M3G_API M3Gprimitive m3gGetPrimitive(M3GIndexBuffer buffer)
{
    M3G_UNREF(buffer);
    M3G_VALIDATE_OBJECT(buffer);
    M3G_ASSERT(buffer->glPrimitive == GL_TRIANGLE_STRIP);
    return M3G_TRIANGLE_STRIPS;
}