DI-Guy Graphics API, diguyOsgGraphicsShape

diguyOsgGraphicsShape.cpp

DI-Guy API Version 12.5.1

This file was automatically generated from diguyOsgGraphicsShape.cpp. Do not edit this file directly; the changes will be lost.
Introduction
Shapes are the high-level containers of visible geometry. They are attached to links, and contain zero or more mesh objects.

The class diguyOsgGraphicsShape is an example of a diguyGraphicsShape subclass for a scene graph environment using the DI-Guy Loader. The example subclass shows how to use the accessors of the diguyGraphicsShape class and which virtual functions should be overridden for a scene graph environment such as OSG.

Header files and forward declarations
#include "diguyOsgGraphicsShape.h"
#include "diguyOsgGraphicsLink.h"
#include "diguyOsgGraphicsMesh.h"

#include <osg/Drawable>
#include <osg/StateSet>
#include <osg/TexEnv>
#include <osg/Texture2D>

#include <libbdilog.h>
#include <diguyGraphicsTexture.h>
#include <diguyCharacter.h>
#include <diguyScenario.h>
#include <cstring>
#include <assert.h>

/*
 *  Top-level static variable that determines whether LOD calculations are
 *   based strictly on distance of object from camera, or on size of object,
 *   in pixels, in frame.
 */
bool diguyOsgGraphicsShape::s_use_pixel_lods = false;

/*
 *  String to use for the uniform array for bones.
 *   The value of the string ("ufrm_link_matrices" or "ufrm_link_matrices[0]")
 *   is determined in the diguyOsgGraphicsShaderProgram to work around
 *   a bug with name of uniform arrays in OSG.
 */
char* diguyOsgGraphicsShape::s_ufrm_link_matrices_string = NULL;
char* diguyOsgGraphicsShape::s_ufrm_link_colors_string = NULL;
Constructor
Description:

Initialize data members of the class to a known state.

Base class data will not be fully initialized until the Build Stage is done, so most base class accessors should not be called here.

diguyOsgGraphicsShape::diguyOsgGraphicsShape(void* internal_data)
    :
    diguyGraphicsShape(internal_data),
    m_switch_node(NULL),
    m_lod_node(NULL),
    m_manual_lod_switch_node(NULL),
    m_ufrm_link_matrices(NULL),
    m_ufrm_link_colors(NULL)
{
}
build
Build Stage

Description:

Override of diguyGraphicsShape::build().

The build() function should create the necessary objects that will apply offset data of this shape from its parent link during scene graph traversal. It may also need to create objects for level-of-detail switching.

This implementation creates OSG nodes that will serve as attachment points between the meshes that contain visible geometry and the links to which they will be attached.

