Appendix B. Cosmo 3D Sample Application

This appendix discusses a simple Cosmo 3D application, called cube.cxx. The files are in the following locations:

 

Source

Executable

UNIX

/usr/share/Optimizer/src/apps/Cosmo3D directory

/usr/sbin

NT

C:\Program Files\Silicon Graphics\Optimizer\src\apps\cosmo3D

C:\Program Files...\Optimizer\bind

In cube.cxx, two cubes, one red the other green, slowly revolve, as shown (statically) in Figure B-1.

This chapter helps you understand the basic structure of a Cosmo3D application, and explores some of the functionality you can implement in your own applications. The remaining chapters in the book describe the classes and concepts presented in this chapter in greater detail.

These are the sections in this chapter:

Cube.cxx Explained

Example B-1 shows the cube.cxx application. It also includes embedded comments (not found in the cube.cxx file) that explain the functionality of each section of cube.cxx. The sections that follow Example B-1 explain the structure and functionality of the code in more generic terms, so that you can understand the principles of programming Cosmo 3D applications.

Example B-1. cube.cxx


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <Cosmo3D/csField.h>
#include <Cosmo3D/csWindow.h>
#include <Cosmo3D/csColorSet.h>
#include <Cosmo3D/csNormalSet.h>
#include <Cosmo3D/csCoordSet.h>
#include <Cosmo3D/csQuadSet.h>
#include <Cosmo3D/csContext.h>
#include <Cosmo3D/csAppearance.h>
#include <Cosmo3D/csPerspCamera.h>
#include <Cosmo3D/csOrthoCamera.h>
#include <Cosmo3D/csTransform.h>
#include <Cosmo3D/csDrawAction.h>
#include <Cosmo3D/csShape.h>
#include <Cosmo3D/csPointLight.h>
#include <Cosmo3D/csMaterial.h>
#include <Cosmo3D/csEnvironment.h>

static csGroup* makeCube();

static csTransform* fgTransform;
static csTransform* bgTransform;
static csAppearance* highlight;
static csGroup* root;
static csDrawAction* da;
static csContext* ctx;

static int doFlash = 0;
static int doBorders = 1;

int frame(void*);

//Start the application here

int
main(int argc, char *argv[])
{
    int doOrthoCam = 0, doFullScreen = 0;

    if(argc > 1)
    {
        argv++;
    argc--;
        while(argc > 0)
    {
        if(argv[0][0] == '-')
            switch(argv[0][1])
        {
            case 'f':
                doFlash = 1;
            break;
            case 'F':
                doFullScreen = 1;
            doBorders = 0;
            break;
            case 'o':
                doOrthoCam = 1;
            break;
        }

        argv++;
        argc--;
    }
    }
    // Initialize Cosmo3D
    csObject::initClasses();

    // define scene 
    root = makeCube();

    // define render window 
    if (doFullScreen) {
        csWindow::initPosition(0, 0);
        csWindow::initSize(csWindow::get(csWindow::SCREEN_WIDTH),
                       csWindow::get(csWindow::SCREEN_HEIGHT));
    }
    new csWindow("cube");

    // define rendering context 
    csContext *ctx = csWindow::getContext();
    ctx->setDepthEnable(TRUE);
    ctx->setDepthFunc(csContext::LEQUAL_DFUNC);
    ctx->setCullFace(csContext::BACK_CULL);

    //Set up the camera

    csCamera *cam;
    if (doOrthoCam)
    {
    csOrthoCamera *orthoCam = new csOrthoCamera;
        orthoCam->setWidth(4.0f);
        orthoCam->setHeight(4.0f);
        cam = orthoCam;
    } 
    else
    {
    csPerspCamera *perspCam = new csPerspCamera;
        cam = perspCam;
    }

    da = new csDrawAction;
    da->setCamera(cam);

    cam->draw(da);
    
    csWindow::setFrameFunc(&frame, da);
    csWindow::mainLoop();
    
    return 0;
}

int
frame(void*)
{
    static int frame = 0;

    const csEventArray &elist = csWindow::getCurrent()->getEvents();
    for (int i=0; i<elist.getCount(); i++)
    {
    if (elist[i]->id == csEvent::KEY_PRESS) {
        switch (elist[i]->key) {
        case 'b':
            doBorders = !doBorders;
        csWindow::setWindowTitle(doBorders? "cube" : NULL);
            break;
        case 's':
        csWindow::reshapeWindow(300, 300);
            break;
        case 'S':
        csWindow::reshapeWindow(600, 600);
            break;
        case 27: // ESC
            exit(1);
        }
    }
    }

    if(frame % 30 == 0 && doFlash)
    ctx->pushOverrideAppearance(highlight);
    else if(frame % 30 == 15 && doFlash)
    ctx->popOverrideAppearance();

    // clear the window 
    ctx->clear(csContext::COLOR_CLEAR | csContext::DEPTH_CLEAR);

    // update cube rotations 
    bgTransform->setRotation(1.0f, 1.0f, 0.0f, CS_DEG2RAD(frame));
    fgTransform->setRotation(0.5f, 0.1f, 1.0f, CS_DEG2RAD(frame));

    // draw the scene 
    da->apply(root);

    // swap buffers 
    csWindow::swapBuffers();
    frame++;
 
    return csWindow::CONTINUE;
}

