--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/m3g/m3gcore11/src/m3g_mesh.c Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,839 @@
+/*
+* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: Mesh implementation
+*
+*/
+
+
+/*!
+ * \internal
+ * \file
+ * \brief Mesh implementation
+ */
+
+#ifndef M3G_CORE_INCLUDE
+# error included by m3g_core.c; do not compile separately.
+#endif
+
+#include "m3g_mesh.h"
+#include "m3g_memory.h"
+
+/*----------------------------------------------------------------------
+ * Internal functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \internal
+ * \brief Destroys this Mesh object.
+ *
+ * \param obj Mesh object
+ */
+static void m3gDestroyMesh(Object *obj)
+{
+ M3Gint i;
+ Mesh *mesh = (Mesh *) obj;
+ M3G_VALIDATE_OBJECT(mesh);
+
+ for (i = 0; i < mesh->trianglePatchCount; ++i) {
+ M3G_ASSIGN_REF(mesh->indexBuffers[i], NULL);
+ M3G_ASSIGN_REF(mesh->appearances[i], NULL);
+ }
+ M3G_ASSIGN_REF(mesh->vertexBuffer, NULL);
+
+ {
+ Interface *m3g = M3G_INTERFACE(mesh);
+ m3gFree(m3g, mesh->indexBuffers);
+ m3gFree(m3g, mesh->appearances);
+ }
+
+ m3gIncStat(M3G_INTERFACE(obj), M3G_STAT_RENDERABLES, -1);
+
+ m3gDestroyNode(obj);
+}
+
+/*!
+ * \internal
+ * \brief Insert a mesh into a rendering queue
+ */
+static M3Gbool m3gQueueMesh(Mesh *mesh, const Matrix *toCamera,
+ RenderQueue *renderQueue)
+{
+ M3Gint i;
+
+ /* Fetch the cumulative alpha factor for this node */
+
+ mesh->totalAlphaFactor =
+ (M3Gushort) m3gGetTotalAlphaFactor((Node*) mesh, renderQueue->root);
+
+ /* Insert each submesh into the rendering queue */
+
+ for (i = 0; i < mesh->trianglePatchCount; i++) {
+ if (mesh->appearances[i] != NULL) {
+ if (!m3gInsertDrawable(M3G_INTERFACE(mesh),
+ renderQueue,
+ (Node*) mesh,
+ toCamera,
+ i,
+ m3gGetAppearanceSortKey(mesh->appearances[i])))
+ return M3G_FALSE;
+ }
+ }
+ return M3G_TRUE;
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Node method.
+ *
+ * Setup mesh rendering by adding all submeshes to
+ * the render queue.
+ *
+ * \param self Mesh object
+ * \param toCamera transform to camera
+ * \param alphaFactor total alpha factor
+ * \param caller caller node
+ * \param renderQueue RenderQueue
+ *
+ * \retval M3G_TRUE continue render setup
+ * \retval M3G_FALSE abort render setup
+ */
+static M3Gbool m3gMeshSetupRender(Node *self,
+ const Node *caller,
+ SetupRenderState *s,
+ RenderQueue *renderQueue)
+{
+ Mesh *mesh = (Mesh *)self;
+ M3G_UNREF(caller);
+ m3gIncStat(M3G_INTERFACE(self), M3G_STAT_RENDER_NODES, 1);
+
+ if ((self->enableBits & NODE_RENDER_BIT) != 0 &&
+ (self->scope & renderQueue->scope) != 0) {
+
+ /* Check view frustum culling */
+
+# if defined(M3G_ENABLE_VF_CULLING)
+ AABB bbox;
+ m3gGetBoundingBox(mesh->vertexBuffer, &bbox);
+ m3gUpdateCullingMask(s, renderQueue->camera, &bbox);
+ if (s->cullMask == 0) {
+ m3gIncStat(M3G_INTERFACE(self),
+ M3G_STAT_RENDER_NODES_CULLED, 1);
+ return M3G_TRUE;
+ }
+# endif
+
+ /* No dice, let's render... */
+
+ return m3gQueueMesh(mesh, &s->toCamera, renderQueue);
+ }
+ return M3G_TRUE;
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Node method.
+ *
+ * Renders one submesh.
+ *
+ * \param self Mesh object
+ * \param ctx current render context
+ * \param patchIndex submesh index
+ */
+static void m3gMeshDoRender(Node *self,
+ RenderContext *ctx,
+ const Matrix *toCamera,
+ M3Gint patchIndex)
+{
+ Mesh *mesh = (Mesh *)self;
+
+ m3gDrawMesh(ctx,
+ mesh->vertexBuffer,
+ mesh->indexBuffers[patchIndex],
+ mesh->appearances[patchIndex],
+ toCamera,
+ mesh->totalAlphaFactor + 1,
+ self->scope);
+}
+
+/*!
+ * \internal
+ * \brief Internal equivalent routine called
+ * by m3gMeshRayIntersect.
+ *
+ * \param mesh Mesh object
+ * \param vertices VertexBuffer object used in calculations
+ * \param mask pick scope mask
+ * \param ray pick ray
+ * \param ri RayIntersection object
+ * \param toGroup transform to originating group
+ * \retval M3G_TRUE continue pick
+ * \retval M3G_FALSE abort pick
+ */
+static M3Gbool m3gMeshRayIntersectInternal( Mesh *mesh,
+ VertexBuffer *vertices,
+ M3Gint mask,
+ M3Gfloat *ray,
+ RayIntersection *ri,
+ Matrix *toGroup)
+{
+ Vec3 v0, v1, v2, tuv;
+ Vec4 transformed, p0, p1;
+ M3Gint indices[4] = { 0, 0, 0, 0 };
+ M3Gint i, j, k, cullMode;
+ Matrix t; /* Reused as texture transform */
+
+ if (vertices == NULL ||
+ mesh->appearances == NULL ||
+ mesh->indexBuffers == NULL ||
+ (((Node *)mesh)->scope & mask) == 0) {
+ return M3G_TRUE;
+ }
+
+ if (vertices->vertices == NULL) {
+ m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_OPERATION);
+ return M3G_FALSE;
+ }
+
+ p0.x = ray[0];
+ p0.y = ray[1];
+ p0.z = ray[2];
+ p0.w = 1.f;
+
+ p1.x = ray[3];
+ p1.y = ray[4];
+ p1.z = ray[5];
+ p1.w = 1.f;
+
+ m3gCopyMatrix(&t, toGroup);
+ M3G_BEGIN_PROFILE(M3G_INTERFACE(mesh), M3G_PROFILE_TRANSFORM_INVERT);
+ if (!m3gInvertMatrix(&t)) {
+ m3gRaiseError(M3G_INTERFACE(mesh), M3G_ARITHMETIC_ERROR);
+ return M3G_FALSE;
+ }
+ M3G_END_PROFILE(M3G_INTERFACE(mesh), M3G_PROFILE_TRANSFORM_INVERT);
+ m3gTransformVec4(&t, &p0);
+ m3gTransformVec4(&t, &p1);
+
+ m3gScaleVec3((Vec3*) &p0, m3gRcp(p0.w));
+ m3gScaleVec3((Vec3*) &p1, m3gRcp(p1.w));
+
+ m3gSubVec4(&p1, &p0);
+
+ /* Quick bounding box test for Meshes */
+ if (m3gGetClass((Object *)mesh) == M3G_CLASS_MESH) {
+ AABB boundingBox;
+ m3gGetBoundingBox(vertices, &boundingBox);
+
+ if (!m3gIntersectBox((Vec3*) &p0, (Vec3*) &p1, &boundingBox)) {
+ return M3G_TRUE;
+ }
+ }
+
+ /* Apply the inverse of the vertex scale and bias to the ray */
+
+ if (!IS_ZERO(vertices->vertexScale)) {
+ const Vec3 *bias = (const Vec3*) vertices->vertexBias;
+ M3Gfloat ooScale = m3gRcp(vertices->vertexScale);
+ m3gSubVec3((Vec3*) &p0, bias);
+ m3gScaleVec3((Vec3*) &p0, ooScale);
+ m3gScaleVec3((Vec3*) &p1, ooScale); /* direction vector -> no bias */
+ }
+ else {
+ m3gRaiseError(M3G_INTERFACE(mesh), M3G_ARITHMETIC_ERROR);
+ return M3G_FALSE;
+ }
+
+ /* Go through all submeshes */
+ for (i = 0; i < mesh->trianglePatchCount; i++) {
+ /* Do not pick submeshes with null appearance */
+ if (mesh->appearances[i] == NULL ||
+ mesh->indexBuffers[i] == NULL) continue;
+
+ /* Validate indices versus vertex buffer */
+ if (m3gGetMaxIndex(mesh->indexBuffers[i]) >= m3gGetNumVertices(vertices)) {
+ m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_OPERATION);
+ return M3G_FALSE;
+ }
+
+ if (mesh->appearances[i]->polygonMode != NULL) {
+ cullMode = m3gGetWinding(mesh->appearances[i]->polygonMode) == M3G_WINDING_CCW ? 0 : 1;
+ switch(m3gGetCulling(mesh->appearances[i]->polygonMode)) {
+ case M3G_CULL_FRONT: cullMode ^= 1; break;
+ case M3G_CULL_NONE: cullMode = 2; break;
+ }
+ }
+ else {
+ cullMode = 0;
+ }
+
+ /* Go through all triangels */
+ for (j = 0; m3gGetIndices(mesh->indexBuffers[i], j, indices); j++) {
+ /* Ignore zero area triangles */
+ if ( indices[0] == indices[1] ||
+ indices[0] == indices[2] ||
+ indices[1] == indices[2]) continue;
+
+ m3gGetVertex(vertices, indices[0], &v0);
+ m3gGetVertex(vertices, indices[1], &v1);
+ m3gGetVertex(vertices, indices[2], &v2);
+
+ if (m3gIntersectTriangle((Vec3*)&p0, (Vec3*)&p1, &v0, &v1, &v2, &tuv, indices[3] ^ cullMode)) {
+ /* Check that we are going to fill this intersection */
+ if (tuv.x <= 0.f || tuv.x >= ri->tMin) continue;
+
+ /* Fill in to RayIntersection */
+ ri->tMin = tuv.x;
+ ri->distance = tuv.x;
+ ri->submeshIndex = i;
+ ri->intersected = (Node *)mesh;
+
+ /* Fetch normal */
+ if (m3gGetNormal(vertices, indices[0], &v0)) {
+ m3gGetNormal(vertices, indices[1], &v1);
+ m3gGetNormal(vertices, indices[2], &v2);
+
+ ri->normal[0] = m3gAdd(
+ m3gMul(v0.x, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
+ m3gAdd(
+ m3gMul(v1.x, tuv.y),
+ m3gMul(v2.x, tuv.z)));
+
+ ri->normal[1] = m3gAdd(
+ m3gMul(v0.y, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
+ m3gAdd(
+ m3gMul(v1.y, tuv.y),
+ m3gMul(v2.y, tuv.z)));
+
+ ri->normal[2] = m3gAdd(
+ m3gMul(v0.z, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
+ m3gAdd(
+ m3gMul(v1.z, tuv.y),
+ m3gMul(v2.z, tuv.z)));
+ }
+ else {
+ ri->normal[0] = 0.f;
+ ri->normal[1] = 0.f;
+ ri->normal[2] = 1.f;
+ }
+
+ /* Fetch texture coordinates for each unit */
+ for (k = 0; k < M3G_NUM_TEXTURE_UNITS; k++) {
+ if (m3gGetTexCoord(vertices, indices[0], k, &v0)) {
+ m3gGetTexCoord(vertices, indices[1], k, &v1);
+ m3gGetTexCoord(vertices, indices[2], k, &v2);
+
+ /* Calculate transformed S and T */
+ transformed.x = m3gAdd(
+ m3gMul(v0.x, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
+ m3gAdd(
+ m3gMul(v1.x, tuv.y),
+ m3gMul(v2.x, tuv.z)));
+
+ transformed.y = m3gAdd(
+ m3gMul(v0.y, m3gSub(1.f, m3gAdd(tuv.y, tuv.z))),
+ m3gAdd(
+ m3gMul(v1.y, tuv.y),
+ m3gMul(v2.y, tuv.z)));
+
+ transformed.z = 0;
+ transformed.w = 1;
+
+ /* Transform and * 1/w */
+ if (mesh->appearances[i]->texture[k] != NULL) {
+ m3gGetCompositeTransform((Transformable *)mesh->appearances[i]->texture[k], &t);
+ m3gTransformVec4(&t, &transformed);
+ m3gScaleVec4(&transformed, m3gRcp(transformed.w));
+ }
+
+ ri->textureS[k] = transformed.x;
+ ri->textureT[k] = transformed.y;
+ }
+ else {
+ ri->textureS[k] = 0.f;
+ ri->textureT[k] = 0.f;
+ }
+ }
+ }
+ }
+ }
+
+ return M3G_TRUE;
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Node method.
+ *
+ * Just forward call internal ray intersect.
+ *
+ * \param self Mesh object
+ * \param mask pick scope mask
+ * \param ray pick ray
+ * \param ri RayIntersection object
+ * \param toGroup transform to originating group
+ * \retval M3G_TRUE continue pick
+ * \retval M3G_FALSE abort pick
+ */
+static M3Gbool m3gMeshRayIntersect( Node *self,
+ M3Gint mask,
+ M3Gfloat *ray,
+ RayIntersection *ri,
+ Matrix *toGroup)
+{
+ Mesh *mesh = (Mesh *)self;
+ return m3gMeshRayIntersectInternal(mesh, mesh->vertexBuffer, mask, ray, ri, toGroup);
+}
+
+/*!
+ * \internal
+ * \brief Initializes a Mesh object. See specification
+ * for default values.
+ *
+ * \param m3g M3G interface
+ * \param mesh Mesh object
+ * \param hVertices VertexBuffer object
+ * \param hTriangles array of IndexBuffer objects
+ * \param hAppearances array of Appearance objects
+ * \param trianglePatchCount number of submeshes
+ * \param vfTable virtual function table
+ * \retval M3G_TRUE success
+ * \retval M3G_FALSE failed
+ */
+static M3Gbool m3gInitMesh(Interface *m3g,
+ Mesh *mesh,
+ M3GVertexBuffer hVertices,
+ M3GIndexBuffer *hTriangles,
+ M3GAppearance *hAppearances,
+ M3Gint trianglePatchCount,
+ M3GClass classID)
+{
+ M3Gint i;
+
+ /* Out of memory if more than 65535 triangle patches */
+ if (trianglePatchCount > 65535) {
+ m3gRaiseError(m3g, M3G_OUT_OF_MEMORY);
+ return M3G_FALSE;
+ }
+
+ for (i = 0; i < trianglePatchCount; i++) {
+ if (hTriangles[i] == NULL) {
+ m3gRaiseError(m3g, M3G_NULL_POINTER);
+ return M3G_FALSE;
+ }
+ }
+
+ mesh->indexBuffers =
+ m3gAllocZ(m3g, sizeof(IndexBuffer *) * trianglePatchCount);
+ if (!mesh->indexBuffers) {
+ return M3G_FALSE;
+ }
+
+ mesh->appearances =
+ m3gAllocZ(m3g, sizeof(Appearance *) * trianglePatchCount);
+ if (!mesh->appearances) {
+ m3gFree(m3g, mesh->indexBuffers);
+ return M3G_FALSE;
+ }
+
+ /* Mesh is derived from node */
+ m3gInitNode(m3g, &mesh->node, classID);
+ mesh->node.hasRenderables = M3G_TRUE;
+ mesh->node.dirtyBits |= NODE_BBOX_BIT;
+
+ for (i = 0; i < trianglePatchCount; i++) {
+ M3G_ASSIGN_REF(mesh->indexBuffers[i], hTriangles[i]);
+ }
+
+ if (hAppearances != NULL) {
+ for (i = 0; i < trianglePatchCount; i++) {
+ M3G_ASSIGN_REF(mesh->appearances[i], hAppearances[i]);
+ }
+ }
+ else {
+ m3gZero(mesh->appearances, sizeof(Appearance *) * trianglePatchCount);
+ }
+
+ M3G_ASSIGN_REF(mesh->vertexBuffer, hVertices);
+ mesh->trianglePatchCount = (M3Gshort) trianglePatchCount;
+
+ m3gIncStat(M3G_INTERFACE(mesh), M3G_STAT_RENDERABLES, 1);
+
+ return M3G_TRUE;
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Object3D method.
+ *
+ * \param self Mesh object
+ * \param references array of reference objects
+ * \return number of references
+ */
+static M3Gint m3gMeshDoGetReferences(Object *self, Object **references)
+{
+ Mesh *mesh = (Mesh *)self;
+ M3Gint i, num = m3gObjectDoGetReferences(self, references);
+ if (references != NULL)
+ references[num] = (Object *)mesh->vertexBuffer;
+ num++;
+ for (i = 0; i < mesh->trianglePatchCount; i++) {
+ if (mesh->indexBuffers[i] != NULL) {
+ if (references != NULL)
+ references[num] = (Object *)mesh->indexBuffers[i];
+ num++;
+ }
+ if (mesh->appearances[i] != NULL) {
+ if (references != NULL)
+ references[num] = (Object *)mesh->appearances[i];
+ num++;
+ }
+ }
+ return num;
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Object3D method.
+ */
+static Object *m3gMeshFindID(Object *self, M3Gint userID)
+{
+ int i;
+ Mesh *mesh = (Mesh *)self;
+ Object *found = m3gObjectFindID(self, userID);
+
+ if (!found) {
+ found = m3gFindID((Object*) mesh->vertexBuffer, userID);
+ }
+ for (i = 0; !found && i < mesh->trianglePatchCount; ++i) {
+ if (mesh->indexBuffers[i] != NULL) {
+ found = m3gFindID((Object*) mesh->indexBuffers[i], userID);
+ }
+ if (!found && mesh->appearances[i] != NULL) {
+ found = m3gFindID((Object*) mesh->appearances[i], userID);
+ }
+ }
+ return found;
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Object3D method.
+ *
+ * \param originalObj original Mesh object
+ * \param cloneObj pointer to cloned Mesh object
+ * \param pairs array for all object-duplicate pairs
+ * \param numPairs number of pairs
+ */
+static M3Gbool m3gMeshDuplicate(const Object *originalObj,
+ Object **cloneObj,
+ Object **pairs,
+ M3Gint *numPairs)
+{
+ /* Create the clone if it doesn't exist; otherwise we'll be all
+ * set by the derived class(es) and can just call through to the
+ * base class */
+
+ if (*cloneObj == NULL) {
+ Mesh *original = (Mesh *)originalObj;
+ Mesh *clone = (Mesh *)m3gCreateMesh(originalObj->interface,
+ original->vertexBuffer,
+ original->indexBuffers,
+ original->appearances,
+ original->trianglePatchCount);
+ *cloneObj = (Object *)clone;
+ if (*cloneObj == NULL) {
+ return M3G_FALSE;
+ }
+ }
+
+ return m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs);
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Object3D method.
+ *
+ * \param self Mesh object
+ * \param time current world time
+ * \return minimum validity
+ */
+static M3Gint m3gMeshApplyAnimation(Object *self, M3Gint time)
+{
+ M3Gint validity, minValidity;
+ Mesh *mesh = (Mesh *)self;
+ Object *vb;
+ M3G_VALIDATE_OBJECT(mesh);
+
+ minValidity = m3gObjectApplyAnimation(self, time);
+
+ vb = (Object *) mesh->vertexBuffer;
+
+ if (vb != NULL && minValidity > 0) {
+ validity = M3G_VFUNC(Object, vb, applyAnimation)(vb, time);
+ minValidity = M3G_MIN(validity, minValidity);
+ }
+
+ if (mesh->appearances != NULL) {
+ Object *app;
+ M3Gint i, n;
+ n = mesh->trianglePatchCount;
+
+ for (i = 0; i < n && minValidity > 0; ++i) {
+ app = (Object *) mesh->appearances[i];
+ if (app != NULL) {
+ validity = M3G_VFUNC(Object, app, applyAnimation)(app, time);
+ minValidity = M3G_MIN(validity, minValidity);
+ }
+ }
+ }
+
+ return minValidity;
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Node method
+ */
+static M3Gint m3gMeshGetBBox(Node *self, AABB *bbox)
+{
+ Mesh *mesh = (Mesh *) self;
+ VertexBuffer *vb = mesh->vertexBuffer;
+
+ if (vb->vertices) {
+ m3gGetBoundingBox(vb, bbox);
+ return VFC_BBOX_COST + VFC_NODE_OVERHEAD;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Overloaded Node method
+ */
+static M3Gbool m3gMeshValidate(Node *self, M3Gbitmask stateBits, M3Gint scope)
+{
+ Mesh *mesh = (Mesh *) self;
+ VertexBuffer *vb = mesh->vertexBuffer;
+ int i;
+
+ if ((scope & self->scope) != 0) {
+ if (stateBits & self->enableBits) {
+
+ /* Validate vertex buffer components */
+
+ for (i = 0; i < mesh->trianglePatchCount; ++i) {
+ Appearance *app = mesh->appearances[i];
+ if (app) {
+ if (!m3gValidateVertexBuffer(
+ vb, app, m3gGetMaxIndex(mesh->indexBuffers[i]))) {
+ m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_OPERATION);
+ return M3G_FALSE;
+ }
+ }
+ }
+
+ /* Invalidate cached vertex stuff if source buffer changed */
+ {
+ M3Gint vbTimestamp = m3gGetTimestamp(vb);
+ if (mesh->vbTimestamp != vbTimestamp) {
+ m3gInvalidateNode(self, NODE_BBOX_BIT);
+ mesh->vbTimestamp = vbTimestamp;
+ }
+ }
+
+ return m3gNodeValidate(self, stateBits, scope);
+ }
+ }
+ return M3G_TRUE;
+}
+
+#if 0
+/*!
+ * \internal
+ * \brief Computes the estimated rendering cost for this Mesh node
+ */
+static M3Gint m3gMeshRenderingCost(const Mesh *mesh)
+{
+ /* Since we're using strips, just assume that each vertex
+ * generates a new triangle... */
+
+ return
+ mesh->vertexBuffer->vertexCount * (VFC_VERTEX_COST +
+ VFC_TRIANGLE_COST) +
+ mesh->trianglePatchCount * VFC_RENDERCALL_OVERHEAD +
+ VFC_NODE_OVERHEAD;
+}
+#endif
+
+/*----------------------------------------------------------------------
+ * Virtual function table
+ *--------------------------------------------------------------------*/
+
+static const NodeVFTable m3gvf_Mesh = {
+ {
+ {
+ m3gMeshApplyAnimation,
+ m3gNodeIsCompatible,
+ m3gNodeUpdateProperty,
+ m3gMeshDoGetReferences,
+ m3gMeshFindID,
+ m3gMeshDuplicate,
+ m3gDestroyMesh
+ }
+ },
+ m3gNodeAlign,
+ m3gMeshDoRender,
+ m3gMeshGetBBox,
+ m3gMeshRayIntersect,
+ m3gMeshSetupRender,
+ m3gNodeUpdateDuplicateReferences,
+ m3gMeshValidate
+};
+
+
+/*----------------------------------------------------------------------
+ * Public API functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates a Mesh object.
+ *
+ * \param interface M3G interface
+ * \param hVertices VertexBuffer object
+ * \param hTriangles array of IndexBuffer objects
+ * \param hAppearances array of Appearance objects
+ * \param trianglePatchCount number of submeshes
+ * \retval Mesh new Mesh object
+ * \retval NULL Mesh creating failed
+ */
+M3G_API M3GMesh m3gCreateMesh(M3GInterface interface,
+ M3GVertexBuffer hVertices,
+ M3GIndexBuffer *hTriangles,
+ M3GAppearance *hAppearances,
+ M3Gint trianglePatchCount)
+{
+ Interface *m3g = (Interface *) interface;
+ M3G_VALIDATE_INTERFACE(m3g);
+
+ {
+ Mesh *mesh = m3gAllocZ(m3g, sizeof(Mesh));
+
+ if (mesh != NULL) {
+ if (!m3gInitMesh(m3g, mesh,
+ hVertices, hTriangles, hAppearances,
+ trianglePatchCount,
+ M3G_CLASS_MESH)) {
+ m3gFree(m3g, mesh);
+ return NULL;
+ }
+ }
+
+ return (M3GMesh)mesh;
+ }
+}
+
+/*!
+ * \brief Sets submesh appearance.
+ *
+ * \param handle Mesh object
+ * \param appearanceIndex submesh index
+ * \param hAppearance Appearance object
+ */
+M3G_API void m3gSetAppearance(M3GMesh handle,
+ M3Gint appearanceIndex,
+ M3GAppearance hAppearance)
+{
+ Mesh *mesh = (Mesh *)handle;
+ M3G_VALIDATE_OBJECT(mesh);
+
+ if (appearanceIndex < 0 || appearanceIndex >= mesh->trianglePatchCount) {
+ m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_INDEX);
+ return;
+ }
+
+ M3G_ASSIGN_REF(mesh->appearances[appearanceIndex], (Appearance *) hAppearance);
+}
+
+/*!
+ * \brief Gets submesh appearance.
+ *
+ * \param handle Mesh object
+ * \param idx submesh index
+ * \return Appearance object
+ */
+M3G_API M3GAppearance m3gGetAppearance(M3GMesh handle,
+ M3Gint idx)
+{
+ Mesh *mesh = (Mesh *)handle;
+ M3G_VALIDATE_OBJECT(mesh);
+
+ if (idx < 0 || idx >= mesh->trianglePatchCount) {
+ m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_INDEX);
+ return NULL;
+ }
+
+ return mesh->appearances[idx];
+}
+
+/*!
+ * \brief Gets submesh index buffer.
+ *
+ * \param handle Mesh object
+ * \param idx submesh index
+ * \return IndexBuffer object
+ */
+M3G_API M3GIndexBuffer m3gGetIndexBuffer(M3GMesh handle,
+ M3Gint idx)
+{
+ Mesh *mesh = (Mesh *)handle;
+ M3G_VALIDATE_OBJECT(mesh);
+
+ if (idx < 0 || idx >= mesh->trianglePatchCount) {
+ m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_INDEX);
+ return NULL;
+ }
+
+ return mesh->indexBuffers[idx];
+}
+
+/*!
+ * \brief Gets VertexBuffer.
+ *
+ * \param handle Mesh object
+ * \return VertexBuffer object
+ */
+M3G_API M3GVertexBuffer m3gGetVertexBuffer(M3GMesh handle)
+{
+ Mesh *mesh = (Mesh *)handle;
+ M3G_VALIDATE_OBJECT(mesh);
+
+ return mesh->vertexBuffer;
+}
+
+/*!
+ * \brief Gets submesh count.
+ *
+ * \param handle Mesh object
+ * \return submesh count
+ */
+M3G_API M3Gint m3gGetSubmeshCount(M3GMesh handle)
+{
+ Mesh *mesh = (Mesh *)handle;
+ M3G_VALIDATE_OBJECT(mesh);
+
+ return mesh->trianglePatchCount;
+}
+