void
diguyOsgGraphicsShape::build()
{
    int lod;

    diguyGraphicsLink* diguy_link = get_link();
    diguyOsgGraphicsLink* osg_link = (diguyOsgGraphicsLink *) diguy_link;

    osg::MatrixTransform* link_dof_node = osg_link->get_dof_node();

    if (!link_dof_node)
        return;

    /*
     *  By default we'll attach meshes to the link's dof node.  If this
     *   shape is switched, however (e.g. for switchable hands), we'll create
     *   a switch node and attach them to the switch.
     */
    osg::ref_ptr<osg::Group> lod_attach_node = link_dof_node;

    /*
     *  Check if there's a per shape variation texture to bind. This texture 
     *  modifies the color of various pieces of the character.
     */
    unsigned char * variation_texture = get_variation_texture();
    if (variation_texture)
    {
        /*
         * Add a new node to the hierarchy that is the child of the link
         * and holds the unique ramp texture for this mesh. 
        */
        osg::ref_ptr<osg::Group> shape_node = new osg::Group();
        lod_attach_node->addChild(shape_node);
        
        /*
         *  Change the LOD attach node to this new group so that having the texture 
         *  propagates down the render graph.
         */
        lod_attach_node = shape_node;

        int width = get_variation_texture_size();
        int height = 1;
        GLenum pixel_format = GL_RGBA;

        GLint internal_texture_format = GL_RGBA;

        /*
         *  Make a copy of the texture map data.  The osg image will take
         *   ownership of it.
         */
        
        unsigned char* texture_map_data_copy = new unsigned char [width*4];
        memcpy(texture_map_data_copy, variation_texture, sizeof(unsigned char)*width*4);

        /*
         *  Create an osg image.
         */
        osg::ref_ptr<osg::Image> osg_image = new osg::Image();

        osg_image->setImage(width,
            height,
            1,
            internal_texture_format,
            pixel_format,
            GL_UNSIGNED_BYTE,
            texture_map_data_copy,
            osg::Image::USE_NEW_DELETE,   // osg will deallocate texture_map_data_copy with call to delete
            4);                           // pack/unpack alignment

        /*
         *  Create a texture object, and load it with the osg::Image created
         *   above.
         */
        osg::ref_ptr<osg::Texture2D> m_texture;
        m_texture = new osg::Texture2D();

        // not sure this is needed
        m_texture->setUnRefImageDataAfterApply(true);

        m_texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP);
        m_texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP);

        m_texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
        m_texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);

        m_texture->setResizeNonPowerOfTwoHint(true);
        m_texture->setImage(osg_image);

        /*
         *  Setup the texture unit and turn the texture on.
         */
        osg::ref_ptr<osg::StateSet> texture_state_set = new osg::StateSet;

        texture_state_set->setTextureAttribute(DIGUY_TEXTURE_MAP_TYPE_COMPONENT_RAMP, m_texture);

        texture_state_set->setTextureMode(DIGUY_TEXTURE_MAP_TYPE_COMPONENT_RAMP,
            GL_TEXTURE_2D,
            osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
        lod_attach_node->setStateSet(texture_state_set);
    }

    /*
     *  See if we should create a switch.
     */
    if (get_switch_var_name() != NULL)
    {
        m_switch_node = new osg::Switch();
        m_switch_node->setNewChildDefaultValue(true);
        link_dof_node->addChild(m_switch_node);

        lod_attach_node = m_switch_node;
    }

    /*
     *  Determine whether automatic graphics LOD switching is enabled.  This
     *   will determine which kind of node we create for LOD switching.
     */
    int automatic_graphics_lod_switching = 1;
    diguyCharacter* character = get_character();
    if (character)
    {
        diguyScenario* scenario = character->get_scenario();
        automatic_graphics_lod_switching = scenario->get_automatic_graphics_lod_switching();
    }

    /*
     *  If automatic switching is enabled, create an osg::LOD() node.
     */
    if (automatic_graphics_lod_switching)
    {
        /*
         *  Determine the lod_ranges.
         */
        float* lod_ranges = get_lod_ranges();

        /*
         *  Create an lod node.
         */
        m_lod_node = new osg::LOD();
        m_lod_node->setCenter(osg::Vec3f(0.0f, 0.0f, 0.0f));

        if (s_use_pixel_lods)
            m_lod_node->setRangeMode(osg::LOD::PIXEL_SIZE_ON_SCREEN);
        else
            m_lod_node->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);

        lod_attach_node->addChild(m_lod_node);

        /*
         *  Create a geode for each lod.  Attach mesh drawables to geodes.
         */
        for (lod=1; lod<=get_num_lods(); lod++)
        {
            osg::Geode* geode = new osg::Geode();

            if (s_use_pixel_lods)
            {
                /*
                 *  Because DI-Guy LOD ranges must increase at each index,
                 *   but unlike with distances the earlier numbers represent
                 *   low LODs, we have to reverse the order of lod ranges.
                 *
                 *  LOD 1: lod_ranges[6] to lod_ranges[7]
                 *  LOD 2: lod_ranges[5] to lod_ranges[6]
                 *  etc.
                 */
                m_lod_node->addChild(geode, lod_ranges[7 - lod], lod_ranges[8 - lod]);
            }
            else
            {
                /*
                 *  LOD 1: lod_ranges[0] to lod_ranges[1]
                 *  LOD 2: lod_ranges[1] to lod_ranges[2]
                 *  etc.
                 */
                m_lod_node->addChild(geode, lod_ranges[lod-1], lod_ranges[lod]);
            }

            int num_meshes = get_num_meshes(lod);
            int mesh_index;

            for (mesh_index=0; mesh_index<num_meshes; mesh_index++)
            {
                diguyGraphicsMesh* diguy_mesh;
                diguyOsgGraphicsMesh* diguy_osg_mesh;

                diguy_mesh = get_mesh_at_index_for_lod(mesh_index, lod);
                diguy_osg_mesh = (diguyOsgGraphicsMesh*) diguy_mesh;

                if (diguy_osg_mesh)
                {
                    osg::Drawable* drawable = (osg::Drawable*) diguy_osg_mesh->get_drawable();
                    geode->addDrawable(drawable);
                }
            }
        }
    }

    /*
     *  Create a geode for each lod.  Attach mesh drawables to geodes.
     */
    else
    {
        m_manual_lod_switch_node = new osg::Switch();
        m_manual_lod_switch_node->setNewChildDefaultValue(false);

        lod_attach_node->addChild(m_manual_lod_switch_node);

        /*
         *  Create a geode for each lod.  Attach mesh drawables to geodes.
         */
        for (lod=1; lod<=get_num_lods(); lod++)
        {
            osg::Geode* geode = new osg::Geode();

            m_manual_lod_switch_node->addChild(geode);

            int num_meshes = get_num_meshes(lod);
            int mesh_index;

            for (mesh_index=0; mesh_index<num_meshes; mesh_index++)
            {
                diguyGraphicsMesh* diguy_mesh;
                diguyOsgGraphicsMesh* diguy_osg_mesh;

                diguy_mesh = get_mesh_at_index_for_lod(mesh_index, lod);
                diguy_osg_mesh = (diguyOsgGraphicsMesh*) diguy_mesh;

                if (diguy_osg_mesh)
                {
                    /*
                     *  Some meshes can have multiple graphics states
                     *   associated with them (e.g., everything in the mesh
                     *   is that same except that it may have multiple texture
                     *   options).
                     *
                     *  Determine which graphics state we should use for
                     *   meshes attached to this shape.
                     */
                    int graphics_state_switch_index = get_graphics_state_switch_index();
                    osg::Drawable* drawable = (osg::Drawable*) diguy_osg_mesh->get_drawable(graphics_state_switch_index);
                    geode->addDrawable(drawable);
                }
            }

            m_manual_lod_switch_node->setSingleChildOn(get_graphics_lod() - 1);
        }
    }

    
    
}
unbuild
Unbuild Stage

