/*
* 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
};