os/graphics/m3g/m3gcore11/src/m3g_vertexarray.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /*
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     5 * under the terms of the License "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description: VertexArray implementation
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief VertexArray implementation
    23  *
    24  */
    25 
    26 #ifndef M3G_CORE_INCLUDE
    27 #   error included by m3g_core.c; do not compile separately.
    28 #endif
    29 
    30 #include "m3g_vertexarray.h"
    31 
    32 #define DIRTY_ALPHA_FACTOR (-1)
    33 
    34 /*----------------------------------------------------------------------
    35  * Private functions
    36  *--------------------------------------------------------------------*/
    37 
    38 /*!
    39  * \internal
    40  * \brief Signals that the contents of an array have changed
    41  */
    42 static M3G_INLINE void m3gInvalidateArray(VertexArray *array)
    43 {
    44     array->cachedAlphaFactor = DIRTY_ALPHA_FACTOR;
    45     array->rangeMin = 1;
    46     array->rangeMax = 0;
    47     
    48     ++array->timestamp;
    49 }
    50     
    51 /*----------------------------------------------------------------------
    52  * Internal functions
    53  *--------------------------------------------------------------------*/
    54 
    55 /*!
    56  * \internal
    57  * \brief Destroys this VertexArray object.
    58  *
    59  * \param obj VertexArray object
    60  */
    61 static void m3gDestroyVertexArray(Object *obj)
    62 {
    63     VertexArray *array = (VertexArray *) obj;
    64     M3G_VALIDATE_OBJECT(array);
    65     M3G_ASSERT(array->numLocks == 0);
    66     {
    67         Interface *m3g = M3G_INTERFACE(array);
    68         m3gFreeObject(m3g, array->data);
    69         m3gFreeObject(m3g, array->cachedColors);
    70     }
    71     m3gDestroyObject(&array->object);
    72 }
    73 
    74 /*!
    75  * \internal
    76  * \brief Sends color array to OpenGL.
    77  *
    78  * \note Alpha scaling currently prevents an array from being used for
    79  * anything else while it is being bound as a color array.
    80  *
    81  * \param array         VertexArray object
    82  * \param alphaFactor   1.16 alpha factor in [0, 0x10000]
    83  */
    84 static void m3gLockColorArray(const VertexArray *array, M3Gint alphaFactor)
    85 {
    86     Interface *m3g = M3G_INTERFACE(array);
    87     M3G_VALIDATE_OBJECT(array);
    88     M3G_ASSERT(!array->mapCount);
    89     M3G_ASSERT(array->numLocks == 0);
    90     M3G_ASSERT(m3gInRange(alphaFactor, 0, 0x10000));
    91 
    92     /* With an alpha factor of 1.0, we can just load up the original data */
    93     
    94     if (alphaFactor >= 0x10000) {
    95         GLenum type = array->elementType;
    96         if (type >= GL_BYTE && type <= GL_UNSIGNED_SHORT) {
    97             type |= 0x01; /* force type to unsigned for GL */
    98         }
    99         glColorPointer(type == GL_UNSIGNED_BYTE ? 4 : array->elementSize,
   100                        type,
   101                        array->stride,
   102                        m3gMapObject(m3g, array->data));
   103     }
   104     else {
   105 
   106         /* With a non-unit alpha factor, we may need to update the
   107          * cached pre-scaled colors. */
   108 
   109         M3Gubyte* const cache = (M3Gubyte *)
   110             m3gMapObject(m3g, array->cachedColors);
   111             
   112         if (array->cachedAlphaFactor != alphaFactor) {
   113             M3Gubyte *dst = cache;
   114             int i, n;
   115 
   116             M3G_VALIDATE_MEMBLOCK(cache);
   117             
   118             /* Scale the colors, converting from the source format */
   119 
   120             n = array->vertexCount;
   121             
   122             /* Byte colors are always padded to 4 bytes per entry,
   123              * with the implicit alpha set to 0xFF for RGB colors, so
   124              * we can do a near-straight copy. */
   125             
   126             switch (array->elementType) {
   127             case GL_BYTE:
   128             case GL_UNSIGNED_BYTE:
   129             {   
   130                 const M3Gubyte *src = (M3Gubyte *)m3gMapObject(m3g,
   131                                                                array->data);
   132                 for (i = 0; i < n; ++i) {
   133                     *dst++ = *src++;
   134                     *dst++ = *src++;
   135                     *dst++ = *src++;
   136                     {
   137                         M3Guint tmp = *src++ * (M3Guint) alphaFactor;
   138                         *dst++ = (M3Gubyte)(tmp >> 16);
   139                     }
   140                 }
   141                 m3gUnmapObject(m3g, array->data);
   142                 break;
   143             }
   144             default:
   145                 M3G_ASSERT(M3G_FALSE);
   146             }
   147             
   148             ((VertexArray*)array)->cachedAlphaFactor = alphaFactor;
   149         }
   150         
   151         /* We now have the scaled colors in the cache, so just set the
   152          * pointer there */
   153         
   154         glColorPointer(4, GL_UNSIGNED_BYTE, 0, cache);
   155     }
   156     M3G_ASSERT_GL;
   157     
   158     ++((VertexArray*)array)->numLocks;
   159 }
   160 
   161 /*!
   162  * \internal
   163  * \brief Creates the color cache required for alpha factors
   164  */
   165 static M3Gbool m3gCreateAlphaColorCache(VertexArray *array)
   166 {
   167     M3G_VALIDATE_OBJECT(array);
   168     M3G_ASSERT(array->cachedColors == 0);
   169     
   170     /* There are always four bytes per color entry */
   171     
   172     array->cachedColors = m3gAllocObject(M3G_INTERFACE(array),
   173                                          4 * array->vertexCount);
   174     
   175     return (array->cachedColors != 0);
   176 }
   177 
   178 /*!
   179  * \internal
   180  * \brief Sends normal array to OpenGL.
   181  *
   182  * \param array VertexArray object
   183  */
   184 static void m3gLockNormalArray(const VertexArray *array)
   185 {
   186     M3G_VALIDATE_OBJECT(array);
   187     M3G_ASSERT(!array->mapCount);
   188 
   189     glNormalPointer(array->elementType, array->stride,
   190                     m3gMapObject(M3G_INTERFACE(array), array->data));
   191     M3G_ASSERT_GL;
   192     
   193     ++((VertexArray*)array)->numLocks;
   194 }
   195 
   196 /*!
   197  * \internal
   198  * \brief Sends texture coordinate array to OpenGL.
   199  *
   200  * \param array VertexArray object
   201  */
   202 static void m3gLockTexCoordArray(const VertexArray *array)
   203 {
   204     M3G_VALIDATE_OBJECT(array);
   205     M3G_ASSERT(!array->mapCount);
   206 
   207     glTexCoordPointer(array->elementSize,
   208                       array->elementType,
   209                       array->stride,
   210                       m3gMapObject(M3G_INTERFACE(array), array->data));
   211     M3G_ASSERT_GL;
   212     
   213     ++((VertexArray*)array)->numLocks;
   214 }
   215 
   216 /*!
   217  * \internal
   218  * \brief Sends vertex array to OpenGL.
   219  *
   220  * \param array VertexArray object
   221  */
   222 static void m3gLockVertexArray(const VertexArray *array)
   223 {
   224     M3G_VALIDATE_OBJECT(array);
   225     M3G_ASSERT(!array->mapCount);
   226 
   227     glVertexPointer(array->elementSize,
   228                     array->elementType,
   229                     array->stride,
   230                     m3gMapObject(M3G_INTERFACE(array), array->data));
   231     M3G_ASSERT_GL;
   232     
   233     ++((VertexArray*)array)->numLocks;
   234 }
   235 
   236 /*!
   237  * \internal
   238  * \brief Decreases array lock count.
   239  *
   240  * \param array VertexArray object
   241  */
   242 static void m3gUnlockArray(const VertexArray *array)
   243 {
   244     M3G_VALIDATE_OBJECT(array);
   245     M3G_ASSERT(array->numLocks > 0);
   246     
   247     m3gUnmapObject(M3G_INTERFACE(array), array->data);
   248     
   249     --((VertexArray*)array)->numLocks;
   250 }
   251 
   252 /*!
   253  * \internal
   254  * \brief Clones a VertexArray.
   255  *
   256  * Used by MorphingMesh.
   257  *
   258  * \param array VertexArray object
   259  * \return cloned VertexArray object
   260  *
   261  */
   262 static VertexArray *m3gCloneVertexArray(const VertexArray *array)
   263 {
   264 	VertexArray *clone;
   265 	Interface *m3g = M3G_INTERFACE(array);
   266 	
   267     M3G_VALIDATE_OBJECT(array);
   268     M3G_ASSERT(!array->mapCount);
   269 
   270 	clone = (VertexArray *) m3gAlloc(m3g, sizeof(VertexArray));
   271 	if (clone == NULL) {
   272         return NULL;
   273     }
   274 
   275 	m3gCopy(clone, array, sizeof(VertexArray));
   276     m3gInitObject((Object*) clone, m3g, M3G_CLASS_VERTEX_ARRAY);
   277 
   278 	clone->data = m3gAllocObject(m3g, array->vertexCount * array->stride);
   279 
   280 	if (!clone->data) {
   281         m3gDestroyObject((Object*) clone);
   282 		m3gFree(m3g, clone);
   283 		return NULL;
   284 	}
   285 
   286 	m3gCopy(m3gMapObject(m3g, clone->data),
   287             m3gMapObject(m3g, array->data),
   288             array->vertexCount * array->stride);
   289     m3gUnmapObject(m3g, clone->data);
   290     m3gUnmapObject(m3g, array->data);
   291 
   292 	return clone;
   293 }
   294 
   295 /*!
   296  * \internal
   297  * \brief Gets array vertex count.
   298  *
   299  * \param array VertexArray object
   300  * \return number of vertices
   301  */
   302 static M3Gint m3gGetArrayVertexCount(const VertexArray *array)
   303 {
   304     return array->vertexCount;
   305 }
   306 
   307 /*!
   308  * \internal
   309  * \brief Returns the minimum and maximum value stored in the array
   310  */
   311 static void m3gGetArrayValueRange(const VertexArray *array,
   312                                   M3Gint *minValue, M3Gint *maxValue)
   313 {
   314     Interface *m3g = M3G_INTERFACE(array);
   315     
   316     if (array->rangeMin > array->rangeMax) {
   317         M3Gint count = array->elementSize * array->vertexCount;
   318         M3Gint minVal = 0, maxVal = 0;
   319         
   320         if (count > 0) {
   321             switch (array->elementType) {
   322             case GL_BYTE:
   323             {
   324                 const GLbyte *src = (const GLbyte*) m3gMapObject(m3g,
   325                                                                  array->data);
   326                 const M3Gint c = array->elementSize;
   327                 const M3Gint skip = array->stride - c;
   328                 minVal = maxVal = (M3Gint) *src++;
   329                 while (count) {
   330                     M3Gint i;
   331                     for (i = 0; i < c; ++i) {
   332                         M3Gint v = (M3Gint) *src++;
   333                         minVal = M3G_MIN(minVal, v);
   334                         maxVal = M3G_MAX(maxVal, v);
   335                     }
   336                     count -= c;
   337                     src += skip;
   338                 }
   339                 break;
   340             }
   341             case GL_UNSIGNED_BYTE:
   342             {
   343                 const GLubyte *src = (const GLubyte*) m3gMapObject(m3g,
   344                                                                    array->data);
   345                 const M3Gint c = array->elementSize;
   346                 const M3Gint skip = array->stride - c;
   347                 minVal = maxVal = (M3Gint) *src++;
   348                 while (count) {
   349                     M3Gint i;
   350                     for (i = 0; i < c; ++i) {
   351                         M3Gint v = (M3Gint) *src++;
   352                         minVal = M3G_MIN(minVal, v);
   353                         maxVal = M3G_MAX(maxVal, v);
   354                     }
   355                     count -= c;
   356                     src += skip;
   357                 }
   358                 break;
   359             }
   360             case GL_SHORT:
   361             {
   362                 const GLshort *src = (const GLshort*)
   363                     m3gMapObject(m3g, array->data);
   364                 minVal = maxVal = (M3Gint) *src++;
   365                 while (--count) {
   366                     M3Gint v = (M3Gint) *src++;
   367                     minVal = M3G_MIN(minVal, v);
   368                     maxVal = M3G_MAX(maxVal, v);
   369                 }
   370                 break;
   371             }
   372             case GL_UNSIGNED_SHORT:
   373             {
   374                 const GLushort *src = (const GLushort*)
   375                     m3gMapObject(m3g, array->data);
   376                 minVal = maxVal = (M3Gint) *src++;
   377                 while (--count) {
   378                     M3Gint v = (M3Gint) *src++;
   379                     minVal = M3G_MIN(minVal, v);
   380                     maxVal = M3G_MAX(maxVal, v);
   381                 }
   382                 break;
   383             }
   384             default:
   385                 M3G_ASSERT(M3G_FALSE);
   386             }
   387         }
   388         m3gUnmapObject(m3g, array->data);
   389 
   390         M3G_ASSERT(m3gInRange(minVal, -32768, 32767));
   391         M3G_ASSERT(m3gInRange(maxVal, -32768, 32767));
   392         
   393         ((VertexArray*)array)->rangeMin = (M3Gshort) minVal;
   394         ((VertexArray*)array)->rangeMax = (M3Gshort) maxVal;
   395     }
   396     
   397     *minValue = array->rangeMin;
   398     *maxValue = array->rangeMax;
   399 }
   400 
   401 /*!
   402  * \internal
   403  * \brief Compares attributes of two vertex arrays.
   404  *
   405  * \param array         VertexArray object
   406  * \param other         VertexArray object
   407  * \retval M3G_TRUE     arrays are compatible
   408  * \retval M3G_FALSE    arrays are not compatible
   409  */
   410 static M3Gbool m3gIsCompatible(const VertexArray *array, const VertexArray *other)
   411 {
   412     return( other != NULL &&
   413             other->elementType == array->elementType &&
   414             other->elementSize == array->elementSize &&
   415             other->vertexCount == array->vertexCount);
   416 }
   417 
   418 /*!
   419  * \internal
   420  * \brief Overloaded Object3D method.
   421  *
   422  * \param originalObj original VertexArray object
   423  * \param cloneObj pointer to cloned VertexArray object
   424  * \param pairs array for all object-duplicate pairs
   425  * \param numPairs number of pairs
   426  */
   427 static M3Gbool m3gVertexArrayDuplicate(const Object *originalObj,
   428                                        Object **cloneObj,
   429                                        Object **pairs,
   430                                        M3Gint *numPairs)
   431 {
   432     VertexArray *clone = m3gCloneVertexArray((VertexArray *)originalObj);
   433     if (!clone) {
   434         return M3G_FALSE;
   435     }
   436     *cloneObj = (Object*) clone;
   437     return m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs);
   438 }
   439 
   440 /*!
   441  * \internal
   442  * \brief Gets array timestamp.
   443  *
   444  * \param array VertexArray object
   445  * \return timestamp
   446  */
   447 static M3Gint m3gGetArrayTimestamp(const VertexArray *array)
   448 {
   449     return array->timestamp;
   450 }
   451 
   452 /*!
   453  * \internal
   454  * \brief Gets array bounding box as shorts.
   455  *
   456  * \param array VertexArray object
   457  * \param boundingBox   array to fill in
   458  *                      \arg [0] = minX
   459  *                      \arg [1] = minY
   460  *                      \arg [2] = minZ
   461  *                      \arg [3] = maxX
   462  *                      \arg [4] = maxY
   463  *                      \arg [5] = maxZ
   464  */
   465 static void m3gGetArrayBoundingBox(const VertexArray *array, M3Gshort *boundingBox)
   466 {
   467     Interface *m3g = M3G_INTERFACE(array);
   468     M3Gint i;
   469     M3Gshort minX, minY, minZ;
   470     M3Gshort maxX, maxY, maxZ;
   471     M3Gbyte *bptr;
   472     M3Gshort *sptr;
   473     
   474     /* Only support 3 component arrays */
   475     if (array->elementSize != 3 || array->vertexCount == 0) {
   476         return;
   477     }
   478 
   479     switch(array->elementType) {
   480     case M3G_GLTYPE(M3G_BYTE):
   481     case M3G_GLTYPE(M3G_UBYTE):
   482         bptr = (M3Gbyte *) m3gMapObject(m3g, array->data);
   483 
   484         minX = maxX = bptr[0];
   485         minY = maxY = bptr[1];
   486         minZ = maxZ = bptr[2];
   487         bptr += 4;
   488 
   489         for (i = 0; i < array->vertexCount - 1; i++) {
   490             if (bptr[0] < minX) minX = bptr[0];
   491             if (bptr[0] > maxX) maxX = bptr[0];
   492             if (bptr[1] < minY) minY = bptr[1];
   493             if (bptr[1] > maxY) maxY = bptr[1];
   494             if (bptr[2] < minZ) minZ = bptr[2];
   495             if (bptr[2] > maxZ) maxZ = bptr[2];
   496             bptr += 4;
   497         }
   498         break;
   499 
   500     case M3G_GLTYPE(M3G_SHORT):
   501     case M3G_GLTYPE(M3G_USHORT):
   502         sptr = (M3Gshort *) m3gMapObject(m3g, array->data);
   503 
   504         minX = maxX = sptr[0];
   505         minY = maxY = sptr[1];
   506         minZ = maxZ = sptr[2];
   507         sptr += 3;
   508 
   509         for (i = 0; i < array->vertexCount - 1; i++) {
   510             if (sptr[0] < minX) minX = sptr[0];
   511             if (sptr[0] > maxX) maxX = sptr[0];
   512             if (sptr[1] < minY) minY = sptr[1];
   513             if (sptr[1] > maxY) maxY = sptr[1];
   514             if (sptr[2] < minZ) minZ = sptr[2];
   515             if (sptr[2] > maxZ) maxZ = sptr[2];
   516             sptr += 3;
   517         }
   518         break;
   519 
   520     default: /* Error */
   521         M3G_ASSERT(0);
   522         return;
   523     }
   524 
   525     m3gUnmapObject(m3g, array->data);
   526 
   527     boundingBox[0] = minX;
   528     boundingBox[1] = minY;
   529     boundingBox[2] = minZ;
   530     boundingBox[3] = maxX;
   531     boundingBox[4] = maxY;
   532     boundingBox[5] = maxZ;
   533 }
   534 
   535 /*!
   536  * \internal
   537  * \brief Gets a coordinate from vertex array.
   538  *
   539  * \param va            VertexArray object
   540  * \param elementCount  elemens in coordinate
   541  * \param idx           index of coordinate
   542  * \param v             vector to fill in
   543  * \retval              M3G_TRUE get ok
   544  * \retval              M3G_FALSE no such vertex
   545  */
   546 static M3Gbool m3gGetCoordinates(VertexArray *va,
   547                                  M3Gint elementCount,
   548                                  M3Gint idx,
   549                                  M3Gfloat *v)
   550 {
   551     Interface *m3g;
   552     M3Gbyte *bptr;
   553     M3Gshort *sptr;
   554     int i;
   555     
   556     if (!va) {
   557         return M3G_FALSE;
   558     }
   559 
   560     m3g = M3G_INTERFACE(va);
   561 
   562     switch (va->elementType) {
   563     case M3G_GLTYPE(M3G_BYTE):
   564     case M3G_GLTYPE(M3G_UBYTE):
   565         idx *= 4;
   566         bptr = (M3Gbyte *) m3gMapObject(m3g, va->data);
   567         bptr += idx;
   568         for (i = 0; i < elementCount; ++i) {
   569             *v++ = *bptr++;
   570         }
   571         break;
   572 
   573     case M3G_GLTYPE(M3G_SHORT):
   574     case M3G_GLTYPE(M3G_USHORT):
   575         idx *= elementCount;
   576         sptr = (M3Gshort *) m3gMapObject(m3g, va->data);
   577         sptr += idx;
   578         for (i = 0; i < elementCount; ++i) {
   579             *v++ = *sptr++;
   580         }
   581         break;
   582     }
   583 
   584     m3gUnmapObject(m3g, va->data);
   585     return M3G_TRUE;
   586 }
   587 
   588 /*----------------------------------------------------------------------
   589  * Virtual function table
   590  *--------------------------------------------------------------------*/
   591 
   592 static const ObjectVFTable m3gvf_VertexArray = {
   593     m3gObjectApplyAnimation,
   594     m3gObjectIsCompatible,
   595     m3gObjectUpdateProperty,
   596     m3gObjectDoGetReferences,
   597     m3gObjectFindID,
   598     m3gVertexArrayDuplicate,
   599     m3gDestroyVertexArray
   600 };
   601         
   602 
   603 /*----------------------------------------------------------------------
   604  * Public API functions
   605  *--------------------------------------------------------------------*/
   606 
   607 /*!
   608  * \brief Creates a VertexArray object.
   609  *
   610  * \param interface     M3G interface
   611  * \param count         Count of vertices
   612  * \param size          Size of each element [2, 4]
   613  * \param type          Type of elements
   614  * \retval VertexArray new VertexArray object
   615  * \retval NULL VertexArray creating failed
   616  */
   617 
   618 /*@access M3Ginterface@*/
   619 /*@access M3GVertexArray@*/
   620 M3G_API M3GVertexArray m3gCreateVertexArray(M3GInterface interface,
   621                                             M3Gsizei count,
   622                                             M3Gint size,
   623                                             M3Gdatatype type)
   624 {
   625     Interface *m3g = (Interface *) interface;
   626     M3G_VALIDATE_INTERFACE(m3g);
   627     
   628     /* Check errors */
   629     if (count < 1 || count > 65535 ||
   630         size < 2 || size > 4 ||
   631         (type != M3G_BYTE && type != M3G_SHORT)) {
   632         m3gRaiseError(m3g, M3G_INVALID_VALUE);
   633         return NULL;
   634     }
   635 
   636     {
   637         /* Allocate the array object and its data buffer */
   638         
   639         VertexArray *array = m3gAllocZ(m3g, (M3Gsizei) sizeof(VertexArray));
   640         if (!array) {
   641             return NULL;
   642         }
   643 
   644         switch (type) {
   645         case M3G_BYTE:
   646             /* always padded to 4 bytes */
   647             array->stride = 4;
   648             break;
   649         case M3G_SHORT:
   650             array->stride = size * sizeof(M3Gshort);
   651             break;
   652         }
   653 
   654         /* Alloc and initialize all values to zero */
   655         array->data = m3gAllocObject(m3g, count * array->stride);
   656         if (!array->data) {
   657             m3gFree(m3g, array);
   658             return NULL;
   659         }
   660         else {
   661             void *ptr = m3gMapObject(m3g, array->data);
   662             m3gZero(ptr, count * array->stride);
   663             m3gUnmapObject(m3g, array->data);
   664         }
   665 
   666         m3gInitObject(&array->object, m3g, M3G_CLASS_VERTEX_ARRAY);
   667 
   668         array->elementType = M3G_GLTYPE(type);
   669         array->elementSize = size;
   670         array->vertexCount = count;
   671         m3gInvalidateArray(array);
   672         
   673         return (M3GVertexArray) array;
   674     }
   675 }
   676 
   677 /*!
   678  * \brief Returns the data layout parameters for a vertex array
   679  *
   680  * This gives the format of the data mapped to user memory with \c
   681  * m3gMapVertexArray.
   682  *
   683  * \param handle  array handle
   684  * \param count   pointer for number of vertices (output)
   685  * \param size    pointer for components per vertex (output)
   686  * \param type    pointer to data element type (output)
   687  * \param stride  pointer to stride, i.e. number of bytes from
   688  *                the beginning of one vertex to the next (output)
   689  */
   690 M3G_API void m3gGetVertexArrayParams(M3GVertexArray handle,
   691                                      M3Gsizei *count,
   692                                      M3Gint *size,
   693                                      M3Gdatatype *type,
   694                                      M3Gsizei *stride)
   695 {
   696     VertexArray *array = (VertexArray *) handle;
   697     M3G_VALIDATE_OBJECT(array);
   698 
   699     if (count) {
   700         *count = array->vertexCount;
   701     }
   702     if (size) {
   703         *size = array->elementSize;
   704     }
   705     if (type) {
   706         *type = (M3Gdatatype) M3G_M3GTYPE(array->elementType);
   707     }
   708     if (stride) {
   709         *stride = array->stride;
   710     }
   711 }
   712 
   713 /*!
   714  * \brief Maps the data of a vertex array to application memory
   715  *
   716  * The contents of the array will remain mapped to application memory
   717  * until a matching \c m3gUnMapVertexArray call. While mapped to user
   718  * memory, the array can not be used for rendering.
   719  *
   720  * Deleting a mapped array will also implicitly unmap it.
   721  *
   722  * \param handle handle of the array to map
   723  * \return pointer to the array data
   724  */
   725 M3G_API void *m3gMapVertexArray(M3GVertexArray handle)
   726 {
   727     void *ptr = (void*) m3gMapVertexArrayReadOnly(handle);
   728     if (ptr) {
   729         m3gInvalidateArray((VertexArray*) handle);
   730     }
   731     return ptr;
   732 }
   733 
   734 /*!
   735  * \brief Maps a vertex array for reading only
   736  *
   737  * This is the same as m3gMapVertexArray, but maps the array for
   738  * reading only, allowing internal optimizations.
   739  *
   740  */
   741 M3G_API const void *m3gMapVertexArrayReadOnly(M3GVertexArray handle)
   742 {
   743     VertexArray *array = (VertexArray *) handle;
   744     M3G_VALIDATE_OBJECT(array);
   745     
   746     if (array->numLocks > 0) {
   747         m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION);
   748         return NULL;
   749     }
   750     
   751     ++array->mapCount;
   752     return m3gMapObject(M3G_INTERFACE(array), array->data);
   753 }
   754 
   755 /*!
   756  * \brief Releases an array mapped to user memory
   757  *
   758  * The pointer obtained with a preceding \c m3gMapVertexArray call
   759  * will not be valid after unmapping the array.
   760  *
   761  * \param handle handle of the array to release
   762  */
   763 M3G_API void m3gUnmapVertexArray(M3GVertexArray handle)
   764 {
   765     VertexArray *array = (VertexArray *) handle;
   766     M3G_VALIDATE_OBJECT(array);
   767     M3G_ASSERT(array->mapCount);
   768 
   769     m3gUnmapObject(M3G_INTERFACE(array), array->data);
   770     --array->mapCount;
   771 }
   772 
   773 /*!
   774  * \brief Set a range of vertex array elements
   775  *
   776  * \param handle array handle
   777  * \param first  index of first vertex to set
   778  * \param count  number of total vertices to set
   779  * \param srcLength length of source data
   780  * \param type  data type of source data
   781  * \param src   source data
   782  */
   783 M3G_API void m3gSetVertexArrayElements(M3GVertexArray handle,
   784                                        M3Gint first, M3Gsizei count,
   785                                        M3Gsizei srcLength,
   786                                        M3Gdatatype type,
   787                                        const void *src)
   788 {
   789     VertexArray *array = (VertexArray *) handle;
   790     M3G_VALIDATE_OBJECT(array);
   791 
   792     M3G_ASSERT(array->numLocks == 0);
   793 
   794     /* Check errors */
   795     if (array->mapCount) {
   796         m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION);
   797         return;
   798     }
   799     if (src == NULL) {
   800         m3gRaiseError(M3G_INTERFACE(array), M3G_NULL_POINTER);
   801         return;
   802     }
   803     if (first < 0 || first + count > array->vertexCount) {
   804         m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_INDEX);
   805         return;
   806     }
   807     if (count < 0 || srcLength < count * array->elementSize) {
   808         m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_VALUE);
   809         return;
   810     }
   811 
   812     /* Copy source data according to destination array type */
   813     {
   814         int values = count * array->elementSize;
   815         
   816         switch (array->elementType) {
   817         case GL_BYTE:
   818         case GL_UNSIGNED_BYTE:
   819             if (type != M3G_BYTE) {
   820                 m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION);
   821                 return;
   822             }
   823             else {
   824                 GLubyte *dst =
   825                     ((GLubyte *)m3gMapObject(M3G_INTERFACE(array),
   826                                              array->data))
   827                     + first * array->stride;
   828                 GLubyte *srcByte = (GLubyte *) src;
   829 
   830                 M3G_ASSERT(array->elementSize >= 2 && array->elementSize <= 4);
   831                 M3G_ASSERT(array->stride == 4);
   832                 
   833                 while (values > 0) {
   834                     *dst++ = *srcByte++;
   835                     *dst++ = *srcByte++;
   836                     *dst++ = (M3Gubyte)((array->elementSize >= 3) ? *srcByte++ : 0x00);
   837                     *dst++ = (M3Gubyte)((array->elementSize == 4) ? *srcByte++ : 0xFF);
   838                     values -= array->elementSize;
   839                 }
   840             }
   841             break;
   842         
   843         case GL_SHORT:
   844         case GL_UNSIGNED_SHORT:
   845             if (type != M3G_SHORT) {
   846                 m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION);
   847                 return;
   848             }
   849             else {
   850                 GLushort *dst =
   851                     ((GLushort *)m3gMapObject(M3G_INTERFACE(array),
   852                                               array->data))
   853                     + first * array->stride / 2;
   854                 GLushort *srcShort = (GLushort *) src;
   855                 M3G_ASSERT(array->stride == (GLsizei)(array->elementSize * sizeof(*dst)));
   856                 
   857                 while (values--) {
   858                     *dst++ = *srcShort++;
   859                 }
   860             }
   861             break;
   862 
   863         default:
   864             M3G_ASSERT(0);      /* fatal internal error */
   865         }
   866     }
   867 
   868     m3gUnmapObject(M3G_INTERFACE(array), array->data);
   869     m3gInvalidateArray(array);
   870 }
   871 
   872 /*!
   873  * \brief Get a range of vertex array elements
   874  *
   875  * \param handle array handle
   876  * \param first  index of first vertex to set
   877  * \param count  number of total vertices to set
   878  * \param dstLength length of destination data
   879  * \param type  data type of destination data
   880  * \param dst   destination data
   881  */
   882 M3G_API void m3gGetVertexArrayElements(M3GVertexArray handle,
   883                                        M3Gint first, M3Gsizei count,
   884                                        M3Gsizei dstLength, M3Gdatatype type, void *dst)
   885 {
   886     VertexArray *array = (VertexArray *) handle;
   887     M3G_VALIDATE_OBJECT(array);
   888 
   889     M3G_ASSERT(array->numLocks == 0);
   890 
   891     /* Check errors */
   892     if (array->mapCount) {
   893         m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION);
   894         return;
   895     }
   896     if (dst == NULL) {
   897         m3gRaiseError(M3G_INTERFACE(array), M3G_NULL_POINTER);
   898         return;
   899     }
   900     if (first < 0 || first + count > array->vertexCount) {
   901         m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_INDEX);
   902         return;
   903     }
   904     if (count < 0 || dstLength < count * array->elementSize) {
   905         m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_VALUE);
   906         return;
   907     }
   908 
   909     /* Data according to destination array type */
   910     {
   911         int values = count * array->elementSize;
   912         
   913         switch (array->elementType) {
   914         case GL_BYTE:
   915         case GL_UNSIGNED_BYTE:
   916             if (type != M3G_BYTE) {
   917                 m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION);
   918                 return;
   919             }
   920             else {
   921                 GLubyte *src =
   922                     ((GLubyte *)m3gMapObject(M3G_INTERFACE(array),
   923                                              array->data))
   924                     + first * array->stride;
   925                 GLubyte *dstByte = (GLubyte *) dst;
   926 
   927                 M3G_ASSERT(array->elementSize >= 2 && array->elementSize <= 4);
   928                 M3G_ASSERT(array->stride == 4);
   929                 
   930                 while (values > 0) {
   931                     *dstByte++ = src[0];
   932                     *dstByte++ = src[1];
   933                     if (array->elementSize >= 3) {
   934                         *dstByte++ = src[2];
   935                     }
   936                     if (array->elementSize == 4) {
   937                         *dstByte++ = src[3];
   938                     }
   939                     src += 4;
   940                     values -= array->elementSize;
   941                 }
   942             }
   943             break;
   944         
   945         case GL_SHORT:
   946         case GL_UNSIGNED_SHORT:
   947             if (type != M3G_SHORT) {
   948                 m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_OPERATION);
   949                 return;
   950             }
   951             else {
   952                 GLushort *src =
   953                     ((GLushort *)m3gMapObject(M3G_INTERFACE(array),
   954                                               array->data))
   955                     + first * array->stride / 2;
   956                 GLushort *dstShort = (GLushort *) dst;
   957                 M3G_ASSERT(array->stride == (GLsizei)(array->elementSize * sizeof(*src)));
   958                 
   959                 while (values--) {
   960                     *dstShort++ = *src++;
   961                 }
   962             }
   963             break;
   964 
   965         default:
   966             M3G_ASSERT(0);      /* fatal internal error */
   967         }
   968     }
   969 
   970     m3gUnmapObject(M3G_INTERFACE(array), array->data);
   971 }
   972 
   973 /*!
   974  * \brief Transform vertex array with
   975  * given transform and w.
   976  *
   977  * \param handle        array handle
   978  * \param transform     transform
   979  * \param out           output array to fill in
   980  * \param outLength     length of the output array
   981  * \param w             use w
   982  */
   983 M3G_API void m3gTransformArray(M3GVertexArray handle,
   984                                M3GMatrix *transform,
   985                                M3Gfloat *out, M3Gint outLength,
   986                                M3Gbool w)
   987 {
   988     M3Gbyte *bptr;
   989     M3Gshort *sptr;
   990     M3Gfloat *outPtr = out;
   991     M3Gint i;
   992     M3GVec4 vec;
   993     VertexArray *array = (VertexArray *) handle;
   994     M3G_VALIDATE_OBJECT(array);
   995 
   996     /* Check for errors */
   997     if (outLength < (4 * array->vertexCount) ||
   998         array->elementSize == 4) {
   999         m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_VALUE);
  1000         return;
  1001     }
  1002 
  1003     switch(array->elementType) {
  1004         case GL_BYTE:
  1005         case GL_UNSIGNED_BYTE:
  1006             bptr = (M3Gbyte *)m3gMapObject(M3G_INTERFACE(array), array->data);
  1007 
  1008             for (i = 0; i < array->vertexCount * 4; i += 4) {
  1009                 vec.x = bptr[i + 0];
  1010                 vec.y = bptr[i + 1];
  1011                 vec.z = 0;
  1012                 if (array->elementSize == 3) {
  1013                     vec.z = bptr[i + 2];
  1014                 }
  1015                 vec.w = (M3Gfloat)w;
  1016 
  1017                 m3gTransformVec4(transform, &vec);
  1018 
  1019                 *outPtr++ = vec.x;
  1020                 *outPtr++ = vec.y;
  1021                 *outPtr++ = vec.z;
  1022                 *outPtr++ = vec.w;
  1023             }
  1024             break;
  1025 
  1026         case GL_SHORT:
  1027         case GL_UNSIGNED_SHORT:
  1028             sptr = (M3Gshort *)m3gMapObject(M3G_INTERFACE(array), array->data);
  1029 
  1030             for (i = 0; i < array->vertexCount * array->elementSize; i += array->elementSize) {
  1031                 vec.x = sptr[i + 0];
  1032                 vec.y = sptr[i + 1];
  1033                 vec.z = 0;
  1034                 if (array->elementSize == 3) {
  1035                     vec.z = sptr[i + 2];
  1036                 }
  1037                 vec.w = (M3Gfloat)w;
  1038 
  1039                 m3gTransformVec4(transform, &vec);
  1040 
  1041                 *outPtr++ = vec.x;
  1042                 *outPtr++ = vec.y;
  1043                 *outPtr++ = vec.z;
  1044                 *outPtr++ = vec.w;
  1045             }
  1046             break;
  1047     }
  1048     m3gUnmapObject(M3G_INTERFACE(array), array->data);
  1049 }
  1050 
  1051 #undef DIRTY_ALPHA_FACTOR
  1052