m3g/m3gcore11/src/m3g_indexbuffer.c
changeset 0 5d03bc08d59c
child 18 5e30ef2e26cb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/m3g/m3gcore11/src/m3g_indexbuffer.c	Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,555 @@
+/*
+* 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];
+    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;
+}
+