os/graphics/m3g/m3gcore11/src/m3g_mesh.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     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: Mesh implementation
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Mesh 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_mesh.h"
    30 #include "m3g_memory.h"
    31 
    32 /*----------------------------------------------------------------------
    33  * Internal functions
    34  *--------------------------------------------------------------------*/
    35 
    36 /*!
    37  * \internal
    38  * \brief Destroys this Mesh object.
    39  *
    40  * \param obj Mesh object
    41  */
    42 static void m3gDestroyMesh(Object *obj)
    43 {
    44     M3Gint i;
    45     Mesh *mesh = (Mesh *) obj;
    46     M3G_VALIDATE_OBJECT(mesh);
    47 
    48     for (i = 0; i < mesh->trianglePatchCount; ++i) {
    49         M3G_ASSIGN_REF(mesh->indexBuffers[i], NULL);
    50         M3G_ASSIGN_REF(mesh->appearances[i], NULL);
    51     }
    52     M3G_ASSIGN_REF(mesh->vertexBuffer, NULL);
    53 
    54 	{
    55 		Interface *m3g = M3G_INTERFACE(mesh);
    56 		m3gFree(m3g, mesh->indexBuffers);
    57 		m3gFree(m3g, mesh->appearances);
    58 	}
    59 
    60     m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_RENDERABLES, -1);
    61     
    62     m3gDestroyNode(obj);
    63 }
    64 
    65 /*!
    66  * \internal
    67  * \brief Insert a mesh into a rendering queue
    68  */
    69 static M3Gbool m3gQueueMesh(Mesh *mesh, const Matrix *toCamera,
    70                             RenderQueue *renderQueue)
    71 {
    72     M3Gint i;
    73 
    74     /* Fetch the cumulative alpha factor for this node */
    75     
    76     mesh->totalAlphaFactor =
    77         (M3Gushort) m3gGetTotalAlphaFactor((Node*) mesh, renderQueue->root);
    78         
    79     /* Insert each submesh into the rendering queue */
    80             
    81     for (i = 0; i < mesh->trianglePatchCount; i++) {
    82         if (mesh->appearances[i] != NULL) {
    83             if (!m3gInsertDrawable(M3G_INTERFACE(mesh),
    84                                    renderQueue,
    85                                    (Node*) mesh,
    86                                    toCamera,
    87                                    i,
    88                                    m3gGetAppearanceSortKey(mesh->appearances[i])))
    89                 return M3G_FALSE;
    90         }
    91     }
    92     return M3G_TRUE;
    93 }
    94 
    95 /*!
    96  * \internal
    97  * \brief Overloaded Node method.
    98  *
    99  * Setup mesh rendering by adding all submeshes to
   100  * the render queue.
   101  *
   102  * \param self Mesh object
   103  * \param toCamera transform to camera
   104  * \param alphaFactor total alpha factor
   105  * \param caller caller node
   106  * \param renderQueue RenderQueue
   107  *
   108  * \retval M3G_TRUE continue render setup
   109  * \retval M3G_FALSE abort render setup
   110  */
   111 static M3Gbool m3gMeshSetupRender(Node *self,
   112                                   const Node *caller,
   113                                   SetupRenderState *s,
   114                                   RenderQueue *renderQueue)
   115 {
   116 	Mesh *mesh = (Mesh *)self;
   117     M3G_UNREF(caller);
   118     m3gIncStat(M3G_INTERFACE(self), M3G_STAT_RENDER_NODES, 1);
   119     
   120 	if ((self->enableBits & NODE_RENDER_BIT) != 0 &&
   121         (self->scope & renderQueue->scope) != 0) {
   122 
   123         /* Check view frustum culling */
   124         
   125 #       if defined(M3G_ENABLE_VF_CULLING)
   126         AABB bbox;
   127         m3gGetBoundingBox(mesh->vertexBuffer, &bbox);
   128         m3gUpdateCullingMask(s, renderQueue->camera, &bbox);
   129         if (s->cullMask == 0) {
   130             m3gIncStat(M3G_INTERFACE(self),
   131                        M3G_STAT_RENDER_NODES_CULLED, 1);
   132             return M3G_TRUE;
   133         }
   134 #       endif
   135 
   136         /* No dice, let's render... */
   137 
   138         return m3gQueueMesh(mesh, &s->toCamera, renderQueue);
   139     }
   140     return M3G_TRUE;
   141 }
   142 
   143 /*!
   144  * \internal
   145  * \brief Overloaded Node method.
   146  *
   147  * Renders one submesh.
   148  *
   149  * \param self Mesh object
   150  * \param ctx current render context
   151  * \param patchIndex submesh index
   152  */
   153 static void m3gMeshDoRender(Node *self,
   154                             RenderContext *ctx,
   155                             const Matrix *toCamera,
   156                             M3Gint patchIndex)
   157 {
   158     Mesh *mesh = (Mesh *)self;
   159 
   160 	m3gDrawMesh(ctx,
   161                 mesh->vertexBuffer,
   162                 mesh->indexBuffers[patchIndex],
   163                 mesh->appearances[patchIndex],
   164                 toCamera,
   165                 mesh->totalAlphaFactor + 1,
   166                 self->scope);
   167 }
   168 
   169 /*!
   170  * \internal
   171  * \brief Internal equivalent routine called
   172  * by m3gMeshRayIntersect.
   173  *
   174  * \param mesh      Mesh object
   175  * \param vertices  VertexBuffer object used in calculations
   176  * \param mask      pick scope mask
   177  * \param ray       pick ray
   178  * \param ri        RayIntersection object
   179  * \param toGroup   transform to originating group
   180  * \retval          M3G_TRUE    continue pick
   181  * \retval          M3G_FALSE   abort pick
   182  */
   183 static M3Gbool m3gMeshRayIntersectInternal(	Mesh *mesh,
   184                                             VertexBuffer *vertices,
   185             								M3Gint mask,
   186             								M3Gfloat *ray,
   187             								RayIntersection *ri,
   188             								Matrix *toGroup)
   189 {
   190     Vec3 v0, v1, v2, tuv;
   191     Vec4 transformed, p0, p1;
   192     M3Gint indices[4] = { 0, 0, 0, 0 }; 
   193     M3Gint i, j, k, cullMode;
   194     Matrix t;   /* Reused as texture transform */
   195 
   196     if (vertices == NULL ||
   197          mesh->appearances == NULL ||
   198          mesh->indexBuffers == NULL ||
   199          (((Node *)mesh)->scope & mask) == 0) {
   200         return M3G_TRUE;
   201     }
   202 
   203     if (vertices->vertices == NULL) {
   204         m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_OPERATION);
   205         return M3G_FALSE;
   206     }
   207 
   208     p0.x = ray[0];
   209     p0.y = ray[1];
   210     p0.z = ray[2];
   211     p0.w = 1.f;
   212 
   213     p1.x = ray[3];
   214     p1.y = ray[4];
   215     p1.z = ray[5];
   216     p1.w = 1.f;
   217 
   218     m3gCopyMatrix(&t, toGroup);
   219     M3G_BEGIN_PROFILE(M3G_INTERFACE(mesh), M3G_PROFILE_TRANSFORM_INVERT);
   220     if (!m3gInvertMatrix(&t)) {
   221         m3gRaiseError(M3G_INTERFACE(mesh), M3G_ARITHMETIC_ERROR);
   222         return M3G_FALSE;
   223     }
   224     M3G_END_PROFILE(M3G_INTERFACE(mesh), M3G_PROFILE_TRANSFORM_INVERT);
   225     m3gTransformVec4(&t, &p0);
   226     m3gTransformVec4(&t, &p1);
   227     
   228     m3gScaleVec3((Vec3*) &p0, m3gRcp(p0.w));
   229     m3gScaleVec3((Vec3*) &p1, m3gRcp(p1.w));
   230 
   231     m3gSubVec4(&p1, &p0);
   232 
   233     /* Quick bounding box test for Meshes */
   234     if (m3gGetClass((Object *)mesh) == M3G_CLASS_MESH) {
   235         AABB boundingBox;
   236         m3gGetBoundingBox(vertices, &boundingBox);
   237 
   238         if (!m3gIntersectBox((Vec3*) &p0, (Vec3*) &p1, &boundingBox)) {
   239             return M3G_TRUE;
   240         }
   241     }
   242 
   243     /* Apply the inverse of the vertex scale and bias to the ray */
   244     
   245     if (!IS_ZERO(vertices->vertexScale)) {
   246         const Vec3 *bias = (const Vec3*) vertices->vertexBias;
   247         M3Gfloat ooScale = m3gRcp(vertices->vertexScale);
   248         m3gSubVec3((Vec3*) &p0, bias);
   249         m3gScaleVec3((Vec3*) &p0, ooScale);
   250         m3gScaleVec3((Vec3*) &p1, ooScale); /* direction vector -> no bias */
   251     }
   252     else {
   253         m3gRaiseError(M3G_INTERFACE(mesh), M3G_ARITHMETIC_ERROR);
   254         return M3G_FALSE;
   255     }
   256     
   257     /* Go through all submeshes */
   258     for (i = 0; i < mesh->trianglePatchCount; i++) {
   259         /* Do not pick submeshes with null appearance */
   260         if (mesh->appearances[i] == NULL ||
   261             mesh->indexBuffers[i] == NULL) continue;
   262 
   263         /* Validate indices versus vertex buffer */
   264         if (m3gGetMaxIndex(mesh->indexBuffers[i]) >= m3gGetNumVertices(vertices)) {
   265             m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_OPERATION);
   266             return M3G_FALSE;
   267         }
   268 
   269         if (mesh->appearances[i]->polygonMode != NULL) {
   270             cullMode = m3gGetWinding(mesh->appearances[i]->polygonMode) == M3G_WINDING_CCW ? 0 : 1;
   271             switch(m3gGetCulling(mesh->appearances[i]->polygonMode)) {
   272             case M3G_CULL_FRONT: cullMode ^= 1; break;
   273             case M3G_CULL_NONE:  cullMode  = 2; break;
   274             }
   275         }
   276         else {
   277             cullMode = 0;
   278         }
   279 
   280         /* Go through all triangels */
   281         for (j = 0; m3gGetIndices(mesh->indexBuffers[i], j, indices); j++) {
   282             /* Ignore zero area triangles */
   283             if ( indices[0] == indices[1] ||
   284                  indices[0] == indices[2] ||
   285                  indices[1] == indices[2]) continue;
   286 
   287             m3gGetVertex(vertices, indices[0], &v0);
   288             m3gGetVertex(vertices, indices[1], &v1);
   289             m3gGetVertex(vertices, indices[2], &v2);
   290 
   291             if (m3gIntersectTriangle((Vec3*)&p0, (Vec3*)&p1, &v0, &v1, &v2, &tuv, indices[3] ^ cullMode)) {
   292                 /* Check that we are going to fill this intersection */
   293                 if (tuv.x <= 0.f || tuv.x >= ri->tMin) continue;
   294 
   295                 /* Fill in to RayIntersection */
   296                 ri->tMin = tuv.x;
   297                 ri->distance = tuv.x;
   298                 ri->submeshIndex = i;
   299                 ri->intersected = (Node *)mesh;
   300 
   301                 /* Fetch normal */
   302                 if (m3gGetNormal(vertices, indices[0], &v0)) {
   303                     m3gGetNormal(vertices, indices[1], &v1);
   304                     m3gGetNormal(vertices, indices[2], &v2);
   305 
   306                     ri->normal[0] = m3gAdd(
   307                         m3gMul(v0.x, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
   308                         m3gAdd(
   309                             m3gMul(v1.x, tuv.y),
   310                             m3gMul(v2.x, tuv.z)));
   311                     
   312                     ri->normal[1] = m3gAdd(
   313                         m3gMul(v0.y, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
   314                         m3gAdd(
   315                             m3gMul(v1.y, tuv.y),
   316                             m3gMul(v2.y, tuv.z)));
   317                     
   318                     ri->normal[2] = m3gAdd(
   319                         m3gMul(v0.z, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
   320                         m3gAdd(
   321                             m3gMul(v1.z, tuv.y),
   322                             m3gMul(v2.z, tuv.z)));
   323                 }
   324                 else {
   325                     ri->normal[0] = 0.f;
   326                     ri->normal[1] = 0.f;
   327                     ri->normal[2] = 1.f;
   328                 }
   329             
   330                 /* Fetch texture coordinates for each unit */
   331                 for (k = 0; k < M3G_NUM_TEXTURE_UNITS; k++) {
   332                     if (m3gGetTexCoord(vertices, indices[0], k, &v0)) {
   333                         m3gGetTexCoord(vertices, indices[1], k, &v1);
   334                         m3gGetTexCoord(vertices, indices[2], k, &v2);
   335 
   336                         /* Calculate transformed S and T */
   337                         transformed.x = m3gAdd(
   338                             m3gMul(v0.x, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
   339                             m3gAdd(
   340                                 m3gMul(v1.x, tuv.y),
   341                                 m3gMul(v2.x, tuv.z)));
   342                         
   343                         transformed.y = m3gAdd(
   344                             m3gMul(v0.y, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
   345                             m3gAdd(
   346                                 m3gMul(v1.y, tuv.y),
   347                                 m3gMul(v2.y, tuv.z)));
   348                         
   349                         transformed.z = 0;
   350                         transformed.w = 1;
   351 
   352                         /* Transform and * 1/w */
   353                         if (mesh->appearances[i]->texture[k] != NULL) {
   354                             m3gGetCompositeTransform((Transformable *)mesh->appearances[i]->texture[k], &t);
   355                             m3gTransformVec4(&t, &transformed);
   356                             m3gScaleVec4(&transformed, m3gRcp(transformed.w));
   357                         }
   358 
   359                         ri->textureS[k] = transformed.x;
   360                         ri->textureT[k] = transformed.y;
   361                     }
   362                     else {
   363                         ri->textureS[k] = 0.f;
   364                         ri->textureT[k] = 0.f;
   365                     }
   366                 }
   367             }
   368         }
   369     }
   370 
   371     return M3G_TRUE;
   372 }
   373 
   374 /*!
   375  * \internal
   376  * \brief Overloaded Node method.
   377  *
   378  * Just forward call internal ray intersect.
   379  *
   380  * \param self      Mesh object
   381  * \param mask      pick scope mask
   382  * \param ray       pick ray
   383  * \param ri        RayIntersection object
   384  * \param toGroup   transform to originating group
   385  * \retval          M3G_TRUE    continue pick
   386  * \retval          M3G_FALSE   abort pick
   387  */
   388 static M3Gbool m3gMeshRayIntersect( Node *self,
   389     								M3Gint mask,
   390     								M3Gfloat *ray,
   391     								RayIntersection *ri,
   392     								Matrix *toGroup)
   393 {
   394     Mesh *mesh = (Mesh *)self;
   395     return m3gMeshRayIntersectInternal(mesh, mesh->vertexBuffer, mask, ray, ri, toGroup);
   396 }
   397 
   398 /*!
   399  * \internal
   400  * \brief Initializes a Mesh object. See specification
   401  * for default values.
   402  *
   403  * \param m3g                   M3G interface
   404  * \param mesh                  Mesh object
   405  * \param hVertices             VertexBuffer object
   406  * \param hTriangles            array of IndexBuffer objects
   407  * \param hAppearances          array of Appearance objects
   408  * \param trianglePatchCount    number of submeshes
   409  * \param vfTable               virtual function table
   410  * \retval                      M3G_TRUE success
   411  * \retval                      M3G_FALSE failed
   412  */
   413 static M3Gbool m3gInitMesh(Interface *m3g,
   414                            Mesh *mesh,
   415                            M3GVertexBuffer hVertices,
   416                            M3GIndexBuffer *hTriangles,
   417                            M3GAppearance *hAppearances,
   418                            M3Gint trianglePatchCount,
   419                            M3GClass classID)
   420 {
   421     M3Gint i;
   422 	
   423     /* Out of memory if more than 65535 triangle patches */
   424     if (trianglePatchCount > 65535) {
   425         m3gRaiseError(m3g, M3G_OUT_OF_MEMORY);
   426         return M3G_FALSE;
   427     }
   428 
   429 	for (i = 0; i < trianglePatchCount; i++) {
   430 		if (hTriangles[i] == NULL) {
   431 			m3gRaiseError(m3g, M3G_NULL_POINTER);
   432             return M3G_FALSE;
   433 		}
   434 	}
   435 
   436 	mesh->indexBuffers =
   437         m3gAllocZ(m3g, sizeof(IndexBuffer *) * trianglePatchCount);
   438 	if (!mesh->indexBuffers) {
   439 		return M3G_FALSE;
   440 	}
   441 
   442 	mesh->appearances =
   443         m3gAllocZ(m3g, sizeof(Appearance *) * trianglePatchCount);
   444 	if (!mesh->appearances) {
   445 		m3gFree(m3g, mesh->indexBuffers);
   446 		return M3G_FALSE;
   447 	}
   448 
   449 	/* Mesh is derived from node */
   450 	m3gInitNode(m3g, &mesh->node, classID);
   451     mesh->node.hasRenderables = M3G_TRUE;
   452     mesh->node.dirtyBits |= NODE_BBOX_BIT;
   453 
   454     for (i = 0; i < trianglePatchCount; i++) {
   455         M3G_ASSIGN_REF(mesh->indexBuffers[i], hTriangles[i]);
   456     }
   457 	
   458 	if (hAppearances != NULL) {
   459         for (i = 0; i < trianglePatchCount; i++) {
   460             M3G_ASSIGN_REF(mesh->appearances[i], hAppearances[i]);
   461         }
   462 	}
   463 	else {
   464 		m3gZero(mesh->appearances, sizeof(Appearance *) * trianglePatchCount);
   465     }
   466 	
   467     M3G_ASSIGN_REF(mesh->vertexBuffer, hVertices);
   468 	mesh->trianglePatchCount = (M3Gshort) trianglePatchCount;
   469 
   470     m3gIncStat(M3G_INTERFACE(mesh), M3G_STAT_RENDERABLES, 1);
   471     
   472 	return M3G_TRUE;
   473 }
   474 
   475 /*!
   476  * \internal
   477  * \brief Overloaded Object3D method.
   478  *
   479  * \param self Mesh object
   480  * \param references array of reference objects
   481  * \return number of references
   482  */
   483 static M3Gint m3gMeshDoGetReferences(Object *self, Object **references)
   484 {
   485     Mesh *mesh = (Mesh *)self;
   486     M3Gint i, num = m3gObjectDoGetReferences(self, references);
   487     if (references != NULL)
   488         references[num] = (Object *)mesh->vertexBuffer;
   489     num++;
   490     for (i = 0; i < mesh->trianglePatchCount; i++) {
   491         if (mesh->indexBuffers[i] != NULL) {
   492             if (references != NULL)
   493                 references[num] = (Object *)mesh->indexBuffers[i];
   494             num++;
   495         }
   496         if (mesh->appearances[i] != NULL) {
   497             if (references != NULL)
   498                 references[num] = (Object *)mesh->appearances[i];
   499             num++;
   500         }
   501     }
   502     return num;
   503 }
   504 
   505 /*!
   506  * \internal
   507  * \brief Overloaded Object3D method.
   508  */
   509 static Object *m3gMeshFindID(Object *self, M3Gint userID)
   510 {
   511     int i;
   512     Mesh *mesh = (Mesh *)self;
   513     Object *found = m3gObjectFindID(self, userID);
   514 
   515     if (!found) {
   516         found = m3gFindID((Object*) mesh->vertexBuffer, userID);
   517     }    
   518     for (i = 0; !found && i < mesh->trianglePatchCount; ++i) {
   519         if (mesh->indexBuffers[i] != NULL) {
   520             found = m3gFindID((Object*) mesh->indexBuffers[i], userID);
   521         }
   522         if (!found && mesh->appearances[i] != NULL) {
   523             found = m3gFindID((Object*) mesh->appearances[i], userID);
   524         }
   525     }
   526     return found;
   527 }
   528 
   529 /*!
   530  * \internal
   531  * \brief Overloaded Object3D method.
   532  *
   533  * \param originalObj original Mesh object
   534  * \param cloneObj pointer to cloned Mesh object
   535  * \param pairs array for all object-duplicate pairs
   536  * \param numPairs number of pairs
   537  */
   538 static M3Gbool m3gMeshDuplicate(const Object *originalObj,
   539                                 Object **cloneObj,
   540                                 Object **pairs,
   541                                 M3Gint *numPairs)
   542 {
   543     /* Create the clone if it doesn't exist; otherwise we'll be all
   544      * set by the derived class(es) and can just call through to the
   545      * base class */
   546     
   547     if (*cloneObj == NULL) {
   548         Mesh *original = (Mesh *)originalObj;
   549         Mesh *clone = (Mesh *)m3gCreateMesh(originalObj->interface,
   550                                             original->vertexBuffer,
   551                                             original->indexBuffers,
   552                                             original->appearances,
   553                                             original->trianglePatchCount);
   554         *cloneObj = (Object *)clone;
   555         if (*cloneObj == NULL) {
   556             return M3G_FALSE;
   557         }
   558     }
   559     
   560     return m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs);
   561 }
   562 
   563 /*!
   564  * \internal
   565  * \brief Overloaded Object3D method.
   566  *
   567  * \param self Mesh object
   568  * \param time current world time
   569  * \return minimum validity
   570  */
   571 static M3Gint m3gMeshApplyAnimation(Object *self, M3Gint time)
   572 {
   573     M3Gint validity, minValidity;
   574     Mesh *mesh = (Mesh *)self;
   575     Object *vb;
   576     M3G_VALIDATE_OBJECT(mesh);
   577 
   578     minValidity = m3gObjectApplyAnimation(self, time);
   579 
   580     vb = (Object *) mesh->vertexBuffer;
   581 
   582     if (vb != NULL && minValidity > 0) {
   583         validity = M3G_VFUNC(Object, vb, applyAnimation)(vb, time);
   584         minValidity = M3G_MIN(validity, minValidity);
   585     }
   586     
   587     if (mesh->appearances != NULL) {
   588         Object *app;
   589         M3Gint i, n;
   590         n = mesh->trianglePatchCount;
   591         
   592         for (i = 0; i < n && minValidity > 0; ++i) {
   593             app = (Object *) mesh->appearances[i];
   594             if (app != NULL) {
   595                 validity = M3G_VFUNC(Object, app, applyAnimation)(app, time);
   596                 minValidity = M3G_MIN(validity, minValidity);
   597             }
   598         }
   599     }
   600 
   601     return minValidity;
   602 }
   603 
   604 /*!
   605  * \internal
   606  * \brief Overloaded Node method
   607  */
   608 static M3Gint m3gMeshGetBBox(Node *self, AABB *bbox)
   609 {
   610     Mesh *mesh = (Mesh *) self;
   611     VertexBuffer *vb = mesh->vertexBuffer;
   612 
   613     if (vb->vertices) {
   614         m3gGetBoundingBox(vb, bbox);
   615         return VFC_BBOX_COST + VFC_NODE_OVERHEAD;
   616     }
   617     else {
   618         return 0;
   619     }
   620 }
   621 
   622 /*!
   623  * \internal
   624  * \brief Overloaded Node method
   625  */
   626 static M3Gbool m3gMeshValidate(Node *self, M3Gbitmask stateBits, M3Gint scope)
   627 {
   628     Mesh *mesh = (Mesh *) self;
   629     VertexBuffer *vb = mesh->vertexBuffer;
   630     int i;
   631 
   632     if ((scope & self->scope) != 0) {
   633         if (stateBits & self->enableBits) {
   634             
   635             /* Validate vertex buffer components */
   636             
   637             for (i = 0; i < mesh->trianglePatchCount; ++i) {
   638                 Appearance *app = mesh->appearances[i];
   639                 if (app) {
   640                     if (!m3gValidateVertexBuffer(
   641                             vb, app, m3gGetMaxIndex(mesh->indexBuffers[i]))) {
   642                         m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_OPERATION);
   643                         return M3G_FALSE;
   644                     }
   645                 }
   646             }
   647     
   648             /* Invalidate cached vertex stuff if source buffer changed */
   649             {
   650                 M3Gint vbTimestamp = m3gGetTimestamp(vb);
   651                 if (mesh->vbTimestamp != vbTimestamp) {
   652                     m3gInvalidateNode(self, NODE_BBOX_BIT);
   653                     mesh->vbTimestamp = vbTimestamp;
   654                 }
   655             }
   656             
   657             return m3gNodeValidate(self, stateBits, scope);
   658         }
   659     }
   660     return M3G_TRUE;
   661 }
   662 
   663 #if 0
   664 /*!
   665  * \internal
   666  * \brief Computes the estimated rendering cost for this Mesh node
   667  */
   668 static M3Gint m3gMeshRenderingCost(const Mesh *mesh)
   669 {
   670     /* Since we're using strips, just assume that each vertex
   671      * generates a new triangle... */
   672     
   673     return
   674         mesh->vertexBuffer->vertexCount * (VFC_VERTEX_COST +
   675                                            VFC_TRIANGLE_COST) +
   676         mesh->trianglePatchCount * VFC_RENDERCALL_OVERHEAD +
   677         VFC_NODE_OVERHEAD;
   678 }
   679 #endif
   680 
   681 /*----------------------------------------------------------------------
   682  * Virtual function table
   683  *--------------------------------------------------------------------*/
   684 
   685 static const NodeVFTable m3gvf_Mesh = {
   686     {
   687         {
   688             m3gMeshApplyAnimation,
   689             m3gNodeIsCompatible,
   690             m3gNodeUpdateProperty,
   691             m3gMeshDoGetReferences,
   692             m3gMeshFindID,
   693             m3gMeshDuplicate,
   694             m3gDestroyMesh
   695         }
   696     },
   697     m3gNodeAlign,
   698     m3gMeshDoRender,
   699     m3gMeshGetBBox,
   700     m3gMeshRayIntersect,
   701     m3gMeshSetupRender,
   702     m3gNodeUpdateDuplicateReferences,
   703     m3gMeshValidate
   704 };
   705 
   706 
   707 /*----------------------------------------------------------------------
   708  * Public API functions
   709  *--------------------------------------------------------------------*/
   710 
   711 /*!
   712  * \brief Creates a Mesh object.
   713  *
   714  * \param interface             M3G interface
   715  * \param hVertices             VertexBuffer object
   716  * \param hTriangles            array of IndexBuffer objects
   717  * \param hAppearances          array of Appearance objects
   718  * \param trianglePatchCount    number of submeshes
   719  * \retval Mesh new Mesh object
   720  * \retval NULL Mesh creating failed
   721  */
   722 M3G_API M3GMesh m3gCreateMesh(M3GInterface interface,
   723                               M3GVertexBuffer hVertices,
   724                               M3GIndexBuffer *hTriangles,
   725                               M3GAppearance *hAppearances,
   726                               M3Gint trianglePatchCount)
   727 {
   728     Interface *m3g = (Interface *) interface;
   729     M3G_VALIDATE_INTERFACE(m3g);
   730 
   731 	{
   732 		Mesh *mesh = m3gAllocZ(m3g, sizeof(Mesh));
   733 
   734 		if (mesh != NULL) {
   735     		if (!m3gInitMesh(m3g, mesh,
   736                              hVertices, hTriangles, hAppearances,
   737                              trianglePatchCount,
   738                              M3G_CLASS_MESH)) {
   739     			m3gFree(m3g, mesh);
   740     			return NULL;
   741     		}
   742 		}
   743 
   744 		return (M3GMesh)mesh;
   745 	}
   746 }
   747 
   748 /*!
   749  * \brief Sets submesh appearance.
   750  *
   751  * \param handle                Mesh object
   752  * \param appearanceIndex       submesh index
   753  * \param hAppearance           Appearance object
   754  */
   755 M3G_API void m3gSetAppearance(M3GMesh handle,
   756                               M3Gint appearanceIndex,
   757                               M3GAppearance hAppearance)
   758 {
   759 	Mesh *mesh = (Mesh *)handle;
   760     M3G_VALIDATE_OBJECT(mesh);
   761 	
   762 	if (appearanceIndex < 0 || appearanceIndex >= mesh->trianglePatchCount) {
   763 		m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_INDEX);
   764         return;
   765 	}
   766 
   767     M3G_ASSIGN_REF(mesh->appearances[appearanceIndex], (Appearance *) hAppearance);
   768 }
   769 
   770 /*!
   771  * \brief Gets submesh appearance.
   772  *
   773  * \param handle                Mesh object
   774  * \param idx                   submesh index
   775  * \return                      Appearance object
   776  */
   777 M3G_API M3GAppearance m3gGetAppearance(M3GMesh handle,
   778                                        M3Gint idx)
   779 {
   780 	Mesh *mesh = (Mesh *)handle;
   781     M3G_VALIDATE_OBJECT(mesh);
   782 	
   783 	if (idx < 0 || idx >= mesh->trianglePatchCount) {
   784 		m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_INDEX);
   785         return NULL;
   786 	}
   787 
   788     return mesh->appearances[idx];
   789 }
   790 
   791 /*!
   792  * \brief Gets submesh index buffer.
   793  *
   794  * \param handle                Mesh object
   795  * \param idx                   submesh index
   796  * \return                      IndexBuffer object
   797  */
   798 M3G_API M3GIndexBuffer m3gGetIndexBuffer(M3GMesh handle,
   799                                          M3Gint idx)
   800 {
   801 	Mesh *mesh = (Mesh *)handle;
   802     M3G_VALIDATE_OBJECT(mesh);
   803 	
   804 	if (idx < 0 || idx >= mesh->trianglePatchCount) {
   805 		m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_INDEX);
   806         return NULL;
   807 	}
   808 
   809     return mesh->indexBuffers[idx];
   810 }
   811 
   812 /*!
   813  * \brief Gets VertexBuffer.
   814  *
   815  * \param handle                Mesh object
   816  * \return                      VertexBuffer object
   817  */
   818 M3G_API M3GVertexBuffer m3gGetVertexBuffer(M3GMesh handle)
   819 {
   820 	Mesh *mesh = (Mesh *)handle;
   821     M3G_VALIDATE_OBJECT(mesh);
   822 
   823     return mesh->vertexBuffer;
   824 }
   825 
   826 /*!
   827  * \brief Gets submesh count.
   828  *
   829  * \param handle                Mesh object
   830  * \return                      submesh count
   831  */
   832 M3G_API M3Gint m3gGetSubmeshCount(M3GMesh handle)
   833 {
   834 	Mesh *mesh = (Mesh *)handle;
   835     M3G_VALIDATE_OBJECT(mesh);
   836 
   837     return mesh->trianglePatchCount;
   838 }
   839