panda3d/panda/src/glstuff/glGraphicsStateGuardian_src.cxx
2004-12-30 21:06:42 +00:00

6107 lines
198 KiB
C++

// Filename: glGraphicsStateGuardian_src.cxx
// Created by: drose (02Feb99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "config_util.h"
#include "displayRegion.h"
#include "renderBuffer.h"
#include "geom.h"
#include "geomIssuer.h"
#include "graphicsWindow.h"
#include "lens.h"
#include "perspectiveLens.h"
#include "directionalLight.h"
#include "pointLight.h"
#include "spotlight.h"
#include "planeNode.h"
#include "textureAttrib.h"
#include "lightAttrib.h"
#include "cullFaceAttrib.h"
#include "transparencyAttrib.h"
#include "alphaTestAttrib.h"
#include "depthTestAttrib.h"
#include "depthWriteAttrib.h"
#include "colorWriteAttrib.h"
#include "texMatrixAttrib.h"
#include "texGenAttrib.h"
#include "cgShaderAttrib.h"
#include "materialAttrib.h"
#include "renderModeAttrib.h"
#include "rescaleNormalAttrib.h"
#include "fogAttrib.h"
#include "depthOffsetAttrib.h"
#include "fog.h"
#include "clockObject.h"
#include "string_utils.h"
#include "nodePath.h"
#include "dcast.h"
#include "pvector.h"
#include "vector_string.h"
#include "string_utils.h"
#ifdef DO_PSTATS
#include "pStatTimer.h"
#endif
#include <algorithm>
TypeHandle CLP(GraphicsStateGuardian)::_type_handle;
#ifdef DO_PSTATS
PStatCollector CLP(GraphicsStateGuardian)::_vertices_display_list_pcollector("Vertices:Display lists");
#endif
static void
issue_vertex_gl(const Geom *geom, Geom::VertexIterator &viterator,
GraphicsStateGuardianBase *) {
const Vertexf &vertex = geom->get_next_vertex(viterator);
// GLCAT.spam() << "Issuing vertex " << vertex << "\n";
GLP(Vertex3fv)(vertex.get_data());
}
static void
issue_normal_gl(const Geom *geom, Geom::NormalIterator &niterator,
GraphicsStateGuardianBase *) {
const Normalf &normal = geom->get_next_normal(niterator);
// GLCAT.spam() << "Issuing normal " << normal << "\n";
GLP(Normal3fv)(normal.get_data());
}
static void
issue_texcoord_single_gl(const Geom *geom,
Geom::MultiTexCoordIterator &tciterator,
GraphicsStateGuardianBase *) {
const TexCoordf &texcoord = geom->get_next_multitexcoord(tciterator, 0);
// GLCAT.spam() << "Issuing texcoord " << texcoord << " on unit 0 (single-texture mode)\n";
GLP(TexCoord2fv)(texcoord.get_data());
}
static void
issue_texcoord_multi_gl(const Geom *geom,
Geom::MultiTexCoordIterator &tciterator,
GraphicsStateGuardianBase *gsgbase) {
CLP(GraphicsStateGuardian) *gsg;
DCAST_INTO_V(gsg, gsgbase);
for (int i = 0; i < tciterator._num_stages; i++) {
const TexCoordf &texcoord = geom->get_next_multitexcoord(tciterator, i);
int stage_index = tciterator._stage_index[i];
// GLCAT.spam() << "Issuing texcoord " << texcoord << " on unit " << stage_index << "\n";
gsg->_glMultiTexCoord2fv(GL_TEXTURE0 + stage_index, texcoord.get_data());
}
}
static void
issue_color_gl(const Geom *geom, Geom::ColorIterator &citerator,
GraphicsStateGuardianBase *) {
const Colorf &color = geom->get_next_color(citerator);
// GLCAT.spam() << "Issuing color " << color << "\n";
GLP(Color4fv)(color.get_data());
}
static void
issue_transformed_color_gl(const Geom *geom, Geom::ColorIterator &citerator,
GraphicsStateGuardianBase *gsg) {
const CLP(GraphicsStateGuardian) *glgsg = DCAST(CLP(GraphicsStateGuardian), gsg);
const Colorf &color = geom->get_next_color(citerator);
glgsg->issue_transformed_color(color);
}
// The following noop functions are assigned to the corresponding
// glext function pointers in the class, in case the functions are not
// defined by the GL, just so it will always be safe to call the
// extension functions.
static void APIENTRY
null_glActiveTexture(GLenum gl_texture_stage) {
// If we don't support multitexture, we'd better not try to request
// a texture beyond the first texture stage.
nassertv(gl_texture_stage == GL_TEXTURE0);
}
static void APIENTRY
null_glBlendEquation(GLenum) {
}
static void APIENTRY
null_glBlendColor(GLclampf, GLclampf, GLclampf, GLclampf) {
}
////////////////////////////////////////////////////////////////////
// Function: uchar_bgr_to_rgb
// Description: Recopies the given array of pixels, converting from
// BGR to RGB arrangement.
////////////////////////////////////////////////////////////////////
static void
uchar_bgr_to_rgb(unsigned char *dest, const unsigned char *source,
int num_pixels) {
for (int i = 0; i < num_pixels; i++) {
dest[0] = source[2];
dest[1] = source[1];
dest[2] = source[0];
dest += 3;
source += 3;
}
}
////////////////////////////////////////////////////////////////////
// Function: uchar_bgra_to_rgba
// Description: Recopies the given array of pixels, converting from
// BGRA to RGBA arrangement.
////////////////////////////////////////////////////////////////////
static void
uchar_bgra_to_rgba(unsigned char *dest, const unsigned char *source,
int num_pixels) {
for (int i = 0; i < num_pixels; i++) {
dest[0] = source[2];
dest[1] = source[1];
dest[2] = source[0];
dest[3] = source[3];
dest += 4;
source += 4;
}
}
////////////////////////////////////////////////////////////////////
// Function: ushort_bgr_to_rgb
// Description: Recopies the given array of pixels, converting from
// BGR to RGB arrangement.
////////////////////////////////////////////////////////////////////
static void
ushort_bgr_to_rgb(unsigned short *dest, const unsigned short *source,
int num_pixels) {
for (int i = 0; i < num_pixels; i++) {
dest[0] = source[2];
dest[1] = source[1];
dest[2] = source[0];
dest += 3;
source += 3;
}
}
////////////////////////////////////////////////////////////////////
// Function: ushort_bgra_to_rgba
// Description: Recopies the given array of pixels, converting from
// BGRA to RGBA arrangement.
////////////////////////////////////////////////////////////////////
static void
ushort_bgra_to_rgba(unsigned short *dest, const unsigned short *source,
int num_pixels) {
for (int i = 0; i < num_pixels; i++) {
dest[0] = source[2];
dest[1] = source[1];
dest[2] = source[0];
dest[3] = source[3];
dest += 4;
source += 4;
}
}
////////////////////////////////////////////////////////////////////
// Function: fix_component_ordering
// Description: Reverses the order of the components within the
// image, to convert (for instance) GL_BGR to GL_RGB.
// Returns the PTA_uchar representing the converted
// image, or the original image if it is unchanged.
////////////////////////////////////////////////////////////////////
static PTA_uchar
fix_component_ordering(GLenum external_format, PixelBuffer *pb) {
PTA_uchar new_image = pb->_image;
switch (external_format) {
case GL_RGB:
switch (pb->get_image_type()) {
case PixelBuffer::T_unsigned_byte:
new_image = PTA_uchar::empty_array(pb->_image.size());
uchar_bgr_to_rgb(new_image, pb->_image, pb->_image.size() / 3);
break;
case PixelBuffer::T_unsigned_short:
new_image = PTA_uchar::empty_array(pb->_image.size());
ushort_bgr_to_rgb((unsigned short *)new_image.p(),
(unsigned short *)pb->_image.p(),
pb->_image.size() / 6);
break;
default:
break;
}
break;
case GL_RGBA:
switch (pb->get_image_type()) {
case PixelBuffer::T_unsigned_byte:
new_image = PTA_uchar::empty_array(pb->_image.size());
uchar_bgra_to_rgba(new_image, pb->_image, pb->_image.size() / 4);
break;
case PixelBuffer::T_unsigned_short:
new_image = PTA_uchar::empty_array(pb->_image.size());
ushort_bgra_to_rgba((unsigned short *)new_image.p(),
(unsigned short *)pb->_image.p(),
pb->_image.size() / 8);
break;
default:
break;
}
break;
default:
break;
}
return new_image;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
CLP(GraphicsStateGuardian)::
CLP(GraphicsStateGuardian)(const FrameBufferProperties &properties) :
GraphicsStateGuardian(properties, CS_yup_right)
{
_error_count = 0;
#ifdef HAVE_CGGL
_cg_shader = (CgShader *)NULL;
#endif
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
CLP(GraphicsStateGuardian)::
~CLP(GraphicsStateGuardian)() {
close_gsg();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::reset
// Access: Public, Virtual
// Description: Resets all internal state as if the gsg were newly
// created.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
reset() {
free_pointers();
GraphicsStateGuardian::reset();
// Output the vendor and version strings.
get_gl_version();
// Save the extensions tokens.
save_extensions((const char *)GLP(GetString)(GL_EXTENSIONS));
get_extra_extensions();
report_extensions();
_supports_bgr =
has_extension("GL_EXT_bgra") || is_at_least_version(1, 2);
_supports_rescale_normal =
has_extension("GL_EXT_rescale_normal") || is_at_least_version(1, 2);
_supports_multisample =
has_extension("GL_ARB_multisample");
_supports_generate_mipmap =
has_extension("GL_SGIS_generate_mipmap") || is_at_least_version(1, 4);
_supports_multitexture = false;
if (is_at_least_version(1, 3)) {
_supports_multitexture = true;
_glActiveTexture = (PFNGLACTIVETEXTUREPROC)
get_extension_func(GLPREFIX_QUOTED, "ActiveTexture");
_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)
get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fv");
} else if (has_extension("GL_ARB_multitexture")) {
_supports_multitexture = true;
_glActiveTexture = (PFNGLACTIVETEXTUREPROC)
get_extension_func(GLPREFIX_QUOTED, "ActiveTextureARB");
_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)
get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fvARB");
}
if (_supports_multitexture) {
if (_glActiveTexture == NULL || _glMultiTexCoord2fv == NULL) {
GLCAT.warning()
<< "Multitexture advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
_supports_multitexture = false;
}
}
if (!_supports_multitexture) {
_glActiveTexture = null_glActiveTexture;
}
_glBlendEquation = NULL;
if (has_extension("GL_EXT_blend_minmax") || is_at_least_version(1, 2)) {
_glBlendEquation = (PFNGLBLENDEQUATIONPROC)
get_extension_func(GLPREFIX_QUOTED, "BlendEquationEXT");
if (_glBlendEquation == NULL) {
GLCAT.warning()
<< "BlendEquation advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
}
}
if (_glBlendEquation == NULL) {
_glBlendEquation = null_glBlendEquation;
}
_glBlendColor = NULL;
if (has_extension("GL_EXT_blend_color") || is_at_least_version(1, 2)) {
_glBlendColor = (PFNGLBLENDCOLORPROC)
get_extension_func(GLPREFIX_QUOTED, "BlendColorEXT");
if (_glBlendColor == NULL) {
GLCAT.warning()
<< "BlendColor advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n";
}
}
if (_glBlendColor == NULL) {
_glBlendColor = null_glBlendColor;
}
_edge_clamp = GL_CLAMP;
if (has_extension("GL_SGIS_texture_edge_clamp") ||
is_at_least_version(1, 2)) {
_edge_clamp = GL_CLAMP_TO_EDGE;
}
_border_clamp = GL_CLAMP;
if (has_extension("GL_ARB_texture_border_clamp") ||
is_at_least_version(1, 3)) {
_border_clamp = GL_CLAMP_TO_BORDER;
}
_mirror_repeat = GL_REPEAT;
if (has_extension("GL_ARB_texture_mirrored_repeat") ||
is_at_least_version(1, 4)) {
_mirror_repeat = GL_MIRRORED_REPEAT;
}
_mirror_clamp = GL_CLAMP;
_mirror_edge_clamp = _edge_clamp;
_mirror_border_clamp = _border_clamp;
if (has_extension("GL_EXT_texture_mirror_clamp")) {
_mirror_clamp = GL_MIRROR_CLAMP_EXT;
_mirror_edge_clamp = GL_MIRROR_CLAMP_TO_EDGE_EXT;
_mirror_border_clamp = GL_MIRROR_CLAMP_TO_BORDER_EXT;
}
if (_supports_multisample) {
GLint sample_buffers;
GLP(GetIntegerv)(GL_SAMPLE_BUFFERS, &sample_buffers);
if (sample_buffers != 1) {
// Even if the API supports multisample, we might have ended up
// with a framebuffer that doesn't have any multisample bits.
// (It's also possible the graphics card doesn't provide any
// framebuffers with multisample.) In this case, we don't
// really support the multisample API's, since they won't do
// anything.
_supports_multisample = false;
}
}
report_my_gl_errors();
_auto_rescale_normal = false;
// All GL implementations have the following buffers.
_buffer_mask = (RenderBuffer::T_color |
RenderBuffer::T_depth |
RenderBuffer::T_stencil |
RenderBuffer::T_accum);
// If we don't have double-buffering, don't attempt to write to the
// back buffer.
GLboolean has_back;
GLP(GetBooleanv)(GL_DOUBLEBUFFER, &has_back);
if (!has_back) {
_buffer_mask &= ~RenderBuffer::T_back;
}
#if 0
//Note 8/01: This is incorrect for mono displays, if stereo is not in use, we need
// to use the GL_BACK constants and not the GL_BACK_LEFT ones, which
// under the RenderBuffer flag schemes require both left and right flags set.
// Check to see if we have stereo (and therefore a right buffer).
GLboolean has_stereo;
GLP(GetBooleanv)(GL_STEREO, &has_stereo);
if (!has_stereo) {
_buffer_mask &= ~RenderBuffer::T_right;
}
#endif
// Set up the specific state values to GL's known initial values.
GLP(FrontFace)(GL_CCW);
// Set up all the enabled/disabled flags to GL's known initial
// values: everything off.
_multisample_enabled = false;
_line_smooth_enabled = false;
_point_smooth_enabled = false;
_scissor_enabled = false;
_stencil_test_enabled = false;
_multisample_alpha_one_enabled = false;
_multisample_alpha_mask_enabled = false;
_blend_enabled = false;
_depth_test_enabled = false;
_fog_enabled = false;
_alpha_test_enabled = false;
_polygon_offset_enabled = false;
_decal_level = 0;
// Dither is on by default in GL; let's turn it off
GLP(Disable)(GL_DITHER);
_dithering_enabled = false;
_texgen_forced_normal = false;
// Antialiasing.
enable_line_smooth(false);
enable_point_smooth(false);
enable_multisample(true);
#ifdef HAVE_CGGL
_cg_shader = (CgShader *)NULL;
#endif
// Count the max number of lights
GLint max_lights;
GLP(GetIntegerv)(GL_MAX_LIGHTS, &max_lights);
_max_lights = max_lights;
// Count the max number of clipping planes
GLint max_clip_planes;
GLP(GetIntegerv)(GL_MAX_CLIP_PLANES, &max_clip_planes);
_max_clip_planes = max_clip_planes;
_current_projection_mat = LMatrix4f::ident_mat();
_projection_mat_stack_count = 0;
if (_supports_multitexture) {
GLint max_texture_stages;
GLP(GetIntegerv)(GL_MAX_TEXTURE_UNITS, &max_texture_stages);
_max_texture_stages = max_texture_stages;
}
_current_texture = DCAST(TextureAttrib, TextureAttrib::make_all_off());
_current_tex_mat = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
_needs_tex_mat = false;
_current_tex_gen = DCAST(TexGenAttrib, TexGenAttrib::make());
_needs_tex_gen = false;
report_my_gl_errors();
// Make sure the GL state matches all of our initial attribute
// states.
CPT(RenderAttrib) dta = DepthTestAttrib::make(DepthTestAttrib::M_less);
CPT(RenderAttrib) dwa = DepthWriteAttrib::make(DepthWriteAttrib::M_on);
CPT(RenderAttrib) cfa = CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise);
CPT(RenderAttrib) ta = TextureAttrib::make_off();
dta->issue(this);
dwa->issue(this);
cfa->issue(this);
ta->issue(this);
Material empty;
apply_material(&empty);
if (CLP(cheap_textures)) {
GLCAT.info()
<< "Setting GLP(Hint)() for fastest textures.\n";
GLP(Hint)(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
}
// use per-vertex fog if per-pixel fog requires SW renderer
GLP(Hint)(GL_FOG_HINT, GL_DONT_CARE);
GLint num_red_bits;
GLP(GetIntegerv)(GL_RED_BITS, &num_red_bits);
if (num_red_bits < 8) {
GLP(Enable)(GL_DITHER);
_dithering_enabled = true;
if (GLCAT.is_debug()) {
GLCAT.debug()
<< "frame buffer depth = " << num_red_bits
<< " bits/channel, enabling dithering\n";
}
}
_error_count = 0;
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::clear
// Access: Public, Virtual
// Description: Clears all of the indicated buffers to their assigned
// colors.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
do_clear(const RenderBuffer &buffer) {
nassertv(buffer._gsg == this);
int buffer_type = buffer._buffer_type;
GLbitfield mask = 0;
CPT(RenderState) state = RenderState::make_empty();
if (buffer_type & RenderBuffer::T_color) {
GLP(ClearColor)(_color_clear_value[0],
_color_clear_value[1],
_color_clear_value[2],
_color_clear_value[3]);
state = state->add_attrib(ColorWriteAttrib::make(ColorWriteAttrib::M_on));
mask |= GL_COLOR_BUFFER_BIT;
set_draw_buffer(buffer);
}
if (buffer_type & RenderBuffer::T_depth) {
GLP(ClearDepth)(_depth_clear_value);
mask |= GL_DEPTH_BUFFER_BIT;
// In order to clear the depth buffer, the depth mask must enable
// writing to the depth buffer.
state = state->add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on));
}
if (buffer_type & RenderBuffer::T_stencil) {
GLP(ClearStencil)(_stencil_clear_value != false);
mask |= GL_STENCIL_BUFFER_BIT;
}
if (buffer_type & RenderBuffer::T_accum) {
GLP(ClearAccum)(_accum_clear_value[0],
_accum_clear_value[1],
_accum_clear_value[2],
_accum_clear_value[3]);
mask |= GL_ACCUM_BUFFER_BIT;
}
#ifdef GSG_VERBOSE
GLCAT.spam() << "glClear(";
if (mask & GL_COLOR_BUFFER_BIT) {
GLCAT.spam(false) << "GL_COLOR_BUFFER_BIT|";
}
if (mask & GL_DEPTH_BUFFER_BIT) {
GLCAT.spam(false) << "GL_DEPTH_BUFFER_BIT|";
}
if (mask & GL_STENCIL_BUFFER_BIT) {
GLCAT.spam(false) << "GL_STENCIL_BUFFER_BIT|";
}
if (mask & GL_ACCUM_BUFFER_BIT) {
GLCAT.spam(false) << "GL_ACCUM_BUFFER_BIT|";
}
GLCAT.spam(false) << ")" << endl;
#endif
modify_state(state);
GLP(Clear)(mask);
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::prepare_display_region
// Access: Public, Virtual
// Description: Prepare a display region for rendering (set up
// scissor region and viewport)
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
prepare_display_region() {
if (_current_display_region == (DisplayRegion*)0L) {
GLCAT.error()
<< "Invalid NULL display region in prepare_display_region()\n";
enable_scissor(false);
} else if (_current_display_region != _actual_display_region) {
_actual_display_region = _current_display_region;
int l, b, w, h;
_actual_display_region->get_region_pixels(l, b, w, h);
GLint x = GLint(l);
GLint y = GLint(b);
GLsizei width = GLsizei(w);
GLsizei height = GLsizei(h);
enable_scissor( true );
GLP(Scissor)( x, y, width, height );
GLP(Viewport)( x, y, width, height );
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::prepare_lens
// Access: Public, Virtual
// Description: Makes the current lens (whichever lens was most
// recently specified with set_scene()) active, so
// that it will transform future rendered geometry.
// Normally this is only called from the draw process,
// and usually it is called by set_scene().
//
// The return value is true if the lens is acceptable,
// false if it is not.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
prepare_lens() {
if (_current_lens == (Lens *)NULL) {
return false;
}
if (!_current_lens->is_linear()) {
return false;
}
const LMatrix4f &projection_mat = _current_lens->get_projection_mat();
// The projection matrix must always be right-handed Y-up, even if
// our coordinate system of choice is otherwise, because certain GL
// calls (specifically glTexGen(GL_SPHERE_MAP)) assume this kind of
// a coordinate system. Sigh. In order to implement a Z-up (or
// other arbitrary) coordinate system, we'll use a Y-up projection
// matrix, and store the conversion to our coordinate system of
// choice in the modelview matrix.
LMatrix4f new_projection_mat =
LMatrix4f::convert_mat(CS_yup_right, _current_lens->get_coordinate_system()) *
projection_mat;
if (_scene_setup->get_inverted()) {
// If the scene is supposed to be inverted, then invert the
// projection matrix.
static LMatrix4f invert_mat = LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
new_projection_mat *= invert_mat;
}
#ifdef GSG_VERBOSE
GLCAT.spam()
<< "glMatrixMode(GL_PROJECTION): " << new_projection_mat << endl;
#endif
GLP(MatrixMode)(GL_PROJECTION);
GLP(LoadMatrixf)(new_projection_mat.get_data());
report_my_gl_errors();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::begin_frame
// Access: Public, Virtual
// Description: Called before each frame is rendered, to allow the
// GSG a chance to do any internal cleanup before
// beginning the frame.
//
// The return value is true if successful (in which case
// the frame will be drawn and end_frame() will be
// called later), or false if unsuccessful (in which
// case nothing will be drawn and end_frame() will not
// be called).
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
begin_frame() {
if (!GraphicsStateGuardian::begin_frame()) {
return false;
}
#ifdef DO_PSTATS
_vertices_display_list_pcollector.clear_level();
#endif
_actual_display_region = NULL;
report_my_gl_errors();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::end_frame
// Access: Public, Virtual
// Description: Called after each frame is rendered, to allow the
// GSG a chance to do any internal cleanup after
// rendering the frame, and before the window flips.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
end_frame() {
GraphicsStateGuardian::end_frame();
{
#ifdef DO_PSTATS
PStatTimer timer(_flush_pcollector);
#endif
// Calling glFlush() at the end of the frame is particularly
// necessary if this is a single-buffered visual, so that the frame
// will be finished drawing before we return to the application.
// It's not clear what effect this has on our total frame time.
GLP(Flush)();
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_point
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_point(GeomPoint *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_point()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
PStatTimer timer(_draw_primitive_pcollector);
_vertices_other_pcollector.add_level(geom->get_num_vertices());
#endif
GLP(PointSize)(geom->get_size());
issue_scene_graph_color();
int nprims = geom->get_num_prims();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::NormalIterator ni = geom->make_normal_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
// Draw overall
issuer.issue_color(G_OVERALL, ci);
issuer.issue_normal(G_OVERALL, ni);
GLP(Begin)(GL_POINTS);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
issuer.issue_normal(G_PER_PRIM, ni);
// Draw per vertex, same thing.
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
GLP(End)();
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_line
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_line(GeomLine *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_line()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
PStatTimer timer(_draw_primitive_pcollector);
_vertices_other_pcollector.add_level(geom->get_num_vertices());
#endif
GLP(LineWidth)(geom->get_width());
issue_scene_graph_color();
int nprims = geom->get_num_prims();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::NormalIterator ni = geom->make_normal_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
if ((geom->get_binding(G_COLOR) == G_PER_VERTEX && wants_colors()) ||
(geom->get_binding(G_NORMAL) == G_PER_VERTEX && wants_normals())) {
GLP(ShadeModel)(GL_SMOOTH);
} else {
GLP(ShadeModel)(GL_FLAT);
}
// Draw overall
issuer.issue_color(G_OVERALL, ci);
issuer.issue_normal(G_OVERALL, ni);
GLP(Begin)(GL_LINES);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
issuer.issue_normal(G_PER_PRIM, ni);
for (int j = 0; j < 2; j++) {
// Draw per vertex
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
}
GLP(End)();
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_linestrip
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_linestrip(GeomLinestrip *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_linestrip()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
// PStatTimer timer(_draw_primitive_pcollector);
// Using PStatTimer may cause a compiler crash.
_draw_primitive_pcollector.start();
_vertices_other_pcollector.add_level(geom->get_num_vertices());
#endif
GLP(LineWidth)(geom->get_width());
issue_scene_graph_color();
int nprims = geom->get_num_prims();
const int *plen = geom->get_lengths();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::NormalIterator ni = geom->make_normal_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
if ((geom->get_binding(G_COLOR) == G_PER_VERTEX && wants_colors()) ||
(geom->get_binding(G_NORMAL) == G_PER_VERTEX && wants_normals())) {
GLP(ShadeModel)(GL_SMOOTH);
} else {
GLP(ShadeModel)(GL_FLAT);
}
// Draw overall
issuer.issue_color(G_OVERALL, ci);
issuer.issue_normal(G_OVERALL, ni);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
issuer.issue_normal(G_PER_PRIM, ni);
int num_verts = *(plen++);
nassertv(num_verts >= 2);
GLP(Begin)(GL_LINE_STRIP);
// Per-component attributes for the first line segment?
issuer.issue_color(G_PER_COMPONENT, ci);
issuer.issue_normal(G_PER_COMPONENT, ni);
// Draw the first 2 vertices
int v;
for (v = 0; v < 2; v++) {
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
// Now draw each of the remaining vertices. Each vertex from
// this point on defines a new line segment.
for (v = 2; v < num_verts; v++) {
// Per-component attributes?
issuer.issue_color(G_PER_COMPONENT, ci);
issuer.issue_normal(G_PER_COMPONENT, ni);
// Per-vertex attributes
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
GLP(End)();
}
report_my_gl_errors();
DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_sprite
// Access: Public, Virtual
// Description: CSN, 7/11/00
////////////////////////////////////////////////////////////////////
// this class exists because an alpha sort is necessary for correct
// sprite rendering, and we can't simply sort the vertex arrays as
// each vertex may or may not have corresponding information in the
// x/y texel-world-ratio and rotation arrays.
class WrappedSprite {
public:
Vertexf _v;
Colorf _c;
float _x_ratio;
float _y_ratio;
float _theta;
};
// this struct exists because the STL can sort faster than i can.
struct draw_sprite_vertex_less {
INLINE bool operator ()(const WrappedSprite& v0,
const WrappedSprite& v1) const {
return v0._v[2] < v1._v[2]; }
};
void CLP(GraphicsStateGuardian)::
draw_sprite(GeomSprite *geom, GeomContext *) {
// this is a little bit of a mess, but it's ok. Here's the deal:
// we want to draw, and draw quickly, an arbitrarily large number
// of sprites all facing the screen. Performing the billboard math
// for ~1000 sprites is way too slow. Ideally, we want one
// matrix transformation that will handle everything, and this is
// just about what ends up happening. We're getting the front-facing
// effect by setting up a new frustum (of the same z-depth as the
// current one) that is very small in x and y. This way regularly
// rendered triangles that might not be EXACTLY facing the camera
// will certainly look close enough. Then, we transform to camera-space
// by hand and apply the inverse frustum to the transformed point.
// For some cracked out reason, this actually works.
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_sprite()" << endl;
#endif
// get the array traversal set up.
int nprims = geom->get_num_prims();
if (nprims==0) {
return;
}
#ifdef DO_PSTATS
// PStatTimer timer(_draw_primitive_pcollector);
// Using PStatTimer may cause a compiler crash.
_draw_primitive_pcollector.start();
_vertices_other_pcollector.add_level(geom->get_num_vertices());
#endif
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::ColorIterator ci = geom->make_color_iterator();
// need some interface so user can set 2d dimensions if no texture specified
float tex_xsize = 1.0f;
float tex_ysize = 1.0f;
Texture *tex = geom->get_texture();
if(tex != NULL) {
// set up the texture-rendering state
modify_state(RenderState::make
(TextureAttrib::make(tex),
TextureApplyAttrib::make(TextureApplyAttrib::M_modulate)));
tex_xsize = tex->_pbuffer->get_xsize();
tex_ysize = tex->_pbuffer->get_ysize();
}
// save the modelview matrix
const LMatrix4f &modelview_mat = _transform->get_mat();
// We don't need to mess with the aspect ratio, since we are now
// using the default projection matrix, which has the right aspect
// ratio built in.
// load up our own matrices
GLP(MatrixMode)(GL_MODELVIEW);
GLP(LoadIdentity)();
// precomputation stuff
float tex_left = geom->get_ll_uv()[0];
float tex_right = geom->get_ur_uv()[0];
float tex_bottom = geom->get_ll_uv()[1];
float tex_top = geom->get_ur_uv()[1];
float half_width = 0.5f * tex_xsize * fabs(tex_right - tex_left);
float half_height = 0.5f * tex_ysize * fabs(tex_top - tex_bottom);
float scaled_width = 0.0f;
float scaled_height = 0.0f;
// the user can override alpha sorting if they want
bool alpha = false;
if (!geom->get_alpha_disable()) {
// figure out if alpha's enabled (if not, no reason to sort)
const TransparencyAttrib *trans = _state->get_transparency();
if (trans != (const TransparencyAttrib *)NULL) {
alpha = (trans->get_mode() != TransparencyAttrib::M_none);
}
}
// sort container and iterator
pvector< WrappedSprite > cameraspace_vector;
pvector< WrappedSprite >::iterator vec_iter;
// inner loop vars
int i;
Vertexf source_vert, cameraspace_vert;
float *x_walk = (float *)NULL;
float *y_walk = (float *)NULL;
float *theta_walk = (float *)NULL;
float theta = 0.0f;
nassertv(geom->get_x_bind_type() != G_PER_VERTEX);
nassertv(geom->get_y_bind_type() != G_PER_VERTEX);
// set up the non-built-in bindings
bool x_overall = (geom->get_x_bind_type() == G_OVERALL);
bool y_overall = (geom->get_y_bind_type() == G_OVERALL);
bool theta_overall = (geom->get_theta_bind_type() == G_OVERALL);
bool color_overall = (geom->get_binding(G_COLOR) == G_OVERALL);
bool theta_on = !(geom->get_theta_bind_type() == G_OFF);
// x direction
if (x_overall)
scaled_width = geom->_x_texel_ratio[0] * half_width;
else {
nassertv(((int)geom->_x_texel_ratio.size() >= geom->get_num_prims()));
x_walk = &geom->_x_texel_ratio[0];
}
// y direction
if (y_overall)
scaled_height = geom->_y_texel_ratio[0] * half_height;
else {
nassertv(((int)geom->_y_texel_ratio.size() >= geom->get_num_prims()));
y_walk = &geom->_y_texel_ratio[0];
}
// theta
if (theta_on) {
if (theta_overall)
theta = geom->_theta[0];
else {
nassertv(((int)geom->_theta.size() >= geom->get_num_prims()));
theta_walk = &geom->_theta[0];
}
}
/////////////////////////////////////////////////////////////////////
// INNER LOOP PART 1 STARTS HERE
// Here we transform each point to cameraspace and fill our sort
// vector with the final geometric information.
/////////////////////////////////////////////////////////////////////
cameraspace_vector.reserve(nprims); //pre-alloc space for nprims
// the state is set, start running the prims
for (i = 0; i < nprims; i++) {
WrappedSprite ws;
source_vert = geom->get_next_vertex(vi);
// this mult converts to y-up cameraspace.
cameraspace_vert = source_vert * modelview_mat;
// build the final object that will go into the vector.
ws._v.set(cameraspace_vert[0],cameraspace_vert[1],cameraspace_vert[2]);
if (!color_overall)
ws._c = geom->get_next_color(ci);
if (!x_overall)
ws._x_ratio = *x_walk++;
if (!y_overall)
ws._y_ratio = *y_walk++;
if (theta_on) {
if (!theta_overall)
ws._theta = *theta_walk++;
}
cameraspace_vector.push_back(ws);
}
// now the verts are properly sorted by alpha (if necessary). Of course,
// the sort is only local, not scene-global, so if you look closely you'll
// notice that alphas may be screwy. It's ok though, because this is fast.
// if you want accuracy, use billboards and take the speed hit.
if (alpha) {
sort(cameraspace_vector.begin(), cameraspace_vector.end(),
draw_sprite_vertex_less());
if (_dithering_enabled)
GLP(Disable)(GL_DITHER);
}
Vertexf ul, ur, ll, lr;
if (color_overall)
GLP(Color4fv)(geom->get_next_color(ci).get_data());
////////////////////////////////////////////////////////////////////////////
// INNER LOOP PART 2 STARTS HERE
// Now we run through the cameraspace vector and compute the geometry for each
// tristrip. This includes scaling as per the ratio arrays, as well as
// rotating in the z.
////////////////////////////////////////////////////////////////////////////
vec_iter = cameraspace_vector.begin();
for (; vec_iter != cameraspace_vector.end(); vec_iter++) {
WrappedSprite& cur_image = *vec_iter;
// if not G_OVERALL, calculate the scale factors
if (x_overall == false)
scaled_width = cur_image._x_ratio * half_width;
if (y_overall == false)
scaled_height = cur_image._y_ratio * half_height;
// if not G_OVERALL, do some trig for this z rotate
if (theta_on) {
if (theta_overall == false)
theta = cur_image._theta;
// create the rotated points
LMatrix3f xform_mat = LMatrix3f::rotate_mat(theta) * LMatrix3f::scale_mat(scaled_width, scaled_height);
ur = (LVector3f( 1, 1, 0) * xform_mat) + cur_image._v;
ul = (LVector3f(-1, 1, 0) * xform_mat) + cur_image._v;
lr = (LVector3f( 1, -1, 0) * xform_mat) + cur_image._v;
ll = (LVector3f(-1, -1, 0) * xform_mat) + cur_image._v;
}
else {
// create the normal points
ur.set(scaled_width, scaled_height, 0);
ul.set(-scaled_width, scaled_height, 0);
lr.set(scaled_width, -scaled_height, 0);
ll.set(-scaled_width, -scaled_height, 0);
ur += cur_image._v;
ul += cur_image._v;
lr += cur_image._v;
ll += cur_image._v;
}
// set the color
if (color_overall == false)
GLP(Color4fv)(cur_image._c.get_data());
// draw each one as a 2-element tri-strip
GLP(Begin)(GL_TRIANGLE_STRIP);
GLP(Normal3f)(0.0f, 0.0f, 1.0f);
GLP(TexCoord2f)(tex_left, tex_bottom); GLP(Vertex3fv)(ll.get_data());
GLP(TexCoord2f)(tex_right, tex_bottom); GLP(Vertex3fv)(lr.get_data());
GLP(TexCoord2f)(tex_left, tex_top); GLP(Vertex3fv)(ul.get_data());
GLP(TexCoord2f)(tex_right, tex_top); GLP(Vertex3fv)(ur.get_data());
GLP(End)();
}
// restore the matrices
GLP(LoadMatrixf)(modelview_mat.get_data());
if(alpha && _dithering_enabled)
GLP(Enable)(GL_DITHER);
report_my_gl_errors();
DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_polygon
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_polygon(GeomPolygon *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_polygon()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
// PStatTimer timer(_draw_primitive_pcollector);
// Using PStatTimer may cause a compiler crash.
_draw_primitive_pcollector.start();
_vertices_other_pcollector.add_level(geom->get_num_vertices());
#endif
issue_scene_graph_color();
int nprims = geom->get_num_prims();
const int *plen = geom->get_lengths();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::NormalIterator ni = geom->make_normal_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
if ((geom->get_binding(G_COLOR) == G_PER_VERTEX && wants_colors()) ||
(geom->get_binding(G_NORMAL) == G_PER_VERTEX && wants_normals())) {
GLP(ShadeModel)(GL_SMOOTH);
} else {
GLP(ShadeModel)(GL_FLAT);
}
// Draw overall
issuer.issue_color(G_OVERALL, ci);
issuer.issue_normal(G_OVERALL, ni);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
issuer.issue_normal(G_PER_PRIM, ni);
int num_verts = *(plen++);
nassertv(num_verts >= 3);
GLP(Begin)(GL_POLYGON);
// Draw the vertices.
int v;
for (v = 0; v < num_verts; v++) {
// Per-vertex attributes.
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
GLP(End)();
}
report_my_gl_errors();
DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_tri
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_tri(GeomTri *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_tri()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
// PStatTimer timer(_draw_primitive_pcollector);
// Using PStatTimer may cause a compiler crash.
_draw_primitive_pcollector.start();
_vertices_tri_pcollector.add_level(geom->get_num_vertices());
#endif
issue_scene_graph_color();
int nprims = geom->get_num_prims();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::NormalIterator ni = geom->make_normal_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
if ((geom->get_binding(G_COLOR) == G_PER_VERTEX && wants_colors()) ||
(geom->get_binding(G_NORMAL) == G_PER_VERTEX && wants_normals())) {
GLP(ShadeModel)(GL_SMOOTH);
} else {
GLP(ShadeModel)(GL_FLAT);
}
// Draw overall
issuer.issue_color(G_OVERALL, ci);
issuer.issue_normal(G_OVERALL, ni);
GLP(Begin)(GL_TRIANGLES);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
issuer.issue_normal(G_PER_PRIM, ni);
for (int j = 0; j < 3; j++) {
// Draw per vertex
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
}
GLP(End)();
report_my_gl_errors();
#ifdef DO_PSTATS
_draw_primitive_pcollector.stop();
#endif
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_quad
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_quad(GeomQuad *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_quad()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
// PStatTimer timer(_draw_primitive_pcollector);
// Using PStatTimer may cause a compiler crash.
_draw_primitive_pcollector.start();
_vertices_other_pcollector.add_level(geom->get_num_vertices());
#endif
issue_scene_graph_color();
int nprims = geom->get_num_prims();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::NormalIterator ni = geom->make_normal_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
if ((geom->get_binding(G_COLOR) == G_PER_VERTEX && wants_colors()) ||
(geom->get_binding(G_NORMAL) == G_PER_VERTEX && wants_normals())) {
GLP(ShadeModel)(GL_SMOOTH);
} else {
GLP(ShadeModel)(GL_FLAT);
}
// Draw overall
issuer.issue_color(G_OVERALL, ci);
issuer.issue_normal(G_OVERALL, ni);
GLP(Begin)(GL_QUADS);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
issuer.issue_normal(G_PER_PRIM, ni);
for (int j = 0; j < 4; j++) {
// Draw per vertex
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
}
GLP(End)();
report_my_gl_errors();
DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_tristrip
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_tristrip(GeomTristrip *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_tristrip()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
// PStatTimer timer(_draw_primitive_pcollector);
// Using PStatTimer may cause a compiler crash.
_draw_primitive_pcollector.start();
_vertices_tristrip_pcollector.add_level(geom->get_num_vertices());
#endif
issue_scene_graph_color();
int nprims = geom->get_num_prims();
const int *plen = geom->get_lengths();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::NormalIterator ni = geom->make_normal_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
if ((geom->get_binding(G_COLOR) == G_PER_VERTEX && wants_colors()) ||
(geom->get_binding(G_NORMAL) == G_PER_VERTEX && wants_normals())) {
GLP(ShadeModel)(GL_SMOOTH);
} else {
GLP(ShadeModel)(GL_FLAT);
}
// Draw overall
issuer.issue_color(G_OVERALL, ci);
issuer.issue_normal(G_OVERALL, ni);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
issuer.issue_normal(G_PER_PRIM, ni);
int num_verts = *(plen++);
nassertv(num_verts >= 3);
GLP(Begin)(GL_TRIANGLE_STRIP);
// Per-component attributes for the first triangle?
issuer.issue_color(G_PER_COMPONENT, ci);
issuer.issue_normal(G_PER_COMPONENT, ni);
// Draw the first three vertices.
int v;
for (v = 0; v < 3; v++) {
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
// Now draw each of the remaining vertices. Each vertex from
// this point on defines a new triangle.
for (v = 3; v < num_verts; v++) {
// Per-component attributes?
issuer.issue_color(G_PER_COMPONENT, ci);
issuer.issue_normal(G_PER_COMPONENT, ni);
// Per-vertex attributes.
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
GLP(End)();
}
report_my_gl_errors();
DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_trifan
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_trifan(GeomTrifan *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_trifan()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
// PStatTimer timer(_draw_primitive_pcollector);
// Using PStatTimer may cause a compiler crash.
_draw_primitive_pcollector.start();
_vertices_trifan_pcollector.add_level(geom->get_num_vertices());
#endif
issue_scene_graph_color();
int nprims = geom->get_num_prims();
const int *plen = geom->get_lengths();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::NormalIterator ni = geom->make_normal_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
// If we have per-vertex colors or normals, we need smooth shading.
// Otherwise we want flat shading for performance reasons.
if ((geom->get_binding(G_COLOR) == G_PER_VERTEX && wants_colors()) ||
(geom->get_binding(G_NORMAL) == G_PER_VERTEX && wants_normals())) {
GLP(ShadeModel)(GL_SMOOTH);
} else {
GLP(ShadeModel)(GL_FLAT);
}
// Draw overall
issuer.issue_color(G_OVERALL, ci);
issuer.issue_normal(G_OVERALL, ni);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
issuer.issue_normal(G_PER_PRIM, ni);
int num_verts = *(plen++);
nassertv(num_verts >= 3);
GLP(Begin)(GL_TRIANGLE_FAN);
// Per-component attributes for the first triangle?
issuer.issue_color(G_PER_COMPONENT, ci);
issuer.issue_normal(G_PER_COMPONENT, ni);
// Draw the first three vertices.
int v;
for (v = 0; v < 3; v++) {
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
// Now draw each of the remaining vertices. Each vertex from
// this point on defines a new triangle.
for (v = 3; v < num_verts; v++) {
// Per-component attributes?
issuer.issue_color(G_PER_COMPONENT, ci);
issuer.issue_normal(G_PER_COMPONENT, ni);
// Per-vertex attributes.
issuer.issue_color(G_PER_VERTEX, ci);
issuer.issue_normal(G_PER_VERTEX, ni);
issuer.issue_texcoord(G_PER_VERTEX, ti);
issuer.issue_vertex(G_PER_VERTEX, vi);
}
GLP(End)();
}
report_my_gl_errors();
DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_sphere
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_sphere(GeomSphere *geom, GeomContext *gc) {
#ifdef GSG_VERBOSE
GLCAT.spam() << "draw_sphere()" << endl;
#endif
if (draw_display_list(gc)) {
return;
}
#ifdef DO_PSTATS
// PStatTimer timer(_draw_primitive_pcollector);
// Using PStatTimer may cause a compiler crash.
_draw_primitive_pcollector.start();
_vertices_other_pcollector.add_level(geom->get_num_vertices());
#endif
issue_scene_graph_color();
int nprims = geom->get_num_prims();
Geom::VertexIterator vi = geom->make_vertex_iterator();
Geom::MultiTexCoordIterator ti;
geom->setup_multitexcoord_iterator(ti, _current_texture->get_on_stages(),
_current_tex_gen->get_no_texcoords());
Geom::ColorIterator ci = geom->make_color_iterator();
GeomIssuer::IssueColor *issue_color;
if (_color_transform_enabled == 0) {
issue_color = issue_color_gl;
} else {
issue_color = issue_transformed_color_gl;
}
GeomIssuer issuer(geom, this,
issue_vertex_gl,
issue_normal_gl,
issue_color,
issue_texcoord_single_gl,
issue_texcoord_multi_gl,
ti);
if (wants_normals()) {
GLP(ShadeModel)(GL_SMOOTH);
} else {
GLP(ShadeModel)(GL_FLAT);
}
// Draw overall
issuer.issue_color(G_OVERALL, ci);
GLUquadricObj *sph = GLUP(NewQuadric)();
GLUP(QuadricNormals)(sph, wants_normals() ? (GLenum)GLU_SMOOTH : (GLenum)GLU_NONE);
GLUP(QuadricTexture)(sph, wants_texcoords() ? (GLenum)GL_TRUE : (GLenum)GL_FALSE);
GLUP(QuadricOrientation)(sph, (GLenum)GLU_OUTSIDE);
GLUP(QuadricDrawStyle)(sph, (GLenum)GLU_FILL);
//GLUP(QuadricDrawStyle)(sph, (GLenum)GLU_LINE);
for (int i = 0; i < nprims; i++) {
// Draw per primitive
issuer.issue_color(G_PER_PRIM, ci);
for (int j = 0; j < 2; j++) {
// Draw per vertex
issuer.issue_color(G_PER_VERTEX, ci);
}
Vertexf center = geom->get_next_vertex(vi);
Vertexf edge = geom->get_next_vertex(vi);
LVector3f v = edge - center;
float r = sqrt(dot(v, v));
// Since GLUP(Sphere) doesn't have a center parameter, we have to use
// a matrix transform.
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PushMatrix)();
GLP(MultMatrixf)(LMatrix4f::translate_mat(center).get_data());
// Now render the sphere using GLU calls.
GLUP(Sphere)(sph, r, 16, 10);
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PopMatrix)();
}
GLUP(DeleteQuadric)(sph);
report_my_gl_errors();
DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::prepare_texture
// Access: Public, Virtual
// Description: Creates a new retained-mode representation of the
// given texture, and returns a newly-allocated
// TextureContext pointer to reference it. It is the
// responsibility of the calling function to later
// call release_texture() with this same pointer (which
// will also delete the pointer).
//
// This function should not be called directly to
// prepare a texture. Instead, call Texture::prepare().
////////////////////////////////////////////////////////////////////
TextureContext *CLP(GraphicsStateGuardian)::
prepare_texture(Texture *tex) {
CLP(TextureContext) *gtc = new CLP(TextureContext)(tex);
GLP(GenTextures)(1, &gtc->_index);
bind_texture(gtc);
GLP(PrioritizeTextures)(1, &gtc->_index, &gtc->_priority);
specify_texture(tex);
apply_texture_immediate(gtc, tex);
report_my_gl_errors();
return gtc;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::apply_texture
// Access: Public, Virtual
// Description: Makes the texture the currently available texture for
// rendering.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
apply_texture(TextureContext *tc) {
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
add_to_texture_record(gtc);
bind_texture(gtc);
int dirty = gtc->get_dirty_flags();
if ((dirty & (Texture::DF_wrap | Texture::DF_filter | Texture::DF_border)) != 0) {
// We need to re-specify the texture properties.
specify_texture(gtc->_texture);
}
if ((dirty & (Texture::DF_image | Texture::DF_mipmap | Texture::DF_border)) != 0) {
// We need to re-apply the image.
apply_texture_immediate(gtc, gtc->_texture);
}
gtc->clear_dirty_flags();
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::release_texture
// Access: Public, Virtual
// Description: Frees the GL resources previously allocated for the
// texture. This function should never be called
// directly; instead, call Texture::release() (or simply
// let the Texture destruct).
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
release_texture(TextureContext *tc) {
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
GLP(DeleteTextures)(1, &gtc->_index);
report_my_gl_errors();
gtc->_index = 0;
delete gtc;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::prepare_geom
// Access: Public, Virtual
// Description: Creates a new retained-mode representation of the
// given geom, and returns a newly-allocated
// GeomContext pointer to reference it. It is the
// responsibility of the calling function to later
// call release_geom() with this same pointer (which
// will also delete the pointer).
//
// This function should not be called directly to
// prepare a geom. Instead, call Geom::prepare().
////////////////////////////////////////////////////////////////////
GeomContext *CLP(GraphicsStateGuardian)::
prepare_geom(Geom *geom) {
if (!_vertex_colors_enabled) {
// We can't build a display list (or play back a display list) if
// its color is overridden with a scene graph color. Maybe if we
// take advantage of the OpenGL color matrix we can do this, but
// for now we'll just ignore it.
return NULL;
}
if (geom->is_dynamic()) {
// If the Geom is dynamic in some way, we shouldn't try to
// display-list it.
return NULL;
}
CLP(GeomContext) *ggc = new CLP(GeomContext)(geom);
ggc->_index = GLP(GenLists)(1);
if (GLCAT.is_debug()) {
GLCAT.debug()
<< "preparing " << *geom << ", index " << ggc->_index << "\n";
}
if (ggc->_index == 0) {
GLCAT.error()
<< "Ran out of display list indices.\n";
delete ggc;
return NULL;
}
// We need to temporarily force normals and UV's on, so the display
// list will have them built in.
//force_texcoords();
force_normals();
#ifdef DO_PSTATS
// Count up the number of vertices we're about to render, by
// checking the PStats vertex counters now, and at the end. This is
// kind of hacky, but this is debug code.
float num_verts_before =
_vertices_tristrip_pcollector.get_level() +
_vertices_trifan_pcollector.get_level() +
_vertices_tri_pcollector.get_level() +
_vertices_other_pcollector.get_level();
#endif
// Now define the display list.
GLP(NewList)(ggc->_index, GL_COMPILE);
geom->draw_immediate(this, NULL);
GLP(EndList)();
#ifdef DO_PSTATS
float num_verts_after =
_vertices_tristrip_pcollector.get_level() +
_vertices_trifan_pcollector.get_level() +
_vertices_tri_pcollector.get_level() +
_vertices_other_pcollector.get_level();
float num_verts = num_verts_after - num_verts_before;
ggc->_num_verts = (int)(num_verts + 0.5);
#endif
undo_force_normals();
//undo_force_texcoords();
report_my_gl_errors();
return ggc;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::release_geom
// Access: Public, Virtual
// Description: Frees the GL resources previously allocated for the
// geom. This function should never be called
// directly; instead, call Geom::release() (or simply
// let the Geom destruct).
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
release_geom(GeomContext *gc) {
CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc);
if (GLCAT.is_debug()) {
GLCAT.debug()
<< "releasing index " << ggc->_index << "\n";
}
GLP(DeleteLists)(ggc->_index, 1);
report_my_gl_errors();
ggc->_index = 0;
delete ggc;
}
#if 0
static int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
4096, 0 };
// This function returns the smallest power of two greater than or
// equal to x.
static int binary_log_cap(const int x) {
int i = 0;
for (; (x > logs[i]) && (logs[i] != 0); ++i);
if (logs[i] == 0)
return 4096;
return logs[i];
}
#endif
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::copy_texture
// Access: Public, Virtual
// Description: Copy the pixel region indicated by the display
// region from the framebuffer into texture memory
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
copy_texture(Texture *tex, const DisplayRegion *dr) {
nassertv(tex != NULL && dr != NULL);
int xo, yo, w, h;
dr->get_region_pixels(xo, yo, w, h);
PixelBuffer *pb = tex->_pbuffer;
pb->set_xsize(w);
pb->set_ysize(h);
TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
nassertv(tc != (TextureContext *)NULL);
bind_texture(tc);
GLP(CopyTexImage2D)(GL_TEXTURE_2D, 0,
get_internal_image_format(pb->get_format()),
xo, yo, w, h, tex->get_border_width());
// Clear the internal texture state, since we've just monkeyed with it.
modify_state(get_untextured_state());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::copy_texture
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
copy_texture(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb) {
set_read_buffer(rb);
copy_texture(tex, dr);
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::texture_to_pixel_buffer
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb) {
// This code is now invalidated by the new design; perhaps the
// interface is not needed anyway.
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::texture_to_pixel_buffer
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb,
const DisplayRegion *dr) {
nassertv(tc != NULL && pb != NULL && dr != NULL);
Texture *tex = tc->_texture;
// Do a deep copy to initialize the pixel buffer
pb->copy(tex->_pbuffer);
// If the image was empty, we need to render the texture into the frame
// buffer and then copy the results into the pixel buffer's image
if (pb->_image.empty()) {
int w = pb->get_xsize();
int h = pb->get_ysize();
draw_texture(tc, dr);
pb->_image = PTA_uchar::empty_array(w * h * pb->get_num_components());
copy_pixel_buffer(pb, dr);
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::copy_pixel_buffer
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) {
nassertr(pb != NULL && dr != NULL, false);
GLP(PixelStorei)(GL_PACK_ALIGNMENT, 1);
// Bug fix for RE, RE2, and VTX - need to disable texturing in order
// for GLP(ReadPixels)() to work
// NOTE: reading the depth buffer is *much* slower than reading the
// color buffer
modify_state(get_untextured_state());
int xo, yo, w, h;
dr->get_region_pixels(xo, yo, w, h);
GLenum external_format = get_external_image_format(pb->get_format());
#ifdef GSG_VERBOSE
GLCAT.debug()
<< "glReadPixels(" << xo << ", " << yo
<< ", " << pb->get_xsize() << ", " << pb->get_ysize()
<< ", ";
switch (external_format) {
case GL_DEPTH_COMPONENT:
GLCAT.debug(false) << "GL_DEPTH_COMPONENT, ";
break;
case GL_RGB:
GLCAT.debug(false) << "GL_RGB, ";
break;
case GL_RGBA:
GLCAT.debug(false) << "GL_RGBA, ";
break;
case GL_BGR:
GLCAT.debug(false) << "GL_BGR, ";
break;
case GL_BGRA:
GLCAT.debug(false) << "GL_BGRA, ";
break;
default:
GLCAT.debug(false) << "unknown, ";
break;
}
switch (get_image_type(pb->get_image_type())) {
case GL_UNSIGNED_BYTE:
GLCAT.debug(false) << "GL_UNSIGNED_BYTE, ";
break;
case GL_FLOAT:
GLCAT.debug(false) << "GL_FLOAT, ";
break;
default:
GLCAT.debug(false) << "unknown, ";
break;
}
GLCAT.debug(false)
<< (void *)pb->_image.p() << ")" << endl;
#endif
// pixelbuffer "origin" represents upper left screen point at which
// pixelbuffer should be drawn using draw_pixel_buffer
nassertr(!pb->_image.empty(), false);
GLP(ReadPixels)(xo, yo,
pb->get_xsize(), pb->get_ysize(),
external_format,
get_image_type(pb->get_image_type()),
pb->_image.p());
// We may have to reverse the byte ordering of the image if GL
// didn't do it for us.
pb->_image = fix_component_ordering(external_format, pb);
report_my_gl_errors();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::copy_pixel_buffer
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
const RenderBuffer &rb) {
set_read_buffer(rb);
return copy_pixel_buffer(pb, dr);
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::apply_material
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::apply_material(const Material *material) {
GLenum face = material->get_twoside() ? GL_FRONT_AND_BACK : GL_FRONT;
GLP(Materialfv)(face, GL_SPECULAR, material->get_specular().get_data());
GLP(Materialfv)(face, GL_EMISSION, material->get_emission().get_data());
GLP(Materialf)(face, GL_SHININESS, material->get_shininess());
if (material->has_ambient() && material->has_diffuse()) {
// The material has both an ambient and diffuse specified. This
// means we do not need glMaterialColor().
GLP(Disable)(GL_COLOR_MATERIAL);
GLP(Materialfv)(face, GL_AMBIENT, material->get_ambient().get_data());
GLP(Materialfv)(face, GL_DIFFUSE, material->get_diffuse().get_data());
} else if (material->has_ambient()) {
// The material specifies an ambient, but not a diffuse component.
// The diffuse component comes from the object's color.
GLP(Materialfv)(face, GL_AMBIENT, material->get_ambient().get_data());
GLP(ColorMaterial)(face, GL_DIFFUSE);
GLP(Enable)(GL_COLOR_MATERIAL);
} else if (material->has_diffuse()) {
// The material specifies a diffuse, but not an ambient component.
// The ambient component comes from the object's color.
GLP(Materialfv)(face, GL_DIFFUSE, material->get_diffuse().get_data());
GLP(ColorMaterial)(face, GL_AMBIENT);
GLP(Enable)(GL_COLOR_MATERIAL);
} else {
// The material specifies neither a diffuse nor an ambient
// component. Both components come from the object's color.
GLP(ColorMaterial)(face, GL_AMBIENT_AND_DIFFUSE);
GLP(Enable)(GL_COLOR_MATERIAL);
}
GLP(LightModeli)(GL_LIGHT_MODEL_LOCAL_VIEWER, material->get_local());
GLP(LightModeli)(GL_LIGHT_MODEL_TWO_SIDE, material->get_twoside());
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::apply_fog
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
apply_fog(Fog *fog) {
Fog::Mode fmode = fog->get_mode();
GLP(Fogi)(GL_FOG_MODE, get_fog_mode_type(fmode));
if (fmode == Fog::M_linear) {
float onset, opaque;
fog->get_linear_range(onset, opaque);
GLP(Fogf)(GL_FOG_START, onset);
GLP(Fogf)(GL_FOG_END, opaque);
} else {
// Exponential fog is always camera-relative.
GLP(Fogf)(GL_FOG_DENSITY, fog->get_exp_density());
}
GLP(Fogfv)(GL_FOG_COLOR, fog->get_color().get_data());
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_transform
// Access: Public, Virtual
// Description: Sends the indicated transform matrix to the graphics
// API to be applied to future vertices.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_transform(const TransformState *transform) {
#ifdef GSG_VERBOSE
GLCAT.spam()
<< "glLoadMatrix(GL_MODELVIEW): " << transform->get_mat() << endl;
#endif
DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1));
GLP(MatrixMode)(GL_MODELVIEW);
GLP(LoadMatrixf)(transform->get_mat().get_data());
_transform = transform;
if (_auto_rescale_normal) {
do_auto_rescale_normal();
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_tex_matrix
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_tex_matrix(const TexMatrixAttrib *attrib) {
// We don't apply the texture matrix right away, since we might yet
// get a TextureAttrib that changes the set of TextureStages we have
// active. Instead, we simply set a flag that indicates we need to
// re-issue the texture matrix after all of the other attribs are
// done being issued.
_current_tex_mat = attrib;
_needs_tex_mat = true;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_cg_shader_bind
// Access: Public, Virtual
// Description: Bind shader of current node
// and unbind the shader of the previous node
// Create a new GLCgShaderContext if this shader
// object is coming in for the first time
// Also maintain the map of CgShader objects to
// respective GLCgShaderContexts
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_cg_shader_bind(const CgShaderAttrib *attrib) {
#ifdef HAVE_CGGL
if (attrib->is_off()) { //Current node has no shaders
if (_cg_shader != (CgShader *) NULL) {
_gl_cg_shader_contexts[_cg_shader]->un_bind();// Prev node had shaders
}
_cg_shader = attrib->get_cg_shader();//Store current node.. here NULL
} else {// Current node has shaders
if (_cg_shader != (CgShader *) NULL) {
_gl_cg_shader_contexts[_cg_shader]->un_bind();// Prev node had shaders
}
_cg_shader = attrib->get_cg_shader();//Store current node
CGSHADERCONTEXTS::const_iterator csci;
csci = _gl_cg_shader_contexts.find(_cg_shader);
if (csci != _gl_cg_shader_contexts.end()) { // Already have context?
(*csci).second->bind(this); // Bind the current shader
} else {// First time CgShader object...need to make a new GLCgShaderContext
PT(CLP(CgShaderContext)) csc = new CLP(CgShaderContext)(_cg_shader);
_cg_shader->load_shaders(); // Profiles created lets load from HD
csc->load_shaders(); // Programs loaded, compile and download to GPU
CGSHADERCONTEXTS::value_type shader_and_context(_cg_shader, csc);
_gl_cg_shader_contexts.insert(shader_and_context);
csc->bind(this);// Bind the new shader
}
}
#endif
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_tex_gen
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_tex_gen(const TexGenAttrib *attrib) {
// We don't apply the texture coordinate generation commands right
// away, since we might yet get a TextureAttrib that changes the set
// of TextureStages we have active. Instead, we simply set a flag
// that indicates we need to re-issue the TexGenAttrib after all of
// the other attribs are done being issued.
_current_tex_gen = attrib;
_needs_tex_gen = true;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_texture
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_texture(const TextureAttrib *attrib) {
DO_PSTATS_STUFF(_texture_state_pcollector.add_level(1));
CPT(TextureAttrib) new_texture = attrib->filter_to_max(_max_texture_stages);
int num_stages = new_texture->get_num_on_stages();
int num_old_stages = _current_texture->get_num_on_stages();
nassertv(num_stages <= _max_texture_stages &&
num_old_stages <= _max_texture_stages);
int i;
for (i = 0; i < num_stages; i++) {
TextureStage *stage = new_texture->get_on_stage(i);
Texture *texture = new_texture->get_on_texture(stage);
nassertv(texture != (Texture *)NULL);
if (i >= num_old_stages ||
stage != _current_texture->get_on_stage(i) ||
texture != _current_texture->get_on_texture(stage)) {
// Stage i has changed. Issue the texture on this stage.
_glActiveTexture(GL_TEXTURE0 + i);
GLP(Enable)(GL_TEXTURE_2D);
TextureContext *tc = texture->prepare_now(_prepared_objects, this);
apply_texture(tc);
GLint glmode = get_texture_apply_mode_type(stage->get_mode());
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, glmode);
GLP(TexEnvfv)(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, stage->get_color().get_data());
if (stage->get_mode() == TextureStage::M_combine) {
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_COMBINE_RGB,
get_texture_combine_type(stage->get_combine_rgb_mode()));
switch (stage->get_num_combine_rgb_operands()) {
case 3:
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_SRC2_RGB,
get_texture_src_type(stage->get_combine_rgb_source2()));
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_OPERAND2_RGB,
get_texture_operand_type(stage->get_combine_rgb_operand2()));
// fall through
case 2:
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_SRC1_RGB,
get_texture_src_type(stage->get_combine_rgb_source1()));
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_OPERAND1_RGB,
get_texture_operand_type(stage->get_combine_rgb_operand1()));
// fall through
case 1:
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_SRC0_RGB,
get_texture_src_type(stage->get_combine_rgb_source0()));
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_OPERAND0_RGB,
get_texture_operand_type(stage->get_combine_rgb_operand0()));
// fall through
default:
break;
}
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_COMBINE_ALPHA,
get_texture_combine_type(stage->get_combine_alpha_mode()));
switch (stage->get_num_combine_alpha_operands()) {
case 3:
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_SRC2_ALPHA,
get_texture_src_type(stage->get_combine_alpha_source2()));
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA,
get_texture_operand_type(stage->get_combine_alpha_operand2()));
// fall through
case 2:
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_SRC1_ALPHA,
get_texture_src_type(stage->get_combine_alpha_source1()));
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA,
get_texture_operand_type(stage->get_combine_alpha_operand1()));
// fall through
case 1:
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_SRC0_ALPHA,
get_texture_src_type(stage->get_combine_alpha_source0()));
GLP(TexEnvi)(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA,
get_texture_operand_type(stage->get_combine_alpha_operand0()));
// fall through
default:
break;
}
}
GLP(MatrixMode)(GL_TEXTURE);
if (_current_tex_mat->has_stage(stage)) {
GLP(LoadMatrixf)(_current_tex_mat->get_mat(stage).get_data());
} else {
GLP(LoadIdentity)();
}
}
}
// Disable the texture stages that are no longer used.
for (i = num_stages; i < num_old_stages; i++) {
_glActiveTexture(GL_TEXTURE0 + i);
GLP(Disable)(GL_TEXTURE_2D);
}
_current_texture = new_texture;
// Changing the set of texture stages will require us to reissue the
// texgen and texmat attribs.
_needs_tex_gen = true;
_needs_tex_mat = true;
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_material
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_material(const MaterialAttrib *attrib) {
const Material *material = attrib->get_material();
if (material != (const Material *)NULL) {
apply_material(material);
} else {
// Apply a default material when materials are turned off.
Material empty;
apply_material(&empty);
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_render_mode
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_render_mode(const RenderModeAttrib *attrib) {
RenderModeAttrib::Mode mode = attrib->get_mode();
switch (mode) {
case RenderModeAttrib::M_filled:
GLP(PolygonMode)(GL_FRONT_AND_BACK, GL_FILL);
break;
case RenderModeAttrib::M_wireframe:
GLP(LineWidth)(attrib->get_line_width());
GLP(PolygonMode)(GL_FRONT_AND_BACK, GL_LINE);
break;
default:
GLCAT.error()
<< "Unknown render mode " << (int)mode << endl;
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_rescale_normal
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_rescale_normal(const RescaleNormalAttrib *attrib) {
RescaleNormalAttrib::Mode mode = attrib->get_mode();
_auto_rescale_normal = false;
switch (mode) {
case RescaleNormalAttrib::M_none:
GLP(Disable)(GL_NORMALIZE);
if (_supports_rescale_normal) {
GLP(Disable)(GL_RESCALE_NORMAL);
}
break;
case RescaleNormalAttrib::M_rescale:
if (_supports_rescale_normal) {
GLP(Enable)(GL_RESCALE_NORMAL);
GLP(Disable)(GL_NORMALIZE);
} else {
GLP(Enable)(GL_NORMALIZE);
}
break;
case RescaleNormalAttrib::M_normalize:
GLP(Enable)(GL_NORMALIZE);
if (_supports_rescale_normal) {
GLP(Disable)(GL_RESCALE_NORMAL);
}
break;
case RescaleNormalAttrib::M_auto:
_auto_rescale_normal = true;
do_auto_rescale_normal();
break;
default:
GLCAT.error()
<< "Unknown rescale_normal mode " << (int)mode << endl;
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_texture_apply
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_texture_apply(const TextureApplyAttrib *) {
// This attrib is no longer used; it is replaced by the parameters
// within TextureStage.
return;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_color_write
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_color_write(const ColorWriteAttrib *attrib) {
// If we did not override this function, the default implementation
// would achieve turning off color writes by changing the blend mode
// in set_blend_mode(). However, since GL does support an easy way
// to disable writes to the color buffer, we can take advantage of
// it here.
if (CLP(color_mask)) {
ColorWriteAttrib::Mode mode = attrib->get_mode();
if (mode == ColorWriteAttrib::M_off) {
GLP(ColorMask)(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
} else {
GLP(ColorMask)(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
report_my_gl_errors();
} else {
// Some implementations don't seem to handle GLP(ColorMask)() very
// robustly, however, so we provide this fallback.
GraphicsStateGuardian::issue_color_write(attrib);
}
}
// PandaCompareFunc - 1 + 0x200 === GL_NEVER, etc. order is sequential
#define PANDA_TO_GL_COMPAREFUNC(PANDACMPFUNC) (PANDACMPFUNC-1 +0x200)
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_depth_test
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_depth_test(const DepthTestAttrib *attrib) {
DepthTestAttrib::PandaCompareFunc mode = attrib->get_mode();
if (mode == DepthTestAttrib::M_none) {
enable_depth_test(false);
} else {
enable_depth_test(true);
GLP(DepthFunc)(PANDA_TO_GL_COMPAREFUNC(mode));
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_alpha_test
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_alpha_test(const AlphaTestAttrib *attrib) {
AlphaTestAttrib::PandaCompareFunc mode = attrib->get_mode();
if (mode == AlphaTestAttrib::M_none) {
enable_alpha_test(false);
} else {
assert(GL_NEVER==(AlphaTestAttrib::M_never-1+0x200));
GLP(AlphaFunc)(PANDA_TO_GL_COMPAREFUNC(mode), attrib->get_reference_alpha());
enable_alpha_test(true);
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_depth_write
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_depth_write(const DepthWriteAttrib *attrib) {
DepthWriteAttrib::Mode mode = attrib->get_mode();
if (mode == DepthWriteAttrib::M_off) {
GLP(DepthMask)(GL_FALSE);
} else {
GLP(DepthMask)(GL_TRUE);
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_cull_face
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_cull_face(const CullFaceAttrib *attrib) {
CullFaceAttrib::Mode mode = attrib->get_effective_mode();
switch (mode) {
case CullFaceAttrib::M_cull_none:
GLP(Disable)(GL_CULL_FACE);
break;
case CullFaceAttrib::M_cull_clockwise:
GLP(Enable)(GL_CULL_FACE);
GLP(CullFace)(GL_BACK);
break;
case CullFaceAttrib::M_cull_counter_clockwise:
GLP(Enable)(GL_CULL_FACE);
GLP(CullFace)(GL_FRONT);
break;
default:
GLCAT.error()
<< "invalid cull face mode " << (int)mode << endl;
break;
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_fog
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_fog(const FogAttrib *attrib) {
if (!attrib->is_off()) {
enable_fog(true);
Fog *fog = attrib->get_fog();
nassertv(fog != (Fog *)NULL);
apply_fog(fog);
} else {
enable_fog(false);
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_depth_offset
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_depth_offset(const DepthOffsetAttrib *attrib) {
int offset = attrib->get_offset();
if (offset != 0) {
// The relationship between these two parameters is a little
// unclear and poorly explained in the GL man pages.
GLP(PolygonOffset)((GLfloat) -offset, (GLfloat) -offset);
enable_polygon_offset(true);
} else {
enable_polygon_offset(false);
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::bind_light
// Access: Public, Virtual
// Description: Called the first time a particular light has been
// bound to a given id within a frame, this should set
// up the associated hardware light with the light's
// properties.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
bind_light(PointLight *light_obj, const NodePath &light, int light_id) {
GLenum id = get_light_id(light_id);
static const Colorf black(0.0f, 0.0f, 0.0f, 1.0f);
GLP(Lightfv)(id, GL_AMBIENT, black.get_data());
GLP(Lightfv)(id, GL_DIFFUSE, light_obj->get_color().get_data());
GLP(Lightfv)(id, GL_SPECULAR, light_obj->get_specular_color().get_data());
// Position needs to specify x, y, z, and w
// w == 1 implies non-infinite position
const LMatrix4f &light_mat = light.get_mat(_scene_setup->get_scene_root());
LPoint3f pos = light_obj->get_point() * light_mat;
LPoint4f fpos(pos[0], pos[1], pos[2], 1.0f);
GLP(Lightfv)(id, GL_POSITION, fpos.get_data());
// GL_SPOT_DIRECTION is not significant when cutoff == 180
// Exponent == 0 implies uniform light distribution
GLP(Lightf)(id, GL_SPOT_EXPONENT, 0.0f);
// Cutoff == 180 means uniform point light source
GLP(Lightf)(id, GL_SPOT_CUTOFF, 180.0f);
const LVecBase3f &att = light_obj->get_attenuation();
GLP(Lightf)(id, GL_CONSTANT_ATTENUATION, att[0]);
GLP(Lightf)(id, GL_LINEAR_ATTENUATION, att[1]);
GLP(Lightf)(id, GL_QUADRATIC_ATTENUATION, att[2]);
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::bind_light
// Access: Public, Virtual
// Description: Called the first time a particular light has been
// bound to a given id within a frame, this should set
// up the associated hardware light with the light's
// properties.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
GLenum id = get_light_id( light_id );
static const Colorf black(0.0f, 0.0f, 0.0f, 1.0f);
GLP(Lightfv)(id, GL_AMBIENT, black.get_data());
GLP(Lightfv)(id, GL_DIFFUSE, light_obj->get_color().get_data());
GLP(Lightfv)(id, GL_SPECULAR, light_obj->get_specular_color().get_data());
// Position needs to specify x, y, z, and w.
// w == 0 implies light is at infinity
const LMatrix4f &light_mat = light.get_mat(_scene_setup->get_scene_root());
LVector3f dir = light_obj->get_direction() * light_mat;
LPoint4f fdir(-dir[0], -dir[1], -dir[2], 0);
GLP(Lightfv)(id, GL_POSITION, fdir.get_data());
// GL_SPOT_DIRECTION is not significant when cutoff == 180
// In this case, position x, y, z specifies direction
// Exponent == 0 implies uniform light distribution
GLP(Lightf)(id, GL_SPOT_EXPONENT, 0.0f);
// Cutoff == 180 means uniform point light source
GLP(Lightf)(id, GL_SPOT_CUTOFF, 180.0f);
// Default attenuation values (only spotlight and point light can
// modify these)
GLP(Lightf)(id, GL_CONSTANT_ATTENUATION, 1.0f);
GLP(Lightf)(id, GL_LINEAR_ATTENUATION, 0.0f);
GLP(Lightf)(id, GL_QUADRATIC_ATTENUATION, 0.0f);
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::bind_light
// Access: Public, Virtual
// Description: Called the first time a particular light has been
// bound to a given id within a frame, this should set
// up the associated hardware light with the light's
// properties.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
Lens *lens = light_obj->get_lens();
nassertv(lens != (Lens *)NULL);
GLenum id = get_light_id(light_id);
static const Colorf black(0.0f, 0.0f, 0.0f, 1.0f);
GLP(Lightfv)(id, GL_AMBIENT, black.get_data());
GLP(Lightfv)(id, GL_DIFFUSE, light_obj->get_color().get_data());
GLP(Lightfv)(id, GL_SPECULAR, light_obj->get_specular_color().get_data());
// Position needs to specify x, y, z, and w
// w == 1 implies non-infinite position
const LMatrix4f &light_mat = light.get_mat(_scene_setup->get_scene_root());
LPoint3f pos = lens->get_nodal_point() * light_mat;
LVector3f dir = lens->get_view_vector() * light_mat;
LPoint4f fpos(pos[0], pos[1], pos[2], 1.0f);
GLP(Lightfv)(id, GL_POSITION, fpos.get_data());
GLP(Lightfv)(id, GL_SPOT_DIRECTION, dir.get_data());
GLP(Lightf)(id, GL_SPOT_EXPONENT, light_obj->get_exponent());
GLP(Lightf)(id, GL_SPOT_CUTOFF, lens->get_hfov());
const LVecBase3f &att = light_obj->get_attenuation();
GLP(Lightf)(id, GL_CONSTANT_ATTENUATION, att[0]);
GLP(Lightf)(id, GL_LINEAR_ATTENUATION, att[1]);
GLP(Lightf)(id, GL_QUADRATIC_ATTENUATION, att[2]);
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::wants_texcoords
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
wants_texcoords() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::compute_distance_to
// Access: Public, Virtual
// Description: This function may only be called during a render
// traversal; it will compute the distance to the
// indicated point, assumed to be in modelview
// coordinates, from the camera plane.
////////////////////////////////////////////////////////////////////
float CLP(GraphicsStateGuardian)::
compute_distance_to(const LPoint3f &point) const {
// In the case of a CLP(GraphicsStateGuardian), we know that the
// modelview matrix already includes the relative transform from the
// camera, as well as a to-y-up conversion. Thus, the distance to
// the camera plane is simply the -z distance.
return -point[2];
}
////////////////////////////////////////////////////////////////////
// Function: report_errors_loop
// Access: Protected, Static
// Description: The internal implementation of report_errors().
// Don't call this function; use report_errors()
// instead. The return value is true if everything is
// ok, or false if we should shut down.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
report_errors_loop(int line, const char *source_file, GLenum error_code,
int &error_count) {
#ifndef NDEBUG
static const int max_gl_errors_reported = 20;
while ((error_count < max_gl_errors_reported) &&
(error_code != GL_NO_ERROR)) {
const GLubyte *error_string = GLUP(ErrorString)(error_code);
if (error_string != (const GLubyte *)NULL) {
GLCAT.error()
<< "at " << line << " of " << source_file << ": "
<< error_string << "\n";
} else {
GLCAT.error()
<< "at " << line << " of " << source_file << ": "
<< "GL error " << (int)error_code << "\n";
}
error_code = GLP(GetError)();
error_count++;
}
#endif
return (error_code == GL_NO_ERROR);
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::show_gl_string
// Access: Protected
// Description: Outputs the result of glGetString() on the indicated
// tag.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
show_gl_string(const string &name, GLenum id) {
if (GLCAT.is_debug()) {
const GLubyte *text = GLP(GetString)(id);
if (text == (const GLubyte *)NULL) {
GLCAT.debug()
<< "Unable to query " << name << "\n";
} else {
GLCAT.debug()
<< name << " = " << (const char *)text << "\n";
}
}
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::get_gl_version
// Access: Protected, Virtual
// Description: Queries the runtime version of OpenGL in use.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
get_gl_version() {
show_gl_string("GL_VENDOR", GL_VENDOR);
show_gl_string("GL_RENDERER", GL_RENDERER);
_gl_version_major = 0;
_gl_version_minor = 0;
_gl_version_release = 0;
const GLubyte *text = GLP(GetString)(GL_VERSION);
if (text == (const GLubyte *)NULL) {
GLCAT.debug()
<< "Unable to query GL_VERSION\n";
} else {
string input((const char *)text);
size_t space = input.find(' ');
if (space != string::npos) {
input = input.substr(0, space);
}
vector_string components;
tokenize(input, components, ".");
if (components.size() >= 1) {
string_to_int(components[0], _gl_version_major);
}
if (components.size() >= 2) {
string_to_int(components[1], _gl_version_minor);
}
if (components.size() >= 3) {
string_to_int(components[2], _gl_version_release);
}
GLCAT.debug()
<< "GL_VERSION = " << (const char *)text << ", decoded to "
<< _gl_version_major << "." << _gl_version_minor
<< "." << _gl_version_release << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::save_extensions
// Access: Protected
// Description: Separates the string returned by GL_EXTENSIONS (or
// glx or wgl extensions) into its individual tokens
// and saves them in the _extensions member.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
save_extensions(const char *extensions) {
if (extensions != (const char *)NULL) {
vector_string tokens;
extract_words(extensions, tokens);
vector_string::iterator ti;
for (ti = tokens.begin(); ti != tokens.end(); ++ti) {
_extensions.insert(*ti);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::get_extra_extensions
// Access: Protected, Virtual
// Description: This may be redefined by a derived class (e.g. glx or
// wgl) to get whatever further extensions strings may
// be appropriate to that interface, in addition to the
// GL extension strings return by glGetString().
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
get_extra_extensions() {
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::report_extensions
// Access: Protected
// Description: Outputs the list of GL extensions to notify, if debug
// mode is enabled.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
report_extensions() const {
if (GLCAT.is_debug()) {
GLCAT.debug()
<< "GL Extensions:\n";
pset<string>::const_iterator ei;
for (ei = _extensions.begin(); ei != _extensions.end(); ++ei) {
GLCAT.debug() << (*ei) << "\n";
}
}
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::has_extension
// Access: Protected
// Description: Returns true if the indicated extension is reported
// by the GL system, false otherwise. The extension
// name is case-sensitive.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
has_extension(const string &extension) const {
return (_extensions.find(extension) != _extensions.end());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::is_at_least_version
// Access: Public
// Description: Returns true if the runtime GL version number is at
// least the indicated value, false otherwise.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
is_at_least_version(int major_version, int minor_version,
int release_version) const {
if (_gl_version_major < major_version) {
return false;
}
if (_gl_version_minor < minor_version) {
return false;
}
if (_gl_version_release < release_version) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_extension_func
// Access: Public, Virtual
// Description: Returns the pointer to the GL extension function with
// the indicated name. It is the responsibility of the
// caller to ensure that the required extension is
// defined in the OpenGL runtime prior to calling this;
// it is an error to call this for a function that is
// not defined.
////////////////////////////////////////////////////////////////////
void *CLP(GraphicsStateGuardian)::
get_extension_func(const char *, const char *) {
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::set_draw_buffer
// Access: Protected
// Description: Sets up the GLP(DrawBuffer) to render into the buffer
// indicated by the RenderBuffer object. This only sets
// up the color bits; it does not affect the depth,
// stencil, accum layers.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
set_draw_buffer(const RenderBuffer &rb) {
switch (rb._buffer_type & RenderBuffer::T_color) {
case RenderBuffer::T_front:
GLP(DrawBuffer)(GL_FRONT);
break;
case RenderBuffer::T_back:
GLP(DrawBuffer)(GL_BACK);
break;
case RenderBuffer::T_right:
GLP(DrawBuffer)(GL_RIGHT);
break;
case RenderBuffer::T_left:
GLP(DrawBuffer)(GL_LEFT);
break;
case RenderBuffer::T_front_right:
GLP(DrawBuffer)(GL_FRONT_RIGHT);
break;
case RenderBuffer::T_front_left:
GLP(DrawBuffer)(GL_FRONT_LEFT);
break;
case RenderBuffer::T_back_right:
GLP(DrawBuffer)(GL_BACK_RIGHT);
break;
case RenderBuffer::T_back_left:
GLP(DrawBuffer)(GL_BACK_LEFT);
break;
default:
GLP(DrawBuffer)(GL_FRONT_AND_BACK);
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::set_read_buffer
// Access: Protected
// Description: Sets up the GLP(ReadBuffer) to render into the buffer
// indicated by the RenderBuffer object. This only sets
// up the color bits; it does not affect the depth,
// stencil, accum layers.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
set_read_buffer(const RenderBuffer &rb) {
switch (rb._buffer_type & RenderBuffer::T_color) {
case RenderBuffer::T_front:
GLP(ReadBuffer)(GL_FRONT);
break;
case RenderBuffer::T_back:
GLP(ReadBuffer)(GL_BACK);
break;
case RenderBuffer::T_right:
GLP(ReadBuffer)(GL_RIGHT);
break;
case RenderBuffer::T_left:
GLP(ReadBuffer)(GL_LEFT);
break;
case RenderBuffer::T_front_right:
GLP(ReadBuffer)(GL_FRONT_RIGHT);
break;
case RenderBuffer::T_front_left:
GLP(ReadBuffer)(GL_FRONT_LEFT);
break;
case RenderBuffer::T_back_right:
GLP(ReadBuffer)(GL_BACK_RIGHT);
break;
case RenderBuffer::T_back_left:
GLP(ReadBuffer)(GL_BACK_LEFT);
break;
default:
GLP(ReadBuffer)(GL_FRONT_AND_BACK);
}
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::bind_texture
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
bind_texture(TextureContext *tc) {
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
#ifdef GSG_VERBOSE
Texture *tex = tc->_texture;
GLCAT.spam()
<< "glBindTexture(): " << tex->get_name() << "(" << (int)gtc->_index
<< ")" << endl;
#endif
GLP(BindTexture)(GL_TEXTURE_2D, gtc->_index);
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::specify_texture
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
specify_texture(Texture *tex) {
GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
get_texture_wrap_mode(tex->get_wrapu()));
GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
get_texture_wrap_mode(tex->get_wrapv()));
Colorf border_color = tex->get_border_color();
GLP(TexParameterfv)(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
border_color.get_data());
Texture::FilterType minfilter = tex->get_minfilter();
Texture::FilterType magfilter = tex->get_magfilter();
bool uses_mipmaps = tex->uses_mipmaps() && !CLP(ignore_mipmaps);
#ifndef NDEBUG
if (CLP(force_mipmaps)) {
minfilter = Texture::FT_linear_mipmap_linear;
magfilter = Texture::FT_linear;
uses_mipmaps = true;
}
#endif
if (_supports_generate_mipmap) {
// If the hardware can automatically generate mipmaps, ask it to
// do so now, but only if the texture requires them.
GLP(TexParameteri)(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, uses_mipmaps);
} else if (!tex->might_have_ram_image()) {
// If the hardware can't automatically generate mipmaps, but it's
// a dynamically generated texture (that is, the RAM image isn't
// available so it didn't pass through the CPU), then we'd better
// not try to enable mipmap filtering, since we can't generate
// mipmaps.
uses_mipmaps = false;
}
GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
get_texture_filter_type(minfilter, !uses_mipmaps));
GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
get_texture_filter_type(magfilter, true));
report_my_gl_errors();
}
#ifndef NDEBUG
////////////////////////////////////////////////////////////////////
// Function: compute_gl_image_size
// Description: Calculates how many bytes GL will expect to read for
// a texture image, based on the number of pixels and
// the GL format and type. This is only used for
// debugging.
////////////////////////////////////////////////////////////////////
static int
compute_gl_image_size(int xsize, int ysize, int external_format, int type) {
int num_components = 0;
switch (external_format) {
case GL_COLOR_INDEX:
case GL_STENCIL_INDEX:
case GL_DEPTH_COMPONENT:
case GL_RED:
case GL_GREEN:
case GL_BLUE:
case GL_ALPHA:
case GL_LUMINANCE:
num_components = 1;
break;
case GL_LUMINANCE_ALPHA:
num_components = 2;
break;
case GL_BGR:
case GL_RGB:
num_components = 3;
break;
case GL_BGRA:
case GL_RGBA:
num_components = 4;
break;
}
int pixel_width = 0;
switch (type) {
case GL_UNSIGNED_BYTE:
pixel_width = 1 * num_components;
break;
case GL_UNSIGNED_SHORT:
pixel_width = 2 * num_components;
break;
case GL_UNSIGNED_BYTE_3_3_2:
nassertr(num_components == 3, 0);
pixel_width = 1;
break;
case GL_FLOAT:
pixel_width = 4 * num_components;
break;
}
return xsize * ysize * pixel_width;
}
#endif // NDEBUG
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::apply_texture_immediate
// Access: Protected
// Description: Sends the texture image to GL. This can be used to
// render a texture in immediate mode, or as part of the
// process of creating a GL texture object.
//
// The return value is true if successful, or false if
// the texture has no image.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
apply_texture_immediate(CLP(TextureContext) *gtc, Texture *tex) {
PixelBuffer *pb = tex->get_ram_image();
if (pb == (PixelBuffer *)NULL) {
return false;
}
int width = pb->get_xsize();
int height = pb->get_ysize();
GLint internal_format = get_internal_image_format(pb->get_format());
GLint external_format = get_external_image_format(pb->get_format());
GLenum type = get_image_type(pb->get_image_type());
PTA_uchar image = pb->_image;
nassertr(!image.empty(), false);
if (!_supports_bgr) {
// If the GL doesn't claim to support BGR, we may have to reverse
// the component ordering of the image.
image = fix_component_ordering(external_format, pb);
}
#ifndef NDEBUG
int wanted_size =
compute_gl_image_size(width, height, external_format, type);
nassertr(wanted_size == (int)pb->_image.size(), false);
#endif // NDEBUG
GLP(PixelStorei)(GL_UNPACK_ALIGNMENT, 1);
#ifdef GSG_VERBOSE
GLCAT.debug()
<< "glTexImage2D(GL_TEXTURE_2D, "
<< (int)internal_format << ", "
<< width << ", " << height << ", "
<< tex->get_border_width() << ", " << (int)external_format << ", "
<< (int)type << ", " << tex->get_name() << ")\n";
#endif
bool uses_mipmaps = tex->uses_mipmaps() && !CLP(ignore_mipmaps);
#ifndef NDEBUG
if (CLP(force_mipmaps)) {
uses_mipmaps = true;
}
#endif
if (uses_mipmaps) {
#ifndef NDEBUG
if (CLP(show_mipmaps)) {
build_phony_mipmaps(tex);
report_my_gl_errors();
return true;
} else
#endif
if (!_supports_generate_mipmap) {
// We only need to build the mipmaps by hand if the GL
// doesn't support generating them automatically.
GLUP(Build2DMipmaps)(GL_TEXTURE_2D, internal_format,
width, height,
external_format, type, image);
gtc->_already_applied = false;
gtc->_internal_format = internal_format;
gtc->_width = width;
gtc->_height = height;
gtc->_border_width = 0;
#ifndef NDEBUG
if (CLP(save_mipmaps)) {
save_mipmap_images(tex);
}
#endif
report_my_gl_errors();
return true;
}
}
GLint border_width = tex->get_border_width();
if (!gtc->_already_applied ||
gtc->_internal_format != internal_format ||
gtc->_width != width ||
gtc->_height != height ||
gtc->_border_width != border_width) {
// We need to reload a new image.
GLP(TexImage2D)(GL_TEXTURE_2D, 0, internal_format,
width, height, border_width,
external_format, type, image);
gtc->_already_applied = true;
gtc->_internal_format = internal_format;
gtc->_width = width;
gtc->_height = height;
gtc->_border_width = border_width;
} else {
// We can reload the image over the previous image, saving on
// texture memory fragmentation.
GLP(TexSubImage2D)(GL_TEXTURE_2D, 0, 0, 0, width, height,
external_format, type, image);
}
//report_my_gl_errors();
// want to give explict error for texture creation failure
GLenum error_code = GLP(GetError)();
if (error_code != GL_NO_ERROR) {
const GLubyte *error_string = GLUP(ErrorString)(error_code);
GLCAT.error()
<< "GL texture creation failed for " << tex->get_name();
if (error_string != (const GLubyte *)NULL) {
GLCAT.error(false)
<< " : " << error_string;
}
GLCAT.error(false)
<< "\n";
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_texture
// Access: Protected
// Description: Copies the texture image directly onto the frame
// buffer within the indicated display region.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_texture(TextureContext *tc, const DisplayRegion *dr) {
nassertv(tc != NULL && dr != NULL);
Texture *tex = tc->_texture;
DisplayRegionStack old_dr = push_display_region(dr);
prepare_display_region();
static CPT(RenderState) basic_state;
if (basic_state == (RenderState *)NULL) {
// Create a State object for rendering textures in general. Lots
// of things get turned off.
const RenderAttrib *attribs[] = {
CullFaceAttrib::make(CullFaceAttrib::M_cull_none),
DepthTestAttrib::make(DepthTestAttrib::M_none),
DepthWriteAttrib::make(DepthWriteAttrib::M_off),
TextureApplyAttrib::make(TextureApplyAttrib::M_decal),
ColorWriteAttrib::make(ColorWriteAttrib::M_on),
RenderModeAttrib::make(RenderModeAttrib::M_filled),
//TexMatrixAttrib::make(LMatrix4f::ident_mat()),
ColorBlendAttrib::make_off(),
TransparencyAttrib::make(TransparencyAttrib::M_none),
};
basic_state =
RenderState::make(attribs, sizeof(attribs) / sizeof(void *));
}
CPT(RenderState) state = basic_state->compose
(RenderState::make(TextureAttrib::make(tex)));
modify_state(state);
set_transform(TransformState::make_identity());
// We set up an orthographic projection that defines our entire
// viewport to the range [0..1] in both dimensions. Then, when we
// create a unit square polygon below, it will exactly fill the
// viewport (and thus exactly fill the display region).
GLP(MatrixMode)(GL_PROJECTION);
GLP(PushMatrix)();
GLP(LoadIdentity)();
GLUP(Ortho2D)(0, 1, 0, 1);
float txl, txr, tyt, tyb;
txl = tyb = 0.0f;
#if 0
// remove this auto-scaling stuff for now
// has_requested_size is only used here for draw_texture()
if (tex->_has_requested_size) {
txr = ((float)(tex->_requested_w)) / ((float)(tex->_pbuffer->get_xsize()));
tyt = ((float)(tex->_requested_h)) / ((float)(tex->_pbuffer->get_ysize()));
} else
#endif
{
txr = tyt = 1.0f;
}
// This two-triangle strip is actually a quad. But it's usually
// better to render quads as tristrips anyway.
GLP(Begin)(GL_TRIANGLE_STRIP);
GLP(TexCoord2f)(txl, tyb); GLP(Vertex2i)(0, 0);
GLP(TexCoord2f)(txr, tyb); GLP(Vertex2i)(1, 0);
GLP(TexCoord2f)(txl, tyt); GLP(Vertex2i)(0, 1);
GLP(TexCoord2f)(txr, tyt); GLP(Vertex2i)(1, 1);
GLP(End)();
GLP(MatrixMode)(GL_PROJECTION);
GLP(PopMatrix)();
pop_display_region(old_dr);
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_texture
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_texture(TextureContext *tc, const DisplayRegion *dr,
const RenderBuffer &rb) {
set_draw_buffer(rb);
draw_texture(tc, dr);
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_pixel_buffer
// Access: Protected
// Description: Copies the indicated pixel buffer into the frame
// buffer in the given display region.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) {
// This code is now invalidated by the new design; perhaps the
// interface is not needed anyway.
#if 0
nassertv(pb != NULL && dr != NULL);
nassertv(!pb->_image.empty());
DisplayRegionStack old_dr = push_display_region(dr);
prepare_display_region();
static CPT(RenderState) depth_state;
static CPT(RenderState) color_state;
if (depth_state == (RenderState *)NULL) {
// Create a State object for rendering depth-mask pixel buffers.
depth_state =
RenderState::make(TextureAttrib::make_off(),
ColorWriteAttrib::make(ColorWriteAttrib::M_off),
DepthTestAttrib::make(DepthTestAttrib::M_always),
DepthWriteAttrib::make(DepthWriteAttrib::M_on));
// And one for rendering color buffers.
color_state =
RenderState::make(TextureAttrib::make_off(),
ColorWriteAttrib::make(ColorWriteAttrib::M_on),
DepthTestAttrib::make(DepthTestAttrib::M_none),
DepthWriteAttrib::make(DepthWriteAttrib::M_off));
}
switch (pb->get_format()) {
case PixelBuffer::F_depth_component:
modify_state(depth_state);
break;
case PixelBuffer::F_rgb:
case PixelBuffer::F_rgb5:
case PixelBuffer::F_rgb8:
case PixelBuffer::F_rgb12:
case PixelBuffer::F_rgba:
case PixelBuffer::F_rgbm:
case PixelBuffer::F_rgba4:
case PixelBuffer::F_rgba5:
case PixelBuffer::F_rgba8:
case PixelBuffer::F_rgba12:
modify_state(color_state);
break;
default:
GLCAT.error()
<< "draw_pixel_buffer(): unknown buffer format" << endl;
}
set_transform(TransformState::make_identity());
GLP(PixelStorei)(GL_UNPACK_ALIGNMENT, 1);
WindowProperties props = _win->get_properties();
GLP(MatrixMode)( GL_PROJECTION );
GLP(PushMatrix)();
GLP(LoadIdentity)();
GLUP(Ortho2D)(0, props.get_x_size(),
0, props.get_y_size());
#ifdef GSG_VERBOSE
GLCAT.debug()
<< "glDrawPixels(" << pb->get_xsize() << ", " << pb->get_ysize()
<< ", ";
switch (get_external_image_format(pb->get_format())) {
case GL_DEPTH_COMPONENT:
GLCAT.debug(false) << "GL_DEPTH_COMPONENT, ";
break;
case GL_RGB:
GLCAT.debug(false) << "GL_RGB, ";
break;
case GL_RGBA:
GLCAT.debug(false) << "GL_RGBA, ";
break;
case GL_BGR:
GLCAT.debug(false) << "GL_BGR, ";
break;
case GL_BGRA:
GLCAT.debug(false) << "GL_BGRA, ";
break;
default:
GLCAT.debug(false) << "unknown, ";
break;
}
switch (get_image_type(pb->get_image_type())) {
case GL_UNSIGNED_BYTE:
GLCAT.debug(false) << "GL_UNSIGNED_BYTE, ";
break;
case GL_FLOAT:
GLCAT.debug(false) << "GL_FLOAT, ";
break;
default:
GLCAT.debug(false) << "unknown, ";
break;
}
GLCAT.debug(false)
<< (void *)pb->_image.p() << ")" << endl;
#endif
GLP(RasterPos2i)(0, 0);
GLP(DrawPixels)(pb->get_xsize(), pb->get_ysize(),
get_external_image_format(pb->get_format()),
get_image_type(pb->get_image_type()),
pb->_image.p() );
GLP(MatrixMode)( GL_PROJECTION );
GLP(PopMatrix)();
pop_display_region(old_dr);
report_my_gl_errors();
#endif
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::draw_pixel_buffer
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
draw_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
const RenderBuffer &rb) {
set_draw_buffer(rb);
draw_pixel_buffer(pb, dr);
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_texture_wrap_mode
// Access: Protected
// Description: Maps from the Texture's internal wrap mode symbols to
// GL's.
////////////////////////////////////////////////////////////////////
GLenum CLP(GraphicsStateGuardian)::
get_texture_wrap_mode(Texture::WrapMode wm) const {
if (CLP(ignore_clamp)) {
return GL_REPEAT;
}
switch (wm) {
case Texture::WM_clamp:
return _edge_clamp;
case Texture::WM_repeat:
return GL_REPEAT;
case Texture::WM_mirror:
return _mirror_repeat;
case Texture::WM_mirror_once:
return _mirror_border_clamp;
case Texture::WM_border_color:
return _border_clamp;
case Texture::WM_invalid:
break;
}
GLCAT.error() << "Invalid Texture::WrapMode value!\n";
return _edge_clamp;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_texture_filter_type
// Access: Protected, Static
// Description: Maps from the Texture's internal filter type symbols
// to GL's.
////////////////////////////////////////////////////////////////////
GLenum CLP(GraphicsStateGuardian)::
get_texture_filter_type(Texture::FilterType ft, bool ignore_mipmaps) {
if (CLP(ignore_filters)) {
return GL_NEAREST;
} else if (ignore_mipmaps) {
switch (ft) {
case Texture::FT_nearest_mipmap_nearest:
case Texture::FT_nearest:
return GL_NEAREST;
case Texture::FT_linear:
case Texture::FT_linear_mipmap_nearest:
case Texture::FT_nearest_mipmap_linear:
case Texture::FT_linear_mipmap_linear:
return GL_LINEAR;
case Texture::FT_invalid:
break;
}
} else {
switch (ft) {
case Texture::FT_nearest:
return GL_NEAREST;
case Texture::FT_linear:
return GL_LINEAR;
case Texture::FT_nearest_mipmap_nearest:
return GL_NEAREST_MIPMAP_NEAREST;
case Texture::FT_linear_mipmap_nearest:
return GL_LINEAR_MIPMAP_NEAREST;
case Texture::FT_nearest_mipmap_linear:
return GL_NEAREST_MIPMAP_LINEAR;
case Texture::FT_linear_mipmap_linear:
return GL_LINEAR_MIPMAP_LINEAR;
case Texture::FT_invalid:
break;
}
}
GLCAT.error() << "Invalid Texture::FilterType value!\n";
return GL_NEAREST;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_image_type
// Access: Protected, Static
// Description: Maps from the PixelBuffer's internal Type symbols
// to GL's.
////////////////////////////////////////////////////////////////////
GLenum CLP(GraphicsStateGuardian)::
get_image_type(PixelBuffer::Type type) {
switch (type) {
case PixelBuffer::T_unsigned_byte:
return GL_UNSIGNED_BYTE;
case PixelBuffer::T_unsigned_short:
return GL_UNSIGNED_SHORT;
case PixelBuffer::T_unsigned_byte_332:
return GL_UNSIGNED_BYTE_3_3_2;
case PixelBuffer::T_float:
return GL_FLOAT;
default:
GLCAT.error() << "Invalid PixelBuffer::Type value!\n";
return GL_UNSIGNED_BYTE;
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_external_image_format
// Access: Protected
// Description: Maps from the PixelBuffer's Format symbols
// to GL's.
////////////////////////////////////////////////////////////////////
GLint CLP(GraphicsStateGuardian)::
get_external_image_format(PixelBuffer::Format format) const {
switch (format) {
case PixelBuffer::F_color_index:
return GL_COLOR_INDEX;
case PixelBuffer::F_stencil_index:
return GL_STENCIL_INDEX;
case PixelBuffer::F_depth_component:
return GL_DEPTH_COMPONENT;
case PixelBuffer::F_red:
return GL_RED;
case PixelBuffer::F_green:
return GL_GREEN;
case PixelBuffer::F_blue:
return GL_BLUE;
case PixelBuffer::F_alpha:
return GL_ALPHA;
case PixelBuffer::F_rgb:
case PixelBuffer::F_rgb5:
case PixelBuffer::F_rgb8:
case PixelBuffer::F_rgb12:
case PixelBuffer::F_rgb332:
return _supports_bgr ? GL_BGR : GL_RGB;
case PixelBuffer::F_rgba:
case PixelBuffer::F_rgbm:
case PixelBuffer::F_rgba4:
case PixelBuffer::F_rgba5:
case PixelBuffer::F_rgba8:
case PixelBuffer::F_rgba12:
return _supports_bgr ? GL_BGRA : GL_RGBA;
case PixelBuffer::F_luminance:
return GL_LUMINANCE;
case PixelBuffer::F_luminance_alphamask:
case PixelBuffer::F_luminance_alpha:
return GL_LUMINANCE_ALPHA;
}
GLCAT.error()
<< "Invalid PixelBuffer::Format value in get_external_image_format(): "
<< (int)format << "\n";
return GL_RGB;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_internal_image_format
// Access: Protected, Static
// Description: Maps from the PixelBuffer's Format symbols to a
// suitable internal format for GL textures.
////////////////////////////////////////////////////////////////////
GLint CLP(GraphicsStateGuardian)::
get_internal_image_format(PixelBuffer::Format format) {
switch (format) {
case PixelBuffer::F_rgba:
case PixelBuffer::F_rgbm:
return GL_RGBA;
case PixelBuffer::F_rgba4:
return GL_RGBA4;
case PixelBuffer::F_rgba8:
return GL_RGBA8;
case PixelBuffer::F_rgba12:
return GL_RGBA12;
case PixelBuffer::F_rgb:
return GL_RGB;
case PixelBuffer::F_rgb5:
return GL_RGB5;
case PixelBuffer::F_rgba5:
return GL_RGB5_A1;
case PixelBuffer::F_rgb8:
return GL_RGB8;
case PixelBuffer::F_rgb12:
return GL_RGB12;
case PixelBuffer::F_rgb332:
return GL_R3_G3_B2;
case PixelBuffer::F_alpha:
return GL_ALPHA;
case PixelBuffer::F_red:
case PixelBuffer::F_green:
case PixelBuffer::F_blue:
case PixelBuffer::F_luminance:
return GL_LUMINANCE;
case PixelBuffer::F_luminance_alpha:
case PixelBuffer::F_luminance_alphamask:
return GL_LUMINANCE_ALPHA;
default:
GLCAT.error()
<< "Invalid image format in get_internal_image_format(): "
<< (int)format << "\n";
return GL_RGB;
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_texture_apply_mode_type
// Access: Protected, Static
// Description: Maps from the texture stage's mode types
// to the corresponding OpenGL ids
////////////////////////////////////////////////////////////////////
GLint CLP(GraphicsStateGuardian)::
get_texture_apply_mode_type(TextureStage::Mode am) {
switch (am) {
case TextureStage::M_modulate: return GL_MODULATE;
case TextureStage::M_decal: return GL_DECAL;
case TextureStage::M_blend: return GL_BLEND;
case TextureStage::M_replace: return GL_REPLACE;
case TextureStage::M_add: return GL_ADD;
case TextureStage::M_combine: return GL_COMBINE;
}
GLCAT.error()
<< "Invalid TextureStage::Mode value" << endl;
return GL_MODULATE;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_texture_combine_type
// Access: Protected, Static
// Description: Maps from the texture stage's CombineMode types
// to the corresponding OpenGL ids
////////////////////////////////////////////////////////////////////
GLint CLP(GraphicsStateGuardian)::
get_texture_combine_type(TextureStage::CombineMode cm) {
switch (cm) {
case TextureStage::CM_undefined: // fall through
case TextureStage::CM_replace: return GL_REPLACE;
case TextureStage::CM_modulate: return GL_MODULATE;
case TextureStage::CM_add: return GL_ADD;
case TextureStage::CM_add_signed: return GL_ADD_SIGNED;
case TextureStage::CM_interpolate: return GL_INTERPOLATE;
case TextureStage::CM_subtract: return GL_SUBTRACT;
case TextureStage::CM_dot3_rgb: return GL_DOT3_RGB;
case TextureStage::CM_dot3_rgba: return GL_DOT3_RGBA;
}
GLCAT.error()
<< "Invalid TextureStage::CombineMode value" << endl;
return GL_REPLACE;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_texture_src_type
// Access: Protected, Static
// Description: Maps from the texture stage's CombineSource types
// to the corresponding OpenGL ids
////////////////////////////////////////////////////////////////////
GLint CLP(GraphicsStateGuardian)::
get_texture_src_type(TextureStage::CombineSource cs) {
switch (cs) {
case TextureStage::CS_undefined: // fall through
case TextureStage::CS_texture: return GL_TEXTURE;
case TextureStage::CS_constant: return GL_CONSTANT;
case TextureStage::CS_primary_color: return GL_PRIMARY_COLOR;
case TextureStage::CS_previous: return GL_PREVIOUS;
}
GLCAT.error()
<< "Invalid TextureStage::CombineSource value" << endl;
return GL_TEXTURE;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_texture_operand_type
// Access: Protected, Static
// Description: Maps from the texture stage's CombineOperand types
// to the corresponding OpenGL ids
////////////////////////////////////////////////////////////////////
GLint CLP(GraphicsStateGuardian)::
get_texture_operand_type(TextureStage::CombineOperand co) {
switch (co) {
case TextureStage::CO_undefined: // fall through
case TextureStage::CO_src_alpha: return GL_SRC_ALPHA;
case TextureStage::CO_one_minus_src_alpha: return GL_ONE_MINUS_SRC_ALPHA;
case TextureStage::CO_src_color: return GL_SRC_COLOR;
case TextureStage::CO_one_minus_src_color: return GL_ONE_MINUS_SRC_COLOR;
}
GLCAT.error()
<< "Invalid TextureStage::CombineOperand value" << endl;
return GL_SRC_COLOR;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_fog_mode_type
// Access: Protected, Static
// Description: Maps from the fog types to gl version
////////////////////////////////////////////////////////////////////
GLenum CLP(GraphicsStateGuardian)::
get_fog_mode_type(Fog::Mode m) {
switch(m) {
case Fog::M_linear: return GL_LINEAR;
case Fog::M_exponential: return GL_EXP;
case Fog::M_exponential_squared: return GL_EXP2;
/*
case Fog::M_spline: return GL_FOG_FUNC_SGIS;
*/
default:
GLCAT.error() << "Invalid Fog::Mode value" << endl;
return GL_EXP;
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_blend_equation_type
// Access: Protected, Static
// Description: Maps from ColorBlendAttrib::Mode to glBlendEquation
// value.
////////////////////////////////////////////////////////////////////
GLenum CLP(GraphicsStateGuardian)::
get_blend_equation_type(ColorBlendAttrib::Mode mode) {
switch (mode) {
case ColorBlendAttrib::M_none:
case ColorBlendAttrib::M_add:
return GL_FUNC_ADD;
case ColorBlendAttrib::M_subtract:
return GL_FUNC_SUBTRACT;
case ColorBlendAttrib::M_inv_subtract:
return GL_FUNC_REVERSE_SUBTRACT;
case ColorBlendAttrib::M_min:
return GL_MIN;
case ColorBlendAttrib::M_max:
return GL_MAX;
}
GLCAT.error()
<< "Unknown color blend mode " << (int)mode << endl;
return GL_FUNC_ADD;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_blend_func
// Access: Protected, Static
// Description: Maps from ColorBlendAttrib::Operand to glBlendFunc
// value.
////////////////////////////////////////////////////////////////////
GLenum CLP(GraphicsStateGuardian)::
get_blend_func(ColorBlendAttrib::Operand operand) {
switch (operand) {
case ColorBlendAttrib::O_zero:
return GL_ZERO;
case ColorBlendAttrib::O_one:
return GL_ONE;
case ColorBlendAttrib::O_incoming_color:
return GL_SRC_COLOR;
case ColorBlendAttrib::O_one_minus_incoming_color:
return GL_ONE_MINUS_SRC_COLOR;
case ColorBlendAttrib::O_fbuffer_color:
return GL_DST_COLOR;
case ColorBlendAttrib::O_one_minus_fbuffer_color:
return GL_ONE_MINUS_DST_COLOR;
case ColorBlendAttrib::O_incoming_alpha:
return GL_SRC_ALPHA;
case ColorBlendAttrib::O_one_minus_incoming_alpha:
return GL_ONE_MINUS_SRC_ALPHA;
case ColorBlendAttrib::O_fbuffer_alpha:
return GL_DST_ALPHA;
case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
return GL_ONE_MINUS_DST_ALPHA;
case ColorBlendAttrib::O_constant_color:
return GL_CONSTANT_COLOR;
case ColorBlendAttrib::O_one_minus_constant_color:
return GL_ONE_MINUS_CONSTANT_COLOR;
case ColorBlendAttrib::O_constant_alpha:
return GL_CONSTANT_ALPHA;
case ColorBlendAttrib::O_one_minus_constant_alpha:
return GL_ONE_MINUS_CONSTANT_ALPHA;
case ColorBlendAttrib::O_incoming_color_saturate:
return GL_SRC_ALPHA_SATURATE;
}
GLCAT.error()
<< "Unknown color blend operand " << (int)operand << endl;
return GL_ZERO;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::print_gfx_visual
// Access: Public
// Description: Prints a description of the current visual selected.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
print_gfx_visual() {
GLint i;
GLboolean j;
cout << "Graphics Visual Info (# bits of each):" << endl;
cout << "RGBA: ";
GLP(GetIntegerv)( GL_RED_BITS, &i ); cout << i << " ";
GLP(GetIntegerv)( GL_GREEN_BITS, &i ); cout << i << " ";
GLP(GetIntegerv)( GL_BLUE_BITS, &i ); cout << i << " ";
GLP(GetIntegerv)( GL_ALPHA_BITS, &i ); cout << i << endl;
cout << "Accum RGBA: ";
GLP(GetIntegerv)( GL_ACCUM_RED_BITS, &i ); cout << i << " ";
GLP(GetIntegerv)( GL_ACCUM_GREEN_BITS, &i ); cout << i << " ";
GLP(GetIntegerv)( GL_ACCUM_BLUE_BITS, &i ); cout << i << " ";
GLP(GetIntegerv)( GL_ACCUM_ALPHA_BITS, &i ); cout << i << endl;
GLP(GetIntegerv)( GL_INDEX_BITS, &i ); cout << "Color Index: " << i << endl;
GLP(GetIntegerv)( GL_DEPTH_BITS, &i ); cout << "Depth: " << i << endl;
GLP(GetIntegerv)( GL_ALPHA_BITS, &i ); cout << "Alpha: " << i << endl;
GLP(GetIntegerv)( GL_STENCIL_BITS, &i ); cout << "Stencil: " << i << endl;
GLP(GetBooleanv)( GL_DOUBLEBUFFER, &j ); cout << "DoubleBuffer? "
<< (int)j << endl;
GLP(GetBooleanv)( GL_STEREO, &j ); cout << "Stereo? " << (int)j << endl;
if (_supports_multisample) {
GLP(GetBooleanv)( GL_MULTISAMPLE, &j ); cout << "Multisample? " << (int)j << endl;
GLP(GetIntegerv)( GL_SAMPLES, &i ); cout << "Samples: " << i << endl;
}
GLP(GetBooleanv)( GL_BLEND, &j ); cout << "Blend? " << (int)j << endl;
GLP(GetBooleanv)( GL_POINT_SMOOTH, &j ); cout << "Point Smooth? "
<< (int)j << endl;
GLP(GetBooleanv)( GL_LINE_SMOOTH, &j ); cout << "Line Smooth? "
<< (int)j << endl;
GLP(GetIntegerv)( GL_AUX_BUFFERS, &i ); cout << "Aux Buffers: " << i << endl;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::issue_transformed_color
// Access: Public
// Description: Transform the color by the current color matrix, and
// calls the appropriate glColor function.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
issue_transformed_color(const Colorf &color) const {
Colorf transformed
((color[0] * _current_color_scale[0]) + _current_color_offset[0],
(color[1] * _current_color_scale[1]) + _current_color_offset[1],
(color[2] * _current_color_scale[2]) + _current_color_offset[2],
(color[3] * _current_color_scale[3]) + _current_color_offset[3]);
GLP(Color4fv)(transformed.get_data());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::slot_new_light
// Access: Protected, Virtual
// Description: This will be called by the base class before a
// particular light id will be used for the first time.
// It is intended to allow the derived class to reserve
// any additional resources, if required, for the new
// light; and also to indicate whether the hardware
// supports this many simultaneous lights.
//
// The return value should be true if the additional
// light is supported, or false if it is not.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
slot_new_light(int light_id) {
return (light_id < _max_lights);
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::enable_lighting
// Access: Protected, Virtual
// Description: Intended to be overridden by a derived class to
// enable or disable the use of lighting overall. This
// is called by issue_light() according to whether any
// lights are in use or not.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
enable_lighting(bool enable) {
if (enable) {
GLP(Enable)(GL_LIGHTING);
} else {
GLP(Disable)(GL_LIGHTING);
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::set_ambient_light
// Access: Protected, Virtual
// Description: Intended to be overridden by a derived class to
// indicate the color of the ambient light that should
// be in effect. This is called by issue_light() after
// all other lights have been enabled or disabled.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
set_ambient_light(const Colorf &color) {
GLP(LightModelfv)(GL_LIGHT_MODEL_AMBIENT, color.get_data());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::enable_light
// Access: Protected, Virtual
// Description: Intended to be overridden by a derived class to
// enable the indicated light id. A specific Light will
// already have been bound to this id via bind_light().
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
enable_light(int light_id, bool enable) {
if (enable) {
GLP(Enable)(get_light_id(light_id));
} else {
GLP(Disable)(get_light_id(light_id));
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::begin_bind_lights
// Access: Protected, Virtual
// Description: Called immediately before bind_light() is called,
// this is intended to provide the derived class a hook
// in which to set up some state (like transform) that
// might apply to several lights.
//
// The sequence is: begin_bind_lights() will be called,
// then one or more bind_light() calls, then
// end_bind_lights().
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
begin_bind_lights() {
// We need to temporarily load a new matrix so we can define the
// light in a known coordinate system. We pick the transform of the
// root. (Alternatively, we could leave the current transform where
// it is and compute the light position relative to that transform
// instead of relative to the root, by composing with the matrix
// computed by _transform->invert_compose(render_transform). But I
// think loading a completely new matrix is simpler.)
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PushMatrix)();
GLP(LoadMatrixf)(_scene_setup->get_render_transform()->get_mat().get_data());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::end_bind_lights
// Access: Protected, Virtual
// Description: Called after before bind_light() has been called one
// or more times (but before any geometry is issued or
// additional state is changed), this is intended to
// clean up any temporary changes to the state that may
// have been made by begin_bind_lights().
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
end_bind_lights() {
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PopMatrix)();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::slot_new_clip_plane
// Access: Protected, Virtual
// Description: This will be called by the base class before a
// particular clip plane id will be used for the first
// time. It is intended to allow the derived class to
// reserve any additional resources, if required, for
// the new clip plane; and also to indicate whether the
// hardware supports this many simultaneous clipping
// planes.
//
// The return value should be true if the additional
// plane is supported, or false if it is not.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
slot_new_clip_plane(int plane_id) {
return (plane_id < _max_clip_planes);
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::enable_clip_plane
// Access: Protected, Virtual
// Description: Intended to be overridden by a derived class to
// enable the indicated clip_plane id. A specific
// PlaneNode will already have been bound to this id via
// bind_clip_plane().
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
enable_clip_plane(int plane_id, bool enable) {
if (enable) {
GLP(Enable)(get_clip_plane_id(plane_id));
} else {
GLP(Disable)(get_clip_plane_id(plane_id));
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::begin_bind_clip_planes
// Access: Protected, Virtual
// Description: Called immediately before bind_clip_plane() is called,
// this is intended to provide the derived class a hook
// in which to set up some state (like transform) that
// might apply to several clip_planes.
//
// The sequence is: begin_bind_clip_planes() will be called,
// then one or more bind_clip_plane() calls, then
// end_bind_clip_planes().
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
begin_bind_clip_planes() {
// We need to temporarily load a new matrix so we can define the
// clip_plane in a known coordinate system. We pick the transform of the
// root. (Alternatively, we could leave the current transform where
// it is and compute the clip_plane position relative to that transform
// instead of relative to the root, by composing with the matrix
// computed by _transform->invert_compose(render_transform). But I
// think loading a completely new matrix is simpler.)
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PushMatrix)();
GLP(LoadMatrixf)(_scene_setup->get_render_transform()->get_mat().get_data());
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::bind_clip_plane
// Access: Protected, Virtual
// Description: Called the first time a particular clip_plane has been
// bound to a given id within a frame, this should set
// up the associated hardware clip_plane with the clip_plane's
// properties.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
bind_clip_plane(PlaneNode *plane, int plane_id) {
GLenum id = get_clip_plane_id(plane_id);
NodePath plane_np(plane);
const LMatrix4f &plane_mat = plane_np.get_mat(_scene_setup->get_scene_root());
Planef xformed_plane = plane->get_plane() * plane_mat;
Planed double_plane(LCAST(double, xformed_plane));
GLP(ClipPlane)(id, double_plane.get_data());
report_my_gl_errors();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::end_bind_clip_planes
// Access: Protected, Virtual
// Description: Called after before bind_clip_plane() has been called one
// or more times (but before any geometry is issued or
// additional state is changed), this is intended to
// clean up any temporary changes to the state that may
// have been made by begin_bind_clip_planes().
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
end_bind_clip_planes() {
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PopMatrix)();
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::set_blend_mode
// Access: Protected, Virtual
// Description: Called after any of these three blending states have
// changed; this function is responsible for setting the
// appropriate color blending mode based on the given
// properties.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
ColorBlendAttrib::Mode color_blend_mode,
TransparencyAttrib::Mode transparency_mode) {
// If color_write_mode is off, we disable writing to the color using
// blending. This case is only used if we can't use GLP(ColorMask) to
// disable the color writing for some reason (usually a driver
// problem).
if (color_write_mode == ColorWriteAttrib::M_off) {
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(false);
enable_blend(true);
_glBlendEquation(GL_FUNC_ADD);
GLP(BlendFunc)(GL_ZERO, GL_ONE);
return;
}
// Is there a color blend set?
if (color_blend_mode != ColorBlendAttrib::M_none) {
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(false);
enable_blend(true);
_glBlendEquation(get_blend_equation_type(color_blend_mode));
GLP(BlendFunc)(get_blend_func(_color_blend->get_operand_a()),
get_blend_func(_color_blend->get_operand_b()));
Colorf c = _color_blend->get_color();
_glBlendColor(c[0], c[1], c[2], c[3]);
return;
}
// No color blend; is there a transparency set?
switch (transparency_mode) {
case TransparencyAttrib::M_none:
case TransparencyAttrib::M_binary:
break;
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_dual:
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(false);
enable_blend(true);
_glBlendEquation(GL_FUNC_ADD);
GLP(BlendFunc)(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return;
case TransparencyAttrib::M_multisample:
enable_multisample_alpha_one(true);
enable_multisample_alpha_mask(true);
enable_blend(false);
return;
case TransparencyAttrib::M_multisample_mask:
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(true);
enable_blend(false);
return;
default:
GLCAT.error()
<< "invalid transparency mode " << (int)transparency_mode << endl;
break;
}
// Nothing's set, so disable blending.
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(false);
enable_blend(false);
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::finish_modify_state
// Access: Protected, Virtual
// Description: Called after the GSG state has been modified via
// modify_state() or set_state(), this hook is provided
// for the derived class to do any further state setup
// work.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
finish_modify_state() {
// Apply the texture matrix, if needed.
if (_needs_tex_mat) {
_needs_tex_mat = false;
int num_stages = _current_texture->get_num_on_stages();
nassertv(num_stages <= _max_texture_stages);
for (int i = 0; i < num_stages; i++) {
TextureStage *stage = _current_texture->get_on_stage(i);
_glActiveTexture(GL_TEXTURE0 + i);
GLP(MatrixMode)(GL_TEXTURE);
if (_current_tex_mat->has_stage(stage)) {
GLP(LoadMatrixf)(_current_tex_mat->get_mat(stage).get_data());
} else {
GLP(LoadIdentity)();
// For some reason, the glLoadIdentity() call doesn't work on
// my Dell laptop's IBM OpenGL driver, when used in
// conjunction with glTexGen(), below. But explicitly loading
// an identity matrix does work. But this buggy-driver
// workaround might have other performance implications, so I
// leave it out.
//GLP(LoadMatrixf)(LMatrix4f::ident_mat().get_data());
}
}
report_my_gl_errors();
}
if (_needs_tex_gen) {
_needs_tex_gen = false;
bool force_normal = false;
int num_stages = _current_texture->get_num_on_stages();
nassertv(num_stages <= _max_texture_stages);
// These are passed in for the four OBJECT_PLANE or EYE_PLANE
// values; they effectively define an identity matrix that maps
// the spatial coordinates one-for-one to UV's. If you want a
// mapping other than identity, use a TexMatrixAttrib (or a
// TexProjectorEffect).
static const float s_data[4] = { 1, 0, 0, 0 };
static const float t_data[4] = { 0, 1, 0, 0 };
static const float r_data[4] = { 0, 0, 1, 0 };
static const float q_data[4] = { 0, 0, 0, 1 };
for (int i = 0; i < num_stages; i++) {
TextureStage *stage = _current_texture->get_on_stage(i);
_glActiveTexture(GL_TEXTURE0 + i);
switch (_current_tex_gen->get_mode(stage)) {
case TexGenAttrib::M_off:
case TexGenAttrib::M_cube_map:
GLP(Disable)(GL_TEXTURE_GEN_S);
GLP(Disable)(GL_TEXTURE_GEN_T);
GLP(Disable)(GL_TEXTURE_GEN_R);
GLP(Disable)(GL_TEXTURE_GEN_Q);
break;
case TexGenAttrib::M_sphere_map:
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
GLP(TexGeni)(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
GLP(Enable)(GL_TEXTURE_GEN_S);
GLP(Enable)(GL_TEXTURE_GEN_T);
GLP(Disable)(GL_TEXTURE_GEN_R);
GLP(Disable)(GL_TEXTURE_GEN_Q);
force_normal = true;
break;
case TexGenAttrib::M_object_position:
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
GLP(TexGeni)(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
GLP(TexGeni)(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
GLP(TexGeni)(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
GLP(TexGenfv)(GL_S, GL_OBJECT_PLANE, s_data);
GLP(TexGenfv)(GL_T, GL_OBJECT_PLANE, t_data);
GLP(TexGenfv)(GL_R, GL_OBJECT_PLANE, r_data);
GLP(TexGenfv)(GL_Q, GL_OBJECT_PLANE, q_data);
GLP(Enable)(GL_TEXTURE_GEN_S);
GLP(Enable)(GL_TEXTURE_GEN_T);
GLP(Enable)(GL_TEXTURE_GEN_R);
GLP(Enable)(GL_TEXTURE_GEN_Q);
break;
case TexGenAttrib::M_eye_position:
// To represent eye position correctly, we need to temporarily
// load the coordinate-system transform.
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PushMatrix)();
GLP(LoadMatrixf)(_scene_setup->get_cs_transform()->get_mat().get_data());
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
GLP(TexGeni)(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
GLP(TexGeni)(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
GLP(TexGeni)(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
GLP(TexGenfv)(GL_S, GL_EYE_PLANE, s_data);
GLP(TexGenfv)(GL_T, GL_EYE_PLANE, t_data);
GLP(TexGenfv)(GL_R, GL_EYE_PLANE, r_data);
GLP(TexGenfv)(GL_Q, GL_EYE_PLANE, q_data);
GLP(Enable)(GL_TEXTURE_GEN_S);
GLP(Enable)(GL_TEXTURE_GEN_T);
GLP(Enable)(GL_TEXTURE_GEN_R);
GLP(Enable)(GL_TEXTURE_GEN_Q);
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PopMatrix)();
break;
case TexGenAttrib::M_world_position:
// We achieve world position coordinates by using the eye
// position mode, and loading the transform of the root
// node--thus putting the "eye" at the root.
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PushMatrix)();
CPT(TransformState) root_transform = _scene_setup->get_render_transform();
GLP(LoadMatrixf)(root_transform->get_mat().get_data());
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
GLP(TexGeni)(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
GLP(TexGeni)(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
GLP(TexGeni)(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
GLP(TexGenfv)(GL_S, GL_EYE_PLANE, s_data);
GLP(TexGenfv)(GL_T, GL_EYE_PLANE, t_data);
GLP(TexGenfv)(GL_R, GL_EYE_PLANE, r_data);
GLP(TexGenfv)(GL_Q, GL_EYE_PLANE, q_data);
GLP(Enable)(GL_TEXTURE_GEN_S);
GLP(Enable)(GL_TEXTURE_GEN_T);
GLP(Enable)(GL_TEXTURE_GEN_R);
GLP(Enable)(GL_TEXTURE_GEN_Q);
GLP(MatrixMode)(GL_MODELVIEW);
GLP(PopMatrix)();
break;
}
}
// Certain texgen modes (sphere_map, cube_map) require forcing the
// normal to be sent to the GL while the texgen mode is in effect.
if (force_normal != _texgen_forced_normal) {
if (force_normal) {
force_normals();
} else {
undo_force_normals();
}
_texgen_forced_normal = force_normal;
}
report_my_gl_errors();
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::free_pointers
// Access: Protected, Virtual
// Description: Frees some memory that was explicitly allocated
// within the glgsg.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
free_pointers() {
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::save_frame_buffer
// Access: Public
// Description: Saves the indicated planes of the frame buffer
// (within the indicated display region) and returns it
// in some meaningful form that can be restored later
// via restore_frame_buffer(). This is a helper
// function for push_frame_buffer() and
// pop_frame_buffer().
////////////////////////////////////////////////////////////////////
PT(SavedFrameBuffer) CLP(GraphicsStateGuardian)::
save_frame_buffer(const RenderBuffer &buffer,
CPT(DisplayRegion) dr) {
CLP(SavedFrameBuffer) *sfb = new CLP(SavedFrameBuffer)(buffer, dr);
if (buffer._buffer_type & RenderBuffer::T_depth) {
// Save the depth buffer.
sfb->_depth =
new PixelBuffer(PixelBuffer::depth_buffer(dr->get_pixel_width(),
dr->get_pixel_height()));
copy_pixel_buffer(sfb->_depth, dr, buffer);
}
if (buffer._buffer_type & RenderBuffer::T_back) {
// Save the color buffer.
sfb->_back_rgba = new Texture;
copy_texture(sfb->_back_rgba, dr, buffer);
}
return sfb;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::restore_frame_buffer
// Access: Public
// Description: Restores the frame buffer that was previously saved.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
restore_frame_buffer(SavedFrameBuffer *frame_buffer) {
CLP(SavedFrameBuffer) *sfb = DCAST(CLP(SavedFrameBuffer), frame_buffer);
if (sfb->_back_rgba != (Texture *)NULL &&
(sfb->_buffer._buffer_type & RenderBuffer::T_back) != 0) {
// Restore the color buffer.
TextureContext *tc = sfb->_back_rgba->prepare_now(_prepared_objects, this);
draw_texture(tc, sfb->_display_region, sfb->_buffer);
}
if (sfb->_depth != (PixelBuffer *)NULL &&
(sfb->_buffer._buffer_type & RenderBuffer::T_depth) != 0) {
// Restore the depth buffer.
draw_pixel_buffer(sfb->_depth, sfb->_display_region, sfb->_buffer);
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::get_untextured_state
// Access: Protected, Static
// Description: Returns a RenderState object that represents
// texturing off.
////////////////////////////////////////////////////////////////////
CPT(RenderState) CLP(GraphicsStateGuardian)::
get_untextured_state() {
static CPT(RenderState) state;
if (state == (RenderState *)NULL) {
state = RenderState::make(TextureAttrib::make_off());
}
return state;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::do_auto_rescale_normal
// Access: Protected
// Description: Issues the appropriate GL commands to either rescale
// or normalize the normals according to the current
// transform.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
do_auto_rescale_normal() {
if (_transform->has_uniform_scale()) {
if (IS_NEARLY_EQUAL(_transform->get_uniform_scale(), 1.0f)) {
// If there's no scale at all, don't do anything.
GLP(Disable)(GL_NORMALIZE);
if (_supports_rescale_normal) {
GLP(Disable)(GL_RESCALE_NORMAL);
}
} else {
// There's a uniform scale; use the rescale feature if available.
if (_supports_rescale_normal) {
GLP(Enable)(GL_RESCALE_NORMAL);
GLP(Disable)(GL_NORMALIZE);
} else {
GLP(Enable)(GL_NORMALIZE);
}
}
} else {
// If there's a non-uniform scale, normalize everything.
GLP(Enable)(GL_NORMALIZE);
if (_supports_rescale_normal) {
GLP(Disable)(GL_RESCALE_NORMAL);
}
}
}
#ifndef NDEBUG
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::build_phony_mipmaps
// Access: Protected
// Description: Generates a series of colored mipmap levels to aid in
// visualizing the mipmap levels as the hardware applies
// them.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
build_phony_mipmaps(Texture *tex) {
PixelBuffer *pb = tex->_pbuffer;
int xsize = pb->get_xsize();
int ysize = pb->get_ysize();
GLCAT.info()
<< "Building phony mipmap levels for " << tex->get_name() << "\n";
int level = 0;
while (xsize > 0 && ysize > 0) {
GLCAT.info(false)
<< " level " << level << " is " << xsize << " by " << ysize << "\n";
build_phony_mipmap_level(level, xsize, ysize);
xsize >>= 1;
ysize >>= 1;
level++;
}
while (xsize > 0) {
GLCAT.info(false)
<< " level " << level << " is " << xsize << " by 1\n";
build_phony_mipmap_level(level, xsize, 1);
xsize >>= 1;
level++;
}
while (ysize > 0) {
GLCAT.info(false)
<< " level " << level << " is 1 by " << ysize << "\n";
build_phony_mipmap_level(level, 1, ysize);
ysize >>= 1;
level++;
}
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::build_phony_mipmap_level
// Access: Protected
// Description: Generates a single colored mipmap level.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
build_phony_mipmap_level(int level, int xsize, int ysize) {
static const int num_levels = 10;
static const char *level_filenames[num_levels] = {
"mipmap_level_0.rgb",
"mipmap_level_1.rgb",
"mipmap_level_2.rgb",
"mipmap_level_3.rgb",
"mipmap_level_4.rgb",
"mipmap_level_5.rgb",
"mipmap_level_6.rgb",
"mipmap_level_7.rgb",
"mipmap_level_8.rgb",
"mipmap_level_9.rgb"
};
static const RGBColorf level_colors[num_levels] = {
RGBColorf(1.0f, 1.0f, 1.0f),
RGBColorf(1.0f, 0.0f, 0.0f),
RGBColorf(0.0f, 1.0f, 0.0f),
RGBColorf(0.0f, 0.0f, 1.0f),
RGBColorf(1.0f, 1.0f, 0.0f),
RGBColorf(0.0f, 1.0f, 1.0f),
RGBColorf(1.0f, 0.0f, 1.0f),
RGBColorf(1.0f, 0.5, 0.0f),
RGBColorf(0.0f, 1.0f, 0.5),
RGBColorf(0.83, 0.71, 1.0f)
};
level = level % num_levels;
Filename filename(level_filenames[level]);
PNMImage image_sized(xsize, ysize);
PNMImage image_source;
if (filename.resolve_filename(get_texture_path()) ||
filename.resolve_filename(get_model_path())) {
image_source.read(filename);
}
if (image_source.is_valid()) {
image_sized.quick_filter_from(image_source);
} else {
GLCAT.info(false)
<< " " << filename << " cannot be read, making solid color mipmap.\n";
image_sized.fill(level_colors[level][0],
level_colors[level][1],
level_colors[level][2]);
}
PixelBuffer *pb = new PixelBuffer;
pb->load(image_sized);
GLenum internal_format = get_internal_image_format(pb->get_format());
GLenum external_format = get_external_image_format(pb->get_format());
GLenum type = get_image_type(pb->get_image_type());
GLP(TexImage2D)(GL_TEXTURE_2D, level, internal_format,
pb->get_xsize(), pb->get_ysize(), 0,
external_format, type, pb->_image );
delete pb;
}
////////////////////////////////////////////////////////////////////
// Function: CLP(GraphicsStateGuardian)::save_mipmap_images
// Access: Protected
// Description: Saves out each mipmap level of the indicated texture
// (which must also be the currently active texture in
// the GL state) as a separate image file to disk.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
save_mipmap_images(Texture *tex) {
Filename filename = tex->get_name();
string name;
if (filename.empty()) {
static int index = 0;
name = "texture" + format_string(index);
index++;
} else {
name = filename.get_basename_wo_extension();
}
PixelBuffer *pb = tex->get_ram_image();
nassertv(pb != (PixelBuffer *)NULL);
GLenum external_format = get_external_image_format(pb->get_format());
GLenum type = get_image_type(pb->get_image_type());
int xsize = pb->get_xsize();
int ysize = pb->get_ysize();
// Specify byte-alignment for the pixels on output.
GLP(PixelStorei)(GL_PACK_ALIGNMENT, 1);
int mipmap_level = 0;
do {
xsize = max(xsize, 1);
ysize = max(ysize, 1);
PT(PixelBuffer) mpb =
new PixelBuffer(xsize, ysize, pb->get_num_components(),
pb->get_component_width(), pb->get_image_type(),
pb->get_format());
GLP(GetTexImage)(GL_TEXTURE_2D, mipmap_level, external_format,
type, mpb->_image);
Filename mipmap_filename = name + "_" + format_string(mipmap_level) + ".rgb";
nout << "Writing mipmap level " << mipmap_level
<< " (" << xsize << " by " << ysize << ") "
<< mipmap_filename << "\n";
mpb->write(mipmap_filename);
xsize >>= 1;
ysize >>= 1;
mipmap_level++;
} while (xsize > 0 && ysize > 0);
}
#endif // NDEBUG
TypeHandle CLP(GraphicsStateGuardian)::get_type(void) const {
return get_class_type();
}
TypeHandle CLP(GraphicsStateGuardian)::get_class_type(void) {
return _type_handle;
}
void CLP(GraphicsStateGuardian)::init_type(void) {
GraphicsStateGuardian::init_type();
register_type(_type_handle, CLASSPREFIX_QUOTED "GraphicsStateGuardian",
GraphicsStateGuardian::get_class_type());
}
#ifdef GSG_VERBOSE
void CLP(GraphicsStateGuardian)::
dump_state(void)
{
if (GLCAT.is_debug())
{
ostream &dump = GLCAT.debug(false);
GLCAT.debug() << "Dumping GL State" << endl;
dump << "\t\t" << "GL_LINE_SMOOTH " << _line_smooth_enabled << " " << (bool)GLP(IsEnabled)(GL_LINE_SMOOTH) << "\n";
dump << "\t\t" << "GL_POINT_SMOOTH " << _point_smooth_enabled << " " << (bool)GLP(IsEnabled)(GL_POINT_SMOOTH) << "\n";
dump << "\t\t" << "GL_LIGHTING " << _lighting_enabled << " " << (bool)GLP(IsEnabled)(GL_LIGHTING) << "\n";
dump << "\t\t" << "GL_SCISSOR_TEST " << _scissor_enabled << " " << (bool)GLP(IsEnabled)(GL_SCISSOR_TEST) << "\n";
dump << "\t\t" << "GL_STENCIL_TEST " << " " << (bool)GLP(IsEnabled)(GL_STENCIL_TEST) << "\n";
dump << "\t\t" << "GL_BLEND " << _blend_enabled << " " << (bool)GLP(IsEnabled)(GL_BLEND) << "\n";
dump << "\t\t" << "GL_DEPTH_TEST " << _depth_test_enabled << " " << (bool)GLP(IsEnabled)(GL_DEPTH_TEST) << "\n";
dump << "\t\t" << "GL_FOG " << _fog_enabled << " " << (bool)GLP(IsEnabled)(GL_FOG) << "\n";
dump << "\t\t" << "GL_ALPHA_TEST " << _alpha_test_enabled << " " << (bool)GLP(IsEnabled)(GL_ALPHA_TEST) << "\n";
dump << "\t\t" << "GL_POLYGON_OFFSET_FILL " << _polygon_offset_enabled << " " << (bool)GLP(IsEnabled)(GL_POLYGON_OFFSET_FILL) << "\n";
dump << endl;
}
}
#else // GSG_VERBOSE
// This function does nothing unless GSG_VERBOSE is compiled in.
void CLP(GraphicsStateGuardian)::
dump_state(void)
{
}
#endif // GSG_VERBOSE
#ifdef GSG_VERBOSE
// This is a handy function to output a GLenum value as a string, for
// debugging.
ostream &output_gl_enum(ostream &out, GLenum v) {
switch (v) {
case GL_FALSE:
return out << "GL_FALSE";
case GL_TRUE:
return out << "GL_TRUE";
/* Data types */
case GL_BYTE:
return out << "GL_BYTE";
case GL_UNSIGNED_BYTE:
return out << "GL_UNSIGNED_BYTE";
case GL_SHORT:
return out << "GL_SHORT";
case GL_UNSIGNED_SHORT:
return out << "GL_UNSIGNED_SHORT";
case GL_INT:
return out << "GL_INT";
case GL_UNSIGNED_INT:
return out << "GL_UNSIGNED_INT";
case GL_FLOAT:
return out << "GL_FLOAT";
case GL_DOUBLE:
return out << "GL_DOUBLE";
case GL_2_BYTES:
return out << "GL_2_BYTES";
case GL_3_BYTES:
return out << "GL_3_BYTES";
case GL_4_BYTES:
return out << "GL_4_BYTES";
/* Primitives */
/*
case GL_LINES:
return out << "GL_LINES";
case GL_POINTS:
return out << "GL_POINTS";
*/
case GL_LINE_STRIP:
return out << "GL_LINE_STRIP";
case GL_LINE_LOOP:
return out << "GL_LINE_LOOP";
case GL_TRIANGLES:
return out << "GL_TRIANGLES";
case GL_TRIANGLE_STRIP:
return out << "GL_TRIANGLE_STRIP";
case GL_TRIANGLE_FAN:
return out << "GL_TRIANGLE_FAN";
case GL_QUADS:
return out << "GL_QUADS";
case GL_QUAD_STRIP:
return out << "GL_QUAD_STRIP";
case GL_POLYGON:
return out << "GL_POLYGON";
case GL_EDGE_FLAG:
return out << "GL_EDGE_FLAG";
/* Vertex Arrays */
case GL_VERTEX_ARRAY:
return out << "GL_VERTEX_ARRAY";
case GL_NORMAL_ARRAY:
return out << "GL_NORMAL_ARRAY";
case GL_COLOR_ARRAY:
return out << "GL_COLOR_ARRAY";
case GL_INDEX_ARRAY:
return out << "GL_INDEX_ARRAY";
case GL_TEXTURE_COORD_ARRAY:
return out << "GL_TEXTURE_COORD_ARRAY";
case GL_EDGE_FLAG_ARRAY:
return out << "GL_EDGE_FLAG_ARRAY";
case GL_VERTEX_ARRAY_SIZE:
return out << "GL_VERTEX_ARRAY_SIZE";
case GL_VERTEX_ARRAY_TYPE:
return out << "GL_VERTEX_ARRAY_TYPE";
case GL_VERTEX_ARRAY_STRIDE:
return out << "GL_VERTEX_ARRAY_STRIDE";
case GL_NORMAL_ARRAY_TYPE:
return out << "GL_NORMAL_ARRAY_TYPE";
case GL_NORMAL_ARRAY_STRIDE:
return out << "GL_NORMAL_ARRAY_STRIDE";
case GL_COLOR_ARRAY_SIZE:
return out << "GL_COLOR_ARRAY_SIZE";
case GL_COLOR_ARRAY_TYPE:
return out << "GL_COLOR_ARRAY_TYPE";
case GL_COLOR_ARRAY_STRIDE:
return out << "GL_COLOR_ARRAY_STRIDE";
case GL_INDEX_ARRAY_TYPE:
return out << "GL_INDEX_ARRAY_TYPE";
case GL_INDEX_ARRAY_STRIDE:
return out << "GL_INDEX_ARRAY_STRIDE";
case GL_TEXTURE_COORD_ARRAY_SIZE:
return out << "GL_TEXTURE_COORD_ARRAY_SIZE";
case GL_TEXTURE_COORD_ARRAY_TYPE:
return out << "GL_TEXTURE_COORD_ARRAY_TYPE";
case GL_TEXTURE_COORD_ARRAY_STRIDE:
return out << "GL_TEXTURE_COORD_ARRAY_STRIDE";
case GL_EDGE_FLAG_ARRAY_STRIDE:
return out << "GL_EDGE_FLAG_ARRAY_STRIDE";
case GL_VERTEX_ARRAY_POINTER:
return out << "GL_VERTEX_ARRAY_POINTER";
case GL_NORMAL_ARRAY_POINTER:
return out << "GL_NORMAL_ARRAY_POINTER";
case GL_COLOR_ARRAY_POINTER:
return out << "GL_COLOR_ARRAY_POINTER";
case GL_INDEX_ARRAY_POINTER:
return out << "GL_INDEX_ARRAY_POINTER";
case GL_TEXTURE_COORD_ARRAY_POINTER:
return out << "GL_TEXTURE_COORD_ARRAY_POINTER";
case GL_EDGE_FLAG_ARRAY_POINTER:
return out << "GL_EDGE_FLAG_ARRAY_POINTER";
case GL_V2F:
return out << "GL_V2F";
case GL_V3F:
return out << "GL_V3F";
case GL_C4UB_V2F:
return out << "GL_C4UB_V2F";
case GL_C4UB_V3F:
return out << "GL_C4UB_V3F";
case GL_C3F_V3F:
return out << "GL_C3F_V3F";
case GL_N3F_V3F:
return out << "GL_N3F_V3F";
case GL_C4F_N3F_V3F:
return out << "GL_C4F_N3F_V3F";
case GL_T2F_V3F:
return out << "GL_T2F_V3F";
case GL_T4F_V4F:
return out << "GL_T4F_V4F";
case GL_T2F_C4UB_V3F:
return out << "GL_T2F_C4UB_V3F";
case GL_T2F_C3F_V3F:
return out << "GL_T2F_C3F_V3F";
case GL_T2F_N3F_V3F:
return out << "GL_T2F_N3F_V3F";
case GL_T2F_C4F_N3F_V3F:
return out << "GL_T2F_C4F_N3F_V3F";
case GL_T4F_C4F_N3F_V4F:
return out << "GL_T4F_C4F_N3F_V4F";
/* Matrix Mode */
case GL_MATRIX_MODE:
return out << "GL_MATRIX_MODE";
case GL_MODELVIEW:
return out << "GL_MODELVIEW";
case GL_PROJECTION:
return out << "GL_PROJECTION";
case GL_TEXTURE:
return out << "GL_TEXTURE";
/* Points */
case GL_POINT_SMOOTH:
return out << "GL_POINT_SMOOTH";
case GL_POINT_SIZE:
return out << "GL_POINT_SIZE";
case GL_POINT_SIZE_GRANULARITY:
return out << "GL_POINT_SIZE_GRANULARITY";
case GL_POINT_SIZE_RANGE:
return out << "GL_POINT_SIZE_RANGE";
/* Lines */
case GL_LINE_SMOOTH:
return out << "GL_LINE_SMOOTH";
case GL_LINE_STIPPLE:
return out << "GL_LINE_STIPPLE";
case GL_LINE_STIPPLE_PATTERN:
return out << "GL_LINE_STIPPLE_PATTERN";
case GL_LINE_STIPPLE_REPEAT:
return out << "GL_LINE_STIPPLE_REPEAT";
case GL_LINE_WIDTH:
return out << "GL_LINE_WIDTH";
case GL_LINE_WIDTH_GRANULARITY:
return out << "GL_LINE_WIDTH_GRANULARITY";
case GL_LINE_WIDTH_RANGE:
return out << "GL_LINE_WIDTH_RANGE";
/* Polygons */
case GL_POINT:
return out << "GL_POINT";
case GL_LINE:
return out << "GL_LINE";
case GL_FILL:
return out << "GL_FILL";
case GL_CCW:
return out << "GL_CCW";
case GL_CW:
return out << "GL_CW";
case GL_FRONT:
return out << "GL_FRONT";
case GL_BACK:
return out << "GL_BACK";
case GL_CULL_FACE:
return out << "GL_CULL_FACE";
case GL_CULL_FACE_MODE:
return out << "GL_CULL_FACE_MODE";
case GL_POLYGON_SMOOTH:
return out << "GL_POLYGON_SMOOTH";
case GL_POLYGON_STIPPLE:
return out << "GL_POLYGON_STIPPLE";
case GL_FRONT_FACE:
return out << "GL_FRONT_FACE";
case GL_POLYGON_MODE:
return out << "GL_POLYGON_MODE";
case GL_POLYGON_OFFSET_FACTOR:
return out << "GL_POLYGON_OFFSET_FACTOR";
case GL_POLYGON_OFFSET_UNITS:
return out << "GL_POLYGON_OFFSET_UNITS";
case GL_POLYGON_OFFSET_POINT:
return out << "GL_POLYGON_OFFSET_POINT";
case GL_POLYGON_OFFSET_LINE:
return out << "GL_POLYGON_OFFSET_LINE";
case GL_POLYGON_OFFSET_FILL:
return out << "GL_POLYGON_OFFSET_FILL";
/* Display Lists */
case GL_COMPILE:
return out << "GL_COMPILE";
case GL_COMPILE_AND_EXECUTE:
return out << "GL_COMPILE_AND_EXECUTE";
case GL_LIST_BASE:
return out << "GL_LIST_BASE";
case GL_LIST_INDEX:
return out << "GL_LIST_INDEX";
case GL_LIST_MODE:
return out << "GL_LIST_MODE";
/* Depth buffer */
case GL_NEVER:
return out << "GL_NEVER";
case GL_LESS:
return out << "GL_LESS";
case GL_GEQUAL:
return out << "GL_GEQUAL";
case GL_LEQUAL:
return out << "GL_LEQUAL";
case GL_GREATER:
return out << "GL_GREATER";
case GL_NOTEQUAL:
return out << "GL_NOTEQUAL";
case GL_EQUAL:
return out << "GL_EQUAL";
case GL_ALWAYS:
return out << "GL_ALWAYS";
case GL_DEPTH_TEST:
return out << "GL_DEPTH_TEST";
case GL_DEPTH_BITS:
return out << "GL_DEPTH_BITS";
case GL_DEPTH_CLEAR_VALUE:
return out << "GL_DEPTH_CLEAR_VALUE";
case GL_DEPTH_FUNC:
return out << "GL_DEPTH_FUNC";
case GL_DEPTH_RANGE:
return out << "GL_DEPTH_RANGE";
case GL_DEPTH_WRITEMASK:
return out << "GL_DEPTH_WRITEMASK";
case GL_DEPTH_COMPONENT:
return out << "GL_DEPTH_COMPONENT";
/* Lighting */
case GL_LIGHTING:
return out << "GL_LIGHTING";
case GL_LIGHT0:
return out << "GL_LIGHT0";
case GL_LIGHT1:
return out << "GL_LIGHT1";
case GL_LIGHT2:
return out << "GL_LIGHT2";
case GL_LIGHT3:
return out << "GL_LIGHT3";
case GL_LIGHT4:
return out << "GL_LIGHT4";
case GL_LIGHT5:
return out << "GL_LIGHT5";
case GL_LIGHT6:
return out << "GL_LIGHT6";
case GL_LIGHT7:
return out << "GL_LIGHT7";
case GL_SPOT_EXPONENT:
return out << "GL_SPOT_EXPONENT";
case GL_SPOT_CUTOFF:
return out << "GL_SPOT_CUTOFF";
case GL_CONSTANT_ATTENUATION:
return out << "GL_CONSTANT_ATTENUATION";
case GL_LINEAR_ATTENUATION:
return out << "GL_LINEAR_ATTENUATION";
case GL_QUADRATIC_ATTENUATION:
return out << "GL_QUADRATIC_ATTENUATION";
case GL_AMBIENT:
return out << "GL_AMBIENT";
case GL_DIFFUSE:
return out << "GL_DIFFUSE";
case GL_SPECULAR:
return out << "GL_SPECULAR";
case GL_SHININESS:
return out << "GL_SHININESS";
case GL_EMISSION:
return out << "GL_EMISSION";
case GL_POSITION:
return out << "GL_POSITION";
case GL_SPOT_DIRECTION:
return out << "GL_SPOT_DIRECTION";
case GL_AMBIENT_AND_DIFFUSE:
return out << "GL_AMBIENT_AND_DIFFUSE";
case GL_COLOR_INDEXES:
return out << "GL_COLOR_INDEXES";
case GL_LIGHT_MODEL_TWO_SIDE:
return out << "GL_LIGHT_MODEL_TWO_SIDE";
case GL_LIGHT_MODEL_LOCAL_VIEWER:
return out << "GL_LIGHT_MODEL_LOCAL_VIEWER";
case GL_LIGHT_MODEL_AMBIENT:
return out << "GL_LIGHT_MODEL_AMBIENT";
case GL_FRONT_AND_BACK:
return out << "GL_FRONT_AND_BACK";
case GL_SHADE_MODEL:
return out << "GL_SHADE_MODEL";
case GL_FLAT:
return out << "GL_FLAT";
case GL_SMOOTH:
return out << "GL_SMOOTH";
case GL_COLOR_MATERIAL:
return out << "GL_COLOR_MATERIAL";
case GL_COLOR_MATERIAL_FACE:
return out << "GL_COLOR_MATERIAL_FACE";
case GL_COLOR_MATERIAL_PARAMETER:
return out << "GL_COLOR_MATERIAL_PARAMETER";
case GL_NORMALIZE:
return out << "GL_NORMALIZE";
/* User clipping planes */
case GL_CLIP_PLANE0:
return out << "GL_CLIP_PLANE0";
case GL_CLIP_PLANE1:
return out << "GL_CLIP_PLANE1";
case GL_CLIP_PLANE2:
return out << "GL_CLIP_PLANE2";
case GL_CLIP_PLANE3:
return out << "GL_CLIP_PLANE3";
case GL_CLIP_PLANE4:
return out << "GL_CLIP_PLANE4";
case GL_CLIP_PLANE5:
return out << "GL_CLIP_PLANE5";
/* Accumulation buffer */
case GL_ACCUM_RED_BITS:
return out << "GL_ACCUM_RED_BITS";
case GL_ACCUM_GREEN_BITS:
return out << "GL_ACCUM_GREEN_BITS";
case GL_ACCUM_BLUE_BITS:
return out << "GL_ACCUM_BLUE_BITS";
case GL_ACCUM_ALPHA_BITS:
return out << "GL_ACCUM_ALPHA_BITS";
case GL_ACCUM_CLEAR_VALUE:
return out << "GL_ACCUM_CLEAR_VALUE";
case GL_ACCUM:
return out << "GL_ACCUM";
case GL_ADD:
return out << "GL_ADD";
case GL_LOAD:
return out << "GL_LOAD";
case GL_MULT:
return out << "GL_MULT";
/* Alpha testing */
case GL_ALPHA_TEST:
return out << "GL_ALPHA_TEST";
case GL_ALPHA_TEST_REF:
return out << "GL_ALPHA_TEST_REF";
case GL_ALPHA_TEST_FUNC:
return out << "GL_ALPHA_TEST_FUNC";
/* Blending */
case GL_BLEND:
return out << "GL_BLEND";
case GL_BLEND_SRC:
return out << "GL_BLEND_SRC";
case GL_BLEND_DST:
return out << "GL_BLEND_DST";
/*
case GL_ZERO:
return out << "GL_ZERO";
case GL_ONE:
return out << "GL_ONE";
*/
case GL_SRC_COLOR:
return out << "GL_SRC_COLOR";
case GL_ONE_MINUS_SRC_COLOR:
return out << "GL_ONE_MINUS_SRC_COLOR";
case GL_DST_COLOR:
return out << "GL_DST_COLOR";
case GL_ONE_MINUS_DST_COLOR:
return out << "GL_ONE_MINUS_DST_COLOR";
case GL_SRC_ALPHA:
return out << "GL_SRC_ALPHA";
case GL_ONE_MINUS_SRC_ALPHA:
return out << "GL_ONE_MINUS_SRC_ALPHA";
case GL_DST_ALPHA:
return out << "GL_DST_ALPHA";
case GL_ONE_MINUS_DST_ALPHA:
return out << "GL_ONE_MINUS_DST_ALPHA";
case GL_SRC_ALPHA_SATURATE:
return out << "GL_SRC_ALPHA_SATURATE";
case GL_CONSTANT_COLOR:
return out << "GL_CONSTANT_COLOR";
case GL_ONE_MINUS_CONSTANT_COLOR:
return out << "GL_ONE_MINUS_CONSTANT_COLOR";
case GL_CONSTANT_ALPHA:
return out << "GL_CONSTANT_ALPHA";
case GL_ONE_MINUS_CONSTANT_ALPHA:
return out << "GL_ONE_MINUS_CONSTANT_ALPHA";
/* Render Mode */
case GL_FEEDBACK:
return out << "GL_FEEDBACK";
case GL_RENDER:
return out << "GL_RENDER";
case GL_SELECT:
return out << "GL_SELECT";
/* Feedback */
case GL_2D:
return out << "GL_2D";
case GL_3D:
return out << "GL_3D";
case GL_3D_COLOR:
return out << "GL_3D_COLOR";
case GL_3D_COLOR_TEXTURE:
return out << "GL_3D_COLOR_TEXTURE";
case GL_4D_COLOR_TEXTURE:
return out << "GL_4D_COLOR_TEXTURE";
case GL_POINT_TOKEN:
return out << "GL_POINT_TOKEN";
case GL_LINE_TOKEN:
return out << "GL_LINE_TOKEN";
case GL_LINE_RESET_TOKEN:
return out << "GL_LINE_RESET_TOKEN";
case GL_POLYGON_TOKEN:
return out << "GL_POLYGON_TOKEN";
case GL_BITMAP_TOKEN:
return out << "GL_BITMAP_TOKEN";
case GL_DRAW_PIXEL_TOKEN:
return out << "GL_DRAW_PIXEL_TOKEN";
case GL_COPY_PIXEL_TOKEN:
return out << "GL_COPY_PIXEL_TOKEN";
case GL_PASS_THROUGH_TOKEN:
return out << "GL_PASS_THROUGH_TOKEN";
case GL_FEEDBACK_BUFFER_POINTER:
return out << "GL_FEEDBACK_BUFFER_POINTER";
case GL_FEEDBACK_BUFFER_SIZE:
return out << "GL_FEEDBACK_BUFFER_SIZE";
case GL_FEEDBACK_BUFFER_TYPE:
return out << "GL_FEEDBACK_BUFFER_TYPE";
/* Selection */
case GL_SELECTION_BUFFER_POINTER:
return out << "GL_SELECTION_BUFFER_POINTER";
case GL_SELECTION_BUFFER_SIZE:
return out << "GL_SELECTION_BUFFER_SIZE";
/* Fog */
case GL_FOG:
return out << "GL_FOG";
case GL_FOG_MODE:
return out << "GL_FOG_MODE";
case GL_FOG_DENSITY:
return out << "GL_FOG_DENSITY";
case GL_FOG_COLOR:
return out << "GL_FOG_COLOR";
case GL_FOG_INDEX:
return out << "GL_FOG_INDEX";
case GL_FOG_START:
return out << "GL_FOG_START";
case GL_FOG_END:
return out << "GL_FOG_END";
case GL_LINEAR:
return out << "GL_LINEAR";
case GL_EXP:
return out << "GL_EXP";
case GL_EXP2:
return out << "GL_EXP2";
/* Logic Ops */
case GL_LOGIC_OP:
return out << "GL_LOGIC_OP";
/*
case GL_INDEX_LOGIC_OP:
return out << "GL_INDEX_LOGIC_OP";
*/
case GL_COLOR_LOGIC_OP:
return out << "GL_COLOR_LOGIC_OP";
case GL_LOGIC_OP_MODE:
return out << "GL_LOGIC_OP_MODE";
case GL_CLEAR:
return out << "GL_CLEAR";
case GL_SET:
return out << "GL_SET";
case GL_COPY:
return out << "GL_COPY";
case GL_COPY_INVERTED:
return out << "GL_COPY_INVERTED";
case GL_NOOP:
return out << "GL_NOOP";
case GL_INVERT:
return out << "GL_INVERT";
case GL_AND:
return out << "GL_AND";
case GL_NAND:
return out << "GL_NAND";
case GL_OR:
return out << "GL_OR";
case GL_NOR:
return out << "GL_NOR";
case GL_XOR:
return out << "GL_XOR";
case GL_EQUIV:
return out << "GL_EQUIV";
case GL_AND_REVERSE:
return out << "GL_AND_REVERSE";
case GL_AND_INVERTED:
return out << "GL_AND_INVERTED";
case GL_OR_REVERSE:
return out << "GL_OR_REVERSE";
case GL_OR_INVERTED:
return out << "GL_OR_INVERTED";
/* Stencil */
case GL_STENCIL_TEST:
return out << "GL_STENCIL_TEST";
case GL_STENCIL_WRITEMASK:
return out << "GL_STENCIL_WRITEMASK";
case GL_STENCIL_BITS:
return out << "GL_STENCIL_BITS";
case GL_STENCIL_FUNC:
return out << "GL_STENCIL_FUNC";
case GL_STENCIL_VALUE_MASK:
return out << "GL_STENCIL_VALUE_MASK";
case GL_STENCIL_REF:
return out << "GL_STENCIL_REF";
case GL_STENCIL_FAIL:
return out << "GL_STENCIL_FAIL";
case GL_STENCIL_PASS_DEPTH_PASS:
return out << "GL_STENCIL_PASS_DEPTH_PASS";
case GL_STENCIL_PASS_DEPTH_FAIL:
return out << "GL_STENCIL_PASS_DEPTH_FAIL";
case GL_STENCIL_CLEAR_VALUE:
return out << "GL_STENCIL_CLEAR_VALUE";
case GL_STENCIL_INDEX:
return out << "GL_STENCIL_INDEX";
case GL_KEEP:
return out << "GL_KEEP";
case GL_REPLACE:
return out << "GL_REPLACE";
case GL_INCR:
return out << "GL_INCR";
case GL_DECR:
return out << "GL_DECR";
/* Buffers, Pixel Drawing/Reading */
/*
case GL_NONE:
return out << "GL_NONE";
*/
case GL_LEFT:
return out << "GL_LEFT";
case GL_RIGHT:
return out << "GL_RIGHT";
case GL_FRONT_LEFT:
return out << "GL_FRONT_LEFT";
case GL_FRONT_RIGHT:
return out << "GL_FRONT_RIGHT";
case GL_BACK_LEFT:
return out << "GL_BACK_LEFT";
case GL_BACK_RIGHT:
return out << "GL_BACK_RIGHT";
case GL_AUX0:
return out << "GL_AUX0";
case GL_AUX1:
return out << "GL_AUX1";
case GL_AUX2:
return out << "GL_AUX2";
case GL_AUX3:
return out << "GL_AUX3";
case GL_COLOR_INDEX:
return out << "GL_COLOR_INDEX";
case GL_RED:
return out << "GL_RED";
case GL_GREEN:
return out << "GL_GREEN";
case GL_BLUE:
return out << "GL_BLUE";
case GL_ALPHA:
return out << "GL_ALPHA";
case GL_LUMINANCE:
return out << "GL_LUMINANCE";
case GL_LUMINANCE_ALPHA:
return out << "GL_LUMINANCE_ALPHA";
case GL_ALPHA_BITS:
return out << "GL_ALPHA_BITS";
case GL_RED_BITS:
return out << "GL_RED_BITS";
case GL_GREEN_BITS:
return out << "GL_GREEN_BITS";
case GL_BLUE_BITS:
return out << "GL_BLUE_BITS";
case GL_INDEX_BITS:
return out << "GL_INDEX_BITS";
case GL_SUBPIXEL_BITS:
return out << "GL_SUBPIXEL_BITS";
case GL_AUX_BUFFERS:
return out << "GL_AUX_BUFFERS";
case GL_READ_BUFFER:
return out << "GL_READ_BUFFER";
case GL_DRAW_BUFFER:
return out << "GL_DRAW_BUFFER";
case GL_DOUBLEBUFFER:
return out << "GL_DOUBLEBUFFER";
case GL_STEREO:
return out << "GL_STEREO";
case GL_BITMAP:
return out << "GL_BITMAP";
case GL_COLOR:
return out << "GL_COLOR";
case GL_DEPTH:
return out << "GL_DEPTH";
case GL_STENCIL:
return out << "GL_STENCIL";
case GL_DITHER:
return out << "GL_DITHER";
case GL_RGB:
return out << "GL_RGB";
case GL_RGBA:
return out << "GL_RGBA";
/* Implementation limits */
case GL_MAX_LIST_NESTING:
return out << "GL_MAX_LIST_NESTING";
case GL_MAX_ATTRIB_STACK_DEPTH:
return out << "GL_MAX_ATTRIB_STACK_DEPTH";
case GL_MAX_MODELVIEW_STACK_DEPTH:
return out << "GL_MAX_MODELVIEW_STACK_DEPTH";
case GL_MAX_NAME_STACK_DEPTH:
return out << "GL_MAX_NAME_STACK_DEPTH";
case GL_MAX_PROJECTION_STACK_DEPTH:
return out << "GL_MAX_PROJECTION_STACK_DEPTH";
case GL_MAX_TEXTURE_STACK_DEPTH:
return out << "GL_MAX_TEXTURE_STACK_DEPTH";
case GL_MAX_EVAL_ORDER:
return out << "GL_MAX_EVAL_ORDER";
case GL_MAX_LIGHTS:
return out << "GL_MAX_LIGHTS";
case GL_MAX_CLIP_PLANES:
return out << "GL_MAX_CLIP_PLANES";
case GL_MAX_TEXTURE_SIZE:
return out << "GL_MAX_TEXTURE_SIZE";
case GL_MAX_PIXEL_MAP_TABLE:
return out << "GL_MAX_PIXEL_MAP_TABLE";
case GL_MAX_VIEWPORT_DIMS:
return out << "GL_MAX_VIEWPORT_DIMS";
case GL_MAX_CLIENT_ATTRIB_STACK_DEPTH:
return out << "GL_MAX_CLIENT_ATTRIB_STACK_DEPTH";
/* Gets */
case GL_ATTRIB_STACK_DEPTH:
return out << "GL_ATTRIB_STACK_DEPTH";
case GL_CLIENT_ATTRIB_STACK_DEPTH:
return out << "GL_CLIENT_ATTRIB_STACK_DEPTH";
case GL_COLOR_CLEAR_VALUE:
return out << "GL_COLOR_CLEAR_VALUE";
case GL_COLOR_WRITEMASK:
return out << "GL_COLOR_WRITEMASK";
case GL_CURRENT_INDEX:
return out << "GL_CURRENT_INDEX";
case GL_CURRENT_COLOR:
return out << "GL_CURRENT_COLOR";
case GL_CURRENT_NORMAL:
return out << "GL_CURRENT_NORMAL";
case GL_CURRENT_RASTER_COLOR:
return out << "GL_CURRENT_RASTER_COLOR";
case GL_CURRENT_RASTER_DISTANCE:
return out << "GL_CURRENT_RASTER_DISTANCE";
case GL_CURRENT_RASTER_INDEX:
return out << "GL_CURRENT_RASTER_INDEX";
case GL_CURRENT_RASTER_POSITION:
return out << "GL_CURRENT_RASTER_POSITION";
case GL_CURRENT_RASTER_TEXTURE_COORDS:
return out << "GL_CURRENT_RASTER_TEXTURE_COORDS";
case GL_CURRENT_RASTER_POSITION_VALID:
return out << "GL_CURRENT_RASTER_POSITION_VALID";
case GL_CURRENT_TEXTURE_COORDS:
return out << "GL_CURRENT_TEXTURE_COORDS";
case GL_INDEX_CLEAR_VALUE:
return out << "GL_INDEX_CLEAR_VALUE";
case GL_INDEX_MODE:
return out << "GL_INDEX_MODE";
case GL_INDEX_WRITEMASK:
return out << "GL_INDEX_WRITEMASK";
case GL_MODELVIEW_MATRIX:
return out << "GL_MODELVIEW_MATRIX";
case GL_MODELVIEW_STACK_DEPTH:
return out << "GL_MODELVIEW_STACK_DEPTH";
case GL_NAME_STACK_DEPTH:
return out << "GL_NAME_STACK_DEPTH";
case GL_PROJECTION_MATRIX:
return out << "GL_PROJECTION_MATRIX";
case GL_PROJECTION_STACK_DEPTH:
return out << "GL_PROJECTION_STACK_DEPTH";
case GL_RENDER_MODE:
return out << "GL_RENDER_MODE";
case GL_RGBA_MODE:
return out << "GL_RGBA_MODE";
case GL_TEXTURE_MATRIX:
return out << "GL_TEXTURE_MATRIX";
case GL_TEXTURE_STACK_DEPTH:
return out << "GL_TEXTURE_STACK_DEPTH";
case GL_VIEWPORT:
return out << "GL_VIEWPORT";
/* Evaluators */
case GL_AUTO_NORMAL:
return out << "GL_AUTO_NORMAL";
case GL_MAP1_COLOR_4:
return out << "GL_MAP1_COLOR_4";
case GL_MAP1_GRID_DOMAIN:
return out << "GL_MAP1_GRID_DOMAIN";
case GL_MAP1_GRID_SEGMENTS:
return out << "GL_MAP1_GRID_SEGMENTS";
case GL_MAP1_INDEX:
return out << "GL_MAP1_INDEX";
case GL_MAP1_NORMAL:
return out << "GL_MAP1_NORMAL";
case GL_MAP1_TEXTURE_COORD_1:
return out << "GL_MAP1_TEXTURE_COORD_1";
case GL_MAP1_TEXTURE_COORD_2:
return out << "GL_MAP1_TEXTURE_COORD_2";
case GL_MAP1_TEXTURE_COORD_3:
return out << "GL_MAP1_TEXTURE_COORD_3";
case GL_MAP1_TEXTURE_COORD_4:
return out << "GL_MAP1_TEXTURE_COORD_4";
case GL_MAP1_VERTEX_3:
return out << "GL_MAP1_VERTEX_3";
case GL_MAP1_VERTEX_4:
return out << "GL_MAP1_VERTEX_4";
case GL_MAP2_COLOR_4:
return out << "GL_MAP2_COLOR_4";
case GL_MAP2_GRID_DOMAIN:
return out << "GL_MAP2_GRID_DOMAIN";
case GL_MAP2_GRID_SEGMENTS:
return out << "GL_MAP2_GRID_SEGMENTS";
case GL_MAP2_INDEX:
return out << "GL_MAP2_INDEX";
case GL_MAP2_NORMAL:
return out << "GL_MAP2_NORMAL";
case GL_MAP2_TEXTURE_COORD_1:
return out << "GL_MAP2_TEXTURE_COORD_1";
case GL_MAP2_TEXTURE_COORD_2:
return out << "GL_MAP2_TEXTURE_COORD_2";
case GL_MAP2_TEXTURE_COORD_3:
return out << "GL_MAP2_TEXTURE_COORD_3";
case GL_MAP2_TEXTURE_COORD_4:
return out << "GL_MAP2_TEXTURE_COORD_4";
case GL_MAP2_VERTEX_3:
return out << "GL_MAP2_VERTEX_3";
case GL_MAP2_VERTEX_4:
return out << "GL_MAP2_VERTEX_4";
case GL_COEFF:
return out << "GL_COEFF";
case GL_DOMAIN:
return out << "GL_DOMAIN";
case GL_ORDER:
return out << "GL_ORDER";
/* Hints */
case GL_FOG_HINT:
return out << "GL_FOG_HINT";
case GL_LINE_SMOOTH_HINT:
return out << "GL_LINE_SMOOTH_HINT";
case GL_PERSPECTIVE_CORRECTION_HINT:
return out << "GL_PERSPECTIVE_CORRECTION_HINT";
case GL_POINT_SMOOTH_HINT:
return out << "GL_POINT_SMOOTH_HINT";
case GL_POLYGON_SMOOTH_HINT:
return out << "GL_POLYGON_SMOOTH_HINT";
case GL_DONT_CARE:
return out << "GL_DONT_CARE";
case GL_FASTEST:
return out << "GL_FASTEST";
case GL_NICEST:
return out << "GL_NICEST";
/* Scissor box */
case GL_SCISSOR_TEST:
return out << "GL_SCISSOR_TEST";
case GL_SCISSOR_BOX:
return out << "GL_SCISSOR_BOX";
/* Pixel Mode / Transfer */
case GL_MAP_COLOR:
return out << "GL_MAP_COLOR";
case GL_MAP_STENCIL:
return out << "GL_MAP_STENCIL";
case GL_INDEX_SHIFT:
return out << "GL_INDEX_SHIFT";
case GL_INDEX_OFFSET:
return out << "GL_INDEX_OFFSET";
case GL_RED_SCALE:
return out << "GL_RED_SCALE";
case GL_RED_BIAS:
return out << "GL_RED_BIAS";
case GL_GREEN_SCALE:
return out << "GL_GREEN_SCALE";
case GL_GREEN_BIAS:
return out << "GL_GREEN_BIAS";
case GL_BLUE_SCALE:
return out << "GL_BLUE_SCALE";
case GL_BLUE_BIAS:
return out << "GL_BLUE_BIAS";
case GL_ALPHA_SCALE:
return out << "GL_ALPHA_SCALE";
case GL_ALPHA_BIAS:
return out << "GL_ALPHA_BIAS";
case GL_DEPTH_SCALE:
return out << "GL_DEPTH_SCALE";
case GL_DEPTH_BIAS:
return out << "GL_DEPTH_BIAS";
case GL_PIXEL_MAP_S_TO_S_SIZE:
return out << "GL_PIXEL_MAP_S_TO_S_SIZE";
case GL_PIXEL_MAP_I_TO_I_SIZE:
return out << "GL_PIXEL_MAP_I_TO_I_SIZE";
case GL_PIXEL_MAP_I_TO_R_SIZE:
return out << "GL_PIXEL_MAP_I_TO_R_SIZE";
case GL_PIXEL_MAP_I_TO_G_SIZE:
return out << "GL_PIXEL_MAP_I_TO_G_SIZE";
case GL_PIXEL_MAP_I_TO_B_SIZE:
return out << "GL_PIXEL_MAP_I_TO_B_SIZE";
case GL_PIXEL_MAP_I_TO_A_SIZE:
return out << "GL_PIXEL_MAP_I_TO_A_SIZE";
case GL_PIXEL_MAP_R_TO_R_SIZE:
return out << "GL_PIXEL_MAP_R_TO_R_SIZE";
case GL_PIXEL_MAP_G_TO_G_SIZE:
return out << "GL_PIXEL_MAP_G_TO_G_SIZE";
case GL_PIXEL_MAP_B_TO_B_SIZE:
return out << "GL_PIXEL_MAP_B_TO_B_SIZE";
case GL_PIXEL_MAP_A_TO_A_SIZE:
return out << "GL_PIXEL_MAP_A_TO_A_SIZE";
case GL_PIXEL_MAP_S_TO_S:
return out << "GL_PIXEL_MAP_S_TO_S";
case GL_PIXEL_MAP_I_TO_I:
return out << "GL_PIXEL_MAP_I_TO_I";
case GL_PIXEL_MAP_I_TO_R:
return out << "GL_PIXEL_MAP_I_TO_R";
case GL_PIXEL_MAP_I_TO_G:
return out << "GL_PIXEL_MAP_I_TO_G";
case GL_PIXEL_MAP_I_TO_B:
return out << "GL_PIXEL_MAP_I_TO_B";
case GL_PIXEL_MAP_I_TO_A:
return out << "GL_PIXEL_MAP_I_TO_A";
case GL_PIXEL_MAP_R_TO_R:
return out << "GL_PIXEL_MAP_R_TO_R";
case GL_PIXEL_MAP_G_TO_G:
return out << "GL_PIXEL_MAP_G_TO_G";
case GL_PIXEL_MAP_B_TO_B:
return out << "GL_PIXEL_MAP_B_TO_B";
case GL_PIXEL_MAP_A_TO_A:
return out << "GL_PIXEL_MAP_A_TO_A";
case GL_PACK_ALIGNMENT:
return out << "GL_PACK_ALIGNMENT";
case GL_PACK_LSB_FIRST:
return out << "GL_PACK_LSB_FIRST";
case GL_PACK_ROW_LENGTH:
return out << "GL_PACK_ROW_LENGTH";
case GL_PACK_SKIP_PIXELS:
return out << "GL_PACK_SKIP_PIXELS";
case GL_PACK_SKIP_ROWS:
return out << "GL_PACK_SKIP_ROWS";
case GL_PACK_SWAP_BYTES:
return out << "GL_PACK_SWAP_BYTES";
case GL_UNPACK_ALIGNMENT:
return out << "GL_UNPACK_ALIGNMENT";
case GL_UNPACK_LSB_FIRST:
return out << "GL_UNPACK_LSB_FIRST";
case GL_UNPACK_ROW_LENGTH:
return out << "GL_UNPACK_ROW_LENGTH";
case GL_UNPACK_SKIP_PIXELS:
return out << "GL_UNPACK_SKIP_PIXELS";
case GL_UNPACK_SKIP_ROWS:
return out << "GL_UNPACK_SKIP_ROWS";
case GL_UNPACK_SWAP_BYTES:
return out << "GL_UNPACK_SWAP_BYTES";
case GL_ZOOM_X:
return out << "GL_ZOOM_X";
case GL_ZOOM_Y:
return out << "GL_ZOOM_Y";
/* Texture mapping */
case GL_TEXTURE_ENV:
return out << "GL_TEXTURE_ENV";
case GL_TEXTURE_ENV_MODE:
return out << "GL_TEXTURE_ENV_MODE";
case GL_TEXTURE_1D:
return out << "GL_TEXTURE_1D";
case GL_TEXTURE_2D:
return out << "GL_TEXTURE_2D";
case GL_TEXTURE_WRAP_S:
return out << "GL_TEXTURE_WRAP_S";
case GL_TEXTURE_WRAP_T:
return out << "GL_TEXTURE_WRAP_T";
case GL_TEXTURE_MAG_FILTER:
return out << "GL_TEXTURE_MAG_FILTER";
case GL_TEXTURE_MIN_FILTER:
return out << "GL_TEXTURE_MIN_FILTER";
case GL_TEXTURE_ENV_COLOR:
return out << "GL_TEXTURE_ENV_COLOR";
case GL_TEXTURE_GEN_S:
return out << "GL_TEXTURE_GEN_S";
case GL_TEXTURE_GEN_T:
return out << "GL_TEXTURE_GEN_T";
case GL_TEXTURE_GEN_MODE:
return out << "GL_TEXTURE_GEN_MODE";
case GL_TEXTURE_BORDER_COLOR:
return out << "GL_TEXTURE_BORDER_COLOR";
case GL_TEXTURE_WIDTH:
return out << "GL_TEXTURE_WIDTH";
case GL_TEXTURE_HEIGHT:
return out << "GL_TEXTURE_HEIGHT";
case GL_TEXTURE_BORDER:
return out << "GL_TEXTURE_BORDER";
case GL_TEXTURE_COMPONENTS:
return out << "GL_TEXTURE_COMPONENTS";
case GL_TEXTURE_RED_SIZE:
return out << "GL_TEXTURE_RED_SIZE";
case GL_TEXTURE_GREEN_SIZE:
return out << "GL_TEXTURE_GREEN_SIZE";
case GL_TEXTURE_BLUE_SIZE:
return out << "GL_TEXTURE_BLUE_SIZE";
case GL_TEXTURE_ALPHA_SIZE:
return out << "GL_TEXTURE_ALPHA_SIZE";
case GL_TEXTURE_LUMINANCE_SIZE:
return out << "GL_TEXTURE_LUMINANCE_SIZE";
case GL_TEXTURE_INTENSITY_SIZE:
return out << "GL_TEXTURE_INTENSITY_SIZE";
case GL_NEAREST_MIPMAP_NEAREST:
return out << "GL_NEAREST_MIPMAP_NEAREST";
case GL_NEAREST_MIPMAP_LINEAR:
return out << "GL_NEAREST_MIPMAP_LINEAR";
case GL_LINEAR_MIPMAP_NEAREST:
return out << "GL_LINEAR_MIPMAP_NEAREST";
case GL_LINEAR_MIPMAP_LINEAR:
return out << "GL_LINEAR_MIPMAP_LINEAR";
case GL_OBJECT_LINEAR:
return out << "GL_OBJECT_LINEAR";
case GL_OBJECT_PLANE:
return out << "GL_OBJECT_PLANE";
case GL_EYE_LINEAR:
return out << "GL_EYE_LINEAR";
case GL_EYE_PLANE:
return out << "GL_EYE_PLANE";
case GL_SPHERE_MAP:
return out << "GL_SPHERE_MAP";
case GL_DECAL:
return out << "GL_DECAL";
case GL_MODULATE:
return out << "GL_MODULATE";
case GL_NEAREST:
return out << "GL_NEAREST";
case GL_REPEAT:
return out << "GL_REPEAT";
case GL_CLAMP:
return out << "GL_CLAMP";
case GL_S:
return out << "GL_S";
case GL_T:
return out << "GL_T";
case GL_R:
return out << "GL_R";
case GL_Q:
return out << "GL_Q";
case GL_TEXTURE_GEN_R:
return out << "GL_TEXTURE_GEN_R";
case GL_TEXTURE_GEN_Q:
return out << "GL_TEXTURE_GEN_Q";
/* GL 1.1 texturing */
case GL_PROXY_TEXTURE_1D:
return out << "GL_PROXY_TEXTURE_1D";
case GL_PROXY_TEXTURE_2D:
return out << "GL_PROXY_TEXTURE_2D";
case GL_TEXTURE_PRIORITY:
return out << "GL_TEXTURE_PRIORITY";
case GL_TEXTURE_RESIDENT:
return out << "GL_TEXTURE_RESIDENT";
case GL_TEXTURE_BINDING_1D:
return out << "GL_TEXTURE_BINDING_1D";
case GL_TEXTURE_BINDING_2D:
return out << "GL_TEXTURE_BINDING_2D";
/*
case GL_TEXTURE_INTERNAL_FORMAT:
return out << "GL_TEXTURE_INTERNAL_FORMAT";
*/
/* GL 1.2 texturing */
case GL_PACK_SKIP_IMAGES:
return out << "GL_PACK_SKIP_IMAGES";
case GL_PACK_IMAGE_HEIGHT:
return out << "GL_PACK_IMAGE_HEIGHT";
case GL_UNPACK_SKIP_IMAGES:
return out << "GL_UNPACK_SKIP_IMAGES";
case GL_UNPACK_IMAGE_HEIGHT:
return out << "GL_UNPACK_IMAGE_HEIGHT";
case GL_TEXTURE_3D:
return out << "GL_TEXTURE_3D";
case GL_PROXY_TEXTURE_3D:
return out << "GL_PROXY_TEXTURE_3D";
case GL_TEXTURE_DEPTH:
return out << "GL_TEXTURE_DEPTH";
case GL_TEXTURE_WRAP_R:
return out << "GL_TEXTURE_WRAP_R";
case GL_MAX_3D_TEXTURE_SIZE:
return out << "GL_MAX_3D_TEXTURE_SIZE";
case GL_TEXTURE_BINDING_3D:
return out << "GL_TEXTURE_BINDING_3D";
/* Internal texture formats (GL 1.1) */
case GL_ALPHA4:
return out << "GL_ALPHA4";
case GL_ALPHA8:
return out << "GL_ALPHA8";
case GL_ALPHA12:
return out << "GL_ALPHA12";
case GL_ALPHA16:
return out << "GL_ALPHA16";
case GL_LUMINANCE4:
return out << "GL_LUMINANCE4";
case GL_LUMINANCE8:
return out << "GL_LUMINANCE8";
case GL_LUMINANCE12:
return out << "GL_LUMINANCE12";
case GL_LUMINANCE16:
return out << "GL_LUMINANCE16";
case GL_LUMINANCE4_ALPHA4:
return out << "GL_LUMINANCE4_ALPHA4";
case GL_LUMINANCE6_ALPHA2:
return out << "GL_LUMINANCE6_ALPHA2";
case GL_LUMINANCE8_ALPHA8:
return out << "GL_LUMINANCE8_ALPHA8";
case GL_LUMINANCE12_ALPHA4:
return out << "GL_LUMINANCE12_ALPHA4";
case GL_LUMINANCE12_ALPHA12:
return out << "GL_LUMINANCE12_ALPHA12";
case GL_LUMINANCE16_ALPHA16:
return out << "GL_LUMINANCE16_ALPHA16";
case GL_INTENSITY:
return out << "GL_INTENSITY";
case GL_INTENSITY4:
return out << "GL_INTENSITY4";
case GL_INTENSITY8:
return out << "GL_INTENSITY8";
case GL_INTENSITY12:
return out << "GL_INTENSITY12";
case GL_INTENSITY16:
return out << "GL_INTENSITY16";
case GL_R3_G3_B2:
return out << "GL_R3_G3_B2";
case GL_RGB4:
return out << "GL_RGB4";
case GL_RGB5:
return out << "GL_RGB5";
case GL_RGB8:
return out << "GL_RGB8";
case GL_RGB10:
return out << "GL_RGB10";
case GL_RGB12:
return out << "GL_RGB12";
case GL_RGB16:
return out << "GL_RGB16";
case GL_RGBA2:
return out << "GL_RGBA2";
case GL_RGBA4:
return out << "GL_RGBA4";
case GL_RGB5_A1:
return out << "GL_RGB5_A1";
case GL_RGBA8:
return out << "GL_RGBA8";
case GL_RGB10_A2:
return out << "GL_RGB10_A2";
case GL_RGBA12:
return out << "GL_RGBA12";
case GL_RGBA16:
return out << "GL_RGBA16";
/* Utility */
case GL_VENDOR:
return out << "GL_VENDOR";
case GL_RENDERER:
return out << "GL_RENDERER";
case GL_VERSION:
return out << "GL_VERSION";
case GL_EXTENSIONS:
return out << "GL_EXTENSIONS";
/* Errors */
case GL_INVALID_VALUE:
return out << "GL_INVALID_VALUE";
case GL_INVALID_ENUM:
return out << "GL_INVALID_ENUM";
case GL_INVALID_OPERATION:
return out << "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW:
return out << "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW:
return out << "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY:
return out << "GL_OUT_OF_MEMORY";
/* OpenGL 1.2 */
case GL_RESCALE_NORMAL:
return out << "GL_RESCALE_NORMAL";
case GL_CLAMP_TO_EDGE:
return out << "GL_CLAMP_TO_EDGE";
case GL_MAX_ELEMENTS_VERTICES:
return out << "GL_MAX_ELEMENTS_VERTICES";
case GL_MAX_ELEMENTS_INDICES:
return out << "GL_MAX_ELEMENTS_INDICES";
case GL_BGR:
return out << "GL_BGR";
case GL_BGRA:
return out << "GL_BGRA";
case GL_UNSIGNED_BYTE_3_3_2:
return out << "GL_UNSIGNED_BYTE_3_3_2";
case GL_UNSIGNED_BYTE_2_3_3_REV:
return out << "GL_UNSIGNED_BYTE_2_3_3_REV";
case GL_UNSIGNED_SHORT_5_6_5:
return out << "GL_UNSIGNED_SHORT_5_6_5";
case GL_UNSIGNED_SHORT_5_6_5_REV:
return out << "GL_UNSIGNED_SHORT_5_6_5_REV";
case GL_UNSIGNED_SHORT_4_4_4_4:
return out << "GL_UNSIGNED_SHORT_4_4_4_4";
case GL_UNSIGNED_SHORT_4_4_4_4_REV:
return out << "GL_UNSIGNED_SHORT_4_4_4_4_REV";
case GL_UNSIGNED_SHORT_5_5_5_1:
return out << "GL_UNSIGNED_SHORT_5_5_5_1";
case GL_UNSIGNED_SHORT_1_5_5_5_REV:
return out << "GL_UNSIGNED_SHORT_1_5_5_5_REV";
case GL_UNSIGNED_INT_8_8_8_8:
return out << "GL_UNSIGNED_INT_8_8_8_8";
case GL_UNSIGNED_INT_8_8_8_8_REV:
return out << "GL_UNSIGNED_INT_8_8_8_8_REV";
case GL_UNSIGNED_INT_10_10_10_2:
return out << "GL_UNSIGNED_INT_10_10_10_2";
case GL_UNSIGNED_INT_2_10_10_10_REV:
return out << "GL_UNSIGNED_INT_2_10_10_10_REV";
case GL_LIGHT_MODEL_COLOR_CONTROL:
return out << "GL_LIGHT_MODEL_COLOR_CONTROL";
case GL_SINGLE_COLOR:
return out << "GL_SINGLE_COLOR";
case GL_SEPARATE_SPECULAR_COLOR:
return out << "GL_SEPARATE_SPECULAR_COLOR";
case GL_TEXTURE_MIN_LOD:
return out << "GL_TEXTURE_MIN_LOD";
case GL_TEXTURE_MAX_LOD:
return out << "GL_TEXTURE_MAX_LOD";
case GL_TEXTURE_BASE_LEVEL:
return out << "GL_TEXTURE_BASE_LEVEL";
case GL_TEXTURE_MAX_LEVEL:
return out << "GL_TEXTURE_MAX_LEVEL";
}
return out << (int)v;
}
#endif