os/graphics/m3g/m3gcore11/src/m3g_vertexbuffer.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: Vertex buffer implementation
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Vertex buffer implementation
    23  */
    24 
    25 #ifndef M3G_CORE_INCLUDE
    26 #   error included by m3g_core.c; do not compile separately.
    27 #endif
    28 
    29 #include "m3g_vertexbuffer.h"
    30 #include "m3g_vertexarray.h"
    31 
    32 #include "m3g_appearance.h"
    33 #include "m3g_node.h" /* for NODE_ALPHA_FACTOR_BITS */
    34 
    35 /*----------------------------------------------------------------------
    36  * Private functions
    37  *--------------------------------------------------------------------*/
    38 
    39 /*----------------------------------------------------------------------
    40  * Internal functions
    41  *--------------------------------------------------------------------*/
    42 
    43 /*!
    44  * \internal
    45  * \brief Deletes a vertex buffer
    46  *
    47  * \param obj VertexBuffer object
    48  */
    49 static void m3gDestroyVertexBuffer(Object *obj)
    50 {
    51     M3Gint i;
    52     VertexBuffer *buffer = (VertexBuffer *) obj;
    53     M3G_VALIDATE_OBJECT(buffer);
    54 
    55     if (buffer->locked) {
    56         M3G_ASSERT(M3G_FALSE);
    57         m3gReleaseVertexBuffer(buffer);
    58     }
    59 
    60     M3G_ASSIGN_REF(buffer->vertices, NULL);
    61     M3G_ASSIGN_REF(buffer->normals, NULL);
    62     M3G_ASSIGN_REF(buffer->colors, NULL);
    63     for(i = 0; i < M3G_NUM_TEXTURE_UNITS; i++) {
    64         M3G_ASSIGN_REF(buffer->texCoords[i], NULL);
    65     }
    66     
    67     m3gDestroyObject(&buffer->object);
    68 }
    69 
    70 /*!
    71  * \internal
    72  * \brief Applies the scale and bias values of a vertex buffer to the
    73  * current GL state
    74  *
    75  * The scale and bias transformations are applied to the existing
    76  * values in the GL_MODELVIEW and GL_TEXTURE matrix stacks.
    77  *
    78  * \param buffer VertexBuffer object
    79  */
    80 static void m3gApplyScaleAndBias(const VertexBuffer *buffer)
    81 {
    82     M3G_VALIDATE_OBJECT(buffer);
    83     
    84     glMatrixMode(GL_TEXTURE);
    85     {
    86         M3Gint i;
    87         for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
    88             if (buffer->texCoords[i] != NULL) {
    89                 glActiveTexture((GLenum)(GL_TEXTURE0 + i));
    90                 glTranslatef(buffer->texCoordBias[i][0],
    91                              buffer->texCoordBias[i][1],
    92                              buffer->texCoordBias[i][2]);
    93                 glScalef(buffer->texCoordScale[i],
    94                          buffer->texCoordScale[i],
    95                          buffer->texCoordScale[i]);
    96             }
    97         }
    98     }
    99 
   100     glMatrixMode(GL_MODELVIEW);
   101     if (buffer->vertices != NULL) {
   102         glTranslatef(buffer->vertexBias[0],
   103                      buffer->vertexBias[1],
   104                      buffer->vertexBias[2]);
   105         glScalef(buffer->vertexScale,
   106                  buffer->vertexScale,
   107                  buffer->vertexScale);
   108     }
   109 }
   110 
   111 /*!
   112  * \internal
   113  * \brief Locks a vertex buffer for subsequent rendering
   114  *
   115  * \param buffer        VertexBuffer object
   116  * \param alphaFactor   alpha factor as 1.16 fixed point
   117  */
   118 static void m3gLockVertexBuffer(const VertexBuffer *buffer,
   119                                 M3Gint alphaFactor)
   120 {
   121     M3G_VALIDATE_OBJECT(buffer);
   122     M3G_ASSERT(!buffer->locked);
   123 
   124     if (buffer->colors != NULL) {
   125         glEnableClientState(GL_COLOR_ARRAY);
   126         m3gLockColorArray(buffer->colors, alphaFactor);
   127     }
   128     else {
   129         GLfixed r = buffer->defaultColor.r;
   130         GLfixed g = buffer->defaultColor.g;
   131         GLfixed b = buffer->defaultColor.b;
   132         GLfixed a = buffer->defaultColor.a * alphaFactor;
   133 
   134         r = (r << 8) + r + (r >> 7);
   135         g = (g << 8) + g + (g >> 7);
   136         b = (b << 8) + b + (b >> 7);
   137         a = (a >> (NODE_ALPHA_FACTOR_BITS - 8))
   138             + (a >> NODE_ALPHA_FACTOR_BITS)
   139             + (a >> (NODE_ALPHA_FACTOR_BITS + 7));
   140         
   141         glDisableClientState(GL_COLOR_ARRAY);
   142         glColor4x(r, g, b, a);
   143     }
   144 
   145     if (buffer->normals != NULL) {
   146         glEnableClientState(GL_NORMAL_ARRAY);
   147         m3gLockNormalArray(buffer->normals);
   148     }
   149     else {
   150         glDisableClientState(GL_NORMAL_ARRAY);
   151     }
   152 
   153     if (buffer->vertices != NULL) {
   154         glEnableClientState(GL_VERTEX_ARRAY);
   155         m3gLockVertexArray(buffer->vertices);
   156     }
   157     else {
   158         glDisableClientState(GL_VERTEX_ARRAY);
   159     }
   160     
   161     {
   162         M3Gint i;
   163         for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
   164             const VertexArray *array = buffer->texCoords[i];
   165             glClientActiveTexture(GL_TEXTURE0 + i);
   166             glActiveTexture(GL_TEXTURE0 + i);
   167             if (array != NULL) {
   168                 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   169                 m3gLockTexCoordArray(array);
   170             }
   171             else {
   172                 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   173             }    
   174         }
   175     }
   176     
   177     ((VertexBuffer*)buffer)->locked = M3G_TRUE;
   178 }
   179 
   180 /*!
   181  * \internal
   182  * \brief Releases a vertex buffer
   183  *
   184  * \param buffer        VertexBuffer object
   185  */
   186 static void m3gReleaseVertexBuffer(const VertexBuffer *buffer)
   187 {
   188     M3G_VALIDATE_OBJECT(buffer);
   189     M3G_ASSERT(buffer->locked);
   190 
   191     if (buffer->colors != NULL) {
   192         m3gUnlockArray(buffer->colors);
   193     }
   194     if (buffer->normals != NULL) {
   195         m3gUnlockArray(buffer->normals);
   196     }
   197     if (buffer->vertices != NULL) {
   198         m3gUnlockArray(buffer->vertices);
   199     }
   200     {
   201         M3Gint i;
   202         for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
   203             const VertexArray *array = buffer->texCoords[i];
   204             if (array != NULL) {
   205                 m3gUnlockArray(array);
   206             }
   207         }
   208     }
   209     
   210     ((VertexBuffer*)buffer)->locked = M3G_FALSE;
   211 }
   212 
   213 /*!
   214  * \internal
   215  * \brief Gets a vertex position. Scale and bias
   216  * are not applied.
   217  *
   218  * \param buffer    VertexBuffer object
   219  * \param idx       index of coordinate
   220  * \param v         vector to fill in
   221  * \retval          M3G_TRUE get ok
   222  * \retval          M3G_FALSE no such vertex
   223  */
   224 static M3Gbool m3gGetVertex(const VertexBuffer *buffer, M3Gint idx, M3GVec3 *v)
   225 {
   226     return m3gGetCoordinates(buffer->vertices, 3, idx, &v->x);
   227 }
   228 
   229 /*!
   230  * \internal
   231  * \brief Gets a normal coordinate.
   232  *
   233  * \param buffer    VertexBuffer object
   234  * \param idx       index of coordinate
   235  * \param v         vector to fill in
   236  * \retval          M3G_TRUE get ok
   237  * \retval          M3G_FALSE no such vertex
   238  */
   239 static M3Gbool m3gGetNormal(const VertexBuffer *buffer, M3Gint idx, M3GVec3 *v)
   240 {
   241     return m3gGetCoordinates(buffer->normals, 3, idx, &v->x);
   242 }
   243 
   244 /*!
   245  * \internal
   246  * \brief Gets a texture coordinate, used in pick routines.
   247  * Scale and bias are applied to the coordinates.
   248  *
   249  * \param buffer    VertexBuffer object
   250  * \param idx       index of coordinate
   251  * \param unit      texturing unit
   252  * \param v         vector to fill in
   253  * \retval          M3G_TRUE get ok
   254  * \retval          M3G_FALSE no such vertex
   255  */
   256 static M3Gbool m3gGetTexCoord(const VertexBuffer *buffer, M3Gint idx, M3Gint unit, M3GVec3 *v)
   257 {
   258     M3Gbool res;
   259     res = m3gGetCoordinates(buffer->texCoords[unit], 2, idx, &v->x);
   260 
   261     v->x = m3gMul(v->x, buffer->texCoordScale[unit]);
   262     v->y = m3gMul(v->y, buffer->texCoordScale[unit]);
   263 
   264     v->x = m3gAdd(v->x, buffer->texCoordBias[unit][0]);
   265     v->y = m3gAdd(v->y, buffer->texCoordBias[unit][1]);
   266 
   267     v->z = 0.f;
   268 
   269     return res;
   270 }
   271 
   272 /*!
   273  * \internal
   274  * \brief Gets vertex buffer positions bounding box.
   275  *
   276  * The bounding box is returned as floats, with scale and bias
   277  * applied.
   278  *
   279  * \param buffer        VertexBuffer object
   280  * \param boundingBox   bounding box float array
   281  */
   282 static void m3gGetBoundingBox(VertexBuffer *vb, AABB *boundingBox)
   283 {
   284     /* If timestamp is changed, refresh bounding box */
   285     
   286     if (vb->vertices && (m3gGetArrayTimestamp(vb->vertices)
   287                          != vb->verticesTimestamp)) {
   288         M3Gshort ab[6];
   289         vb->verticesTimestamp = m3gGetArrayTimestamp(vb->vertices);
   290         m3gGetArrayBoundingBox(vb->vertices, ab);
   291         
   292         vb->bbox.min[0] = m3gMadd(ab[0], vb->vertexScale, vb->vertexBias[0]);
   293         vb->bbox.min[1] = m3gMadd(ab[1], vb->vertexScale, vb->vertexBias[1]);
   294         vb->bbox.min[2] = m3gMadd(ab[2], vb->vertexScale, vb->vertexBias[2]);
   295         vb->bbox.max[0] = m3gMadd(ab[3], vb->vertexScale, vb->vertexBias[0]);
   296         vb->bbox.max[1] = m3gMadd(ab[4], vb->vertexScale, vb->vertexBias[1]);
   297         vb->bbox.max[2] = m3gMadd(ab[5], vb->vertexScale, vb->vertexBias[2]);
   298 
   299         /* Flip the bounding box if the scale was negative */
   300         
   301         if (vb->vertexScale < 0) {
   302             M3Gint i;
   303             for (i = 0; i < 3; ++i) {
   304                 M3Gfloat t = vb->bbox.min[i];
   305                 vb->bbox.min[i] = vb->bbox.max[i];
   306                 vb->bbox.max[i] = t;
   307             }
   308         }
   309     }
   310     *boundingBox = vb->bbox;
   311 }
   312 
   313 /*!
   314  * \internal
   315  * \brief Gets vertex buffer timestamp.
   316  *
   317  * \param buffer        VertexBuffer object
   318  * \return timestamp
   319  */
   320 static M3Gint m3gGetTimestamp(const VertexBuffer *buffer)
   321 {
   322     if (buffer->vertices &&
   323         m3gGetArrayTimestamp(buffer->vertices) != buffer->verticesTimestamp) {
   324         return buffer->timestamp + 1;
   325     }
   326     return buffer->timestamp;
   327 }
   328 
   329 /**
   330  * Updates the vertex count bookkeeping when setting vertex
   331  * arrays.
   332  *
   333  * \param buffer        VertexBuffer object
   334  * \param oldArray      VertexArray object
   335  * \param newArray      VertexArray object
   336  * \param maskBit       array mask bit
   337  */
   338 static void m3gUpdateArray(VertexBuffer *buffer,
   339                            VertexArray *oldArray,
   340                            VertexArray *newArray,
   341                            M3Gbitmask maskBit)
   342 {
   343     M3Gint change = (oldArray == NULL && newArray != NULL) ?  1 :
   344         (oldArray != NULL && newArray == NULL) ? -1 :
   345         0;
   346 
   347     /* If adding or replacing an array, set the initial vertex count,
   348      * or compare the new array against the current vertex count */
   349     
   350     if (newArray != NULL) {
   351     	if (buffer->arrayCount == 0 || (buffer->arrayCount == 1
   352                                         && change == 0)) {
   353     		buffer->vertexCount = m3gGetArrayVertexCount(newArray);
   354     	}
   355     	else if (m3gGetArrayVertexCount(newArray) != buffer->vertexCount) {
   356             m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_VALUE);
   357             return;
   358     	}
   359     }
   360 
   361     /* Update the array bitmask */
   362     
   363     if (newArray != NULL) {
   364         buffer->arrayMask |= maskBit;
   365     }
   366     else {
   367         buffer->arrayMask &= ~maskBit;
   368     }
   369 
   370     /* Update the array count, and reset the vertex count to zero if
   371      * no arrays remain */
   372     
   373     buffer->arrayCount += change;
   374     if (buffer->arrayCount == 0) {
   375         M3G_ASSERT(buffer->arrayMask == 0);
   376         buffer->vertexCount = 0;
   377     }
   378 }
   379 
   380 /*!
   381  * \internal
   382  * \brief Overloaded Object3D method
   383  *
   384  * \param self VertexBuffer object
   385  * \param references array of reference objects
   386  * \return number of references
   387  */
   388 static M3Gint m3gVertexBufferDoGetReferences(Object *self, Object **references)
   389 {
   390     VertexBuffer *vb = (VertexBuffer *)self;
   391     M3Gint i, num = m3gObjectDoGetReferences(self, references);
   392     if (vb->vertices != NULL) {
   393         if (references != NULL)
   394             references[num] = (Object *)vb->vertices;
   395         num++;
   396     }
   397     if (vb->normals != NULL) {
   398         if (references != NULL)
   399             references[num] = (Object *)vb->normals;
   400         num++;
   401     }
   402     if (vb->colors != NULL) {
   403         if (references != NULL)
   404             references[num] = (Object *)vb->colors;
   405         num++;
   406     }
   407     for (i = 0; i < M3G_NUM_TEXTURE_UNITS; i++) {
   408         if (vb->texCoords[i] != NULL) {
   409             if (references != NULL)
   410                 references[num] = (Object *)vb->texCoords[i];
   411             num++;
   412         }
   413     }
   414     return num;
   415 }
   416 
   417 /*!
   418  * \internal
   419  * \brief Overloaded Object3D method
   420  */
   421 static Object *m3gVertexBufferFindID(Object *self, M3Gint userID)
   422 {
   423     int i;
   424     VertexBuffer *vb = (VertexBuffer *)self;
   425     Object *found = m3gObjectFindID(self, userID);
   426     
   427     if (!found && vb->vertices != NULL) {
   428         found = m3gFindID((Object*) vb->vertices, userID);
   429     }
   430     if (!found && vb->normals != NULL) {
   431         found = m3gFindID((Object*) vb->normals, userID);
   432     }
   433     if (!found && vb->colors != NULL) {
   434         found = m3gFindID((Object*) vb->colors, userID);
   435     }
   436     for (i = 0; !found && i < M3G_NUM_TEXTURE_UNITS; ++i) {
   437         if (vb->texCoords[i] != NULL) {
   438             found = m3gFindID((Object*) vb->texCoords[i], userID);
   439         }
   440     }
   441     return found;
   442 }
   443 
   444 /*!
   445  * \internal
   446  * \brief Duplicates vertex buffer data and array configuration
   447  */
   448 static void m3gDuplicateVertexBufferData(VertexBuffer *clone,
   449                                          const VertexBuffer *original)
   450 {
   451     M3Gint i;
   452     
   453     clone->vertexScale = original->vertexScale;
   454     m3gCopy(clone->vertexBias, original->vertexBias, 3 * sizeof(GLfloat));
   455     clone->defaultColor = original->defaultColor;
   456     clone->locked = original->locked;
   457     clone->vertexCount = original->vertexCount;
   458     clone->arrayCount = original->arrayCount;
   459     clone->arrayMask = original->arrayMask;
   460     clone->timestamp = original->timestamp;
   461     
   462     for (i = 0; i < M3G_NUM_TEXTURE_UNITS; i++) {
   463         clone->texCoordScale[i] = original->texCoordScale[i];
   464         clone->texCoordBias[i][0] = original->texCoordBias[i][0];
   465         clone->texCoordBias[i][1] = original->texCoordBias[i][1];
   466         clone->texCoordBias[i][2] = original->texCoordBias[i][2];
   467         M3G_ASSIGN_REF(clone->texCoords[i], original->texCoords[i]);
   468     }
   469     M3G_ASSIGN_REF(clone->colors, original->colors);
   470     M3G_ASSIGN_REF(clone->normals, original->normals);
   471     M3G_ASSIGN_REF(clone->vertices, original->vertices);
   472 }
   473 
   474 /*!
   475  * \internal
   476  * \brief Overloaded Object3D method
   477  *
   478  * \param originalObj original VertexBuffer object
   479  * \param cloneObj pointer to cloned VertexBuffer object
   480  * \param pairs array for all object-duplicate pairs
   481  * \param numPairs number of pairs
   482  */
   483 static M3Gbool m3gVertexBufferDuplicate(const Object *originalObj,
   484                                         Object **cloneObj,
   485                                         Object **pairs,
   486                                         M3Gint *numPairs)
   487 {
   488     VertexBuffer *original = (VertexBuffer*) originalObj;
   489     VertexBuffer *clone;
   490     M3G_ASSERT(*cloneObj == NULL); /* no derived classes */
   491 
   492     /* Create the clone object */
   493     
   494     clone = (VertexBuffer*) m3gCreateVertexBuffer(originalObj->interface);
   495     if (!clone) {
   496         return M3G_FALSE;
   497     }
   498     *cloneObj = (Object *)clone;
   499 
   500     /* Duplicate base class data */
   501     
   502     if (!m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
   503         return M3G_FALSE;
   504     }
   505 
   506     /* Duplicate our own data */
   507 
   508     m3gDuplicateVertexBufferData(clone, original);
   509     return M3G_TRUE;
   510 }
   511 
   512 /*!
   513  * \internal
   514  * \brief Overloaded Object3D method.
   515  *
   516  * \param property      animation property
   517  * \retval M3G_TRUE     property supported
   518  * \retval M3G_FALSE    property not supported
   519  */
   520 static M3Gbool m3gVertexBufferIsCompatible(M3Gint property)
   521 {
   522     switch (property) {
   523     case M3G_ANIM_ALPHA:
   524     case M3G_ANIM_COLOR:
   525         return M3G_TRUE;
   526     default:
   527         return m3gObjectIsCompatible(property);
   528     }
   529 }
   530 
   531 /*!
   532  * \internal
   533  * \brief Overloaded Object3D method
   534  *
   535  * \param self          VertexBuffer object
   536  * \param property      animation property
   537  * \param valueSize     size of value array
   538  * \param value         value array
   539  */
   540 static void m3gVertexBufferUpdateProperty(Object *self,
   541                                           M3Gint property,
   542                                           M3Gint valueSize,
   543                                           const M3Gfloat *value)
   544 {
   545     VertexBuffer *buffer = (VertexBuffer *) self;
   546     M3G_VALIDATE_OBJECT(buffer);
   547     M3G_ASSERT_PTR(value);
   548 
   549     switch (property) {
   550     case M3G_ANIM_ALPHA:
   551         M3G_ASSERT(valueSize >= 1);
   552         buffer->defaultColor.a =
   553             (GLubyte)m3gAdd(m3gMul(255.f, m3gClampFloat(value[0], 0.f, 1.f)), 0.5f);
   554         break;
   555     case M3G_ANIM_COLOR:
   556         M3G_ASSERT(valueSize >= 3);
   557         buffer->defaultColor.r =
   558             (GLubyte)m3gAdd(m3gMul(255.f, m3gClampFloat(value[0], 0.f, 1.f)), 0.5f);
   559         buffer->defaultColor.g =
   560             (GLubyte)m3gAdd(m3gMul(255.f, m3gClampFloat(value[1], 0.f, 1.f)), 0.5f);
   561         buffer->defaultColor.b =
   562             (GLubyte)m3gAdd(m3gMul(255.f, m3gClampFloat(value[2], 0.f, 1.f)), 0.5f);
   563         break;
   564     default:
   565         m3gObjectUpdateProperty(self, property, valueSize, value);
   566     }
   567 }
   568 
   569 /*!
   570  * \internal
   571  * \brief Checks that a vertex buffer can be properly rendered
   572  *
   573  * The vertex format required by \c app is compared against the vertex
   574  * arrays included in \c vb, and \c vb is checked to have at least \c
   575  * maxIndex vertex entries.
   576  *
   577  * \param vb        VertexBuffer object
   578  * \param app       Appearance object
   579  * \param maxIndex  maximum index in index buffer
   580  * \retval M3G_TRUE  valid state
   581  * \retval M3G_FALSE invalid state
   582  */
   583 static M3Gbool m3gValidateVertexBuffer(const VertexBuffer *vb,
   584                                        const Appearance *app,
   585                                        M3Gsizei maxIndex)
   586 {
   587     M3Gbitmask reqMask;
   588     M3G_UNREF(app);
   589     reqMask = M3G_POSITION_BIT;
   590     if ((m3gGetArrayMask(vb) & reqMask) != reqMask) {
   591         return M3G_FALSE;
   592     }
   593     return (m3gGetNumVertices(vb) > maxIndex);
   594 }
   595 
   596 /*!
   597  * \internal
   598  * \brief Sets a vertex buffer up for subsequent vertex modification
   599  *
   600  * A specified subset of the arrays of \c srcBuffer will be replicated
   601  * in \c buffer, without copying the contents.  The others will be
   602  * copied as references only.
   603  *
   604  * \param buffer       the modified buffer
   605  * \param srcBuffer    the source buffer
   606  * \param arrayMask    bitmask of arrays to modify
   607  * \param createArrays M3G_TRUE to create the arrays specified by
   608  *                     \c arrayMask, M3G_FALSE to leave them NULL
   609  * \retval M3G_TRUE success
   610  * \retval M3G_FALSE out of memory
   611  */
   612 static M3Gbool m3gMakeModifiedVertexBuffer(VertexBuffer *buffer,
   613                                            const VertexBuffer *srcBuffer,
   614                                            M3Gbitmask arrayMask,
   615                                            M3Gbool createArrays)
   616 {
   617     M3G_VALIDATE_OBJECT(buffer);
   618     M3G_VALIDATE_OBJECT(srcBuffer);
   619     {
   620         Interface *m3g = M3G_INTERFACE(buffer);
   621         VertexArray *array;
   622         int i;
   623 
   624         /* First, just copy the data from the other buffer */
   625 
   626         m3gDuplicateVertexBufferData(buffer, srcBuffer);
   627         
   628         /* Now, override the specified arrays: release the existing
   629          * array, and either allocate a new one or leave as NULL,
   630          * depending on the value of the createArrays flag */
   631 
   632 #       define MODIFY_ARRAY(bit, name)                          \
   633             if (arrayMask & (bit)) {                            \
   634                 array = NULL;                                   \
   635                 if (srcBuffer->name && createArrays) {          \
   636                     array = m3gCreateVertexArray(               \
   637                         m3g,                                    \
   638                         srcBuffer->name->vertexCount,           \
   639                         srcBuffer->name->elementSize,           \
   640                         srcBuffer->name->elementType == GL_SHORT ? M3G_SHORT : M3G_BYTE); \
   641                     if (!array) {                               \
   642                         return M3G_FALSE;                       \
   643                     }                                           \
   644                 }                                               \
   645                 m3gUpdateArray(buffer, buffer->name, array, bit); \
   646                 M3G_ASSIGN_REF(buffer->name, array);            \
   647             }
   648 
   649         MODIFY_ARRAY(M3G_POSITION_BIT, vertices);
   650         MODIFY_ARRAY(M3G_COLOR_BIT, colors);
   651         MODIFY_ARRAY(M3G_NORMAL_BIT, normals);
   652         
   653         for (i = 0; i < M3G_NUM_TEXTURE_UNITS; ++i) {
   654             MODIFY_ARRAY(M3G_TEXCOORD0_BIT << i, texCoords[i]);
   655         }
   656 
   657 #       undef MODIFY_ARRAY
   658 
   659         return M3G_TRUE;
   660     }
   661 }
   662 
   663 /*----------------------------------------------------------------------
   664  * Virtual function table
   665  *--------------------------------------------------------------------*/
   666 
   667 static const ObjectVFTable m3gvf_VertexBuffer = {
   668     m3gObjectApplyAnimation,
   669     m3gVertexBufferIsCompatible,
   670     m3gVertexBufferUpdateProperty,
   671     m3gVertexBufferDoGetReferences,
   672     m3gVertexBufferFindID,
   673     m3gVertexBufferDuplicate,
   674     m3gDestroyVertexBuffer
   675 };
   676 
   677 
   678 /*----------------------------------------------------------------------
   679  * Public API functions
   680  *--------------------------------------------------------------------*/
   681 
   682 /*!
   683  * \brief Creates an empty vertex buffer
   684  *
   685  * \param interface    M3G interface
   686  * \retval VertexBuffer new VertexBuffer object
   687  * \retval NULL VertexBuffer creating failed
   688  */
   689 /*@access M3Ginterface@*/
   690 /*@access M3Gobject@*/
   691 M3G_API M3GVertexBuffer m3gCreateVertexBuffer(M3GInterface interface)
   692 {
   693     Interface *m3g = (Interface *) interface;
   694     M3G_VALIDATE_INTERFACE(m3g);
   695     
   696     {
   697         VertexBuffer *buffer = m3gAllocZ(m3g, sizeof(VertexBuffer));
   698 
   699         if (buffer != NULL) {
   700             m3gInitObject(&buffer->object, m3g, M3G_CLASS_VERTEX_BUFFER);
   701         
   702             /* Set default color to white */
   703             buffer->defaultColor.r = (GLubyte) 0xFF;
   704             buffer->defaultColor.g = (GLubyte) 0xFF;
   705             buffer->defaultColor.b = (GLubyte) 0xFF;
   706             buffer->defaultColor.a = (GLubyte) 0xFF;
   707         }
   708 
   709         return (M3GVertexBuffer) buffer;
   710     }
   711 }
   712 
   713 /*!
   714  * \brief Sets the color array of a vertex buffer
   715  *
   716  * \param hBuffer   VertexBuffer object
   717  * \param hArray    VertexArray object
   718  */
   719 /*@access M3Gobject@*/
   720 M3G_API void m3gSetColorArray(M3GVertexBuffer hBuffer, M3GVertexArray hArray)
   721 {
   722     VertexBuffer *buffer = (VertexBuffer *) hBuffer;
   723     VertexArray *array = (VertexArray *) hArray;
   724     M3G_VALIDATE_OBJECT(buffer);
   725     
   726     if (array != NULL) {
   727         M3G_VALIDATE_OBJECT(array);
   728 
   729         /* Check for errors */
   730         if (!m3gInRange(array->elementSize, 3, 4) ||
   731             array->elementType != M3G_GLTYPE(M3G_BYTE)) {
   732             m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_VALUE);
   733             return;
   734         }
   735     }
   736     
   737     m3gUpdateArray(buffer, buffer->colors, array, M3G_COLOR_BIT);
   738     M3G_ASSIGN_REF(buffer->colors, array);
   739     ++buffer->timestamp;
   740 }
   741 
   742 /*!
   743  * \brief Sets the normal array of a vertex buffer
   744  *
   745  * \param hBuffer   VertexBuffer object
   746  * \param hArray    VertexArray object
   747  */
   748 /*@access M3Gobject@*/
   749 M3G_API void m3gSetNormalArray(M3GVertexBuffer hBuffer, M3GVertexArray hArray)
   750 {
   751     VertexBuffer *buffer = (VertexBuffer *) hBuffer;
   752     VertexArray *array = (VertexArray *) hArray;
   753     M3G_VALIDATE_OBJECT(buffer);
   754     if (array != NULL) {
   755         M3G_VALIDATE_OBJECT(array);
   756 
   757         /* Check for errors */
   758         if (array->elementSize != 3) {
   759             m3gRaiseError(M3G_INTERFACE(array), M3G_INVALID_VALUE);
   760             return;
   761         }
   762     }
   763     
   764     m3gUpdateArray(buffer, buffer->normals, array, M3G_NORMAL_BIT);
   765     M3G_ASSIGN_REF(buffer->normals, array);
   766     ++buffer->timestamp;
   767 }
   768 
   769 /*!
   770  * \brief Sets the texture coordinate array of a vertex buffer
   771  *
   772  * \param hBuffer   VertexBuffer object
   773  * \param unit      texturing unit
   774  * \param hArray    VertexArray object
   775  * \param scale     scale
   776  * \param bias      bias array
   777  * \param biasLength bias array length
   778  */
   779 /*@access M3Gobject@*/
   780 M3G_API void m3gSetTexCoordArray(M3GVertexBuffer hBuffer,
   781                                  M3Gint unit,
   782                                  M3GVertexArray hArray,
   783                                  M3Gfloat scale, M3Gfloat *bias,
   784                                  M3Gint biasLength)
   785 {
   786     VertexBuffer *buffer = (VertexBuffer *) hBuffer;
   787     VertexArray *array = (VertexArray *) hArray;
   788 
   789     M3G_VALIDATE_OBJECT(buffer);
   790     if (array != NULL) {
   791         M3G_VALIDATE_OBJECT(array);
   792     }
   793 
   794     /* Check errors */
   795     
   796     if (unit < 0 || unit >= M3G_NUM_TEXTURE_UNITS) {
   797         m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_INDEX);
   798         return;
   799     }
   800 
   801     if (array != NULL) {
   802         if (array->elementSize != 2 && array->elementSize != 3) {
   803             m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_VALUE);
   804             return;
   805         }
   806         if (bias != NULL && biasLength < array->elementSize) {
   807             m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_VALUE);
   808             return;
   809         }
   810     }
   811 
   812     m3gUpdateArray(buffer, buffer->texCoords[unit], array, M3G_TEXCOORD0_BIT << unit);
   813     M3G_ASSIGN_REF(buffer->texCoords[unit], array);
   814 
   815     if (array != NULL && bias != NULL) {
   816         buffer->texCoordBias[unit][0] = bias[0];
   817         buffer->texCoordBias[unit][1] = bias[1];
   818         if (biasLength > 2) {
   819             buffer->texCoordBias[unit][2] = bias[2];
   820         }
   821     }
   822     else {
   823         buffer->texCoordBias[unit][0] = 0.f;
   824         buffer->texCoordBias[unit][1] = 0.f;
   825         buffer->texCoordBias[unit][2] = 0.f;
   826     }
   827 
   828     buffer->texCoordScale[unit] = scale;
   829 
   830     ++buffer->timestamp;
   831 }
   832 
   833 /*!
   834  * \brief Sets the vertex array of a vertex buffer
   835  *
   836  * \param hBuffer   VertexBuffer object
   837  * \param hArray    VertexArray object
   838  * \param scale     scale
   839  * \param bias      bias array
   840  * \param biasLength bias array length
   841  */
   842 /*@access M3Gobject@*/
   843 M3G_API void m3gSetVertexArray(M3GVertexBuffer hBuffer,
   844                                M3GVertexArray hArray,
   845                                M3Gfloat scale,
   846                                M3Gfloat *bias, M3Gint biasLength)
   847 {
   848     VertexBuffer *buffer = (VertexBuffer *) hBuffer;
   849     VertexArray *array = (VertexArray *) hArray;
   850     M3G_VALIDATE_OBJECT(buffer);
   851     if (array != NULL) {
   852         M3G_VALIDATE_OBJECT(array);
   853     }
   854 
   855     if (array != NULL && array->elementSize != 3) {
   856         m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_VALUE);
   857         return;
   858     }
   859 
   860     if (array != NULL && bias != NULL && biasLength < 3) {
   861         m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_VALUE);
   862         return;
   863     }
   864 
   865     m3gUpdateArray(buffer, buffer->vertices, array, M3G_POSITION_BIT);
   866     M3G_ASSIGN_REF(buffer->vertices, array);
   867 
   868     if (array != NULL && bias != NULL) {
   869         buffer->vertexBias[0] = bias[0];
   870         buffer->vertexBias[1] = bias[1];
   871         buffer->vertexBias[2] = bias[2];
   872     }
   873     else {
   874         buffer->vertexBias[0] = 0.f;
   875         buffer->vertexBias[1] = 0.f;
   876         buffer->vertexBias[2] = 0.f;
   877     }
   878     buffer->vertexScale = scale;
   879 
   880     /* Make sure we invalidate the cached bounding box */
   881     
   882     ++buffer->timestamp;
   883     if (array != NULL) {
   884         buffer->verticesTimestamp = ~m3gGetArrayTimestamp(array); /*lint !e502 ok for signed */
   885     }
   886 }
   887 
   888 /*!
   889  * \brief Sets the default color of a vertex buffer
   890  *
   891  * \param handle    VertexBuffer object
   892  * \param ARGB      default color as ARGB
   893  */
   894 /*@access M3Gobject@*/
   895 
   896 M3G_API void m3gSetVertexDefaultColor(M3GVertexBuffer handle, M3Guint ARGB)
   897 {
   898     VertexBuffer *buffer = (VertexBuffer *) handle;
   899     M3G_VALIDATE_OBJECT(buffer);    
   900 
   901     buffer->defaultColor.b = (GLubyte)(ARGB);
   902     buffer->defaultColor.g = (GLubyte)(ARGB >> 8);
   903     buffer->defaultColor.r = (GLubyte)(ARGB >> 16);
   904     buffer->defaultColor.a = (GLubyte)(ARGB >> 24);
   905     ++buffer->timestamp;
   906 }
   907 
   908 /*!
   909  * \brief Gets the default color of a vertex buffer
   910  * \param handle    VertexBuffer object
   911  * \return          default color as ARGB
   912  */
   913 /*@access M3Gobject@*/
   914 
   915 M3G_API M3Guint m3gGetVertexDefaultColor(M3GVertexBuffer handle)
   916 {
   917 	unsigned ARGB;
   918     VertexBuffer *buffer = (VertexBuffer *) handle;
   919     M3G_VALIDATE_OBJECT(buffer);    
   920 
   921 	ARGB = buffer->defaultColor.a;
   922 	ARGB <<= 8;
   923 	ARGB |= buffer->defaultColor.r;
   924 	ARGB <<= 8;
   925 	ARGB |= buffer->defaultColor.g;
   926 	ARGB <<= 8;
   927 	ARGB |= buffer->defaultColor.b;
   928 
   929 	return ARGB;
   930 }
   931 
   932 /*!
   933  * \brief Gets vertex array of a vertex buffer
   934  *
   935  * \param handle    VertexBuffer object
   936  * \param which     which array to get
   937  *                  \arg M3G_GET_POSITIONS
   938  *                  \arg M3G_GET_NORMALS
   939  *                  \arg M3G_GET_COLORS
   940  *                  \arg M3G_GET_TEXCOORDS0
   941  *                  \arg M3G_GET_TEXCOORDS0 + 1
   942  * \param scaleBias array for scale and bias (s, bx, by, bz)
   943  * \param sbLength  length of scale bias array
   944  */
   945 
   946 /*@access M3Gobject@*/
   947 M3G_API M3GVertexArray m3gGetVertexArray(M3GVertexBuffer handle,
   948                                          M3Gint which,
   949                                          M3Gfloat *scaleBias, M3Gint sbLength)
   950 {
   951     M3Gint tci = 1;
   952     VertexBuffer *buffer = (VertexBuffer *) handle;
   953     M3G_VALIDATE_OBJECT(buffer);
   954 
   955     switch(which) {
   956     case M3G_GET_POSITIONS:
   957         if (scaleBias != NULL && sbLength < 4) {
   958             m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_VALUE);
   959             return 0;
   960         }
   961 
   962         if (scaleBias != NULL) {
   963             scaleBias[0] = buffer->vertexScale;
   964             scaleBias[1] = buffer->vertexBias[0];
   965             scaleBias[2] = buffer->vertexBias[1];
   966             scaleBias[3] = buffer->vertexBias[2];
   967         }
   968         return buffer->vertices;
   969     case M3G_GET_NORMALS: return buffer->normals;
   970     case M3G_GET_COLORS:  return buffer->colors;
   971     case M3G_GET_TEXCOORDS0:
   972         --tci;
   973         /* Flow through */
   974     case M3G_GET_TEXCOORDS0 + 1:
   975         if (buffer->texCoords[tci] != NULL) {
   976             if (scaleBias != NULL && sbLength < (buffer->texCoords[tci]->elementSize + 1)) {
   977                 m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_VALUE);
   978                 return 0;
   979             }
   980     
   981             if (scaleBias != NULL) {
   982                 scaleBias[0] = buffer->texCoordScale[tci];
   983                 scaleBias[1] = buffer->texCoordBias[tci][0];
   984                 scaleBias[2] = buffer->texCoordBias[tci][1];
   985                 if (buffer->texCoords[tci]->elementSize > 2) {
   986                     scaleBias[3] = buffer->texCoordBias[tci][2];
   987                 }
   988             }
   989         }
   990         return buffer->texCoords[tci];
   991     default:
   992         m3gRaiseError(M3G_INTERFACE(buffer), M3G_INVALID_VALUE);
   993         break;
   994     }
   995 
   996     return 0; /* Error */
   997 }
   998 
   999 /*!
  1000  * \brief Gets vertex count of a vertex buffer
  1001  *
  1002  * \param handle    VertexBuffer object
  1003  * \return vertex count
  1004  */
  1005 
  1006 /*@access M3Gobject@*/
  1007 M3G_API M3Gint  m3gGetVertexCount(M3GVertexBuffer handle)
  1008 {
  1009     VertexBuffer *buffer = (VertexBuffer *) handle;
  1010     M3G_VALIDATE_OBJECT(buffer);    
  1011 
  1012     return buffer->vertexCount;    
  1013 }
  1014