// Copyright 2006-2007 Nanorex, Inc. See LICENSE file for details. // // Author: Brad Grantham, Nanorex Inc., January 2006 // // Purpose: Provides C API to C++ shape rendering module. Module provides // VBO and vertex array rendering of oriented, scaled, and positioned // spheres and cylinders with screen-distance-based LOD, and rudimentary // surface material. // // Notes // ------ // I implemented a qsort for color sorting, and then elimination of // redundant applyColor, but on 2GHz P4 + FireGL X1 with SRG-Ic Tubes, // the speed difference for just spheres was like 13.7 fps with no // qsort, and 9.5 with qsort! So I just took the sort out. // #include #include #include #include #ifdef _WIN32 /*------------------------------------------------------------*/ #include #include #elif MACOSX /*-------------------------------------------------------------*/ #include #else /* Presumably Linux */ /*---------------------------------------------*/ #include #endif /*-------------------------------------------------------------------*/ #include "bradg.h" #include "vector.h" #include "glextensions.h" template T minimum(T v1, T v2) { return (v1 < v2) ? v1 : v2; } template T clamp(T mini, T v, T maxi) { return (v < mini) ? mini : ((v > maxi) ? maxi : v); } //-------------------------------------------------------------------------- // // Represents the *layout* of an OpenGL vertex array // struct VertexArrayInfo { GLint m_size; GLenum m_type; GLsizei m_stride; GLintptrARB m_offset; void set(GLint size, GLenum type, GLsizei stride, GLintptrARB offset); }; void VertexArrayInfo::set(GLint size, GLenum type, GLsizei stride, GLintptrARB offset) { m_size = size; m_type = type; m_stride = stride; m_offset = offset; } //-------------------------------------------------------------------------- // // Generic OpenGL Data Buffer base class // struct DataBufferBase { unsigned char *m_ptr; DataBufferBase(void *ptr) : m_ptr((unsigned char *)ptr) {}; virtual void bind(GLuint which) = 0; virtual void unbind(GLuint which) = 0; unsigned char *ptr() { return m_ptr; } }; //-------------------------------------------------------------------------- // // Representing OpenGL Vertex Array // struct DataArray : DataBufferBase { DataArray(void *ptr) : DataBufferBase(ptr) {}; virtual void bind(GLuint which) {} virtual void unbind(GLuint which) {} }; //-------------------------------------------------------------------------- // // Representing OpenGL Vertex Buffer Object // struct DataObject : DataBufferBase { GLContext *m_gl; DataObject(GLContext *gl); GLuint m_buffer; virtual void bind(GLuint which) { m_gl->BindBufferARB(which, m_buffer); } virtual void unbind(GLuint which) { m_gl->BindBufferARB(which, 0); } void fill(GLenum which, GLsizeiptrARB size, void *data); }; DataObject::DataObject(GLContext *gl) : DataBufferBase(0), m_gl(gl) { m_gl->GenBuffersARB(1, &m_buffer); } void DataObject::fill(GLenum which, GLsizeiptrARB size, void *data) { bind(which); m_gl->BufferDataARB(which, size, data, GL_STATIC_DRAW_ARB); unbind(which); } //-------------------------------------------------------------------------- // // Contains a DataBuffer and describes the layout of vertex and normal data // within that buffer. Sets state in OpenGL to recognize the vertex and // normal data. // struct VertexData { DataBufferBase *m_data; VertexArrayInfo m_vertexArray; VertexArrayInfo m_normalArray; /* "size" assumed to be 3 */ void unapply(); void apply(); }; #define probe_gl_integerv(thing) \ ({ \ int buf; \ glGetIntegerv(thing, &buf); \ printf(#thing " = %d\n", buf); fflush(stdout); \ }) void VertexData::apply() { m_data->bind(GL_ARRAY_BUFFER_ARB); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(m_vertexArray.m_size, m_vertexArray.m_type, m_vertexArray.m_stride, (void *)(m_data->ptr() + m_vertexArray.m_offset)); glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer(m_normalArray.m_type, m_normalArray.m_stride, (void *)(m_data->ptr() + m_normalArray.m_offset)); } void VertexData::unapply() { glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); m_data->unbind(GL_ARRAY_BUFFER_ARB); } //-------------------------------------------------------------------------- // // Contains a DataBuffer and describes the layout of element (aka "index") // data within that buffer. Provides a function for drawing a // primitive using the element data. // struct ElementData { DataBufferBase *m_data; GLenum m_type; virtual void apply(); virtual void unapply(); void issue(GLenum type, GLuint count, GLsizeiptrARB offset); }; inline void ElementData::issue(GLenum primtype, GLuint count, GLsizeiptrARB offset) { // probe_gl_integerv(GL_ARRAY_BUFFER_BINDING_ARB); // probe_gl_integerv(GL_VERTEX_ARRAY_BUFFER_BINDING_ARB); // probe_gl_integerv(GL_NORMAL_ARRAY_BUFFER_BINDING_ARB); // probe_gl_integerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB); glDrawElements(primtype, count, m_type, (void *)(m_data->ptr() + offset)); } void ElementData::unapply() { m_data->unbind(GL_ELEMENT_ARRAY_BUFFER_ARB); } void ElementData::apply() { m_data->bind(GL_ELEMENT_ARRAY_BUFFER_ARB); } //-------------------------------------------------------------------------- // // A series of indexed OpenGL primitives represented by a vertex array and // element array // // Application must call "prep" or at least perform the same operations as // prep before calling "draw" - the intention is // to sort by "IndexedShape" pointer, then prep once (potentially does // expensive VBO bind) , draw many times, and then unprep once after // that. // struct PrimitiveInfo { GLenum m_type; GLsizeiptr m_offset; int m_count; }; struct IndexedShape { IndexedShape(); VertexData *m_vertexData; ElementData *m_elementData; PrimitiveInfo *m_prims; int m_primCount; bool add(GLenum type, GLsizeiptr offset, int count); void draw(); void prep(); void unprep(); }; IndexedShape::IndexedShape() { m_prims = NULL; m_primCount = 0; } void IndexedShape::prep() { m_vertexData->apply(); m_elementData->apply(); } void IndexedShape::unprep() { m_vertexData->unapply(); m_elementData->unapply(); } inline void IndexedShape::draw() { int i; for(i = 0; i < m_primCount; i++) m_elementData->issue(m_prims[i].m_type, m_prims[i].m_count, m_prims[i].m_offset); } bool IndexedShape::add(GLenum type, GLsizeiptr offset, int count) { m_prims = (PrimitiveInfo *)realloc(m_prims, sizeof(m_prims[0]) * (m_primCount + 1)); if(m_prims == NULL) return false; m_prims[m_primCount].m_type = type; m_prims[m_primCount].m_offset = offset; m_prims[m_primCount].m_count = count; m_primCount++; return true; } //-------------------------------------------------------------------------- // // This buffer grows but doesn't shrink, so presumably after a while the // buffer grows quiescent with regards to allocation. // template struct BigEnoughBuffer { T *m_p; size_t m_count; size_t m_size; size_t m_blocksize; BigEnoughBuffer() : m_p(NULL), m_count(0), m_size(0), m_blocksize(16384) {}; T *make_big_enough(size_t count); operator T*(void) { return m_p; } }; template T *BigEnoughBuffer::make_big_enough(size_t count) { size_t desiredSize = count * sizeof(T); desiredSize = ((desiredSize + m_blocksize - 1) / m_blocksize) * m_blocksize; if(desiredSize > m_size) { T *newp = (T *)realloc(m_p, desiredSize); if(newp == NULL) { return NULL; } m_p = newp; m_size = desiredSize; } m_count = count; return m_p; } // // Classes and routines for sorting object indices by color, in order to // reduce number of colors applied (e.g. reduce number of calls to glMaterial) // struct ColorSortedListHead { float m_color[4]; int m_firstObject; }; // I'll bet there's a really great STL way to do this. BigEnoughBuffer colorSortedListHeads; BigEnoughBuffer colorSortedListIndices; #if 0 // Verify using this function int indices[200000]; ColorSortedListHead heads[200000]; template bool sortByColor(int objectStart, int objectCount, T *objects, int *uniqueColorCount, ColorSortedListHead **listHeads, int **listIndices) { int i; for(i = objectStart; i < objectStart + objectCount; i++) { vec4fCopy(objects[i].m_color, heads[i].m_color); heads[i].m_firstObject = i; indices[i] = -1; } *uniqueColorCount = objectCount; *listHeads = heads; *listIndices = indices; return true; } #else struct Color { float m_color[4]; }; template bool sortByColor(int objectStart, int objectCount, T *objects, int *uniqueColorCount, ColorSortedListHead **listHeads, int **listIndices) { int i, k; int count = 0; ColorSortedListHead *h; if(colorSortedListIndices.make_big_enough(objectStart + objectCount) == NULL) // allocation failed return false; // Naive implementation - could implement this any way you want. // Probably would do good with hashing. // Or Just pass all objects in presorted and skip this! for(i = objectStart; i < objectStart + objectCount; i++) { for(k = 0; k < count; k++) { h = colorSortedListHeads + k; if(h->m_color[0] == objects[i].m_color[0] && h->m_color[1] == objects[i].m_color[1] && h->m_color[2] == objects[i].m_color[2] && h->m_color[3] == objects[i].m_color[3]) break; } if(k == count) { if(colorSortedListHeads.make_big_enough(count + 1) == NULL) // allocation failed return false; h = colorSortedListHeads + k; vec4fCopy(objects[i].m_color, h->m_color); colorSortedListIndices[i] = -1; h->m_firstObject = i; count++; } else { h = colorSortedListHeads + k; colorSortedListIndices[i] = h->m_firstObject; h->m_firstObject = i; } } *uniqueColorCount = count; *listHeads = colorSortedListHeads.m_p; *listIndices = colorSortedListIndices.m_p; return true; } #endif //-------------------------------------------------------------------------- struct Vertex { float n[3]; float v[3]; }; // // Sphere and Cylinder LODs // // XXX The models that follow are built from sphere and cylinder code in // ne-1. The static data *probably* should be replaced with functions // that directly calculate a large series of LODs since the LOD range lookup // has pretty high resolution. As an example, the four cylinder LODs // pop visually because of specular highlighting. And higher res // models would help that. // int sphereTrianglesOffsetCount[][2] = { {0, 60}, {60, 240}, {300, 960}, }; Vertex sphereVertices[] = { {{0.8507f, 0.0000f, 0.5257f}, {0.8507f, 0.0000f, 0.5257f}}, {{0.0000f, 0.5257f, 0.8507f}, {0.0000f, 0.5257f, 0.8507f}}, {{0.0000f, -0.5257f, 0.8507f}, {0.0000f, -0.5257f, 0.8507f}}, {{0.0000f, 0.5257f, -0.8507f}, {0.0000f, 0.5257f, -0.8507f}}, {{-0.8507f, 0.0000f, -0.5257f}, {-0.8507f, 0.0000f, -0.5257f}}, {{-0.5257f, 0.8507f, 0.0000f}, {-0.5257f, 0.8507f, 0.0000f}}, {{0.0000f, -0.5257f, -0.8507f}, {0.0000f, -0.5257f, -0.8507f}}, {{-0.8507f, 0.0000f, 0.5257f}, {-0.8507f, 0.0000f, 0.5257f}}, {{-0.5257f, -0.8507f, 0.0000f}, {-0.5257f, -0.8507f, 0.0000f}}, {{0.8507f, 0.0000f, -0.5257f}, {0.8507f, 0.0000f, -0.5257f}}, {{0.5257f, 0.8507f, 0.0000f}, {0.5257f, 0.8507f, 0.0000f}}, {{0.5257f, -0.8507f, 0.0000f}, {0.5257f, -0.8507f, 0.0000f}}, {{0.5000f, 0.3090f, 0.8090f}, {0.5000f, 0.3090f, 0.8090f}}, {{0.5000f, -0.3090f, 0.8090f}, {0.5000f, -0.3090f, 0.8090f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.0000f, 0.0000f, 1.0000f}}, {{-0.5000f, 0.3090f, -0.8090f}, {-0.5000f, 0.3090f, -0.8090f}}, {{-0.3090f, 0.8090f, -0.5000f}, {-0.3090f, 0.8090f, -0.5000f}}, {{-0.8090f, 0.5000f, -0.3090f}, {-0.8090f, 0.5000f, -0.3090f}}, {{-0.5000f, -0.3090f, -0.8090f}, {-0.5000f, -0.3090f, -0.8090f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.0000f, 0.0000f, -1.0000f}}, {{-1.0000f, 0.0000f, 0.0000f}, {-1.0000f, 0.0000f, 0.0000f}}, {{-0.8090f, -0.5000f, 0.3090f}, {-0.8090f, -0.5000f, 0.3090f}}, {{-0.8090f, -0.5000f, -0.3090f}, {-0.8090f, -0.5000f, -0.3090f}}, {{0.5000f, 0.3090f, -0.8090f}, {0.5000f, 0.3090f, -0.8090f}}, {{0.8090f, 0.5000f, -0.3090f}, {0.8090f, 0.5000f, -0.3090f}}, {{0.3090f, 0.8090f, -0.5000f}, {0.3090f, 0.8090f, -0.5000f}}, {{0.5000f, -0.3090f, -0.8090f}, {0.5000f, -0.3090f, -0.8090f}}, {{1.0000f, 0.0000f, 0.0000f}, {1.0000f, 0.0000f, 0.0000f}}, {{0.8090f, 0.5000f, 0.3090f}, {0.8090f, 0.5000f, 0.3090f}}, {{-0.5000f, -0.3090f, 0.8090f}, {-0.5000f, -0.3090f, 0.8090f}}, {{-0.5000f, 0.3090f, 0.8090f}, {-0.5000f, 0.3090f, 0.8090f}}, {{0.0000f, -1.0000f, 0.0000f}, {0.0000f, -1.0000f, 0.0000f}}, {{-0.3090f, -0.8090f, 0.5000f}, {-0.3090f, -0.8090f, 0.5000f}}, {{0.3090f, -0.8090f, 0.5000f}, {0.3090f, -0.8090f, 0.5000f}}, {{0.0000f, 1.0000f, 0.0000f}, {0.0000f, 1.0000f, 0.0000f}}, {{0.3090f, 0.8090f, 0.5000f}, {0.3090f, 0.8090f, 0.5000f}}, {{-0.3090f, 0.8090f, 0.5000f}, {-0.3090f, 0.8090f, 0.5000f}}, {{0.3090f, -0.8090f, -0.5000f}, {0.3090f, -0.8090f, -0.5000f}}, {{0.8090f, -0.5000f, -0.3090f}, {0.8090f, -0.5000f, -0.3090f}}, {{-0.3090f, -0.8090f, -0.5000f}, {-0.3090f, -0.8090f, -0.5000f}}, {{0.8090f, -0.5000f, 0.3090f}, {0.8090f, -0.5000f, 0.3090f}}, {{-0.8090f, 0.5000f, 0.3090f}, {-0.8090f, 0.5000f, 0.3090f}}, {{0.7020f, 0.1606f, 0.6938f}, {0.7020f, 0.1606f, 0.6938f}}, {{0.7020f, -0.1606f, 0.6938f}, {0.7020f, -0.1606f, 0.6938f}}, {{0.5257f, 0.0000f, 0.8507f}, {0.5257f, 0.0000f, 0.8507f}}, {{0.2629f, 0.1625f, 0.9511f}, {0.2629f, 0.1625f, 0.9511f}}, {{0.2629f, -0.1625f, 0.9511f}, {0.2629f, -0.1625f, 0.9511f}}, {{0.2599f, 0.4339f, 0.8627f}, {0.2599f, 0.4339f, 0.8627f}}, {{0.0000f, 0.2733f, 0.9619f}, {0.0000f, 0.2733f, 0.9619f}}, {{0.2599f, -0.4339f, 0.8627f}, {0.2599f, -0.4339f, 0.8627f}}, {{0.0000f, -0.2733f, 0.9619f}, {0.0000f, -0.2733f, 0.9619f}}, {{-0.2599f, 0.4339f, -0.8627f}, {-0.2599f, 0.4339f, -0.8627f}}, {{-0.1606f, 0.6938f, -0.7020f}, {-0.1606f, 0.6938f, -0.7020f}}, {{-0.4253f, 0.5878f, -0.6882f}, {-0.4253f, 0.5878f, -0.6882f}}, {{-0.6882f, 0.4253f, -0.5878f}, {-0.6882f, 0.4253f, -0.5878f}}, {{-0.5878f, 0.6882f, -0.4253f}, {-0.5878f, 0.6882f, -0.4253f}}, {{-0.7020f, 0.1606f, -0.6938f}, {-0.7020f, 0.1606f, -0.6938f}}, {{-0.8627f, 0.2599f, -0.4339f}, {-0.8627f, 0.2599f, -0.4339f}}, {{-0.4339f, 0.8627f, -0.2599f}, {-0.4339f, 0.8627f, -0.2599f}}, {{-0.6938f, 0.7020f, -0.1606f}, {-0.6938f, 0.7020f, -0.1606f}}, {{-0.7020f, -0.1606f, -0.6938f}, {-0.7020f, -0.1606f, -0.6938f}}, {{-0.5257f, 0.0000f, -0.8507f}, {-0.5257f, 0.0000f, -0.8507f}}, {{-0.2629f, 0.1625f, -0.9511f}, {-0.2629f, 0.1625f, -0.9511f}}, {{-0.2629f, -0.1625f, -0.9511f}, {-0.2629f, -0.1625f, -0.9511f}}, {{0.0000f, 0.2733f, -0.9619f}, {0.0000f, 0.2733f, -0.9619f}}, {{-0.2599f, -0.4339f, -0.8627f}, {-0.2599f, -0.4339f, -0.8627f}}, {{0.0000f, -0.2733f, -0.9619f}, {0.0000f, -0.2733f, -0.9619f}}, {{-0.9619f, 0.0000f, 0.2733f}, {-0.9619f, 0.0000f, 0.2733f}}, {{-0.8627f, -0.2599f, 0.4339f}, {-0.8627f, -0.2599f, 0.4339f}}, {{-0.9511f, -0.2629f, 0.1625f}, {-0.9511f, -0.2629f, 0.1625f}}, {{-0.9511f, -0.2629f, -0.1625f}, {-0.9511f, -0.2629f, -0.1625f}}, {{-0.8507f, -0.5257f, 0.0000f}, {-0.8507f, -0.5257f, 0.0000f}}, {{-0.9619f, 0.0000f, -0.2733f}, {-0.9619f, 0.0000f, -0.2733f}}, {{-0.8627f, -0.2599f, -0.4339f}, {-0.8627f, -0.2599f, -0.4339f}}, {{-0.6938f, -0.7020f, 0.1606f}, {-0.6938f, -0.7020f, 0.1606f}}, {{-0.6938f, -0.7020f, -0.1606f}, {-0.6938f, -0.7020f, -0.1606f}}, {{0.7020f, 0.1606f, -0.6938f}, {0.7020f, 0.1606f, -0.6938f}}, {{0.8627f, 0.2599f, -0.4339f}, {0.8627f, 0.2599f, -0.4339f}}, {{0.6882f, 0.4253f, -0.5878f}, {0.6882f, 0.4253f, -0.5878f}}, {{0.4253f, 0.5878f, -0.6882f}, {0.4253f, 0.5878f, -0.6882f}}, {{0.5878f, 0.6882f, -0.4253f}, {0.5878f, 0.6882f, -0.4253f}}, {{0.2599f, 0.4339f, -0.8627f}, {0.2599f, 0.4339f, -0.8627f}}, {{0.1606f, 0.6938f, -0.7020f}, {0.1606f, 0.6938f, -0.7020f}}, {{0.6938f, 0.7020f, -0.1606f}, {0.6938f, 0.7020f, -0.1606f}}, {{0.4339f, 0.8627f, -0.2599f}, {0.4339f, 0.8627f, -0.2599f}}, {{0.7020f, -0.1606f, -0.6938f}, {0.7020f, -0.1606f, -0.6938f}}, {{0.5257f, 0.0000f, -0.8507f}, {0.5257f, 0.0000f, -0.8507f}}, {{0.2629f, -0.1625f, -0.9511f}, {0.2629f, -0.1625f, -0.9511f}}, {{0.2629f, 0.1625f, -0.9511f}, {0.2629f, 0.1625f, -0.9511f}}, {{0.2599f, -0.4339f, -0.8627f}, {0.2599f, -0.4339f, -0.8627f}}, {{0.9619f, 0.0000f, 0.2733f}, {0.9619f, 0.0000f, 0.2733f}}, {{0.8627f, 0.2599f, 0.4339f}, {0.8627f, 0.2599f, 0.4339f}}, {{0.9511f, 0.2629f, 0.1625f}, {0.9511f, 0.2629f, 0.1625f}}, {{0.9511f, 0.2629f, -0.1625f}, {0.9511f, 0.2629f, -0.1625f}}, {{0.8507f, 0.5257f, 0.0000f}, {0.8507f, 0.5257f, 0.0000f}}, {{0.9619f, 0.0000f, -0.2733f}, {0.9619f, 0.0000f, -0.2733f}}, {{0.6938f, 0.7020f, 0.1606f}, {0.6938f, 0.7020f, 0.1606f}}, {{-0.7020f, -0.1606f, 0.6938f}, {-0.7020f, -0.1606f, 0.6938f}}, {{-0.7020f, 0.1606f, 0.6938f}, {-0.7020f, 0.1606f, 0.6938f}}, {{-0.5257f, 0.0000f, 0.8507f}, {-0.5257f, 0.0000f, 0.8507f}}, {{-0.2629f, -0.1625f, 0.9511f}, {-0.2629f, -0.1625f, 0.9511f}}, {{-0.2629f, 0.1625f, 0.9511f}, {-0.2629f, 0.1625f, 0.9511f}}, {{-0.2599f, -0.4339f, 0.8627f}, {-0.2599f, -0.4339f, 0.8627f}}, {{-0.2599f, 0.4339f, 0.8627f}, {-0.2599f, 0.4339f, 0.8627f}}, {{-0.2733f, -0.9619f, 0.0000f}, {-0.2733f, -0.9619f, 0.0000f}}, {{-0.4339f, -0.8627f, 0.2599f}, {-0.4339f, -0.8627f, 0.2599f}}, {{-0.1625f, -0.9511f, 0.2629f}, {-0.1625f, -0.9511f, 0.2629f}}, {{0.1625f, -0.9511f, 0.2629f}, {0.1625f, -0.9511f, 0.2629f}}, {{0.0000f, -0.8507f, 0.5257f}, {0.0000f, -0.8507f, 0.5257f}}, {{0.2733f, -0.9619f, 0.0000f}, {0.2733f, -0.9619f, 0.0000f}}, {{0.4339f, -0.8627f, 0.2599f}, {0.4339f, -0.8627f, 0.2599f}}, {{-0.1606f, -0.6938f, 0.7020f}, {-0.1606f, -0.6938f, 0.7020f}}, {{0.1606f, -0.6938f, 0.7020f}, {0.1606f, -0.6938f, 0.7020f}}, {{0.0000f, 0.8507f, -0.5257f}, {0.0000f, 0.8507f, -0.5257f}}, {{-0.1625f, 0.9511f, -0.2629f}, {-0.1625f, 0.9511f, -0.2629f}}, {{0.1625f, 0.9511f, -0.2629f}, {0.1625f, 0.9511f, -0.2629f}}, {{-0.2733f, 0.9619f, 0.0000f}, {-0.2733f, 0.9619f, 0.0000f}}, {{0.2733f, 0.9619f, 0.0000f}, {0.2733f, 0.9619f, 0.0000f}}, {{0.4339f, 0.8627f, 0.2599f}, {0.4339f, 0.8627f, 0.2599f}}, {{0.1625f, 0.9511f, 0.2629f}, {0.1625f, 0.9511f, 0.2629f}}, {{-0.1625f, 0.9511f, 0.2629f}, {-0.1625f, 0.9511f, 0.2629f}}, {{0.0000f, 0.8507f, 0.5257f}, {0.0000f, 0.8507f, 0.5257f}}, {{-0.4339f, 0.8627f, 0.2599f}, {-0.4339f, 0.8627f, 0.2599f}}, {{0.1606f, 0.6938f, 0.7020f}, {0.1606f, 0.6938f, 0.7020f}}, {{-0.1606f, 0.6938f, 0.7020f}, {-0.1606f, 0.6938f, 0.7020f}}, {{0.1606f, -0.6938f, -0.7020f}, {0.1606f, -0.6938f, -0.7020f}}, {{0.4253f, -0.5878f, -0.6882f}, {0.4253f, -0.5878f, -0.6882f}}, {{0.6882f, -0.4253f, -0.5878f}, {0.6882f, -0.4253f, -0.5878f}}, {{0.5878f, -0.6882f, -0.4253f}, {0.5878f, -0.6882f, -0.4253f}}, {{0.8627f, -0.2599f, -0.4339f}, {0.8627f, -0.2599f, -0.4339f}}, {{0.4339f, -0.8627f, -0.2599f}, {0.4339f, -0.8627f, -0.2599f}}, {{0.6938f, -0.7020f, -0.1606f}, {0.6938f, -0.7020f, -0.1606f}}, {{-0.4339f, -0.8627f, -0.2599f}, {-0.4339f, -0.8627f, -0.2599f}}, {{-0.5878f, -0.6882f, -0.4253f}, {-0.5878f, -0.6882f, -0.4253f}}, {{-0.6882f, -0.4253f, -0.5878f}, {-0.6882f, -0.4253f, -0.5878f}}, {{-0.4253f, -0.5878f, -0.6882f}, {-0.4253f, -0.5878f, -0.6882f}}, {{-0.1606f, -0.6938f, -0.7020f}, {-0.1606f, -0.6938f, -0.7020f}}, {{0.6882f, 0.4253f, 0.5878f}, {0.6882f, 0.4253f, 0.5878f}}, {{0.5878f, 0.6882f, 0.4253f}, {0.5878f, 0.6882f, 0.4253f}}, {{0.4253f, 0.5878f, 0.6882f}, {0.4253f, 0.5878f, 0.6882f}}, {{0.6938f, -0.7020f, 0.1606f}, {0.6938f, -0.7020f, 0.1606f}}, {{0.5878f, -0.6882f, 0.4253f}, {0.5878f, -0.6882f, 0.4253f}}, {{0.6882f, -0.4253f, 0.5878f}, {0.6882f, -0.4253f, 0.5878f}}, {{0.4253f, -0.5878f, 0.6882f}, {0.4253f, -0.5878f, 0.6882f}}, {{0.8627f, -0.2599f, 0.4339f}, {0.8627f, -0.2599f, 0.4339f}}, {{-0.8627f, 0.2599f, 0.4339f}, {-0.8627f, 0.2599f, 0.4339f}}, {{-0.9511f, 0.2629f, 0.1625f}, {-0.9511f, 0.2629f, 0.1625f}}, {{-0.8507f, 0.5257f, 0.0000f}, {-0.8507f, 0.5257f, 0.0000f}}, {{-0.9511f, 0.2629f, -0.1625f}, {-0.9511f, 0.2629f, -0.1625f}}, {{-0.6938f, 0.7020f, 0.1606f}, {-0.6938f, 0.7020f, 0.1606f}}, {{-0.6882f, 0.4253f, 0.5878f}, {-0.6882f, 0.4253f, 0.5878f}}, {{-0.4253f, 0.5878f, 0.6882f}, {-0.4253f, 0.5878f, 0.6882f}}, {{-0.5878f, 0.6882f, 0.4253f}, {-0.5878f, 0.6882f, 0.4253f}}, {{0.0000f, -0.8507f, -0.5257f}, {0.0000f, -0.8507f, -0.5257f}}, {{0.1625f, -0.9511f, -0.2629f}, {0.1625f, -0.9511f, -0.2629f}}, {{-0.1625f, -0.9511f, -0.2629f}, {-0.1625f, -0.9511f, -0.2629f}}, {{0.9511f, -0.2629f, -0.1625f}, {0.9511f, -0.2629f, -0.1625f}}, {{0.9511f, -0.2629f, 0.1625f}, {0.9511f, -0.2629f, 0.1625f}}, {{0.8507f, -0.5257f, 0.0000f}, {0.8507f, -0.5257f, 0.0000f}}, {{-0.4253f, -0.5878f, 0.6882f}, {-0.4253f, -0.5878f, 0.6882f}}, {{-0.6882f, -0.4253f, 0.5878f}, {-0.6882f, -0.4253f, 0.5878f}}, {{-0.5878f, -0.6882f, 0.4253f}, {-0.5878f, -0.6882f, 0.4253f}}, }; int sphereElements[] = { 0, 1, 2, 3, 4, 5, 4, 3, 6, 7, 4, 8, 9, 3, 10, 9, 6, 3, 0, 9, 10, 7, 2, 1, 8, 11, 2, 3, 5, 10, 10, 5, 1, 6, 9, 11, 8, 4, 6, 0, 10, 1, 11, 0, 2, 7, 5, 4, 7, 1, 5, 6, 11, 8, 9, 0, 11, 2, 7, 8, 0, 12, 13, 12, 14, 13, 12, 1, 14, 13, 14, 2, 3, 15, 16, 15, 17, 16, 15, 4, 17, 16, 17, 5, 4, 15, 18, 15, 19, 18, 15, 3, 19, 18, 19, 6, 7, 20, 21, 20, 22, 21, 20, 4, 22, 21, 22, 8, 9, 23, 24, 23, 25, 24, 23, 3, 25, 24, 25, 10, 9, 26, 23, 26, 19, 23, 26, 6, 19, 23, 19, 3, 0, 27, 28, 27, 24, 28, 27, 9, 24, 28, 24, 10, 7, 29, 30, 29, 14, 30, 29, 2, 14, 30, 14, 1, 8, 31, 32, 31, 33, 32, 31, 11, 33, 32, 33, 2, 3, 16, 25, 16, 34, 25, 16, 5, 34, 25, 34, 10, 10, 34, 35, 34, 36, 35, 34, 5, 36, 35, 36, 1, 6, 26, 37, 26, 38, 37, 26, 9, 38, 37, 38, 11, 8, 22, 39, 22, 18, 39, 22, 4, 18, 39, 18, 6, 0, 28, 12, 28, 35, 12, 28, 10, 35, 12, 35, 1, 11, 40, 33, 40, 13, 33, 40, 0, 13, 33, 13, 2, 7, 41, 20, 41, 17, 20, 41, 5, 17, 20, 17, 4, 7, 30, 41, 30, 36, 41, 30, 1, 36, 41, 36, 5, 6, 37, 39, 37, 31, 39, 37, 11, 31, 39, 31, 8, 9, 27, 38, 27, 40, 38, 27, 0, 40, 38, 40, 11, 2, 29, 32, 29, 21, 32, 29, 7, 21, 32, 21, 8, 0, 42, 43, 42, 44, 43, 42, 12, 44, 43, 44, 13, 12, 45, 44, 45, 46, 44, 45, 14, 46, 44, 46, 13, 12, 47, 45, 47, 48, 45, 47, 1, 48, 45, 48, 14, 13, 46, 49, 46, 50, 49, 46, 14, 50, 49, 50, 2, 3, 51, 52, 51, 53, 52, 51, 15, 53, 52, 53, 16, 15, 54, 53, 54, 55, 53, 54, 17, 55, 53, 55, 16, 15, 56, 54, 56, 57, 54, 56, 4, 57, 54, 57, 17, 16, 55, 58, 55, 59, 58, 55, 17, 59, 58, 59, 5, 4, 56, 60, 56, 61, 60, 56, 15, 61, 60, 61, 18, 15, 62, 61, 62, 63, 61, 62, 19, 63, 61, 63, 18, 15, 51, 62, 51, 64, 62, 51, 3, 64, 62, 64, 19, 18, 63, 65, 63, 66, 65, 63, 19, 66, 65, 66, 6, 7, 67, 68, 67, 69, 68, 67, 20, 69, 68, 69, 21, 20, 70, 69, 70, 71, 69, 70, 22, 71, 69, 71, 21, 20, 72, 70, 72, 73, 70, 72, 4, 73, 70, 73, 22, 21, 71, 74, 71, 75, 74, 71, 22, 75, 74, 75, 8, 9, 76, 77, 76, 78, 77, 76, 23, 78, 77, 78, 24, 23, 79, 78, 79, 80, 78, 79, 25, 80, 78, 80, 24, 23, 81, 79, 81, 82, 79, 81, 3, 82, 79, 82, 25, 24, 80, 83, 80, 84, 83, 80, 25, 84, 83, 84, 10, 9, 85, 76, 85, 86, 76, 85, 26, 86, 76, 86, 23, 26, 87, 86, 87, 88, 86, 87, 19, 88, 86, 88, 23, 26, 89, 87, 89, 66, 87, 89, 6, 66, 87, 66, 19, 23, 88, 81, 88, 64, 81, 88, 19, 64, 81, 64, 3, 0, 90, 91, 90, 92, 91, 90, 27, 92, 91, 92, 28, 27, 93, 92, 93, 94, 92, 93, 24, 94, 92, 94, 28, 27, 95, 93, 95, 77, 93, 95, 9, 77, 93, 77, 24, 28, 94, 96, 94, 83, 96, 94, 24, 83, 96, 83, 10, 7, 97, 98, 97, 99, 98, 97, 29, 99, 98, 99, 30, 29, 100, 99, 100, 101, 99, 100, 14, 101, 99, 101, 30, 29, 102, 100, 102, 50, 100, 102, 2, 50, 100, 50, 14, 30, 101, 103, 101, 48, 103, 101, 14, 48, 103, 48, 1, 8, 104, 105, 104, 106, 105, 104, 31, 106, 105, 106, 32, 31, 107, 106, 107, 108, 106, 107, 33, 108, 106, 108, 32, 31, 109, 107, 109, 110, 107, 109, 11, 110, 107, 110, 33, 32, 108, 111, 108, 112, 111, 108, 33, 112, 111, 112, 2, 3, 52, 82, 52, 113, 82, 52, 16, 113, 82, 113, 25, 16, 114, 113, 114, 115, 113, 114, 34, 115, 113, 115, 25, 16, 58, 114, 58, 116, 114, 58, 5, 116, 114, 116, 34, 25, 115, 84, 115, 117, 84, 115, 34, 117, 84, 117, 10, 10, 117, 118, 117, 119, 118, 117, 34, 119, 118, 119, 35, 34, 120, 119, 120, 121, 119, 120, 36, 121, 119, 121, 35, 34, 116, 120, 116, 122, 120, 116, 5, 122, 120, 122, 36, 35, 121, 123, 121, 124, 123, 121, 36, 124, 123, 124, 1, 6, 89, 125, 89, 126, 125, 89, 26, 126, 125, 126, 37, 26, 127, 126, 127, 128, 126, 127, 38, 128, 126, 128, 37, 26, 85, 127, 85, 129, 127, 85, 9, 129, 127, 129, 38, 37, 128, 130, 128, 131, 130, 128, 38, 131, 130, 131, 11, 8, 75, 132, 75, 133, 132, 75, 22, 133, 132, 133, 39, 22, 134, 133, 134, 135, 133, 134, 18, 135, 133, 135, 39, 22, 73, 134, 73, 60, 134, 73, 4, 60, 134, 60, 18, 39, 135, 136, 135, 65, 136, 135, 18, 65, 136, 65, 6, 0, 91, 42, 91, 137, 42, 91, 28, 137, 42, 137, 12, 28, 138, 137, 138, 139, 137, 138, 35, 139, 137, 139, 12, 28, 96, 138, 96, 118, 138, 96, 10, 118, 138, 118, 35, 12, 139, 47, 139, 123, 47, 139, 35, 123, 47, 123, 1, 11, 140, 110, 140, 141, 110, 140, 40, 141, 110, 141, 33, 40, 142, 141, 142, 143, 141, 142, 13, 143, 141, 143, 33, 40, 144, 142, 144, 43, 142, 144, 0, 43, 142, 43, 13, 33, 143, 112, 143, 49, 112, 143, 13, 49, 112, 49, 2, 7, 145, 67, 145, 146, 67, 145, 41, 146, 67, 146, 20, 41, 147, 146, 147, 148, 146, 147, 17, 148, 146, 148, 20, 41, 149, 147, 149, 59, 147, 149, 5, 59, 147, 59, 17, 20, 148, 72, 148, 57, 72, 148, 17, 57, 72, 57, 4, 7, 98, 145, 98, 150, 145, 98, 30, 150, 145, 150, 41, 30, 151, 150, 151, 152, 150, 151, 36, 152, 150, 152, 41, 30, 103, 151, 103, 124, 151, 103, 1, 124, 151, 124, 36, 41, 152, 149, 152, 122, 149, 152, 36, 122, 149, 122, 5, 6, 125, 136, 125, 153, 136, 125, 37, 153, 136, 153, 39, 37, 154, 153, 154, 155, 153, 154, 31, 155, 153, 155, 39, 37, 130, 154, 130, 109, 154, 130, 11, 109, 154, 109, 31, 39, 155, 132, 155, 104, 132, 155, 31, 104, 132, 104, 8, 9, 95, 129, 95, 156, 129, 95, 27, 156, 129, 156, 38, 27, 157, 156, 157, 158, 156, 157, 40, 158, 156, 158, 38, 27, 90, 157, 90, 144, 157, 90, 0, 144, 157, 144, 40, 38, 158, 131, 158, 140, 131, 158, 40, 140, 131, 140, 11, 2, 102, 111, 102, 159, 111, 102, 29, 159, 111, 159, 32, 29, 160, 159, 160, 161, 159, 160, 21, 161, 159, 161, 32, 29, 97, 160, 97, 68, 160, 97, 7, 68, 160, 68, 21, 32, 161, 105, 161, 74, 105, 161, 21, 74, 105, 74, 8, }; int cylinderTrianglesOffsetCount[][2] = { {0, 30}, {30, 78}, {108, 120}, {228, 480}, {708, 48}, {756, 144}, {900, 228}, {1128, 948}, }; Vertex cylinderVertices[] = { {{1.0000f, 0.0000f, 0.0000f}, {1.0000f, 0.0000f, 1.0000f}}, {{1.0000f, 0.0000f, 0.0000f}, {1.0000f, 0.0000f, 0.0000f}}, {{0.3090f, 0.9511f, 0.0000f}, {0.3090f, 0.9511f, 1.0000f}}, {{0.3090f, 0.9511f, 0.0000f}, {0.3090f, 0.9511f, 0.0000f}}, {{-0.8090f, 0.5878f, 0.0000f}, {-0.8090f, 0.5878f, 1.0000f}}, {{-0.8090f, 0.5878f, 0.0000f}, {-0.8090f, 0.5878f, 0.0000f}}, {{-0.8090f, -0.5878f, 0.0000f}, {-0.8090f, -0.5878f, 1.0000f}}, {{-0.8090f, -0.5878f, 0.0000f}, {-0.8090f, -0.5878f, 0.0000f}}, {{0.3090f, -0.9511f, 0.0000f}, {0.3090f, -0.9511f, 1.0000f}}, {{0.3090f, -0.9511f, 0.0000f}, {0.3090f, -0.9511f, 0.0000f}}, {{0.8855f, 0.4647f, 0.0000f}, {0.8855f, 0.4647f, 1.0000f}}, {{0.8855f, 0.4647f, 0.0000f}, {0.8855f, 0.4647f, 0.0000f}}, {{0.5681f, 0.8230f, 0.0000f}, {0.5681f, 0.8230f, 1.0000f}}, {{0.5681f, 0.8230f, 0.0000f}, {0.5681f, 0.8230f, 0.0000f}}, {{0.1205f, 0.9927f, 0.0000f}, {0.1205f, 0.9927f, 1.0000f}}, {{0.1205f, 0.9927f, 0.0000f}, {0.1205f, 0.9927f, 0.0000f}}, {{-0.3546f, 0.9350f, 0.0000f}, {-0.3546f, 0.9350f, 1.0000f}}, {{-0.3546f, 0.9350f, 0.0000f}, {-0.3546f, 0.9350f, 0.0000f}}, {{-0.7485f, 0.6631f, 0.0000f}, {-0.7485f, 0.6631f, 1.0000f}}, {{-0.7485f, 0.6631f, 0.0000f}, {-0.7485f, 0.6631f, 0.0000f}}, {{-0.9709f, 0.2393f, 0.0000f}, {-0.9709f, 0.2393f, 1.0000f}}, {{-0.9709f, 0.2393f, 0.0000f}, {-0.9709f, 0.2393f, 0.0000f}}, {{-0.9709f, -0.2393f, 0.0000f}, {-0.9709f, -0.2393f, 1.0000f}}, {{-0.9709f, -0.2393f, 0.0000f}, {-0.9709f, -0.2393f, 0.0000f}}, {{-0.7485f, -0.6631f, 0.0000f}, {-0.7485f, -0.6631f, 1.0000f}}, {{-0.7485f, -0.6631f, 0.0000f}, {-0.7485f, -0.6631f, 0.0000f}}, {{-0.3546f, -0.9350f, 0.0000f}, {-0.3546f, -0.9350f, 1.0000f}}, {{-0.3546f, -0.9350f, 0.0000f}, {-0.3546f, -0.9350f, 0.0000f}}, {{0.1205f, -0.9927f, 0.0000f}, {0.1205f, -0.9927f, 1.0000f}}, {{0.1205f, -0.9927f, 0.0000f}, {0.1205f, -0.9927f, 0.0000f}}, {{0.5681f, -0.8230f, 0.0000f}, {0.5681f, -0.8230f, 1.0000f}}, {{0.5681f, -0.8230f, 0.0000f}, {0.5681f, -0.8230f, 0.0000f}}, {{0.8855f, -0.4647f, 0.0000f}, {0.8855f, -0.4647f, 1.0000f}}, {{0.8855f, -0.4647f, 0.0000f}, {0.8855f, -0.4647f, 0.0000f}}, {{0.9511f, 0.3090f, 0.0000f}, {0.9511f, 0.3090f, 1.0000f}}, {{0.9511f, 0.3090f, 0.0000f}, {0.9511f, 0.3090f, 0.0000f}}, {{0.8090f, 0.5878f, 0.0000f}, {0.8090f, 0.5878f, 1.0000f}}, {{0.8090f, 0.5878f, 0.0000f}, {0.8090f, 0.5878f, 0.0000f}}, {{0.5878f, 0.8090f, 0.0000f}, {0.5878f, 0.8090f, 1.0000f}}, {{0.5878f, 0.8090f, 0.0000f}, {0.5878f, 0.8090f, 0.0000f}}, {{0.0000f, 1.0000f, 0.0000f}, {0.0000f, 1.0000f, 1.0000f}}, {{0.0000f, 1.0000f, 0.0000f}, {0.0000f, 1.0000f, 0.0000f}}, {{-0.3090f, 0.9511f, 0.0000f}, {-0.3090f, 0.9511f, 1.0000f}}, {{-0.3090f, 0.9511f, 0.0000f}, {-0.3090f, 0.9511f, 0.0000f}}, {{-0.5878f, 0.8090f, 0.0000f}, {-0.5878f, 0.8090f, 1.0000f}}, {{-0.5878f, 0.8090f, 0.0000f}, {-0.5878f, 0.8090f, 0.0000f}}, {{-0.9511f, 0.3090f, 0.0000f}, {-0.9511f, 0.3090f, 1.0000f}}, {{-0.9511f, 0.3090f, 0.0000f}, {-0.9511f, 0.3090f, 0.0000f}}, {{-1.0000f, 0.0000f, 0.0000f}, {-1.0000f, 0.0000f, 1.0000f}}, {{-1.0000f, 0.0000f, 0.0000f}, {-1.0000f, 0.0000f, 0.0000f}}, {{-0.9511f, -0.3090f, 0.0000f}, {-0.9511f, -0.3090f, 1.0000f}}, {{-0.9511f, -0.3090f, 0.0000f}, {-0.9511f, -0.3090f, 0.0000f}}, {{-0.5878f, -0.8090f, 0.0000f}, {-0.5878f, -0.8090f, 1.0000f}}, {{-0.5878f, -0.8090f, 0.0000f}, {-0.5878f, -0.8090f, 0.0000f}}, {{-0.3090f, -0.9511f, 0.0000f}, {-0.3090f, -0.9511f, 1.0000f}}, {{-0.3090f, -0.9511f, 0.0000f}, {-0.3090f, -0.9511f, 0.0000f}}, {{-0.0000f, -1.0000f, 0.0000f}, {-0.0000f, -1.0000f, 1.0000f}}, {{-0.0000f, -1.0000f, 0.0000f}, {-0.0000f, -1.0000f, 0.0000f}}, {{0.5878f, -0.8090f, 0.0000f}, {0.5878f, -0.8090f, 1.0000f}}, {{0.5878f, -0.8090f, 0.0000f}, {0.5878f, -0.8090f, 0.0000f}}, {{0.8090f, -0.5878f, 0.0000f}, {0.8090f, -0.5878f, 1.0000f}}, {{0.8090f, -0.5878f, 0.0000f}, {0.8090f, -0.5878f, 0.0000f}}, {{0.9511f, -0.3090f, 0.0000f}, {0.9511f, -0.3090f, 1.0000f}}, {{0.9511f, -0.3090f, 0.0000f}, {0.9511f, -0.3090f, 0.0000f}}, {{0.9969f, 0.0785f, 0.0000f}, {0.9969f, 0.0785f, 1.0000f}}, {{0.9969f, 0.0785f, 0.0000f}, {0.9969f, 0.0785f, 0.0000f}}, {{0.9877f, 0.1564f, 0.0000f}, {0.9877f, 0.1564f, 1.0000f}}, {{0.9877f, 0.1564f, 0.0000f}, {0.9877f, 0.1564f, 0.0000f}}, {{0.9724f, 0.2334f, 0.0000f}, {0.9724f, 0.2334f, 1.0000f}}, {{0.9724f, 0.2334f, 0.0000f}, {0.9724f, 0.2334f, 0.0000f}}, {{0.9239f, 0.3827f, 0.0000f}, {0.9239f, 0.3827f, 1.0000f}}, {{0.9239f, 0.3827f, 0.0000f}, {0.9239f, 0.3827f, 0.0000f}}, {{0.8910f, 0.4540f, 0.0000f}, {0.8910f, 0.4540f, 1.0000f}}, {{0.8910f, 0.4540f, 0.0000f}, {0.8910f, 0.4540f, 0.0000f}}, {{0.8526f, 0.5225f, 0.0000f}, {0.8526f, 0.5225f, 1.0000f}}, {{0.8526f, 0.5225f, 0.0000f}, {0.8526f, 0.5225f, 0.0000f}}, {{0.7604f, 0.6494f, 0.0000f}, {0.7604f, 0.6494f, 1.0000f}}, {{0.7604f, 0.6494f, 0.0000f}, {0.7604f, 0.6494f, 0.0000f}}, {{0.7071f, 0.7071f, 0.0000f}, {0.7071f, 0.7071f, 1.0000f}}, {{0.7071f, 0.7071f, 0.0000f}, {0.7071f, 0.7071f, 0.0000f}}, {{0.6494f, 0.7604f, 0.0000f}, {0.6494f, 0.7604f, 1.0000f}}, {{0.6494f, 0.7604f, 0.0000f}, {0.6494f, 0.7604f, 0.0000f}}, {{0.5225f, 0.8526f, 0.0000f}, {0.5225f, 0.8526f, 1.0000f}}, {{0.5225f, 0.8526f, 0.0000f}, {0.5225f, 0.8526f, 0.0000f}}, {{0.4540f, 0.8910f, 0.0000f}, {0.4540f, 0.8910f, 1.0000f}}, {{0.4540f, 0.8910f, 0.0000f}, {0.4540f, 0.8910f, 0.0000f}}, {{0.3827f, 0.9239f, 0.0000f}, {0.3827f, 0.9239f, 1.0000f}}, {{0.3827f, 0.9239f, 0.0000f}, {0.3827f, 0.9239f, 0.0000f}}, {{0.2334f, 0.9724f, 0.0000f}, {0.2334f, 0.9724f, 1.0000f}}, {{0.2334f, 0.9724f, 0.0000f}, {0.2334f, 0.9724f, 0.0000f}}, {{0.1564f, 0.9877f, 0.0000f}, {0.1564f, 0.9877f, 1.0000f}}, {{0.1564f, 0.9877f, 0.0000f}, {0.1564f, 0.9877f, 0.0000f}}, {{0.0785f, 0.9969f, 0.0000f}, {0.0785f, 0.9969f, 1.0000f}}, {{0.0785f, 0.9969f, 0.0000f}, {0.0785f, 0.9969f, 0.0000f}}, {{-0.0785f, 0.9969f, 0.0000f}, {-0.0785f, 0.9969f, 1.0000f}}, {{-0.0785f, 0.9969f, 0.0000f}, {-0.0785f, 0.9969f, 0.0000f}}, {{-0.1564f, 0.9877f, 0.0000f}, {-0.1564f, 0.9877f, 1.0000f}}, {{-0.1564f, 0.9877f, 0.0000f}, {-0.1564f, 0.9877f, 0.0000f}}, {{-0.2334f, 0.9724f, 0.0000f}, {-0.2334f, 0.9724f, 1.0000f}}, {{-0.2334f, 0.9724f, 0.0000f}, {-0.2334f, 0.9724f, 0.0000f}}, {{-0.3827f, 0.9239f, 0.0000f}, {-0.3827f, 0.9239f, 1.0000f}}, {{-0.3827f, 0.9239f, 0.0000f}, {-0.3827f, 0.9239f, 0.0000f}}, {{-0.4540f, 0.8910f, 0.0000f}, {-0.4540f, 0.8910f, 1.0000f}}, {{-0.4540f, 0.8910f, 0.0000f}, {-0.4540f, 0.8910f, 0.0000f}}, {{-0.5225f, 0.8526f, 0.0000f}, {-0.5225f, 0.8526f, 1.0000f}}, {{-0.5225f, 0.8526f, 0.0000f}, {-0.5225f, 0.8526f, 0.0000f}}, {{-0.6494f, 0.7604f, 0.0000f}, {-0.6494f, 0.7604f, 1.0000f}}, {{-0.6494f, 0.7604f, 0.0000f}, {-0.6494f, 0.7604f, 0.0000f}}, {{-0.7071f, 0.7071f, 0.0000f}, {-0.7071f, 0.7071f, 1.0000f}}, {{-0.7071f, 0.7071f, 0.0000f}, {-0.7071f, 0.7071f, 0.0000f}}, {{-0.7604f, 0.6494f, 0.0000f}, {-0.7604f, 0.6494f, 1.0000f}}, {{-0.7604f, 0.6494f, 0.0000f}, {-0.7604f, 0.6494f, 0.0000f}}, {{-0.8526f, 0.5225f, 0.0000f}, {-0.8526f, 0.5225f, 1.0000f}}, {{-0.8526f, 0.5225f, 0.0000f}, {-0.8526f, 0.5225f, 0.0000f}}, {{-0.8910f, 0.4540f, 0.0000f}, {-0.8910f, 0.4540f, 1.0000f}}, {{-0.8910f, 0.4540f, 0.0000f}, {-0.8910f, 0.4540f, 0.0000f}}, {{-0.9239f, 0.3827f, 0.0000f}, {-0.9239f, 0.3827f, 1.0000f}}, {{-0.9239f, 0.3827f, 0.0000f}, {-0.9239f, 0.3827f, 0.0000f}}, {{-0.9724f, 0.2334f, 0.0000f}, {-0.9724f, 0.2334f, 1.0000f}}, {{-0.9724f, 0.2334f, 0.0000f}, {-0.9724f, 0.2334f, 0.0000f}}, {{-0.9877f, 0.1564f, 0.0000f}, {-0.9877f, 0.1564f, 1.0000f}}, {{-0.9877f, 0.1564f, 0.0000f}, {-0.9877f, 0.1564f, 0.0000f}}, {{-0.9969f, 0.0785f, 0.0000f}, {-0.9969f, 0.0785f, 1.0000f}}, {{-0.9969f, 0.0785f, 0.0000f}, {-0.9969f, 0.0785f, 0.0000f}}, {{-0.9969f, -0.0785f, 0.0000f}, {-0.9969f, -0.0785f, 1.0000f}}, {{-0.9969f, -0.0785f, 0.0000f}, {-0.9969f, -0.0785f, 0.0000f}}, {{-0.9877f, -0.1564f, 0.0000f}, {-0.9877f, -0.1564f, 1.0000f}}, {{-0.9877f, -0.1564f, 0.0000f}, {-0.9877f, -0.1564f, 0.0000f}}, {{-0.9724f, -0.2334f, 0.0000f}, {-0.9724f, -0.2334f, 1.0000f}}, {{-0.9724f, -0.2334f, 0.0000f}, {-0.9724f, -0.2334f, 0.0000f}}, {{-0.9239f, -0.3827f, 0.0000f}, {-0.9239f, -0.3827f, 1.0000f}}, {{-0.9239f, -0.3827f, 0.0000f}, {-0.9239f, -0.3827f, 0.0000f}}, {{-0.8910f, -0.4540f, 0.0000f}, {-0.8910f, -0.4540f, 1.0000f}}, {{-0.8910f, -0.4540f, 0.0000f}, {-0.8910f, -0.4540f, 0.0000f}}, {{-0.8526f, -0.5225f, 0.0000f}, {-0.8526f, -0.5225f, 1.0000f}}, {{-0.8526f, -0.5225f, 0.0000f}, {-0.8526f, -0.5225f, 0.0000f}}, {{-0.7604f, -0.6494f, 0.0000f}, {-0.7604f, -0.6494f, 1.0000f}}, {{-0.7604f, -0.6494f, 0.0000f}, {-0.7604f, -0.6494f, 0.0000f}}, {{-0.7071f, -0.7071f, 0.0000f}, {-0.7071f, -0.7071f, 1.0000f}}, {{-0.7071f, -0.7071f, 0.0000f}, {-0.7071f, -0.7071f, 0.0000f}}, {{-0.6494f, -0.7604f, 0.0000f}, {-0.6494f, -0.7604f, 1.0000f}}, {{-0.6494f, -0.7604f, 0.0000f}, {-0.6494f, -0.7604f, 0.0000f}}, {{-0.5225f, -0.8526f, 0.0000f}, {-0.5225f, -0.8526f, 1.0000f}}, {{-0.5225f, -0.8526f, 0.0000f}, {-0.5225f, -0.8526f, 0.0000f}}, {{-0.4540f, -0.8910f, 0.0000f}, {-0.4540f, -0.8910f, 1.0000f}}, {{-0.4540f, -0.8910f, 0.0000f}, {-0.4540f, -0.8910f, 0.0000f}}, {{-0.3827f, -0.9239f, 0.0000f}, {-0.3827f, -0.9239f, 1.0000f}}, {{-0.3827f, -0.9239f, 0.0000f}, {-0.3827f, -0.9239f, 0.0000f}}, {{-0.2334f, -0.9724f, 0.0000f}, {-0.2334f, -0.9724f, 1.0000f}}, {{-0.2334f, -0.9724f, 0.0000f}, {-0.2334f, -0.9724f, 0.0000f}}, {{-0.1564f, -0.9877f, 0.0000f}, {-0.1564f, -0.9877f, 1.0000f}}, {{-0.1564f, -0.9877f, 0.0000f}, {-0.1564f, -0.9877f, 0.0000f}}, {{-0.0785f, -0.9969f, 0.0000f}, {-0.0785f, -0.9969f, 1.0000f}}, {{-0.0785f, -0.9969f, 0.0000f}, {-0.0785f, -0.9969f, 0.0000f}}, {{0.0785f, -0.9969f, 0.0000f}, {0.0785f, -0.9969f, 1.0000f}}, {{0.0785f, -0.9969f, 0.0000f}, {0.0785f, -0.9969f, 0.0000f}}, {{0.1564f, -0.9877f, 0.0000f}, {0.1564f, -0.9877f, 1.0000f}}, {{0.1564f, -0.9877f, 0.0000f}, {0.1564f, -0.9877f, 0.0000f}}, {{0.2334f, -0.9724f, 0.0000f}, {0.2334f, -0.9724f, 1.0000f}}, {{0.2334f, -0.9724f, 0.0000f}, {0.2334f, -0.9724f, 0.0000f}}, {{0.3827f, -0.9239f, 0.0000f}, {0.3827f, -0.9239f, 1.0000f}}, {{0.3827f, -0.9239f, 0.0000f}, {0.3827f, -0.9239f, 0.0000f}}, {{0.4540f, -0.8910f, 0.0000f}, {0.4540f, -0.8910f, 1.0000f}}, {{0.4540f, -0.8910f, 0.0000f}, {0.4540f, -0.8910f, 0.0000f}}, {{0.5225f, -0.8526f, 0.0000f}, {0.5225f, -0.8526f, 1.0000f}}, {{0.5225f, -0.8526f, 0.0000f}, {0.5225f, -0.8526f, 0.0000f}}, {{0.6494f, -0.7604f, 0.0000f}, {0.6494f, -0.7604f, 1.0000f}}, {{0.6494f, -0.7604f, 0.0000f}, {0.6494f, -0.7604f, 0.0000f}}, {{0.7071f, -0.7071f, 0.0000f}, {0.7071f, -0.7071f, 1.0000f}}, {{0.7071f, -0.7071f, 0.0000f}, {0.7071f, -0.7071f, 0.0000f}}, {{0.7604f, -0.6494f, 0.0000f}, {0.7604f, -0.6494f, 1.0000f}}, {{0.7604f, -0.6494f, 0.0000f}, {0.7604f, -0.6494f, 0.0000f}}, {{0.8526f, -0.5225f, 0.0000f}, {0.8526f, -0.5225f, 1.0000f}}, {{0.8526f, -0.5225f, 0.0000f}, {0.8526f, -0.5225f, 0.0000f}}, {{0.8910f, -0.4540f, 0.0000f}, {0.8910f, -0.4540f, 1.0000f}}, {{0.8910f, -0.4540f, 0.0000f}, {0.8910f, -0.4540f, 0.0000f}}, {{0.9239f, -0.3827f, 0.0000f}, {0.9239f, -0.3827f, 1.0000f}}, {{0.9239f, -0.3827f, 0.0000f}, {0.9239f, -0.3827f, 0.0000f}}, {{0.9724f, -0.2334f, 0.0000f}, {0.9724f, -0.2334f, 1.0000f}}, {{0.9724f, -0.2334f, 0.0000f}, {0.9724f, -0.2334f, 0.0000f}}, {{0.9877f, -0.1564f, 0.0000f}, {0.9877f, -0.1564f, 1.0000f}}, {{0.9877f, -0.1564f, 0.0000f}, {0.9877f, -0.1564f, 0.0000f}}, {{0.9969f, -0.0785f, 0.0000f}, {0.9969f, -0.0785f, 1.0000f}}, {{0.9969f, -0.0785f, 0.0000f}, {0.9969f, -0.0785f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {1.0000f, -0.0000f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.3090f, -0.9511f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.8090f, -0.5878f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.8090f, 0.5878f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.3090f, 0.9511f, 0.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {1.0000f, 0.0000f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.3090f, 0.9511f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.8090f, 0.5878f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.8090f, -0.5878f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.3090f, -0.9511f, 1.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.8855f, -0.4647f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.5681f, -0.8230f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.1205f, -0.9927f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.3546f, -0.9350f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.7485f, -0.6631f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9709f, -0.2393f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9709f, 0.2393f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.7485f, 0.6631f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.3546f, 0.9350f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.1205f, 0.9927f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.5681f, 0.8230f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.8855f, 0.4647f, 0.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.8855f, 0.4647f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.5681f, 0.8230f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.1205f, 0.9927f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.3546f, 0.9350f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.7485f, 0.6631f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9709f, 0.2393f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9709f, -0.2393f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.7485f, -0.6631f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.3546f, -0.9350f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.1205f, -0.9927f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.5681f, -0.8230f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.8855f, -0.4647f, 1.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9511f, -0.3090f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.8090f, -0.5878f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.5878f, -0.8090f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.0000f, -1.0000f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.3090f, -0.9511f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.5878f, -0.8090f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9511f, -0.3090f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-1.0000f, 0.0000f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9511f, 0.3090f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.5878f, 0.8090f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.3090f, 0.9511f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.0000f, 1.0000f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.5878f, 0.8090f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.8090f, 0.5878f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9511f, 0.3090f, 0.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9511f, 0.3090f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.8090f, 0.5878f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.5878f, 0.8090f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.0000f, 1.0000f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.3090f, 0.9511f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.5878f, 0.8090f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9511f, 0.3090f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-1.0000f, 0.0000f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9511f, -0.3090f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.5878f, -0.8090f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.3090f, -0.9511f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.0000f, -1.0000f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.5878f, -0.8090f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.8090f, -0.5878f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9511f, -0.3090f, 1.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9969f, -0.0785f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9877f, -0.1564f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9724f, -0.2334f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9239f, -0.3827f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.8910f, -0.4540f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.8526f, -0.5225f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.7604f, -0.6494f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.7071f, -0.7071f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.6494f, -0.7604f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.5225f, -0.8526f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.4540f, -0.8910f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.3827f, -0.9239f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.2334f, -0.9724f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.1564f, -0.9877f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.0785f, -0.9969f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.0785f, -0.9969f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.1564f, -0.9877f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.2334f, -0.9724f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.3827f, -0.9239f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.4540f, -0.8910f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.5225f, -0.8526f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.6494f, -0.7604f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.7071f, -0.7071f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.7604f, -0.6494f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.8526f, -0.5225f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.8910f, -0.4540f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9239f, -0.3827f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9724f, -0.2334f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9877f, -0.1564f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9969f, -0.0785f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9969f, 0.0785f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9877f, 0.1564f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9724f, 0.2334f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.9239f, 0.3827f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.8910f, 0.4540f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.8526f, 0.5225f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.7604f, 0.6494f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.7071f, 0.7071f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.6494f, 0.7604f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.5225f, 0.8526f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.4540f, 0.8910f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.3827f, 0.9239f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.2334f, 0.9724f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.1564f, 0.9877f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {-0.0785f, 0.9969f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.0785f, 0.9969f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.1564f, 0.9877f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.2334f, 0.9724f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.3827f, 0.9239f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.4540f, 0.8910f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.5225f, 0.8526f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.6494f, 0.7604f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.7071f, 0.7071f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.7604f, 0.6494f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.8526f, 0.5225f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.8910f, 0.4540f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9239f, 0.3827f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9724f, 0.2334f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9877f, 0.1564f, 0.0000f}}, {{0.0000f, 0.0000f, -1.0000f}, {0.9969f, 0.0785f, 0.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9969f, 0.0785f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9877f, 0.1564f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9724f, 0.2334f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9239f, 0.3827f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.8910f, 0.4540f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.8526f, 0.5225f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.7604f, 0.6494f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.7071f, 0.7071f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.6494f, 0.7604f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.5225f, 0.8526f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.4540f, 0.8910f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.3827f, 0.9239f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.2334f, 0.9724f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.1564f, 0.9877f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.0785f, 0.9969f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.0785f, 0.9969f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.1564f, 0.9877f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.2334f, 0.9724f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.3827f, 0.9239f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.4540f, 0.8910f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.5225f, 0.8526f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.6494f, 0.7604f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.7071f, 0.7071f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.7604f, 0.6494f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.8526f, 0.5225f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.8910f, 0.4540f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9239f, 0.3827f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9724f, 0.2334f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9877f, 0.1564f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9969f, 0.0785f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9969f, -0.0785f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9877f, -0.1564f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9724f, -0.2334f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.9239f, -0.3827f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.8910f, -0.4540f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.8526f, -0.5225f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.7604f, -0.6494f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.7071f, -0.7071f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.6494f, -0.7604f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.5225f, -0.8526f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.4540f, -0.8910f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.3827f, -0.9239f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.2334f, -0.9724f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.1564f, -0.9877f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {-0.0785f, -0.9969f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.0785f, -0.9969f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.1564f, -0.9877f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.2334f, -0.9724f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.3827f, -0.9239f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.4540f, -0.8910f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.5225f, -0.8526f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.6494f, -0.7604f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.7071f, -0.7071f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.7604f, -0.6494f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.8526f, -0.5225f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.8910f, -0.4540f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9239f, -0.3827f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9724f, -0.2334f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9877f, -0.1564f, 1.0000f}}, {{0.0000f, 0.0000f, 1.0000f}, {0.9969f, -0.0785f, 1.0000f}}, }; int cylinderElements[] = { 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 0, 1, 3, 2, 3, 5, 4, 5, 7, 6, 7, 9, 8, 9, 1, 0, 0, 1, 10, 10, 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 22, 23, 24, 24, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 0, 1, 11, 10, 11, 13, 12, 13, 15, 14, 15, 17, 16, 17, 19, 18, 19, 21, 20, 21, 23, 22, 23, 25, 24, 25, 27, 26, 27, 29, 28, 29, 31, 30, 31, 33, 32, 33, 1, 0, 0, 1, 34, 34, 35, 36, 36, 37, 38, 38, 39, 2, 2, 3, 40, 40, 41, 42, 42, 43, 44, 44, 45, 4, 4, 5, 46, 46, 47, 48, 48, 49, 50, 50, 51, 6, 6, 7, 52, 52, 53, 54, 54, 55, 56, 56, 57, 8, 8, 9, 58, 58, 59, 60, 60, 61, 62, 62, 63, 0, 1, 35, 34, 35, 37, 36, 37, 39, 38, 39, 3, 2, 3, 41, 40, 41, 43, 42, 43, 45, 44, 45, 5, 4, 5, 47, 46, 47, 49, 48, 49, 51, 50, 51, 7, 6, 7, 53, 52, 53, 55, 54, 55, 57, 56, 57, 9, 8, 9, 59, 58, 59, 61, 60, 61, 63, 62, 63, 1, 0, 0, 1, 64, 64, 65, 66, 66, 67, 68, 68, 69, 34, 34, 35, 70, 70, 71, 72, 72, 73, 74, 74, 75, 36, 36, 37, 76, 76, 77, 78, 78, 79, 80, 80, 81, 38, 38, 39, 82, 82, 83, 84, 84, 85, 86, 86, 87, 2, 2, 3, 88, 88, 89, 90, 90, 91, 92, 92, 93, 40, 40, 41, 94, 94, 95, 96, 96, 97, 98, 98, 99, 42, 42, 43, 100, 100, 101, 102, 102, 103, 104, 104, 105, 44, 44, 45, 106, 106, 107, 108, 108, 109, 110, 110, 111, 4, 4, 5, 112, 112, 113, 114, 114, 115, 116, 116, 117, 46, 46, 47, 118, 118, 119, 120, 120, 121, 122, 122, 123, 48, 48, 49, 124, 124, 125, 126, 126, 127, 128, 128, 129, 50, 50, 51, 130, 130, 131, 132, 132, 133, 134, 134, 135, 6, 6, 7, 136, 136, 137, 138, 138, 139, 140, 140, 141, 52, 52, 53, 142, 142, 143, 144, 144, 145, 146, 146, 147, 54, 54, 55, 148, 148, 149, 150, 150, 151, 152, 152, 153, 56, 56, 57, 154, 154, 155, 156, 156, 157, 158, 158, 159, 8, 8, 9, 160, 160, 161, 162, 162, 163, 164, 164, 165, 58, 58, 59, 166, 166, 167, 168, 168, 169, 170, 170, 171, 60, 60, 61, 172, 172, 173, 174, 174, 175, 176, 176, 177, 62, 62, 63, 178, 178, 179, 180, 180, 181, 182, 182, 183, 0, 1, 65, 64, 65, 67, 66, 67, 69, 68, 69, 35, 34, 35, 71, 70, 71, 73, 72, 73, 75, 74, 75, 37, 36, 37, 77, 76, 77, 79, 78, 79, 81, 80, 81, 39, 38, 39, 83, 82, 83, 85, 84, 85, 87, 86, 87, 3, 2, 3, 89, 88, 89, 91, 90, 91, 93, 92, 93, 41, 40, 41, 95, 94, 95, 97, 96, 97, 99, 98, 99, 43, 42, 43, 101, 100, 101, 103, 102, 103, 105, 104, 105, 45, 44, 45, 107, 106, 107, 109, 108, 109, 111, 110, 111, 5, 4, 5, 113, 112, 113, 115, 114, 115, 117, 116, 117, 47, 46, 47, 119, 118, 119, 121, 120, 121, 123, 122, 123, 49, 48, 49, 125, 124, 125, 127, 126, 127, 129, 128, 129, 51, 50, 51, 131, 130, 131, 133, 132, 133, 135, 134, 135, 7, 6, 7, 137, 136, 137, 139, 138, 139, 141, 140, 141, 53, 52, 53, 143, 142, 143, 145, 144, 145, 147, 146, 147, 55, 54, 55, 149, 148, 149, 151, 150, 151, 153, 152, 153, 57, 56, 57, 155, 154, 155, 157, 156, 157, 159, 158, 159, 9, 8, 9, 161, 160, 161, 163, 162, 163, 165, 164, 165, 59, 58, 59, 167, 166, 167, 169, 168, 169, 171, 170, 171, 61, 60, 61, 173, 172, 173, 175, 174, 175, 177, 176, 177, 63, 62, 63, 179, 178, 179, 181, 180, 181, 183, 182, 183, 1, 0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 0, 1, 3, 2, 3, 5, 4, 5, 7, 6, 7, 9, 8, 9, 1, 0, 184, 185, 186, 184, 186, 187, 184, 187, 188, 189, 190, 191, 189, 191, 192, 189, 192, 193, 0, 1, 10, 10, 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 22, 23, 24, 24, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 0, 1, 11, 10, 11, 13, 12, 13, 15, 14, 15, 17, 16, 17, 19, 18, 19, 21, 20, 21, 23, 22, 23, 25, 24, 25, 27, 26, 27, 29, 28, 29, 31, 30, 31, 33, 32, 33, 1, 0, 184, 194, 195, 184, 195, 196, 184, 196, 197, 184, 197, 198, 184, 198, 199, 184, 199, 200, 184, 200, 201, 184, 201, 202, 184, 202, 203, 184, 203, 204, 184, 204, 205, 189, 206, 207, 189, 207, 208, 189, 208, 209, 189, 209, 210, 189, 210, 211, 189, 211, 212, 189, 212, 213, 189, 213, 214, 189, 214, 215, 189, 215, 216, 189, 216, 217, 0, 1, 34, 34, 35, 36, 36, 37, 38, 38, 39, 2, 2, 3, 40, 40, 41, 42, 42, 43, 44, 44, 45, 4, 4, 5, 46, 46, 47, 48, 48, 49, 50, 50, 51, 6, 6, 7, 52, 52, 53, 54, 54, 55, 56, 56, 57, 8, 8, 9, 58, 58, 59, 60, 60, 61, 62, 62, 63, 0, 1, 35, 34, 35, 37, 36, 37, 39, 38, 39, 3, 2, 3, 41, 40, 41, 43, 42, 43, 45, 44, 45, 5, 4, 5, 47, 46, 47, 49, 48, 49, 51, 50, 51, 7, 6, 7, 53, 52, 53, 55, 54, 55, 57, 56, 57, 9, 8, 9, 59, 58, 59, 61, 60, 61, 63, 62, 63, 1, 0, 184, 218, 219, 184, 219, 220, 184, 220, 185, 184, 185, 221, 184, 221, 222, 184, 222, 223, 184, 223, 186, 184, 186, 224, 184, 224, 225, 184, 225, 226, 184, 226, 187, 184, 187, 227, 184, 227, 228, 184, 228, 229, 184, 229, 188, 184, 188, 230, 184, 230, 231, 184, 231, 232, 189, 233, 234, 189, 234, 235, 189, 235, 190, 189, 190, 236, 189, 236, 237, 189, 237, 238, 189, 238, 191, 189, 191, 239, 189, 239, 240, 189, 240, 241, 189, 241, 192, 189, 192, 242, 189, 242, 243, 189, 243, 244, 189, 244, 193, 189, 193, 245, 189, 245, 246, 189, 246, 247, 0, 1, 64, 64, 65, 66, 66, 67, 68, 68, 69, 34, 34, 35, 70, 70, 71, 72, 72, 73, 74, 74, 75, 36, 36, 37, 76, 76, 77, 78, 78, 79, 80, 80, 81, 38, 38, 39, 82, 82, 83, 84, 84, 85, 86, 86, 87, 2, 2, 3, 88, 88, 89, 90, 90, 91, 92, 92, 93, 40, 40, 41, 94, 94, 95, 96, 96, 97, 98, 98, 99, 42, 42, 43, 100, 100, 101, 102, 102, 103, 104, 104, 105, 44, 44, 45, 106, 106, 107, 108, 108, 109, 110, 110, 111, 4, 4, 5, 112, 112, 113, 114, 114, 115, 116, 116, 117, 46, 46, 47, 118, 118, 119, 120, 120, 121, 122, 122, 123, 48, 48, 49, 124, 124, 125, 126, 126, 127, 128, 128, 129, 50, 50, 51, 130, 130, 131, 132, 132, 133, 134, 134, 135, 6, 6, 7, 136, 136, 137, 138, 138, 139, 140, 140, 141, 52, 52, 53, 142, 142, 143, 144, 144, 145, 146, 146, 147, 54, 54, 55, 148, 148, 149, 150, 150, 151, 152, 152, 153, 56, 56, 57, 154, 154, 155, 156, 156, 157, 158, 158, 159, 8, 8, 9, 160, 160, 161, 162, 162, 163, 164, 164, 165, 58, 58, 59, 166, 166, 167, 168, 168, 169, 170, 170, 171, 60, 60, 61, 172, 172, 173, 174, 174, 175, 176, 176, 177, 62, 62, 63, 178, 178, 179, 180, 180, 181, 182, 182, 183, 0, 1, 65, 64, 65, 67, 66, 67, 69, 68, 69, 35, 34, 35, 71, 70, 71, 73, 72, 73, 75, 74, 75, 37, 36, 37, 77, 76, 77, 79, 78, 79, 81, 80, 81, 39, 38, 39, 83, 82, 83, 85, 84, 85, 87, 86, 87, 3, 2, 3, 89, 88, 89, 91, 90, 91, 93, 92, 93, 41, 40, 41, 95, 94, 95, 97, 96, 97, 99, 98, 99, 43, 42, 43, 101, 100, 101, 103, 102, 103, 105, 104, 105, 45, 44, 45, 107, 106, 107, 109, 108, 109, 111, 110, 111, 5, 4, 5, 113, 112, 113, 115, 114, 115, 117, 116, 117, 47, 46, 47, 119, 118, 119, 121, 120, 121, 123, 122, 123, 49, 48, 49, 125, 124, 125, 127, 126, 127, 129, 128, 129, 51, 50, 51, 131, 130, 131, 133, 132, 133, 135, 134, 135, 7, 6, 7, 137, 136, 137, 139, 138, 139, 141, 140, 141, 53, 52, 53, 143, 142, 143, 145, 144, 145, 147, 146, 147, 55, 54, 55, 149, 148, 149, 151, 150, 151, 153, 152, 153, 57, 56, 57, 155, 154, 155, 157, 156, 157, 159, 158, 159, 9, 8, 9, 161, 160, 161, 163, 162, 163, 165, 164, 165, 59, 58, 59, 167, 166, 167, 169, 168, 169, 171, 170, 171, 61, 60, 61, 173, 172, 173, 175, 174, 175, 177, 176, 177, 63, 62, 63, 179, 178, 179, 181, 180, 181, 183, 182, 183, 1, 0, 184, 248, 249, 184, 249, 250, 184, 250, 218, 184, 218, 251, 184, 251, 252, 184, 252, 253, 184, 253, 219, 184, 219, 254, 184, 254, 255, 184, 255, 256, 184, 256, 220, 184, 220, 257, 184, 257, 258, 184, 258, 259, 184, 259, 185, 184, 185, 260, 184, 260, 261, 184, 261, 262, 184, 262, 221, 184, 221, 263, 184, 263, 264, 184, 264, 265, 184, 265, 222, 184, 222, 266, 184, 266, 267, 184, 267, 268, 184, 268, 223, 184, 223, 269, 184, 269, 270, 184, 270, 271, 184, 271, 186, 184, 186, 272, 184, 272, 273, 184, 273, 274, 184, 274, 224, 184, 224, 275, 184, 275, 276, 184, 276, 277, 184, 277, 225, 184, 225, 278, 184, 278, 279, 184, 279, 280, 184, 280, 226, 184, 226, 281, 184, 281, 282, 184, 282, 283, 184, 283, 187, 184, 187, 284, 184, 284, 285, 184, 285, 286, 184, 286, 227, 184, 227, 287, 184, 287, 288, 184, 288, 289, 184, 289, 228, 184, 228, 290, 184, 290, 291, 184, 291, 292, 184, 292, 229, 184, 229, 293, 184, 293, 294, 184, 294, 295, 184, 295, 188, 184, 188, 296, 184, 296, 297, 184, 297, 298, 184, 298, 230, 184, 230, 299, 184, 299, 300, 184, 300, 301, 184, 301, 231, 184, 231, 302, 184, 302, 303, 184, 303, 304, 184, 304, 232, 184, 232, 305, 184, 305, 306, 184, 306, 307, 189, 308, 309, 189, 309, 310, 189, 310, 233, 189, 233, 311, 189, 311, 312, 189, 312, 313, 189, 313, 234, 189, 234, 314, 189, 314, 315, 189, 315, 316, 189, 316, 235, 189, 235, 317, 189, 317, 318, 189, 318, 319, 189, 319, 190, 189, 190, 320, 189, 320, 321, 189, 321, 322, 189, 322, 236, 189, 236, 323, 189, 323, 324, 189, 324, 325, 189, 325, 237, 189, 237, 326, 189, 326, 327, 189, 327, 328, 189, 328, 238, 189, 238, 329, 189, 329, 330, 189, 330, 331, 189, 331, 191, 189, 191, 332, 189, 332, 333, 189, 333, 334, 189, 334, 239, 189, 239, 335, 189, 335, 336, 189, 336, 337, 189, 337, 240, 189, 240, 338, 189, 338, 339, 189, 339, 340, 189, 340, 241, 189, 241, 341, 189, 341, 342, 189, 342, 343, 189, 343, 192, 189, 192, 344, 189, 344, 345, 189, 345, 346, 189, 346, 242, 189, 242, 347, 189, 347, 348, 189, 348, 349, 189, 349, 243, 189, 243, 350, 189, 350, 351, 189, 351, 352, 189, 352, 244, 189, 244, 353, 189, 353, 354, 189, 354, 355, 189, 355, 193, 189, 193, 356, 189, 356, 357, 189, 357, 358, 189, 358, 245, 189, 245, 359, 189, 359, 360, 189, 360, 361, 189, 361, 246, 189, 246, 362, 189, 362, 363, 189, 363, 364, 189, 364, 247, 189, 247, 365, 189, 365, 366, 189, 366, 367, }; //-------------------------------------------------------------------------- // // O(1) LOD range lookup table // #define LOD_TABLE_ENTRIES 16384 #define LOD_TABLE_ENTRY_WIDTH (1.0f / LOD_TABLE_ENTRIES) class LODTable { float m_lodRange; float m_lodOffset; float m_lodEntryWidth; IndexedShape *m_lodTable[LOD_TABLE_ENTRIES]; public: void makeTable(IndexedShape *shapes[], float transitions[], int shapeCount); IndexedShape *lookupLOD(float lod); }; // // transitions[] must be increasing in value with increasing index. // transition count is necessarily "shapeCount - 1". // void LODTable::makeTable(IndexedShape *shapes[], float transitions[], int shapeCount) { int i, lod; m_lodRange = (transitions[shapeCount - 2] - transitions[0]) / (LOD_TABLE_ENTRIES - 2) * LOD_TABLE_ENTRIES; m_lodOffset = transitions[0] - m_lodRange / LOD_TABLE_ENTRIES; m_lodTable[0] = shapes[0]; m_lodTable[LOD_TABLE_ENTRIES - 1] = shapes[shapeCount - 1]; lod = 1; for(i = 1; i < LOD_TABLE_ENTRIES - 1; i++) { float value = m_lodOffset + m_lodRange * (i + .5f) / LOD_TABLE_ENTRIES; while(value > transitions[lod]) lod++; m_lodTable[i] = shapes[lod]; } } IndexedShape *LODTable::lookupLOD(float lodvalue) { float normalized = (lodvalue - m_lodOffset) / m_lodRange; normalized = clamp(0.0f, normalized, 1.0f); int bin = (int)(normalized * LOD_TABLE_ENTRIES); bin = minimum(LOD_TABLE_ENTRIES - 1, bin); return m_lodTable[bin]; } //-------------------------------------------------------------------------- #define FRUSTUM_LEFT 0 #define FRUSTUM_RIGHT 1 #define FRUSTUM_BOTTOM 2 #define FRUSTUM_TOP 3 #define FRUSTUM_NEAR 4 #define FRUSTUM_FAR 5 #define VIEWPORT_X 0 #define VIEWPORT_Y 1 #define VIEWPORT_WIDTH 2 #define VIEWPORT_HEIGHT 3 // // Example LODEvaluator class - called by drawSpheres, drawCylinders below // class LODEvaluator { // Update caching parameters used for LOD calc // Application defined part of this class. float m_modelview[16]; float m_frustum[6]; bool m_isOrtho; int m_viewport[4]; // Cached parameters for LOD value calculation float m_eye[3]; /* eyepoint in modelview space */ float m_viewdir[3]; /* view direction in modelview space */ float m_pixelScale; /* viewport width in pixels * frustnear / frustwidth */ // additional params here... public: void setModelView(float modelview[16]); void setFrustum(float frustum[6]); void setOrtho(float ortho[6]); void setViewport(int viewport[4]); void update(); // additional param setting... // This is the real implementation meat - how is LOD calculated? float calcSphereLODValue(float center[3], float radius); float calcCylinderLODValue(float pos1[3], float pos2[3], float radius); }; // This implementation uses width scaled by distance and viewport width // to approximate pixel footprint float LODEvaluator::calcSphereLODValue(float center[3], float radius) { float pixels; // // The following "if" statement might be an argument for a virtual // function, and a different object for "ortho" LOD // if(m_isOrtho) { /* find projection of 2*radius onto image plane */ pixels = m_pixelScale * radius; } else { float centervec[3]; float projection; /* find projection of center onto view direction */ vec3fBlend(center, 1.0f, m_eye, -1.0f, centervec); projection = vec3fDot(centervec, m_viewdir); /* find projection of 2*radius onto image plane */ pixels = m_pixelScale * radius / projection; } return pixels; } // This implementation uses width scaled by distance and viewport width // to approximate pixel size float LODEvaluator::calcCylinderLODValue(float pos1[3], float pos2[3], float radius) { float pixels; // // The following "if" statement might be an argument for a virtual // function, and a different object for "ortho" LOD // if(m_isOrtho) { /* find projection of 2*radius onto image plane */ pixels = m_pixelScale * radius; } else { float center[3]; float centervec[3]; float projection; vec3fBlend(pos1, .5f, pos2, .5f, center); /* find projection of center onto view direction */ vec3fBlend(center, 1.0f, m_eye, -1.0f, centervec); projection = vec3fDot(centervec, m_viewdir); /* find projection of 2*radius onto image plane */ pixels = m_pixelScale * radius / projection; } return pixels; } void LODEvaluator::update() { static float eye[] = {0.0f, 0.0f, 0.0f}; float view[3]; static float origin[] = {0.0f, 0.0f, 0.0f}; static float radius[] = {1.0f, 0.0f, 0.0f}; float view2[3], neworigin[3], newradius[3]; float localNearClip = 1.0f; float inverse[16]; if(!m_isOrtho) { view[0] = 0.0f; view[1] = 0.0f; view[2] = -m_frustum[FRUSTUM_NEAR]; mat4fInvert(m_modelview, inverse); mat4fMultVec3fPt(inverse, eye, m_eye); mat4fMultVec3fPt(inverse, view, view2); vec3fBlend(view2, 1.0f, m_eye, -1.0f, m_viewdir); localNearClip = vec3fLength(m_viewdir); vec3fNormalize(m_viewdir, m_viewdir); } // We assume uniform scale below // (Should do this matrix math with inverse transpose) mat4fMultVec3fPt(m_modelview, origin, neworigin); mat4fMultVec3fPt(m_modelview, radius, newradius); vec3fBlend(newradius, 1.0, neworigin, -1.0, newradius); m_pixelScale = vec3fLength(newradius) * 2 * m_viewport[VIEWPORT_WIDTH] * localNearClip / (m_frustum[FRUSTUM_RIGHT] - m_frustum[FRUSTUM_LEFT]); } void LODEvaluator::setFrustum(float frustum[6]) { memcpy(m_frustum, frustum, sizeof(m_frustum)); m_isOrtho = false; } void LODEvaluator::setOrtho(float ortho[6]) { memcpy(m_frustum, ortho, sizeof(m_frustum)); m_isOrtho = true; } void LODEvaluator::setModelView(float modelview[16]) { memcpy(m_modelview, modelview, sizeof(m_modelview)); } void LODEvaluator::setViewport(int viewport[4]) { memcpy(m_viewport, viewport, sizeof(m_viewport)); } // // Top level class representing a "renderer". Creates objects, sorts // them, knows how to sort by material and set up their materials, // evaluate LOD, and draw objects. // struct Sphere { float m_color[4]; float m_nameUInt; float m_center[3]; float m_radius; }; struct Cylinder { float m_color[4]; float m_nameUInt; float m_cappedBool; float m_pos1[3]; float m_pos2[3]; float m_radius; }; class ShapeRenderer { VertexData *m_vertexData; ElementData *m_elementData; IndexedShape *m_cylinderShapes; LODTable cylinderOpenLODs; LODTable cylinderClosedLODs; IndexedShape *m_forcedClosedCylinder; IndexedShape *m_forcedOpenCylinder; IndexedShape *m_sphereShapes; IndexedShape *m_forcedSphere; LODTable sphereLODs; float m_lodScale; bool m_useLOD; float m_materialWhiteness; float m_materialBrightness; float m_materialShininess; void applyMaterial(float color[4]); GLContext *m_gl; int m_VBOenabled; public: int getInteger(int what); ShapeRenderer() : m_lodScale(1.0f), m_useLOD(true), m_materialWhiteness(1.0f), m_materialBrightness(1.0f), m_materialShininess(20.0f) {}; LODEvaluator lodeval; bool init(GLContext *gl); void startDrawing(); void finishDrawing(); bool drawSpheres(int count, Sphere *spheres); bool drawCylinders(int count, Cylinder *spheres); bool drawSpheres(int count, float center[][3], float radius[], float color[][4], unsigned int *names); bool drawCylinders(int count, float pos1[][3], float pos2[][3], float radius[], int capped[], float color[][4], unsigned int *names); void setLODScale(float s) { m_lodScale = s; } void setUseDynamicLOD(bool use) { m_useLOD = use; } void setStaticLODLevels(int sphereLOD, int cylinderLOD); float getLODScale(void) { return m_lodScale; } void setMaterialParameters(float whiteness, float brightness, float shininess); }; int ShapeRenderer::getInteger(int what) { int v = -1; switch(what) { case IS_VBO_ENABLED: v = m_VBOenabled; break; default: v = -1; } return v; } void ShapeRenderer::setMaterialParameters(float whiteness, float brightness, float shininess) { m_materialWhiteness = whiteness; m_materialBrightness = brightness; m_materialShininess = shininess; } void ShapeRenderer::applyMaterial(float color[4]) { float specular[4]; specular[0] = m_materialBrightness * (color[0] * (1 - m_materialWhiteness) + 1 * m_materialWhiteness); specular[1] = m_materialBrightness * (color[1] * (1 - m_materialWhiteness) + 1 * m_materialWhiteness); specular[2] = m_materialBrightness * (color[2] * (1 - m_materialWhiteness) + 1 * m_materialWhiteness); specular[3] = color[3]; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); } bool ShapeRenderer::init(GLContext *gl) { DataObject *obj; unsigned int u; void *verticesMerged; void *elementsMerged; ptrdiff_t sphereVerticesOffset; ptrdiff_t sphereElementsOffset; size_t verticesMergedTotalSize; ptrdiff_t cylinderVerticesOffset; ptrdiff_t cylinderElementsOffset; size_t elementsMergedTotalSize; m_gl = gl; m_vertexData = new VertexData; m_elementData = new ElementData; m_elementData->m_type = GL_UNSIGNED_INT; verticesMerged = (void *)malloc( sizeof(sphereVertices) + sizeof(cylinderVertices)); if(verticesMerged == NULL) return false; sphereVerticesOffset = 0; cylinderVerticesOffset = sizeof(sphereVertices); memcpy((unsigned char *)verticesMerged + sphereVerticesOffset, sphereVertices, sizeof(sphereVertices)); memcpy((unsigned char *)verticesMerged + cylinderVerticesOffset, cylinderVertices, sizeof(cylinderVertices)); verticesMergedTotalSize = sizeof(sphereVertices) + sizeof(cylinderVertices); elementsMerged = (void *)malloc( sizeof(sphereElements) + sizeof(cylinderElements)); if(elementsMerged == NULL) return false; sphereElementsOffset = 0; cylinderElementsOffset = sizeof(sphereElements); memcpy((unsigned char *)elementsMerged + sphereElementsOffset, sphereElements, sizeof(sphereElements)); memcpy((unsigned char *)elementsMerged + cylinderElementsOffset, cylinderElements, sizeof(cylinderElements)); elementsMergedTotalSize = sizeof(sphereElements) + sizeof(cylinderElements); /* fix up cylinder elements since they expect to point to beginning of vertices */ int *newCylElem = (int *)elementsMerged + sizeof(sphereElements) / sizeof(sphereElements[0]); int difference = sizeof(sphereVertices) / sizeof(sphereVertices[0]); for(u = 0; u < sizeof(cylinderElements) / sizeof(cylinderElements[0]); u++) newCylElem[u] += difference; // XXX test // gl->has_GL_ARB_vertex_buffer_object = false; // XXX fix for display list creation crash while integrated into // ColorSorter. Take this out when display listing is removed. if(strncmp((char *)glGetString(GL_VENDOR), "ATI", 3) == 0) { fprintf(stderr, "WARNING - ATI OpenGL, Disabling Vertex Buffer Object\n"); gl->has_GL_ARB_vertex_buffer_object = false; } if(gl->has_GL_ARB_vertex_buffer_object) { m_VBOenabled = 1; obj = new DataObject(gl); obj->fill(GL_ARRAY_BUFFER_ARB, verticesMergedTotalSize, verticesMerged); m_vertexData->m_data = obj; obj = new DataObject(gl); obj->fill(GL_ELEMENT_ARRAY_BUFFER_ARB, elementsMergedTotalSize, elementsMerged); m_elementData->m_data = obj; } else { m_VBOenabled = 0; m_vertexData->m_data = new DataArray(verticesMerged); m_elementData->m_data = new DataArray(elementsMerged); } m_vertexData->m_vertexArray.set(3, GL_FLOAT, sizeof(Vertex), offsetof(Vertex, v)); m_vertexData->m_normalArray.set(3, GL_FLOAT, sizeof(Vertex), offsetof(Vertex, n)); // Sphere --------------------------------------------------------------- unsigned int spherePrims = sizeof(sphereTrianglesOffsetCount) / sizeof(sphereTrianglesOffsetCount[0]); m_sphereShapes = new IndexedShape[spherePrims]; for(u = 0; u < spherePrims; u++) { m_sphereShapes[u].m_vertexData = m_vertexData; m_sphereShapes[u].m_elementData = m_elementData; bool success = m_sphereShapes[u].add(GL_TRIANGLES, sizeof(sphereElements[0]) * sphereTrianglesOffsetCount[u][0], sphereTrianglesOffsetCount[u][1]); if(!success) return false; } // XXX Kind of sucks that the following is hardcoded, but the // above is not. IndexedShape *sphereShapePtrs[] = {&m_sphereShapes[0], &m_sphereShapes[1], &m_sphereShapes[2]}; float sphereTransitions[] = {14, 28}; sphereLODs.makeTable(sphereShapePtrs, sphereTransitions, 3); int sphereLOD = 1; if(getenv("SPHERE_LOD")) sphereLOD = atoi(getenv("SPHERE_LOD")); m_forcedSphere = m_sphereShapes + sphereLOD; // Cylinder ------------------------------------------------------------- unsigned int cylinderPrims = sizeof(cylinderTrianglesOffsetCount) / sizeof(cylinderTrianglesOffsetCount[0]); m_cylinderShapes = new IndexedShape[cylinderPrims]; for(u = 0; u < cylinderPrims; u++) { m_cylinderShapes[u].m_vertexData = m_vertexData; m_cylinderShapes[u].m_elementData = m_elementData; bool success = m_cylinderShapes[u].add(GL_TRIANGLES, cylinderElementsOffset + sizeof(cylinderElements[0]) * cylinderTrianglesOffsetCount[u][0], cylinderTrianglesOffsetCount[u][1]); if(!success) return false; } // XXX Kind of sucks that the following is hardcoded, but the above // is not. IndexedShape *cylinderOpenShapePtrs[] = {&m_cylinderShapes[0], &m_cylinderShapes[1], &m_cylinderShapes[2], &m_cylinderShapes[3]}; float cylinderTransitions[] = {10, 14, 28}; cylinderOpenLODs.makeTable(cylinderOpenShapePtrs, cylinderTransitions, 4); IndexedShape *cylinderClosedShapePtrs[] = {&m_cylinderShapes[4], &m_cylinderShapes[5], &m_cylinderShapes[6], &m_cylinderShapes[7]}; cylinderClosedLODs.makeTable(cylinderClosedShapePtrs, cylinderTransitions, 4); int cylLOD = 1; if(getenv("CYLINDER_LOD") != NULL) cylLOD = atoi(getenv("CYLINDER_LOD")); m_forcedOpenCylinder = m_cylinderShapes + cylLOD; m_forcedClosedCylinder = m_cylinderShapes + 4 + cylLOD; return true; } void ShapeRenderer::setStaticLODLevels(int sphereLOD, int cylinderLOD) { if(sphereLOD < 0 || cylinderLOD < 0) return; // XXX should trigger an exception here somehow m_forcedSphere = m_sphereShapes + sphereLOD; m_forcedOpenCylinder = m_cylinderShapes + cylinderLOD; m_forcedClosedCylinder = m_cylinderShapes + 4 + cylinderLOD; } void ShapeRenderer::startDrawing() { m_vertexData->apply(); m_elementData->apply(); } void ShapeRenderer::finishDrawing() { m_elementData->unapply(); m_vertexData->unapply(); } bool ShapeRenderer::drawSpheres(int count, Sphere *spheres) { float lodvalue; int i, j; int uniqueColorCount; ColorSortedListHead *colorSortedListHeads; int *objectNext; glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m_materialShininess); /* This appears to be around 10% faster than draw unsorted on 2GHz + FireGL X1 for SRG-1c Tubes */ if(!sortByColor(0, count, spheres, &uniqueColorCount, &colorSortedListHeads, &objectNext)) { /* allocation failed */ return false; } for(j = 0; j < uniqueColorCount; j++) { applyMaterial(colorSortedListHeads[j].m_color); i = colorSortedListHeads[j].m_firstObject; while(i != -1) { Sphere *sph = spheres + i; if(sph->m_radius != 0.0f) { glPushMatrix(); glTranslatef(sph->m_center[0], sph->m_center[1], sph->m_center[2]); glScalef(sph->m_radius, sph->m_radius, sph->m_radius); unsigned int name = (int)sph->m_nameUInt; if(name != 0) glPushName(name); if(m_useLOD) { lodvalue = m_lodScale * lodeval.calcSphereLODValue(sph->m_center, sph->m_radius); IndexedShape *lod = sphereLODs.lookupLOD(lodvalue); #if 0 // LOD Vis static float lodcolors[][4] = {{1, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}}; static float black[] = {0, 0, 0, 1}; glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, black); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, lodcolors[(lod - m_sphereShapes) % 3]); #endif lod->draw(); } else { m_forcedSphere->draw(); } if(name != 0) glPopName(); glPopMatrix(); } i = objectNext[i]; } } return true; } bool ShapeRenderer::drawSpheres(int count, float center[][3], float radius[], float color[][4], unsigned int *names) { float lodvalue; int i, j; int uniqueColorCount; ColorSortedListHead *colorSortedListHeads; int *objectNext; glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m_materialShininess); /* This appears to be around 10% faster than draw unsorted on 2GHz + FireGL X1 for SRG-1c Tubes */ if(!sortByColor(0, count, (Color *)color, &uniqueColorCount, &colorSortedListHeads, &objectNext)) { /* allocation failed */ return false; } for(j = 0; j < uniqueColorCount; j++) { applyMaterial(colorSortedListHeads[j].m_color); i = colorSortedListHeads[j].m_firstObject; while(i != -1) { if(radius[i] != 0.0f) { glPushMatrix(); glTranslatef(center[i][0], center[i][1], center[i][2]); glScalef(radius[i], radius[i], radius[i]); if((names != NULL) && (names[i] != 0)) glPushName(names[i]); if(m_useLOD) { lodvalue = m_lodScale * lodeval.calcSphereLODValue(center[i], radius[i]); IndexedShape *lod = sphereLODs.lookupLOD(lodvalue); #if 0 // LOD Vis static float lodcolors[][4] = {{1, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}}; static float black[] = {0, 0, 0, 1}; glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, black); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, lodcolors[(lod - m_sphereShapes) % 3]); #endif lod->draw(); } else { m_forcedSphere->draw(); } if((names != NULL) && (names[i] != 0)) glPopName(); glPopMatrix(); } i = objectNext[i]; } } return true; } void applyCylinderMatrix(float pos1[3], float pos2[3], float radius) { float cylAxis[3]; float cylLength; float angle; glTranslatef(pos1[0], pos1[1], pos1[2]); vec3fBlend(pos2, 1.0f, pos1, -1.0f, cylAxis); cylLength = vec3fLength(cylAxis); vec3fNormalize(cylAxis, cylAxis); if (cylAxis[2] < .99999f) { angle = -(float)acos((float)cylAxis[2]) * 180.0f / (float)M_PI; glRotatef(angle, cylAxis[1], -cylAxis[0], 0.0); } glScalef(radius, radius, cylLength); } bool ShapeRenderer::drawCylinders(int count, Cylinder *cylinders) { int i; float lodvalue; int uniqueColorCount; ColorSortedListHead *colorSortedListHeads; int *objectNext; glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m_materialShininess); /* This appears to be around 11% faster than draw unsorted on 2GHz + FireGL X1 for SRG-1c Tubes */ int k; if(!sortByColor(0, count, cylinders, &uniqueColorCount, &colorSortedListHeads, &objectNext)) /* allocation failed */ return false; for(k = 0; k < uniqueColorCount; k++) { applyMaterial(colorSortedListHeads[k].m_color); i = colorSortedListHeads[k].m_firstObject; while(i != -1) { Cylinder *cyl = cylinders + i; if(cyl->m_radius != 0.0f) { glPushMatrix(); applyCylinderMatrix(cyl->m_pos1, cyl->m_pos2, cyl->m_radius); unsigned int name = (int)cyl->m_nameUInt; if(name != 0) glPushName(name); if(m_useLOD) { lodvalue = m_lodScale * lodeval.calcCylinderLODValue(cyl->m_pos1, cyl->m_pos2, cyl->m_radius); IndexedShape *lod; if((int)cyl->m_cappedBool) lod = cylinderClosedLODs.lookupLOD(lodvalue); else lod = cylinderOpenLODs.lookupLOD(lodvalue); #if 0 // LOD Vis static float lodcolors[][4] = {{1, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}}; static float black[] = {0, 0, 0, 1}; glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, black); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, lodcolors[(lod - m_cylinderShapes) % 3]); #endif lod->draw(); } else { if((int)cyl->m_cappedBool) m_forcedClosedCylinder->draw(); else m_forcedOpenCylinder->draw(); } if(name != 0) glPopName(); glPopMatrix(); } i = objectNext[i]; } } return true; } bool ShapeRenderer::drawCylinders(int count, float pos1[][3], float pos2[][3], float radius[], int capped[], float color[][4], unsigned int *names) { int i; float lodvalue; int uniqueColorCount; ColorSortedListHead *colorSortedListHeads; int *objectNext; glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m_materialShininess); /* This appears to be around 11% faster than draw unsorted on 2GHz + FireGL X1 for SRG-1c Tubes */ int k; if(!sortByColor(0, count, (Color *)color, &uniqueColorCount, &colorSortedListHeads, &objectNext)) /* allocation failed */ return false; for(k = 0; k < uniqueColorCount; k++) { applyMaterial(colorSortedListHeads[k].m_color); i = colorSortedListHeads[k].m_firstObject; while(i != -1) { if(radius[i] != 0.0f) { glPushMatrix(); applyCylinderMatrix(pos1[i], pos2[i], radius[i]); if((names != NULL) && (names[i] != 0)) glPushName(names[i]); if(m_useLOD) { lodvalue = m_lodScale * lodeval.calcCylinderLODValue(pos1[i], pos2[i], radius[i]); IndexedShape *lod; if(capped[i]) lod = cylinderClosedLODs.lookupLOD(lodvalue); else lod = cylinderOpenLODs.lookupLOD(lodvalue); #if 0 // LOD Vis static float lodcolors[][4] = {{1, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}}; static float black[] = {0, 0, 0, 1}; glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, black); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, lodcolors[(lod - m_cylinderShapes) % 3]); #endif lod->draw(); } else { if(capped[i]) m_forcedClosedCylinder->draw(); else m_forcedOpenCylinder->draw(); } if((names != NULL) && (names[i] != 0)) glPopName(); glPopMatrix(); } i = objectNext[i]; } } return true; } static ShapeRenderer g_renderer; static GLContext *g_gl; static bool g_initialized = false; #undef PRINT_GL_ERROR extern "C" { PyObject * shapeRendererInit() { if(!g_initialized) { g_gl = new GLContext; bool succeeded = g_renderer.init(g_gl); #if defined(PRINT_GL_ERROR) GLenum error = glGetError(); if(error != GL_NO_ERROR) printf("Error: %04X\n", error); fflush(stdout); #endif if(!succeeded) { PyErr_SetString(PyExc_RuntimeError, "OpenGL init did not succeeed"); return NULL; } g_initialized = true; } Py_INCREF(Py_None); return Py_None; } void shapeRendererStartDrawing() { g_renderer.startDrawing(); #if defined(PRINT_GL_ERROR) GLenum error = glGetError(); if(error != GL_NO_ERROR) printf("Error: %04X\n", error); fflush(stdout); #endif } void shapeRendererFinishDrawing() { g_renderer.finishDrawing(); #if defined(PRINT_GL_ERROR) GLenum error = glGetError(); if(error != GL_NO_ERROR) printf("Error: %04X\n", error); fflush(stdout); #endif } void shapeRendererSetFrustum(float frustum[6]) { g_renderer.lodeval.setFrustum(frustum); } void shapeRendererSetOrtho(float ortho[6]) { g_renderer.lodeval.setOrtho(ortho); } void shapeRendererSetViewport(int viewport[4]) { g_renderer.lodeval.setViewport(viewport); } void shapeRendererSetModelView(float modelview[6]) { g_renderer.lodeval.setModelView(modelview); } void shapeRendererUpdateLODEval() { g_renderer.lodeval.update(); } PyObject * shapeRendererDrawSpheresIlvd(int count, float *data) { bool succeeded = g_renderer.drawSpheres(count, (struct Sphere *)data); #if defined(PRINT_GL_ERROR) GLenum error = glGetError(); if(error != GL_NO_ERROR) printf("Error: %04X\n", error); fflush(stdout); #endif if(!succeeded) { PyErr_SetString(PyExc_RuntimeError, "shapeRendererDrawSpheresIlvd did not succeeed"); return NULL; } Py_INCREF(Py_None); return Py_None; } PyObject * shapeRendererDrawSpheres(int count, float center[][3], float radius[], float color[][4], unsigned int *names) { bool succeeded = g_renderer.drawSpheres(count, center, radius, color, names); #if defined(PRINT_GL_ERROR) GLenum error = glGetError(); if(error != GL_NO_ERROR) printf("Error: %04X\n", error); fflush(stdout); #endif if(!succeeded) { PyErr_SetString(PyExc_RuntimeError, "OpenGL drawSpheres did not succeeed"); return NULL; } Py_INCREF(Py_None); return Py_None; } PyObject * shapeRendererDrawCylindersIlvd(int count, float *cylinders) { bool succeeded = g_renderer.drawCylinders(count, (Cylinder *)cylinders); #if defined(PRINT_GL_ERROR) GLenum error = glGetError(); if(error != GL_NO_ERROR) printf("Error: %04X\n", error); fflush(stdout); #endif if(!succeeded) { PyErr_SetString(PyExc_RuntimeError, "shapeRendererDrawCylindersIlvd did not succeeed"); return NULL; } Py_INCREF(Py_None); return Py_None; } PyObject * shapeRendererDrawCylinders(int count, float pos1[][3], float pos2[][3], float radius[], int capped[], float color[][4], unsigned int *names) { bool succeeded = g_renderer.drawCylinders(count, pos1, pos2, radius, capped, color, names); #if defined(PRINT_GL_ERROR) GLenum error = glGetError(); if(error != GL_NO_ERROR) printf("Error: %04X\n", error); fflush(stdout); #endif if(!succeeded) { PyErr_SetString(PyExc_RuntimeError, "OpenGL drawCylinders did not succeeed"); return NULL; } Py_INCREF(Py_None); return Py_None; } void shapeRendererSetLODScale(float s) { g_renderer.setLODScale(s); } void shapeRendererSetUseDynamicLOD(int useLOD) { g_renderer.setUseDynamicLOD(useLOD ? true : false); } void shapeRendererSetStaticLODLevels(int sphereLOD, int cylinderLOD) { g_renderer.setStaticLODLevels(sphereLOD, cylinderLOD); } void shapeRendererSetMaterialParameters(float whiteness, float brightness, float shininess) { g_renderer.setMaterialParameters(whiteness, brightness, shininess); } int shapeRendererGetInteger(int what) { return g_renderer.getInteger(what); } };