//Create a cube; the cube will be rendered twice.

static csGroup*
makeCube()
{
    // int i;
    static float cubeCoords[24][3] = 
    {
    {-1.0f, -1.0f,  1.0f}, { 1.0f, -1.0f,  1.0f},   // +Z 
    { 1.0f,  1.0f,  1.0f}, {-1.0f,  1.0f,  1.0f},   // +Z 
    {-1.0f, -1.0f, -1.0f}, {-1.0f,  1.0f, -1.0f},   // -Z 
    { 1.0f,  1.0f, -1.0f}, { 1.0f, -1.0f, -1.0f},   // -Z 
    { 1.0f, -1.0f,  1.0f}, { 1.0f, -1.0f, -1.0f},   // +X 
    { 1.0f,  1.0f, -1.0f}, { 1.0f,  1.0f,  1.0f},   // +X 
    {-1.0f, -1.0f,  1.0f}, {-1.0f,  1.0f,  1.0f},   // -X 
    {-1.0f,  1.0f, -1.0f}, {-1.0f, -1.0f, -1.0f},   // -X 
    {-1.0f,  1.0f,  1.0f}, { 1.0f,  1.0f,  1.0f},   // +Y 
    { 1.0f,  1.0f, -1.0f}, {-1.0f,  1.0f, -1.0f},   // +Y 
    {-1.0f, -1.0f,  1.0f}, {-1.0f, -1.0f, -1.0f},   // -Y 
    { 1.0f, -1.0f, -1.0f}, { 1.0f, -1.0f,  1.0f}    // -Y 
    };
    static int numCubeCoords = sizeof(cubeCoords)/sizeof(cubeCoords[0]);

    static float cubeNorms[6][3] = 
    {
    { 0.0f,  0.0f,  1.0f},  // +Z 
    { 0.0f,  0.0f, -1.0f},  // -Z 
    { 1.0f,  0.0f,  0.0f},  // +X 
    {-1.0f,  0.0f,  0.0f},  // -X 
    { 0.0f,  1.0f,  0.0f},  // +Y 
    { 0.0f, -1.0f,  0.0f}   // -Y 
    };
    static int numCubeNorms = sizeof(cubeNorms)/sizeof(cubeNorms[0]);

// Specify the data attributes

    // specify cube as 6 quads

    csQuadSet *gset = new csQuadSet;

    // cube vertices 
    csCoordSet3f *cset = new csCoordSet3f(numCubeCoords);
    cset->point()->edit();
#if 0
    for (i=0; i<numCubeCoords; i++)
        cset->point()->set(i, 
        csVec3f(cubeCoords[i][0], cubeCoords[i][1], cubeCoords[i][2])); 
#else
    cset->point()->setRange(0, numCubeCoords, (csVec3f *)cubeCoords);
#endif
    cset->point()->editDone();
    gset->setCoordSet(cset);

    // cube normals 
    csNormalSet3f *nset = new csNormalSet3f(numCubeNorms);
    nset->vector()->edit();
#if 0
    for (i=0; i<numCubeNorms; i++)
        nset->vector()->set(i, 
        csVec3f(cubeNorms[i][0], cubeNorms[i][1], cubeNorms[i][2])); 
#else
    nset->vector()->setRange(0, numCubeNorms, (csVec3f *)cubeNorms);
#endif
    nset->vector()->editDone();
    gset->setNormalSet(nset);

    gset->setPrimCount(6);
    gset->setNormalBind(csGeoSet::PER_PRIM_NORMAL);

//Specify the appearance and material attributes.

    // highlight, yellow. 
    csMaterial *hlMaterial = new csMaterial;
    hlMaterial->setSpecularColor(0.0f, 0.0f, 0.0f);
    hlMaterial->setDiffuseColor(1.0f, 1.0f, 0.0f);
    hlMaterial->setShininess(.0078125 *16.0f);
    hlMaterial->setTransparency(0.0f);

    highlight = new csAppearance;
    highlight->setMaterial(hlMaterial);
    highlight->setLightEnable(1);
    // red cube 
    csMaterial *redMaterial = new csMaterial;
    redMaterial->setSpecularColor(1.0f, 1.0f, 1.0f);
    redMaterial->setDiffuseColor(0.8f, 0.1f, 0.1f);
    redMaterial->setShininess(.0078125 *16.0f);
    redMaterial->setTransparency(0.5f);

    csAppearance *redAppearance = new csAppearance;
    redAppearance->setMaterial(redMaterial);
    redAppearance->setLightEnable(1);
    redAppearance->setTranspMode(csContext::BLEND_TRANSP);
    redAppearance->setTranspEnable(1);

    csShape *redShape = new csShape;
    redShape->setAppearance(redAppearance);
    redShape->setGeometry(0, gset);

    bgTransform = new csTransform;
    bgTransform->setTranslation(0.0f, 0.0f, -5.0f);
    bgTransform->addChild(redShape);

    // green cube 
    csMaterial *greenMaterial = new csMaterial;
    greenMaterial->setDiffuseColor(0.1f, 0.8f, 0.1f);
    greenMaterial->setShininess(.0078125 *16.0f);
    greenMaterial->setTransparency(0.5f);

    csAppearance *greenAppearance = new csAppearance;
    greenAppearance->setMaterial(greenMaterial);
    greenAppearance->setLightEnable(1);
    greenAppearance->setTranspMode(csContext::BLEND_TRANSP);
    greenAppearance->setTranspEnable(1);

    csShape *greenShape = new csShape;
    greenShape->setAppearance(greenAppearance);
    greenShape->setGeometry(0, gset);

    fgTransform = new csTransform;
    fgTransform->setTranslation(0.5f, 0.1f, -6.0f);
    fgTransform->addChild(greenShape);

    // environment 
    csPointLight *lt = new csPointLight;
    csEnvironment *environment = new csEnvironment;
    environment->light()->append(lt);
    environment->addChild(fgTransform);
    environment->addChild(bgTransform);

    return environment;
}

