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