diff -r 000000000000 -r bde4ae8d615e os/graphics/m3g/m3gcore11/src/m3g_vertexarray.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/graphics/m3g/m3gcore11/src/m3g_vertexarray.c Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,1052 @@ +/* +* 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: VertexArray implementation +* +*/ + + +/*! + * \internal + * \file + * \brief VertexArray implementation + * + */ + +#ifndef M3G_CORE_INCLUDE +# error included by m3g_core.c; do not compile separately. +#endif + +#include "m3g_vertexarray.h" + +#define DIRTY_ALPHA_FACTOR (-1) + +/*---------------------------------------------------------------------- + * Private functions + *--------------------------------------------------------------------*/ + +/*! + * \internal + * \brief Signals that the contents of an array have changed + */ +static M3G_INLINE void m3gInvalidateArray(VertexArray *array) +{ + array->cachedAlphaFactor = DIRTY_ALPHA_FACTOR; + array->rangeMin = 1; + array->rangeMax = 0; + + ++array->timestamp; +} + +/*---------------------------------------------------------------------- + * Internal functions + *--------------------------------------------------------------------*/ + +/*! + * \internal + * \brief Destroys this VertexArray object. + * + * \param obj VertexArray object + */ +static void m3gDestroyVertexArray(Object *obj) +{ + VertexArray *array = (VertexArray *) obj; + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(array->numLocks == 0); + { + Interface *m3g = M3G_INTERFACE(array); + m3gFreeObject(m3g, array->data); + m3gFreeObject(m3g, array->cachedColors); + } + m3gDestroyObject(&array->object); +} + +/*! + * \internal + * \brief Sends color array to OpenGL. + * + * \note Alpha scaling currently prevents an array from being used for + * anything else while it is being bound as a color array. + * + * \param array VertexArray object + * \param alphaFactor 1.16 alpha factor in [0, 0x10000] + */ +static void m3gLockColorArray(const VertexArray *array, M3Gint alphaFactor) +{ + Interface *m3g = M3G_INTERFACE(array); + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(!array->mapCount); + M3G_ASSERT(array->numLocks == 0); + M3G_ASSERT(m3gInRange(alphaFactor, 0, 0x10000)); + + /* With an alpha factor of 1.0, we can just load up the original data */ + + if (alphaFactor >= 0x10000) { + GLenum type = array->elementType; + if (type >= GL_BYTE && type <= GL_UNSIGNED_SHORT) { + type |= 0x01; /* force type to unsigned for GL */ + } + glColorPointer(type == GL_UNSIGNED_BYTE ? 4 : array->elementSize, + type, + array->stride, + m3gMapObject(m3g, array->data)); + } + else { + + /* With a non-unit alpha factor, we may need to update the + * cached pre-scaled colors. */ + + M3Gubyte* const cache = (M3Gubyte *) + m3gMapObject(m3g, array->cachedColors); + + if (array->cachedAlphaFactor != alphaFactor) { + M3Gubyte *dst = cache; + int i, n; + + M3G_VALIDATE_MEMBLOCK(cache); + + /* Scale the colors, converting from the source format */ + + n = array->vertexCount; + + /* Byte colors are always padded to 4 bytes per entry, + * with the implicit alpha set to 0xFF for RGB colors, so + * we can do a near-straight copy. */ + + switch (array->elementType) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + { + const M3Gubyte *src = (M3Gubyte *)m3gMapObject(m3g, + array->data); + for (i = 0; i < n; ++i) { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + { + M3Guint tmp = *src++ * (M3Guint) alphaFactor; + *dst++ = (M3Gubyte)(tmp >> 16); + } + } + m3gUnmapObject(m3g, array->data); + break; + } + default: + M3G_ASSERT(M3G_FALSE); + } + + ((VertexArray*)array)->cachedAlphaFactor = alphaFactor; + } + + /* We now have the scaled colors in the cache, so just set the + * pointer there */ + + glColorPointer(4, GL_UNSIGNED_BYTE, 0, cache); + } + M3G_ASSERT_GL; + + ++((VertexArray*)array)->numLocks; +} + +/*! + * \internal + * \brief Creates the color cache required for alpha factors + */ +static M3Gbool m3gCreateAlphaColorCache(VertexArray *array) +{ + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(array->cachedColors == 0); + + /* There are always four bytes per color entry */ + + array->cachedColors = m3gAllocObject(M3G_INTERFACE(array), + 4 * array->vertexCount); + + return (array->cachedColors != 0); +} + +/*! + * \internal + * \brief Sends normal array to OpenGL. + * + * \param array VertexArray object + */ +static void m3gLockNormalArray(const VertexArray *array) +{ + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(!array->mapCount); + + glNormalPointer(array->elementType, array->stride, + m3gMapObject(M3G_INTERFACE(array), array->data)); + M3G_ASSERT_GL; + + ++((VertexArray*)array)->numLocks; +} + +/*! + * \internal + * \brief Sends texture coordinate array to OpenGL. + * + * \param array VertexArray object + */ +static void m3gLockTexCoordArray(const VertexArray *array) +{ + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(!array->mapCount); + + glTexCoordPointer(array->elementSize, + array->elementType, + array->stride, + m3gMapObject(M3G_INTERFACE(array), array->data)); + M3G_ASSERT_GL; + + ++((VertexArray*)array)->numLocks; +} + +/*! + * \internal + * \brief Sends vertex array to OpenGL. + * + * \param array VertexArray object + */ +static void m3gLockVertexArray(const VertexArray *array) +{ + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(!array->mapCount); + + glVertexPointer(array->elementSize, + array->elementType, + array->stride, + m3gMapObject(M3G_INTERFACE(array), array->data)); + M3G_ASSERT_GL; + + ++((VertexArray*)array)->numLocks; +} + +/*! + * \internal + * \brief Decreases array lock count. + * + * \param array VertexArray object + */ +static void m3gUnlockArray(const VertexArray *array) +{ + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(array->numLocks > 0); + + m3gUnmapObject(M3G_INTERFACE(array), array->data); + + --((VertexArray*)array)->numLocks; +} + +/*! + * \internal + * \brief Clones a VertexArray. + * + * Used by MorphingMesh. + * + * \param array VertexArray object + * \return cloned VertexArray object + * + */ +static VertexArray *m3gCloneVertexArray(const VertexArray *array) +{ + VertexArray *clone; + Interface *m3g = M3G_INTERFACE(array); + + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(!array->mapCount); + + clone = (VertexArray *) m3gAlloc(m3g, sizeof(VertexArray)); + if (clone == NULL) { + return NULL; + } + + m3gCopy(clone, array, sizeof(VertexArray)); + m3gInitObject((Object*) clone, m3g, M3G_CLASS_VERTEX_ARRAY); + + clone->data = m3gAllocObject(m3g, array->vertexCount * array->stride); + + if (!clone->data) { + m3gDestroyObject((Object*) clone); + m3gFree(m3g, clone); + return NULL; + } + + m3gCopy(m3gMapObject(m3g, clone->data), + m3gMapObject(m3g, array->data), + array->vertexCount * array->stride); + m3gUnmapObject(m3g, clone->data); + m3gUnmapObject(m3g, array->data); + + return clone; +} + +/*! + * \internal + * \brief Gets array vertex count. + * + * \param array VertexArray object + * \return number of vertices + */ +static M3Gint m3gGetArrayVertexCount(const VertexArray *array) +{ + return array->vertexCount; +} + +/*! + * \internal + * \brief Returns the minimum and maximum value stored in the array + */ +static void m3gGetArrayValueRange(const VertexArray *array, + M3Gint *minValue, M3Gint *maxValue) +{ + Interface *m3g = M3G_INTERFACE(array); + + if (array->rangeMin > array->rangeMax) { + M3Gint count = array->elementSize * array->vertexCount; + M3Gint minVal = 0, maxVal = 0; + + if (count > 0) { + switch (array->elementType) { + case GL_BYTE: + { + const GLbyte *src = (const GLbyte*) m3gMapObject(m3g, + array->data); + const M3Gint c = array->elementSize; + const M3Gint skip = array->stride - c; + minVal = maxVal = (M3Gint) *src++; + while (count) { + M3Gint i; + for (i = 0; i < c; ++i) { + M3Gint v = (M3Gint) *src++; + minVal = M3G_MIN(minVal, v); + maxVal = M3G_MAX(maxVal, v); + } + count -= c; + src += skip; + } + break; + } + case GL_UNSIGNED_BYTE: + { + const GLubyte *src = (const GLubyte*) m3gMapObject(m3g, + array->data); + const M3Gint c = array->elementSize; + const M3Gint skip = array->stride - c; + minVal = maxVal = (M3Gint) *src++; + while (count) { + M3Gint i; + for (i = 0; i < c; ++i) { + M3Gint v = (M3Gint) *src++; + minVal = M3G_MIN(minVal, v); + maxVal = M3G_MAX(maxVal, v); + } + count -= c; + src += skip; + } + break; + } + case GL_SHORT: + { + const GLshort *src = (const GLshort*) + m3gMapObject(m3g, array->data); + minVal = maxVal = (M3Gint) *src++; + while (--count) { + M3Gint v = (M3Gint) *src++; + minVal = M3G_MIN(minVal, v); + maxVal = M3G_MAX(maxVal, v); + } + break; + } + case GL_UNSIGNED_SHORT: + { + const GLushort *src = (const GLushort*) + m3gMapObject(m3g, array->data); + minVal = maxVal = (M3Gint) *src++; + while (--count) { + M3Gint v = (M3Gint) *src++; + minVal = M3G_MIN(minVal, v); + maxVal = M3G_MAX(maxVal, v); + } + break; + } + default: + M3G_ASSERT(M3G_FALSE); + } + } + m3gUnmapObject(m3g, array->data); + + M3G_ASSERT(m3gInRange(minVal, -32768, 32767)); + M3G_ASSERT(m3gInRange(maxVal, -32768, 32767)); + + ((VertexArray*)array)->rangeMin = (M3Gshort) minVal; + ((VertexArray*)array)->rangeMax = (M3Gshort) maxVal; + } + + *minValue = array->rangeMin; + *maxValue = array->rangeMax; +} + +/*! + * \internal + * \brief Compares attributes of two vertex arrays. + * + * \param array VertexArray object + * \param other VertexArray object + * \retval M3G_TRUE arrays are compatible + * \retval M3G_FALSE arrays are not compatible + */ +static M3Gbool m3gIsCompatible(const VertexArray *array, const VertexArray *other) +{ + return( other != NULL && + other->elementType == array->elementType && + other->elementSize == array->elementSize && + other->vertexCount == array->vertexCount); +} + +/*! + * \internal + * \brief Overloaded Object3D method. + * + * \param originalObj original VertexArray object + * \param cloneObj pointer to cloned VertexArray object + * \param pairs array for all object-duplicate pairs + * \param numPairs number of pairs + */ +static M3Gbool m3gVertexArrayDuplicate(const Object *originalObj, + Object **cloneObj, + Object **pairs, + M3Gint *numPairs) +{ + VertexArray *clone = m3gCloneVertexArray((VertexArray *)originalObj); + if (!clone) { + return M3G_FALSE; + } + *cloneObj = (Object*) clone; + return m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs); +} + +/*! + * \internal + * \brief Gets array timestamp. + * + * \param array VertexArray object + * \return timestamp + */ +static M3Gint m3gGetArrayTimestamp(const VertexArray *array) +{ + return array->timestamp; +} + +/*! + * \internal + * \brief Gets array bounding box as shorts. + * + * \param array VertexArray object + * \param boundingBox array to fill in + * \arg [0] = minX + * \arg [1] = minY + * \arg [2] = minZ + * \arg [3] = maxX + * \arg [4] = maxY + * \arg [5] = maxZ + */ +static void m3gGetArrayBoundingBox(const VertexArray *array, M3Gshort *boundingBox) +{ + Interface *m3g = M3G_INTERFACE(array); + M3Gint i; + M3Gshort minX, minY, minZ; + M3Gshort maxX, maxY, maxZ; + M3Gbyte *bptr; + M3Gshort *sptr; + + /* Only support 3 component arrays */ + if (array->elementSize != 3 || array->vertexCount == 0) { + return; + } + + switch(array->elementType) { + case M3G_GLTYPE(M3G_BYTE): + case M3G_GLTYPE(M3G_UBYTE): + bptr = (M3Gbyte *) m3gMapObject(m3g, array->data); + + minX = maxX = bptr[0]; + minY = maxY = bptr[1]; + minZ = maxZ = bptr[2]; + bptr += 4; + + for (i = 0; i < array->vertexCount - 1; i++) { + if (bptr[0] < minX) minX = bptr[0]; + if (bptr[0] > maxX) maxX = bptr[0]; + if (bptr[1] < minY) minY = bptr[1]; + if (bptr[1] > maxY) maxY = bptr[1]; + if (bptr[2] < minZ) minZ = bptr[2]; + if (bptr[2] > maxZ) maxZ = bptr[2]; + bptr += 4; + } + break; + + case M3G_GLTYPE(M3G_SHORT): + case M3G_GLTYPE(M3G_USHORT): + sptr = (M3Gshort *) m3gMapObject(m3g, array->data); + + minX = maxX = sptr[0]; + minY = maxY = sptr[1]; + minZ = maxZ = sptr[2]; + sptr += 3; + + for (i = 0; i < array->vertexCount - 1; i++) { + if (sptr[0] < minX) minX = sptr[0]; + if (sptr[0] > maxX) maxX = sptr[0]; + if (sptr[1] < minY) minY = sptr[1]; + if (sptr[1] > maxY) maxY = sptr[1]; + if (sptr[2] < minZ) minZ = sptr[2]; + if (sptr[2] > maxZ) maxZ = sptr[2]; + sptr += 3; + } + break; + + default: /* Error */ + M3G_ASSERT(0); + return; + } + + m3gUnmapObject(m3g, array->data); + + boundingBox[0] = minX; + boundingBox[1] = minY; + boundingBox[2] = minZ; + boundingBox[3] = maxX; + boundingBox[4] = maxY; + boundingBox[5] = maxZ; +} + +/*! + * \internal + * \brief Gets a coordinate from vertex array. + * + * \param va VertexArray object + * \param elementCount elemens in coordinate + * \param idx index of coordinate + * \param v vector to fill in + * \retval M3G_TRUE get ok + * \retval M3G_FALSE no such vertex + */ +static M3Gbool m3gGetCoordinates(VertexArray *va, + M3Gint elementCount, + M3Gint idx, + M3Gfloat *v) +{ + Interface *m3g; + M3Gbyte *bptr; + M3Gshort *sptr; + int i; + + if (!va) { + return M3G_FALSE; + } + + m3g = M3G_INTERFACE(va); + + switch (va->elementType) { + case M3G_GLTYPE(M3G_BYTE): + case M3G_GLTYPE(M3G_UBYTE): + idx *= 4; + bptr = (M3Gbyte *) m3gMapObject(m3g, va->data); + bptr += idx; + for (i = 0; i < elementCount; ++i) { + *v++ = *bptr++; + } + break; + + case M3G_GLTYPE(M3G_SHORT): + case M3G_GLTYPE(M3G_USHORT): + idx *= elementCount; + sptr = (M3Gshort *) m3gMapObject(m3g, va->data); + sptr += idx; + for (i = 0; i < elementCount; ++i) { + *v++ = *sptr++; + } + break; + } + + m3gUnmapObject(m3g, va->data); + return M3G_TRUE; +} + +/*---------------------------------------------------------------------- + * Virtual function table + *--------------------------------------------------------------------*/ + +static const ObjectVFTable m3gvf_VertexArray = { + m3gObjectApplyAnimation, + m3gObjectIsCompatible, + m3gObjectUpdateProperty, + m3gObjectDoGetReferences, + m3gObjectFindID, + m3gVertexArrayDuplicate, + m3gDestroyVertexArray +}; + + +/*---------------------------------------------------------------------- + * Public API functions + *--------------------------------------------------------------------*/ + +/*! + * \brief Creates a VertexArray object. + * + * \param interface M3G interface + * \param count Count of vertices + * \param size Size of each element [2, 4] + * \param type Type of elements + * \retval VertexArray new VertexArray object + * \retval NULL VertexArray creating failed + */ + +/*@access M3Ginterface@*/ +/*@access M3GVertexArray@*/ +M3G_API M3GVertexArray m3gCreateVertexArray(M3GInterface interface, + M3Gsizei count, + M3Gint size, + M3Gdatatype type) +{ + Interface *m3g = (Interface *) interface; + M3G_VALIDATE_INTERFACE(m3g); + + /* Check errors */ + if (count < 1 || count > 65535 || + size < 2 || size > 4 || + (type != M3G_BYTE && type != M3G_SHORT)) { + m3gRaiseError(m3g, M3G_INVALID_VALUE); + return NULL; + } + + { + /* Allocate the array object and its data buffer */ + + VertexArray *array = m3gAllocZ(m3g, (M3Gsizei) sizeof(VertexArray)); + if (!array) { + return NULL; + } + + switch (type) { + case M3G_BYTE: + /* always padded to 4 bytes */ + array->stride = 4; + break; + case M3G_SHORT: + array->stride = size * sizeof(M3Gshort); + break; + } + + /* Alloc and initialize all values to zero */ + array->data = m3gAllocObject(m3g, count * array->stride); + if (!array->data) { + m3gFree(m3g, array); + return NULL; + } + else { + void *ptr = m3gMapObject(m3g, array->data); + m3gZero(ptr, count * array->stride); + m3gUnmapObject(m3g, array->data); + } + + m3gInitObject(&array->object, m3g, M3G_CLASS_VERTEX_ARRAY); + + array->elementType = M3G_GLTYPE(type); + array->elementSize = size; + array->vertexCount = count; + m3gInvalidateArray(array); + + return (M3GVertexArray) array; + } +} + +/*! + * \brief Returns the data layout parameters for a vertex array + * + * This gives the format of the data mapped to user memory with \c + * m3gMapVertexArray. + * + * \param handle array handle + * \param count pointer for number of vertices (output) + * \param size pointer for components per vertex (output) + * \param type pointer to data element type (output) + * \param stride pointer to stride, i.e. number of bytes from + * the beginning of one vertex to the next (output) + */ +M3G_API void m3gGetVertexArrayParams(M3GVertexArray handle, + M3Gsizei *count, + M3Gint *size, + M3Gdatatype *type, + M3Gsizei *stride) +{ + VertexArray *array = (VertexArray *) handle; + M3G_VALIDATE_OBJECT(array); + + if (count) { + *count = array->vertexCount; + } + if (size) { + *size = array->elementSize; + } + if (type) { + *type = (M3Gdatatype) M3G_M3GTYPE(array->elementType); + } + if (stride) { + *stride = array->stride; + } +} + +/*! + * \brief Maps the data of a vertex array to application memory + * + * The contents of the array will remain mapped to application memory + * until a matching \c m3gUnMapVertexArray call. While mapped to user + * memory, the array can not be used for rendering. + * + * Deleting a mapped array will also implicitly unmap it. + * + * \param handle handle of the array to map + * \return pointer to the array data + */ +M3G_API void *m3gMapVertexArray(M3GVertexArray handle) +{ + void *ptr = (void*) m3gMapVertexArrayReadOnly(handle); + if (ptr) { + m3gInvalidateArray((VertexArray*) handle); + } + return ptr; +} + +/*! + * \brief Maps a vertex array for reading only + * + * This is the same as m3gMapVertexArray, but maps the array for + * reading only, allowing internal optimizations. + * + */ +M3G_API const void *m3gMapVertexArrayReadOnly(M3GVertexArray handle) +{ + VertexArray *array = (VertexArray *) handle; + M3G_VALIDATE_OBJECT(array); + + if (array->numLocks > 0) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION); + return NULL; + } + + ++array->mapCount; + return m3gMapObject(M3G_INTERFACE(array), array->data); +} + +/*! + * \brief Releases an array mapped to user memory + * + * The pointer obtained with a preceding \c m3gMapVertexArray call + * will not be valid after unmapping the array. + * + * \param handle handle of the array to release + */ +M3G_API void m3gUnmapVertexArray(M3GVertexArray handle) +{ + VertexArray *array = (VertexArray *) handle; + M3G_VALIDATE_OBJECT(array); + M3G_ASSERT(array->mapCount); + + m3gUnmapObject(M3G_INTERFACE(array), array->data); + --array->mapCount; +} + +/*! + * \brief Set a range of vertex array elements + * + * \param handle array handle + * \param first index of first vertex to set + * \param count number of total vertices to set + * \param srcLength length of source data + * \param type data type of source data + * \param src source data + */ +M3G_API void m3gSetVertexArrayElements(M3GVertexArray handle, + M3Gint first, M3Gsizei count, + M3Gsizei srcLength, + M3Gdatatype type, + const void *src) +{ + VertexArray *array = (VertexArray *) handle; + M3G_VALIDATE_OBJECT(array); + + M3G_ASSERT(array->numLocks == 0); + + /* Check errors */ + if (array->mapCount) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION); + return; + } + if (src == NULL) { + m3gRaiseError(M3G_INTERFACE(array), M3G_NULL_POINTER); + return; + } + if (first < 0 || first + count > array->vertexCount) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_INDEX); + return; + } + if (count < 0 || srcLength < count * array->elementSize) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_VALUE); + return; + } + + /* Copy source data according to destination array type */ + { + int values = count * array->elementSize; + + switch (array->elementType) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + if (type != M3G_BYTE) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION); + return; + } + else { + GLubyte *dst = + ((GLubyte *)m3gMapObject(M3G_INTERFACE(array), + array->data)) + + first * array->stride; + GLubyte *srcByte = (GLubyte *) src; + + M3G_ASSERT(array->elementSize >= 2 && array->elementSize <= 4); + M3G_ASSERT(array->stride == 4); + + while (values > 0) { + *dst++ = *srcByte++; + *dst++ = *srcByte++; + *dst++ = (M3Gubyte)((array->elementSize >= 3) ? *srcByte++ : 0x00); + *dst++ = (M3Gubyte)((array->elementSize == 4) ? *srcByte++ : 0xFF); + values -= array->elementSize; + } + } + break; + + case GL_SHORT: + case GL_UNSIGNED_SHORT: + if (type != M3G_SHORT) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION); + return; + } + else { + GLushort *dst = + ((GLushort *)m3gMapObject(M3G_INTERFACE(array), + array->data)) + + first * array->stride / 2; + GLushort *srcShort = (GLushort *) src; + M3G_ASSERT(array->stride == (GLsizei)(array->elementSize * sizeof(*dst))); + + while (values--) { + *dst++ = *srcShort++; + } + } + break; + + default: + M3G_ASSERT(0); /* fatal internal error */ + } + } + + m3gUnmapObject(M3G_INTERFACE(array), array->data); + m3gInvalidateArray(array); +} + +/*! + * \brief Get a range of vertex array elements + * + * \param handle array handle + * \param first index of first vertex to set + * \param count number of total vertices to set + * \param dstLength length of destination data + * \param type data type of destination data + * \param dst destination data + */ +M3G_API void m3gGetVertexArrayElements(M3GVertexArray handle, + M3Gint first, M3Gsizei count, + M3Gsizei dstLength, M3Gdatatype type, void *dst) +{ + VertexArray *array = (VertexArray *) handle; + M3G_VALIDATE_OBJECT(array); + + M3G_ASSERT(array->numLocks == 0); + + /* Check errors */ + if (array->mapCount) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION); + return; + } + if (dst == NULL) { + m3gRaiseError(M3G_INTERFACE(array), M3G_NULL_POINTER); + return; + } + if (first < 0 || first + count > array->vertexCount) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_INDEX); + return; + } + if (count < 0 || dstLength < count * array->elementSize) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_VALUE); + return; + } + + /* Data according to destination array type */ + { + int values = count * array->elementSize; + + switch (array->elementType) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + if (type != M3G_BYTE) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION); + return; + } + else { + GLubyte *src = + ((GLubyte *)m3gMapObject(M3G_INTERFACE(array), + array->data)) + + first * array->stride; + GLubyte *dstByte = (GLubyte *) dst; + + M3G_ASSERT(array->elementSize >= 2 && array->elementSize <= 4); + M3G_ASSERT(array->stride == 4); + + while (values > 0) { + *dstByte++ = src[0]; + *dstByte++ = src[1]; + if (array->elementSize >= 3) { + *dstByte++ = src[2]; + } + if (array->elementSize == 4) { + *dstByte++ = src[3]; + } + src += 4; + values -= array->elementSize; + } + } + break; + + case GL_SHORT: + case GL_UNSIGNED_SHORT: + if (type != M3G_SHORT) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION); + return; + } + else { + GLushort *src = + ((GLushort *)m3gMapObject(M3G_INTERFACE(array), + array->data)) + + first * array->stride / 2; + GLushort *dstShort = (GLushort *) dst; + M3G_ASSERT(array->stride == (GLsizei)(array->elementSize * sizeof(*src))); + + while (values--) { + *dstShort++ = *src++; + } + } + break; + + default: + M3G_ASSERT(0); /* fatal internal error */ + } + } + + m3gUnmapObject(M3G_INTERFACE(array), array->data); +} + +/*! + * \brief Transform vertex array with + * given transform and w. + * + * \param handle array handle + * \param transform transform + * \param out output array to fill in + * \param outLength length of the output array + * \param w use w + */ +M3G_API void m3gTransformArray(M3GVertexArray handle, + M3GMatrix *transform, + M3Gfloat *out, M3Gint outLength, + M3Gbool w) +{ + M3Gbyte *bptr; + M3Gshort *sptr; + M3Gfloat *outPtr = out; + M3Gint i; + M3GVec4 vec; + VertexArray *array = (VertexArray *) handle; + M3G_VALIDATE_OBJECT(array); + + /* Check for errors */ + if (outLength < (4 * array->vertexCount) || + array->elementSize == 4) { + m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_VALUE); + return; + } + + switch(array->elementType) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + bptr = (M3Gbyte *)m3gMapObject(M3G_INTERFACE(array), array->data); + + for (i = 0; i < array->vertexCount * 4; i += 4) { + vec.x = bptr[i + 0]; + vec.y = bptr[i + 1]; + vec.z = 0; + if (array->elementSize == 3) { + vec.z = bptr[i + 2]; + } + vec.w = (M3Gfloat)w; + + m3gTransformVec4(transform, &vec); + + *outPtr++ = vec.x; + *outPtr++ = vec.y; + *outPtr++ = vec.z; + *outPtr++ = vec.w; + } + break; + + case GL_SHORT: + case GL_UNSIGNED_SHORT: + sptr = (M3Gshort *)m3gMapObject(M3G_INTERFACE(array), array->data); + + for (i = 0; i < array->vertexCount * array->elementSize; i += array->elementSize) { + vec.x = sptr[i + 0]; + vec.y = sptr[i + 1]; + vec.z = 0; + if (array->elementSize == 3) { + vec.z = sptr[i + 2]; + } + vec.w = (M3Gfloat)w; + + m3gTransformVec4(transform, &vec); + + *outPtr++ = vec.x; + *outPtr++ = vec.y; + *outPtr++ = vec.z; + *outPtr++ = vec.w; + } + break; + } + m3gUnmapObject(M3G_INTERFACE(array), array->data); +} + +#undef DIRTY_ALPHA_FACTOR +