Understanding the Different Parts of Cube.cxx

The embedded comments in Example B-1 call out the different functional parts of cube.cxx, which include:

Scene Graph for Cube.cxx

Scene graphs provide the structure for Cosmo 3D applications. Cosmo 3D applications use scene graphs to specify the objects rendered. Figure B-2 shows the scene graph used for cube.cxx.

Figure B-2. Cube Scene Graph

Figure B-2 Cube Scene Graph

csShape nodes define a shape; they associate a csAppearance, which describes the look of a shape (such as its color), with a csGeometry, which defines the dimensions of the geometry (such as whether the geometry is a cube or sphere).

In cube.cxx, csGeometry defines a cube and the csAppearance nodes specify the green and red colors of the cubes.


Note: Neither csGeometry nor csAppearance are nodes; they are classes associated by a csShape node.


Relating Local Space to World Space

Once you define the orientation of a shape, you use csTransform nodes to place and orient the shape in a different coordinate system. World space is the coordinate system of the root node. If all the shapes in a scene graph are transformed into world space, a csCamera object attached to the root node can view all the shapes in the scene graph together in one coordinate system.

Objects in world space are rendered when a draw action is applied to the root node of the scene graph. Objects in local space are rendered when a draw action is applied to a subsection of the scene graph. The same object rendered in these two spaces may appear different, for example, a shape in world space may appear smaller than in local space because it is farther from the viewer; it might also be rotated and positioned differently.

Generally, there are many transformation nodes in a scene graph and a shape is often transformed more than once, as shown in Figure B-3.

Figure B-3. Two Transformations Into World Space

Figure B-3 Two Transformations Into World Space

In Figure B-3, after the leaf node is transformed twice, it is placed in world space.

In cube.cxx, two transformation nodes transform the shapes into world space.

Creating the User Interface

csWindow encapsulates the user interface: it includes the methods you use to construct a window in which a Cosmo 3D application runs. csWindow manages a csContext object to control the graphics context as well as a csEvent object that handles user actions, like mouse events.

csWindow is replete with default values that satisfy most application needs. cube.cxx, for example, uses all of the default values except for the title of the window, which is specified with the InitWindow() method.

Cosmo 3D also allows you to construct your own window using X window code. This option gives you complete control over the look and functionality of the window.

For a complete description of csWindow and using X window code, see Chapter 12, “User Interface Mechanisms.”

Rendering World Space

To render a scene graph, the csDrawAction::apply() method is applied to the root node of the scene graph, as follows:

da->apply(root);

where da is a csDrawAction object.

Summary

The following procedure summarizes the steps you take to create and render a very simple scene graph.

  1. Create csAppearance and csGeometry containers to define the appearance and the geometry of an object. For more information on setting csAppearance values, see Chapter 3, “Specifying the Appearance of Geometries.” For more information on setting csGeometry values, see Chapter 2, “Creating Geometries.”

  2. Relate the csAppearance and csGeometry nodes in a csShape node. For more information on setting csShape values, see Chapter 2, “Creating Geometries.”

  3. Add the csShape nodes as children of the csTransform nodes. The csTransform node orients and positions the csShape objects in world space. For more information on setting csTransform values, see Chapter 6, “Placing Shapes in a Scene.”


    Note: A csShape node by itself can be a complete scene graph. Typically, however, scene graphs have many csShape nodes, most of which are connected to other parts of the scene graph with a csTransform nodes.


  4. Add the csTransform nodes to the scene graph. For more information about adding nodes to scene graphs, see Chapter 6, “Placing Shapes in a Scene.”

  5. Create a window, csWindow, in which to view and interact with the application.

  6. Set the current graphical context, csContext.

  7. Draw all of the shapes in world space by applying a csDrawAction to the root of the scene graph. For more information about draw actions, see Chapter 7, “Traversing the Scene Graph.”