Description:

Override of diguyGraphicsShape::unbuild().

The unbuild() function should destroy the objects created in the build() function.

Deref the nodes created in build().

void
diguyOsgGraphicsShape::unbuild()
{
    const char* name = get_name();

    diguyGraphicsLink* diguy_link = get_link();
    diguyOsgGraphicsLink* osg_link = (diguyOsgGraphicsLink *) diguy_link;

    osg::MatrixTransform* link_dof_node = osg_link->get_dof_node();

    if (!link_dof_node)
        return;

    osg::Group* lod_detach_node = link_dof_node;

    /*
     *  Remove all of the group nodes we created and added in the build()
     *   call; this should reduce the ref count of the added nodes by 1, to
     *   1.  Then set the pointers to NULL; this should reduce the ref count
     *   to 0, resulting in the group nodes being deleted.
     *
     *  Setting the m_switch_node ref_ptr to NULL dereferences the Switch
     *   object it points to, possibly deleting it.  Ditto for m_lod_node and
     *   m_manual_lod_switch_node.
     */
    if (m_switch_node != NULL)
        lod_detach_node = m_switch_node;

    if (m_lod_node != NULL)
    {
        lod_detach_node->removeChild(m_lod_node);
        m_lod_node = NULL;
    }
    else if (m_manual_lod_switch_node != NULL)
    {
        lod_detach_node->removeChild(m_manual_lod_switch_node);
        m_manual_lod_switch_node = NULL;
    }

    if (m_switch_node != NULL)
    {
        link_dof_node->removeChild(m_switch_node);
        m_switch_node = NULL;
    }
}
show
Update Stage

Description:

Override of diguyGraphicsShape::show().

Change switch node state to show attached children.

void
diguyOsgGraphicsShape::show()
{
    if (m_switch_node)
        m_switch_node->setAllChildrenOn();
}
hide
Update Stage

Description:

Override of diguyGraphicsShape::hide().

Change switch node state to hide attached children.

void
diguyOsgGraphicsShape::hide()
{
    if (m_switch_node)
        m_switch_node->setAllChildrenOff();
}
update
Update Stage

Description:

Override of diguyGraphicsShape::update().

Send skinned uniforms to shader.

