/* * Copyright (c) 2005-2010 Hypertriton, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Scene graph object. */ #include #include #include #include #include "sg.h" #include "sg_gui.h" #include #include /* Return FreeSG library version. */ void SG_GetVersion(SG_Version *ver) { ver->major = SG_MAJOR_VERSION; ver->minor = SG_MINOR_VERSION; ver->patch = SG_PATCHLEVEL; ver->release = RELEASE; } /* Initialize the SG library. */ void SG_InitSubsystem(void) { M_InitSubsystem(); AG_RegisterNamespace("FreeSG", "SG_", "http://freesg.org/"); /* Register our base Agar object classes. */ AG_RegisterClass(&sgClass); AG_RegisterClass(&sgNodeClass); AG_RegisterClass(&sgMaterialClass); AG_RegisterClass(&sgProgramClass); #ifdef HAVE_CG AG_RegisterClass(&sgCgProgramClass); #endif /* Register our node element classes. */ AG_RegisterClass(&sgDummyClass); AG_RegisterClass(&sgPointClass); AG_RegisterClass(&sgPlaneClass); AG_RegisterClass(&sgCameraClass); AG_RegisterClass(&sgLightClass); AG_RegisterClass(&sgObjectClass); AG_RegisterClass(&sgSolidClass); AG_RegisterClass(&sgSphereClass); AG_RegisterClass(&sgBoxClass); AG_RegisterClass(&sgVoxelClass); /* Initialize GUI facilities for SG_Edit() */ SG_InitGUI(); } /* Cleanup the SG library. */ void SG_DestroySubsystem(void) { SG_DestroyGUI(); AG_UnregisterNamespace("FreeSG"); } /* Create a new SG object. */ SG * SG_New(void *parent, const char *name, Uint flags) { char nameGen[AG_OBJECT_NAME_MAX]; SG *sg; SG_Camera *cam; SG_Light *lt; if (name == NULL) { AG_ObjectGenName(parent, &sgClass, nameGen, sizeof(nameGen)); } else { if (parent != NULL && AG_ObjectFindChild(parent, name) != NULL) { AG_SetError(_("%s: Existing child object %s"), OBJECT(parent)->name, name); return (NULL); } } sg = Malloc(sizeof(SG)); AG_ObjectInit(sg, &sgClass); AG_ObjectSetNameS(sg, (name != NULL) ? name : nameGen); OBJECT(sg)->flags |= AG_OBJECT_RESIDENT; sg->root = (SG_Node *)SG_PointNew(sg, "_root"); SG_PointSize(SGPOINT(sg->root), 3.0); SG_PointColor(SGPOINT(sg->root), M_ColorRGB(0.0, 1.0, 0.0)); if (!(flags & SG_NO_DEFAULT_NODES)) { cam = SG_CameraNew(sg->root, "Camera0"); SG_Translate(cam, 0.0, 0.0, 10.0); SG_CameraSetRotCtrlCircular(cam, sg->root); lt = SG_LightNew(sg->root, "Light0"); SG_Translate(lt, 20.0, 20.0, 20.0); lt = SG_LightNew(sg->root, "Light1"); SG_Translate(lt, -20.0, -20.0, -20.0); } AG_ObjectAttach(parent, sg); return (sg); } static void Init(void *obj) { SG *sg = obj; OBJECT(sg)->flags |= AG_OBJECT_REOPEN_ONLOAD; sg->flags = 0; sg->root = NULL; TAILQ_INIT(&sg->nodes); } static int SaveNode(SG *sg, AG_DataSource *ds, SG_Node *node) { off_t countOffs, skipSizeOffs; Uint32 count; SG_Node *chldNode; /* Save object metadata */ AG_WriteString(ds, OBJECT(node)->name); if (OBJECT_CLASS(node)->libs[0] != '\0') { char s[AG_OBJECT_TYPE_MAX]; Strlcpy(s, OBJECT_CLASS(node)->hier, sizeof(s)); Strlcat(s, "@", sizeof(s)); Strlcat(s, OBJECT_CLASS(node)->libs, sizeof(s)); AG_WriteString(ds, s); } else { AG_WriteString(ds, OBJECT_CLASS(node)->hier); } skipSizeOffs = AG_Tell(ds); AG_WriteUint32(ds, 0); /* Save object dataset */ if (AG_ObjectSerialize(node, ds) == -1) return (-1); /* Save child nodes */ countOffs = AG_Tell(ds); AG_WriteUint32(ds, 0); count = 0; OBJECT_FOREACH_CHILD(chldNode, node, sg_node) { if (SaveNode(sg, ds, chldNode) == -1) { return (-1); } count++; } AG_WriteUint32At(ds, count, countOffs); AG_WriteUint32At(ds, AG_Tell(ds)-skipSizeOffs, skipSizeOffs); return (0); } static int Save(void *obj, AG_DataSource *ds) { SG *sg = obj; AG_WriteUint32(ds, sg->flags); return SaveNode(sg, ds, sg->root); } static int LoadNode(SG *sg, AG_DataSource *ds, SG_Node *parentNode) { Uint count, i; char nodeName[AG_OBJECT_NAME_MAX]; char classSpec[AG_OBJECT_TYPE_MAX]; AG_ObjectClass *nodeClass; Uint32 skipSize; SG_Node *node; /* Load object metadata */ AG_CopyString(nodeName, ds, sizeof(nodeName)); AG_CopyString(classSpec, ds, sizeof(classSpec)); skipSize = AG_ReadUint32(ds); /* Allocate node instance; load DSOs as needed. */ if ((nodeClass = AG_LoadClass(classSpec)) == NULL) { return (-1); } if ((node = AG_TryMalloc(nodeClass->size)) == NULL) { return (-1); } AG_ObjectInit(node, nodeClass); AG_ObjectSetNameS(node, nodeName); /* Load object dataset */ if (AG_ObjectUnserialize(node, ds) == -1) { goto fail; } OBJECT(node)->flags |= AG_OBJECT_RESIDENT; /* Attach node to parent. */ if (parentNode != NULL) { AG_ObjectAttach(parentNode, node); } else { AG_ObjectAttach(sg, node); sg->root = node; } /* Load child nodes */ count = (Uint)AG_ReadUint32(ds); for (i = 0; i < count; i++) { if (LoadNode(sg, ds, node) == -1) goto fail_detach; } return (0); fail_detach: if (node == sg->root) { sg->root = NULL; } fail: AG_ObjectDestroy(node); return (-1); } static int Load(void *obj, AG_DataSource *ds, const AG_Version *ver) { SG *sg = obj; sg->flags = (Uint)AG_ReadUint32(ds); return LoadNode(sg, ds, NULL); } /* Search child nodes by name. */ SG_Node * SG_SearchNodes(SG_Node *node, const char *name) { SG_Node *chld, *rnode; OBJECT_FOREACH_CLASS(chld, node, sg_node, "SG_Node:*") { if (strcmp(OBJECT(chld)->name, name) == 0) { return (chld); } else { if ((rnode = SG_SearchNodes(chld, name)) != NULL) return (rnode); } } return (NULL); } /* Search entire graph for a node by name. */ void * SG_FindNode(SG *sg, const char *name) { if (strcmp(name, OBJECT(sg->root)->name) == 0) { return (sg->root); } else { return ((void *)SG_SearchNodes(sg->root, name)); } } /* * Compute the product of the transform matrices of the given node * and its parents. */ void SG_GetNodeTransform(void *p, M_Matrix44 *T) { SG_Node *node = p; SG_Node *chld = node; TAILQ_HEAD(,sg_node) rnodes = TAILQ_HEAD_INITIALIZER(rnodes); M_MatIdentity44v(T); while (chld != NULL) { TAILQ_INSERT_TAIL(&rnodes, chld, rnodes); if (OBJECT(chld)->parent == NULL || !AG_OfClass(OBJECT(chld)->parent, "SG_Node:*")) { break; } chld = OBJECT(chld)->parent; } TAILQ_FOREACH(chld, &rnodes, rnodes) { M_MatMult44v(T, &chld->T); } } /* * Compute the product of the inverse transform matrices of the given node * and its parents. */ void SG_GetNodeTransformInverse(void *p, M_Matrix44 *T) { SG_Node *node = p; SG_Node *chld = node; TAILQ_HEAD(,sg_node) rnodes = TAILQ_HEAD_INITIALIZER(rnodes); M_MatIdentity44v(T); while (chld != NULL) { TAILQ_INSERT_TAIL(&rnodes, chld, rnodes); if (OBJECT(chld)->parent == NULL || !AG_OfClass(OBJECT(chld)->parent, "SG_Node:*")) { break; } chld = OBJECT(chld)->parent; } TAILQ_FOREACH(chld, &rnodes, rnodes) { M_Matrix44 Tinv; Tinv = M_MatInvert44p(&chld->T); M_MatMult44v(T, &Tinv); } } /* Return the world coordinates of a node. */ M_Vector3 SG_NodePos(void *p) { SG_Node *node = p; M_Matrix44 T; M_Vector3 v = M_VecGet3(0.0, 0.0, 0.0); SG_GetNodeTransformInverse(node, &T); M_MatMult44Vector3v(&v, &T); return (v); } /* Return the world direction of a node. */ M_Vector3 SG_NodeDir(void *p) { SG_Node *node = p; M_Matrix44 T; M_Vector3 v = M_VecK3(); /* Convention */ SG_GetNodeTransform(node, &T); M_MatMult44Vector3v(&v, &T); return (v); } /* Graphically render a node and its children. */ void SG_RenderNode(SG *sg, SG_Node *node, SG_View *view) { M_Matrix44 Tsave, T; SG_Node *chld; GL_FetchMatrixv(GL_MODELVIEW_MATRIX, &Tsave); T = M_MatTranspose44p(&node->T); /* OpenGL is column-major */ GL_MultMatrixv(&T); if (NODE_OPS(node)->draw != NULL) { NODE_OPS(node)->draw(node, view); } OBJECT_FOREACH_CLASS(chld, node, sg_node, "SG_Node:*") { SG_RenderNode(sg, chld, view); } GL_LoadMatrixv(&Tsave); } AG_ObjectClass sgClass = { "SG", sizeof(SG), { 1,0 }, Init, NULL, /* reinit */ NULL, /* destroy */ Load, Save, SG_Edit };