diff -r 000000000000 -r 5d03bc08d59c m3g/m3gcore11/src/m3g_indexbuffer.c --- /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; +} +