void
diguyOsgGraphicsShape::update()
{
    int num_matrices = 0;
    const float* matrix_data = NULL;

    get_shader_matrix_data(&num_matrices, &matrix_data);

    osg::StateSet* shader_state_set = NULL;

    if ((num_matrices > 0) && matrix_data)
    {
        /*
         *  Ideally this would happen at the build stage, but we don't know
         *   the num of matrices yet at that point and we want to avoid
         *   allocating the max num every time.
         */
        if (!m_ufrm_link_matrices && s_ufrm_link_matrices_string)
        {
            shader_state_set = new osg::StateSet();

            /*
             *  Adds a per shape set of uniforms that feed skinned data to the
             *   underlying shader.
             */
            m_ufrm_link_matrices = new osg::Uniform(osg::Uniform::FLOAT_MAT4,
                s_ufrm_link_matrices_string,
                num_matrices);

            shader_state_set->addUniform(m_ufrm_link_matrices);

            /*
             *  Propagate state change to children.
             */
            if (m_lod_node)
                m_lod_node->setStateSet(shader_state_set);

            if (m_manual_lod_switch_node)
                m_manual_lod_switch_node->setStateSet(shader_state_set);

            if (m_switch_node)
                m_switch_node->setStateSet(shader_state_set);
        }

        /*
         *  Update the matrices.
         */
        if (m_ufrm_link_matrices != NULL)
        {
            int i;
            for (i=0; i<num_matrices; i++)
            {
                m_ufrm_link_matrices->setElement(i, osg::Matrixf(matrix_data));
                matrix_data += 16;   // 16 floats per matrix
            }
        }
    }

    int num_colors = 1;
    const float* color_data = NULL;

    const int isSkinned = get_contains_skinned_file();
    if (isSkinned)
    {
        if (get_bind_color_array(&num_colors, &color_data) == -1)
        {
            return;
        }
    }

    if (!m_ufrm_link_colors && s_ufrm_link_colors_string)
    {
        /*
        *  Adds a per shape set of uniforms that feed skinned data to the
        *   underlying shader.
        */
        if (!shader_state_set)
        {
            shader_state_set = new osg::StateSet();
        }

        m_ufrm_link_colors = new osg::Uniform(osg::Uniform::FLOAT_VEC4,
            s_ufrm_link_colors_string,
            num_colors);

        shader_state_set->addUniform(m_ufrm_link_colors);
        
        /*
        *  Propagate state change to children.
        */
        if (m_lod_node)
            m_lod_node->setStateSet(shader_state_set);
        
        if (m_manual_lod_switch_node)
            m_manual_lod_switch_node->setStateSet(shader_state_set);
        
        if (m_switch_node)
            m_switch_node->setStateSet(shader_state_set);
    }

    if (m_ufrm_link_colors)
    {
        if (isSkinned)
        {
            for (int i = 0; i < num_colors; ++i)
            {
                m_ufrm_link_colors->setElement(i,
                    osg::Vec4(color_data[0], color_data[1], color_data[2], color_data[3]));
                color_data += 4;
            }
        }
        else
        {
            unsigned char r, g, b;

            diguyGraphicsLink* link = get_link();
            link->get_unique_color(&r, &g, &b);
            m_ufrm_link_colors->setElement(0, osg::Vec4(r/255.0f, g/255.0f, b/255.0f, 1));
        }
    }
}
set_graphics_lod_ranges
Description:

Override of diguyGraphicsShape::set_graphics_lod_ranges(), called as needed.

The set_graphics_lod_ranges() function should update the ranges of lod objects created in the build() function with the passed ranges.

In this implementation the ranges of the osg::LOD created in build() are updated. If automatic LOD switching is enabled, the LOD node will not exist and this function will have no effect.

void
diguyOsgGraphicsShape::set_graphics_lod_ranges(float* lod_ranges)
{
    if (m_lod_node != NULL)
    {
        int lod;
        for (lod=1; lod<=get_num_lods(); lod++)
        {
            m_lod_node->setRange(lod-1, lod_ranges[lod-1], lod_ranges[lod]);
        }
    }
}
set_graphics_lod
Description:

Override of diguyGraphicsShape::set_graphics_lod(), called as needed.

The set_graphics_lod() function should select and show the object representing the passed LOD.

This implementation manually sets the osg::Switch created in build() to show the correct geode. If automatic LOD switching is enabled, the switch node will not exist and this function will have no effect.

void
diguyOsgGraphicsShape::set_graphics_lod(int lod)
{
    if (m_manual_lod_switch_node != NULL)
    {
        m_manual_lod_switch_node->setSingleChildOn(lod-1);
    }
}
Create function definition
Create and return an object of a subclass of diguyGraphicsShape. This function will be called whenever DI-Guy requires a shape object.

diguyGraphicsShape*
diguyOsgGraphicsShape_create_func(void* internal_data)
{
    return new diguyOsgGraphicsShape(internal_data);
}



Copyright (C) 1992-2012 Boston Dynamics

ALL RIGHTS RESERVED.

These coded instructions, statements, and computer programs contain unpublished proprietary information of Boston Dynamics and are protected by Copyright Laws of the United States. They may not be used, duplicated, or disclosed in any form, in whole or in part, without the prior written consent from Boston Dynamics.

RESTRICTED RIGHTS LEGEND

Use, duplication, or disclosure by the government is subject to restrictions as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Sofware clause at DFARS 252.227-7013 and/or in similar or successor clauses in the FAR, or the DOD or NASA FAR Supplement, or to subparagraphs (c)(1) and (c)(2) of the Commercial Computer Software--Restricted Rights at 48 CFR 52.227-19, as applicable. Unpublished-rights reserved under the Copyright Laws of the United States.

Contractor/Manufacturer is:

Boston Dynamics/78 Fourth Avenue/Waltham MA 02451.