/*
* Copyright (c) 2005-2015 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.
*/
/*
* FreeSG library initialization and implementation of the base
* Scene Graph (SG) class.
*/
#include
#include
#include
#include
#include
#include
#include
#include
/* Standard FreeSG classes */
void *sgStdClasses[] = {
&sgClass,
&sgNodeClass,
&sgProgramClass,
#ifdef HAVE_CG
&sgCgProgramClass,
#endif
&sgTextureClass,
&sgPaletteClass,
&sgScriptClass,
/* Non-geometrical */
&sgDummyClass,
&sgCameraClass,
&sgLightClass,
/* Reference geometry, editor controls */
&sgGeomClass,
&sgWidgetClass,
&sgPointClass,
&sgLineClass,
&sgCircleClass,
&sgSphereClass,
&sgPlaneClass,
&sgPolygonClass,
&sgTriangleClass,
&sgRectangleClass,
/* BREP objects */
&sgObjectClass,
&sgPolyballClass,
&sgPolyboxClass,
/* Volumetric objects */
&sgVoxelClass,
/* Thin objects */
&sgImageClass,
NULL
};
/* File extension mappings */
const AG_FileExtMapping sgFileExtMap[] = {
{ ".sg", N_("Scene"), &sgClass, 1 },
{ ".sgs", N_("Script"), &sgScriptClass, 1 },
{ ".sgt", N_("Texture"), &sgTextureClass, 1 },
{ ".sgp", N_("Palette"), &sgPaletteClass, 1 },
{ ".sgo", N_("Object"), &sgObjectClass, 0 },
};
const Uint sgFileExtCount = sizeof(sgFileExtMap) / sizeof(sgFileExtMap[0]);
int sgInitedSubsystem = 0;
/* 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)
{
void **cls;
if (sgInitedSubsystem++ > 0)
return;
M_InitSubsystem();
AG_RegisterNamespace("FreeSG", "SG_", "http://freesg.org/");
AG_RegisterFileExtMappings(sgFileExtMap, sgFileExtCount);
for (cls = &sgStdClasses[0]; *cls != NULL; cls++)
AG_RegisterClass(*cls);
/* Initialize GUI facilities for SG_Edit() */
SG_InitGUI();
}
/* Cleanup the SG library. */
void
SG_DestroySubsystem(void)
{
void **cls;
if (--sgInitedSubsystem > 0) {
return;
}
SG_DestroyGUI();
for (cls = &sgStdClasses[0]; *cls != NULL; cls++) {
AG_UnregisterClass(*cls);
}
AG_UnregisterNamespace("FreeSG");
}
/* Create a new SG object. */
SG *
SG_New(void *parent, const char *name, Uint flags)
{
SG *sg;
sg = Malloc(sizeof(SG));
AG_ObjectInitNamed(sg, &sgClass, name);
sg->root = (SG_Node *)SG_PointNew(sg, "_root", NULL);
SG_PointSize(SGPOINT(sg->root), 0);
sg->root->flags |= SG_NODE_HIDE;
OBJECT(sg->root)->flags |= AG_OBJECT_INDESTRUCTIBLE;
if (!(flags & SG_NO_DEFAULT_NODES)) {
sg->def.cam = SG_CameraNew(sg->root, "Camera0");
SG_Translate(sg->def.cam, 0.0, 0.0, 10.0);
SG_CameraSetRotCtrlCircular(sg->def.cam, sg->root);
OBJECT(sg->def.cam)->flags |= AG_OBJECT_INDESTRUCTIBLE;
sg->def.lt[0] = SG_LightNew(sg->root, "Light0");
SG_Translate(sg->def.lt[0], 30.0, 30.0, 30.0);
OBJECT(sg->def.lt[0])->flags |= AG_OBJECT_INDESTRUCTIBLE;
sg->def.lt[1] = SG_LightNew(sg->root, "Light1");
SG_Translate(sg->def.lt[1], -30.0, -30.0, -30.0);
OBJECT(sg->def.lt[1])->flags |= AG_OBJECT_INDESTRUCTIBLE;
}
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;
sg->def.cam = NULL;
sg->def.lt[0] = NULL;
sg->def.lt[1] = NULL;
TAILQ_INIT(&sg->nodes);
}
static int
Save(void *obj, AG_DataSource *ds)
{
SG *sg = obj;
AG_WriteUint32(ds, sg->flags);
return SG_NodeSave(sg, ds, sg->root);
}
static int
Load(void *obj, AG_DataSource *ds, const AG_Version *ver)
{
SG *sg = obj;
sg->flags = (Uint)AG_ReadUint32(ds);
return SG_NodeLoad(sg, ds, NULL);
}
/* Search child nodes by name. */
SG_Node *
SG_SearchNodes(SG_Node *node, const char *name)
{
#ifdef AG_THREADS
SG *sg = node->sg;
#endif
SG_Node *chld, *rnode;
AG_ObjectLock(sg);
OBJECT_FOREACH_CLASS(chld, node, sg_node, "SG_Node:*") {
if (strcmp(OBJECT(chld)->name, name) == 0) {
AG_ObjectUnlock(sg);
return (chld);
} else {
if ((rnode = SG_SearchNodes(chld, name)) != NULL) {
AG_ObjectUnlock(sg);
return (rnode);
}
}
}
AG_ObjectUnlock(sg);
return (NULL);
}
/* Search entire graph for a node by name. */
void *
SG_FindNode(SG *sg, const char *name)
{
void *rv;
AG_ObjectLock(sg);
if (strcmp(name, OBJECT(sg->root)->name) == 0) {
rv = sg->root;
} else {
rv = (void *)SG_SearchNodes(sg->root, name);
}
AG_ObjectUnlock(sg);
return (rv);
}
static void
ClearNodes(AG_Object *obj)
{
AG_Object *cob, *ncob;
AG_ObjectLock(obj);
for (cob = TAILQ_FIRST(&obj->children);
cob != TAILQ_END(&obj->children);
cob = ncob) {
ncob = TAILQ_NEXT(cob, cobjs);
ClearNodes(cob);
}
AG_ObjectUnlock(obj);
AG_ObjectDetach(obj);
AG_ObjectDestroy(obj);
}
/*
* Clear the scene, destroying all nodes (except the default nodes which
* are reinitialized).
*/
void
SG_Clear(SG *sg)
{
AG_Object *cob, *ncob;
AG_ObjectLock(sg);
for (cob = TAILQ_FIRST(&OBJECT(sg->root)->children);
cob != TAILQ_END(&OBJECT(sg->root)->children);
cob = ncob) {
ncob = TAILQ_NEXT(cob, cobjs);
if (cob == OBJECT(sg->def.cam) ||
cob == OBJECT(sg->def.lt[0]) ||
cob == OBJECT(sg->def.lt[1])) {
continue;
}
ClearNodes(cob);
}
SG_Identity(sg->def.cam);
SG_Translate(sg->def.cam, 0.0, 0.0, 10.0);
SG_CameraSetRotCtrlCircular(sg->def.cam, sg->root);
SG_Identity(sg->def.lt[0]);
SG_Translate(sg->def.lt[0], 30.0, 30.0, 30.0);
SG_Identity(sg->def.lt[1]);
SG_Translate(sg->def.lt[1], -30.0, -30.0, -30.0);
AG_ObjectUnlock(sg);
}
AG_ObjectClass sgClass = {
"SG",
sizeof(SG),
{ 1,0 },
Init,
NULL, /* reset */
NULL, /* destroy */
Load,
Save,
SG_Edit
};