open_toontown_panda3d/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

2417 lines
89 KiB
C++

// Filename: tinyGraphicsStateGuardian.cxx
// Created by: drose (24Apr08)
//
////////////////////////////////////////////////////////////////////
//
// 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 "tinyGraphicsStateGuardian.h"
#include "tinyGeomMunger.h"
#include "tinyTextureContext.h"
#include "config_tinydisplay.h"
#include "pStatTimer.h"
#include "geomVertexReader.h"
extern "C" {
#include "zgl.h"
#include "zmath.h"
}
TypeHandle TinyGraphicsStateGuardian::_type_handle;
PStatCollector TinyGraphicsStateGuardian::_vertices_immediate_pcollector("Vertices:Immediate mode");
static const ZB_fillTriangleFunc fill_tri_funcs
[2 /* depth write: zon, zoff */]
[3 /* color write: noblend, blend, nocolor */]
[3 /* alpha test: anone, aless, amore */]
[2 /* depth test: znone, zless */]
[3 /* shading: white, flat, smooth */]
[3 /* texturing: untextured, textured, perspective textured */] = {
{ // depth write zon
{ // color write noblend
{ // alpha test anone
{
{ ZB_fillTriangleFlat_xx_zon_noblend_anone_znone,
ZB_fillTriangleMapping_xx_zon_noblend_anone_znone,
ZB_fillTriangleMappingPerspective_xx_zon_noblend_anone_znone },
{ ZB_fillTriangleFlat_xx_zon_noblend_anone_znone,
ZB_fillTriangleMappingFlat_xx_zon_noblend_anone_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_noblend_anone_znone },
{ ZB_fillTriangleSmooth_xx_zon_noblend_anone_znone,
ZB_fillTriangleMappingSmooth_xx_zon_noblend_anone_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_noblend_anone_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_noblend_anone_zless,
ZB_fillTriangleMapping_xx_zon_noblend_anone_zless,
ZB_fillTriangleMappingPerspective_xx_zon_noblend_anone_zless },
{ ZB_fillTriangleFlat_xx_zon_noblend_anone_zless,
ZB_fillTriangleMappingFlat_xx_zon_noblend_anone_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_noblend_anone_zless },
{ ZB_fillTriangleSmooth_xx_zon_noblend_anone_zless,
ZB_fillTriangleMappingSmooth_xx_zon_noblend_anone_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_noblend_anone_zless },
},
},
{ // alpha test aless
{
{ ZB_fillTriangleFlat_xx_zon_noblend_aless_znone,
ZB_fillTriangleMapping_xx_zon_noblend_aless_znone,
ZB_fillTriangleMappingPerspective_xx_zon_noblend_aless_znone },
{ ZB_fillTriangleFlat_xx_zon_noblend_aless_znone,
ZB_fillTriangleMappingFlat_xx_zon_noblend_aless_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_noblend_aless_znone },
{ ZB_fillTriangleSmooth_xx_zon_noblend_aless_znone,
ZB_fillTriangleMappingSmooth_xx_zon_noblend_aless_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_noblend_aless_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_noblend_aless_zless,
ZB_fillTriangleMapping_xx_zon_noblend_aless_zless,
ZB_fillTriangleMappingPerspective_xx_zon_noblend_aless_zless },
{ ZB_fillTriangleFlat_xx_zon_noblend_aless_zless,
ZB_fillTriangleMappingFlat_xx_zon_noblend_aless_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_noblend_aless_zless },
{ ZB_fillTriangleSmooth_xx_zon_noblend_aless_zless,
ZB_fillTriangleMappingSmooth_xx_zon_noblend_aless_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_noblend_aless_zless },
},
},
{ // alpha test amore
{
{ ZB_fillTriangleFlat_xx_zon_noblend_amore_znone,
ZB_fillTriangleMapping_xx_zon_noblend_amore_znone,
ZB_fillTriangleMappingPerspective_xx_zon_noblend_amore_znone },
{ ZB_fillTriangleFlat_xx_zon_noblend_amore_znone,
ZB_fillTriangleMappingFlat_xx_zon_noblend_amore_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_noblend_amore_znone },
{ ZB_fillTriangleSmooth_xx_zon_noblend_amore_znone,
ZB_fillTriangleMappingSmooth_xx_zon_noblend_amore_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_noblend_amore_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_noblend_amore_zless,
ZB_fillTriangleMapping_xx_zon_noblend_amore_zless,
ZB_fillTriangleMappingPerspective_xx_zon_noblend_amore_zless },
{ ZB_fillTriangleFlat_xx_zon_noblend_amore_zless,
ZB_fillTriangleMappingFlat_xx_zon_noblend_amore_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_noblend_amore_zless },
{ ZB_fillTriangleSmooth_xx_zon_noblend_amore_zless,
ZB_fillTriangleMappingSmooth_xx_zon_noblend_amore_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_noblend_amore_zless },
},
},
},
{ // color write blend
{ // alpha test anone
{
{ ZB_fillTriangleFlat_xx_zon_blend_anone_znone,
ZB_fillTriangleMapping_xx_zon_blend_anone_znone,
ZB_fillTriangleMappingPerspective_xx_zon_blend_anone_znone },
{ ZB_fillTriangleFlat_xx_zon_blend_anone_znone,
ZB_fillTriangleMappingFlat_xx_zon_blend_anone_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_blend_anone_znone },
{ ZB_fillTriangleSmooth_xx_zon_blend_anone_znone,
ZB_fillTriangleMappingSmooth_xx_zon_blend_anone_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_blend_anone_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_blend_anone_zless,
ZB_fillTriangleMapping_xx_zon_blend_anone_zless,
ZB_fillTriangleMappingPerspective_xx_zon_blend_anone_zless },
{ ZB_fillTriangleFlat_xx_zon_blend_anone_zless,
ZB_fillTriangleMappingFlat_xx_zon_blend_anone_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_blend_anone_zless },
{ ZB_fillTriangleSmooth_xx_zon_blend_anone_zless,
ZB_fillTriangleMappingSmooth_xx_zon_blend_anone_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_blend_anone_zless },
},
},
{ // alpha test aless
{
{ ZB_fillTriangleFlat_xx_zon_blend_aless_znone,
ZB_fillTriangleMapping_xx_zon_blend_aless_znone,
ZB_fillTriangleMappingPerspective_xx_zon_blend_aless_znone },
{ ZB_fillTriangleFlat_xx_zon_blend_aless_znone,
ZB_fillTriangleMappingFlat_xx_zon_blend_aless_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_blend_aless_znone },
{ ZB_fillTriangleSmooth_xx_zon_blend_aless_znone,
ZB_fillTriangleMappingSmooth_xx_zon_blend_aless_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_blend_aless_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_blend_aless_zless,
ZB_fillTriangleMapping_xx_zon_blend_aless_zless,
ZB_fillTriangleMappingPerspective_xx_zon_blend_aless_zless },
{ ZB_fillTriangleFlat_xx_zon_blend_aless_zless,
ZB_fillTriangleMappingFlat_xx_zon_blend_aless_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_blend_aless_zless },
{ ZB_fillTriangleSmooth_xx_zon_blend_aless_zless,
ZB_fillTriangleMappingSmooth_xx_zon_blend_aless_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_blend_aless_zless },
},
},
{ // alpha test amore
{
{ ZB_fillTriangleFlat_xx_zon_blend_amore_znone,
ZB_fillTriangleMapping_xx_zon_blend_amore_znone,
ZB_fillTriangleMappingPerspective_xx_zon_blend_amore_znone },
{ ZB_fillTriangleFlat_xx_zon_blend_amore_znone,
ZB_fillTriangleMappingFlat_xx_zon_blend_amore_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_blend_amore_znone },
{ ZB_fillTriangleSmooth_xx_zon_blend_amore_znone,
ZB_fillTriangleMappingSmooth_xx_zon_blend_amore_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_blend_amore_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_blend_amore_zless,
ZB_fillTriangleMapping_xx_zon_blend_amore_zless,
ZB_fillTriangleMappingPerspective_xx_zon_blend_amore_zless },
{ ZB_fillTriangleFlat_xx_zon_blend_amore_zless,
ZB_fillTriangleMappingFlat_xx_zon_blend_amore_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_blend_amore_zless },
{ ZB_fillTriangleSmooth_xx_zon_blend_amore_zless,
ZB_fillTriangleMappingSmooth_xx_zon_blend_amore_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_blend_amore_zless },
},
},
},
{ // color write nocolor
{ // alpha test anone
{
{ ZB_fillTriangleFlat_xx_zon_nocolor_anone_znone,
ZB_fillTriangleMapping_xx_zon_nocolor_anone_znone,
ZB_fillTriangleMappingPerspective_xx_zon_nocolor_anone_znone },
{ ZB_fillTriangleFlat_xx_zon_nocolor_anone_znone,
ZB_fillTriangleMappingFlat_xx_zon_nocolor_anone_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_nocolor_anone_znone },
{ ZB_fillTriangleSmooth_xx_zon_nocolor_anone_znone,
ZB_fillTriangleMappingSmooth_xx_zon_nocolor_anone_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_nocolor_anone_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_nocolor_anone_zless,
ZB_fillTriangleMapping_xx_zon_nocolor_anone_zless,
ZB_fillTriangleMappingPerspective_xx_zon_nocolor_anone_zless },
{ ZB_fillTriangleFlat_xx_zon_nocolor_anone_zless,
ZB_fillTriangleMappingFlat_xx_zon_nocolor_anone_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_nocolor_anone_zless },
{ ZB_fillTriangleSmooth_xx_zon_nocolor_anone_zless,
ZB_fillTriangleMappingSmooth_xx_zon_nocolor_anone_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_nocolor_anone_zless },
},
},
{ // alpha test aless
{
{ ZB_fillTriangleFlat_xx_zon_nocolor_aless_znone,
ZB_fillTriangleMapping_xx_zon_nocolor_aless_znone,
ZB_fillTriangleMappingPerspective_xx_zon_nocolor_aless_znone },
{ ZB_fillTriangleFlat_xx_zon_nocolor_aless_znone,
ZB_fillTriangleMappingFlat_xx_zon_nocolor_aless_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_nocolor_aless_znone },
{ ZB_fillTriangleSmooth_xx_zon_nocolor_aless_znone,
ZB_fillTriangleMappingSmooth_xx_zon_nocolor_aless_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_nocolor_aless_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_nocolor_aless_zless,
ZB_fillTriangleMapping_xx_zon_nocolor_aless_zless,
ZB_fillTriangleMappingPerspective_xx_zon_nocolor_aless_zless },
{ ZB_fillTriangleFlat_xx_zon_nocolor_aless_zless,
ZB_fillTriangleMappingFlat_xx_zon_nocolor_aless_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_nocolor_aless_zless },
{ ZB_fillTriangleSmooth_xx_zon_nocolor_aless_zless,
ZB_fillTriangleMappingSmooth_xx_zon_nocolor_aless_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_nocolor_aless_zless },
},
},
{ // alpha test amore
{
{ ZB_fillTriangleFlat_xx_zon_nocolor_amore_znone,
ZB_fillTriangleMapping_xx_zon_nocolor_amore_znone,
ZB_fillTriangleMappingPerspective_xx_zon_nocolor_amore_znone },
{ ZB_fillTriangleFlat_xx_zon_nocolor_amore_znone,
ZB_fillTriangleMappingFlat_xx_zon_nocolor_amore_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_nocolor_amore_znone },
{ ZB_fillTriangleSmooth_xx_zon_nocolor_amore_znone,
ZB_fillTriangleMappingSmooth_xx_zon_nocolor_amore_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_nocolor_amore_znone },
},
{
{ ZB_fillTriangleFlat_xx_zon_nocolor_amore_zless,
ZB_fillTriangleMapping_xx_zon_nocolor_amore_zless,
ZB_fillTriangleMappingPerspective_xx_zon_nocolor_amore_zless },
{ ZB_fillTriangleFlat_xx_zon_nocolor_amore_zless,
ZB_fillTriangleMappingFlat_xx_zon_nocolor_amore_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zon_nocolor_amore_zless },
{ ZB_fillTriangleSmooth_xx_zon_nocolor_amore_zless,
ZB_fillTriangleMappingSmooth_xx_zon_nocolor_amore_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zon_nocolor_amore_zless },
},
},
},
},
{ // depth write zoff
{ // color write noblend
{ // alpha test anone
{
{ ZB_fillTriangleFlat_xx_zoff_noblend_anone_znone,
ZB_fillTriangleMapping_xx_zoff_noblend_anone_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_noblend_anone_znone },
{ ZB_fillTriangleFlat_xx_zoff_noblend_anone_znone,
ZB_fillTriangleMappingFlat_xx_zoff_noblend_anone_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_noblend_anone_znone },
{ ZB_fillTriangleSmooth_xx_zoff_noblend_anone_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_noblend_anone_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_noblend_anone_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_noblend_anone_zless,
ZB_fillTriangleMapping_xx_zoff_noblend_anone_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_noblend_anone_zless },
{ ZB_fillTriangleFlat_xx_zoff_noblend_anone_zless,
ZB_fillTriangleMappingFlat_xx_zoff_noblend_anone_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_noblend_anone_zless },
{ ZB_fillTriangleSmooth_xx_zoff_noblend_anone_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_noblend_anone_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_noblend_anone_zless },
},
},
{ // alpha test aless
{
{ ZB_fillTriangleFlat_xx_zoff_noblend_aless_znone,
ZB_fillTriangleMapping_xx_zoff_noblend_aless_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_noblend_aless_znone },
{ ZB_fillTriangleFlat_xx_zoff_noblend_aless_znone,
ZB_fillTriangleMappingFlat_xx_zoff_noblend_aless_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_noblend_aless_znone },
{ ZB_fillTriangleSmooth_xx_zoff_noblend_aless_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_noblend_aless_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_noblend_aless_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_noblend_aless_zless,
ZB_fillTriangleMapping_xx_zoff_noblend_aless_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_noblend_aless_zless },
{ ZB_fillTriangleFlat_xx_zoff_noblend_aless_zless,
ZB_fillTriangleMappingFlat_xx_zoff_noblend_aless_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_noblend_aless_zless },
{ ZB_fillTriangleSmooth_xx_zoff_noblend_aless_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_noblend_aless_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_noblend_aless_zless },
},
},
{ // alpha test amore
{
{ ZB_fillTriangleFlat_xx_zoff_noblend_amore_znone,
ZB_fillTriangleMapping_xx_zoff_noblend_amore_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_noblend_amore_znone },
{ ZB_fillTriangleFlat_xx_zoff_noblend_amore_znone,
ZB_fillTriangleMappingFlat_xx_zoff_noblend_amore_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_noblend_amore_znone },
{ ZB_fillTriangleSmooth_xx_zoff_noblend_amore_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_noblend_amore_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_noblend_amore_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_noblend_amore_zless,
ZB_fillTriangleMapping_xx_zoff_noblend_amore_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_noblend_amore_zless },
{ ZB_fillTriangleFlat_xx_zoff_noblend_amore_zless,
ZB_fillTriangleMappingFlat_xx_zoff_noblend_amore_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_noblend_amore_zless },
{ ZB_fillTriangleSmooth_xx_zoff_noblend_amore_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_noblend_amore_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_noblend_amore_zless },
},
},
},
{ // color write blend
{ // alpha test anone
{
{ ZB_fillTriangleFlat_xx_zoff_blend_anone_znone,
ZB_fillTriangleMapping_xx_zoff_blend_anone_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_blend_anone_znone },
{ ZB_fillTriangleFlat_xx_zoff_blend_anone_znone,
ZB_fillTriangleMappingFlat_xx_zoff_blend_anone_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_blend_anone_znone },
{ ZB_fillTriangleSmooth_xx_zoff_blend_anone_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_blend_anone_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_blend_anone_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_blend_anone_zless,
ZB_fillTriangleMapping_xx_zoff_blend_anone_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_blend_anone_zless },
{ ZB_fillTriangleFlat_xx_zoff_blend_anone_zless,
ZB_fillTriangleMappingFlat_xx_zoff_blend_anone_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_blend_anone_zless },
{ ZB_fillTriangleSmooth_xx_zoff_blend_anone_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_blend_anone_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_blend_anone_zless },
},
},
{ // alpha test aless
{
{ ZB_fillTriangleFlat_xx_zoff_blend_aless_znone,
ZB_fillTriangleMapping_xx_zoff_blend_aless_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_blend_aless_znone },
{ ZB_fillTriangleFlat_xx_zoff_blend_aless_znone,
ZB_fillTriangleMappingFlat_xx_zoff_blend_aless_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_blend_aless_znone },
{ ZB_fillTriangleSmooth_xx_zoff_blend_aless_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_blend_aless_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_blend_aless_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_blend_aless_zless,
ZB_fillTriangleMapping_xx_zoff_blend_aless_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_blend_aless_zless },
{ ZB_fillTriangleFlat_xx_zoff_blend_aless_zless,
ZB_fillTriangleMappingFlat_xx_zoff_blend_aless_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_blend_aless_zless },
{ ZB_fillTriangleSmooth_xx_zoff_blend_aless_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_blend_aless_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_blend_aless_zless },
},
},
{ // alpha test amore
{
{ ZB_fillTriangleFlat_xx_zoff_blend_amore_znone,
ZB_fillTriangleMapping_xx_zoff_blend_amore_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_blend_amore_znone },
{ ZB_fillTriangleFlat_xx_zoff_blend_amore_znone,
ZB_fillTriangleMappingFlat_xx_zoff_blend_amore_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_blend_amore_znone },
{ ZB_fillTriangleSmooth_xx_zoff_blend_amore_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_blend_amore_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_blend_amore_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_blend_amore_zless,
ZB_fillTriangleMapping_xx_zoff_blend_amore_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_blend_amore_zless },
{ ZB_fillTriangleFlat_xx_zoff_blend_amore_zless,
ZB_fillTriangleMappingFlat_xx_zoff_blend_amore_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_blend_amore_zless },
{ ZB_fillTriangleSmooth_xx_zoff_blend_amore_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_blend_amore_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_blend_amore_zless },
},
},
},
{ // color write nocolor
{ // alpha test anone
{
{ ZB_fillTriangleFlat_xx_zoff_nocolor_anone_znone,
ZB_fillTriangleMapping_xx_zoff_nocolor_anone_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_nocolor_anone_znone },
{ ZB_fillTriangleFlat_xx_zoff_nocolor_anone_znone,
ZB_fillTriangleMappingFlat_xx_zoff_nocolor_anone_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_nocolor_anone_znone },
{ ZB_fillTriangleSmooth_xx_zoff_nocolor_anone_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_nocolor_anone_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_nocolor_anone_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_nocolor_anone_zless,
ZB_fillTriangleMapping_xx_zoff_nocolor_anone_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_nocolor_anone_zless },
{ ZB_fillTriangleFlat_xx_zoff_nocolor_anone_zless,
ZB_fillTriangleMappingFlat_xx_zoff_nocolor_anone_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_nocolor_anone_zless },
{ ZB_fillTriangleSmooth_xx_zoff_nocolor_anone_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_nocolor_anone_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_nocolor_anone_zless },
},
},
{ // alpha test aless
{
{ ZB_fillTriangleFlat_xx_zoff_nocolor_aless_znone,
ZB_fillTriangleMapping_xx_zoff_nocolor_aless_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_nocolor_aless_znone },
{ ZB_fillTriangleFlat_xx_zoff_nocolor_aless_znone,
ZB_fillTriangleMappingFlat_xx_zoff_nocolor_aless_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_nocolor_aless_znone },
{ ZB_fillTriangleSmooth_xx_zoff_nocolor_aless_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_nocolor_aless_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_nocolor_aless_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_nocolor_aless_zless,
ZB_fillTriangleMapping_xx_zoff_nocolor_aless_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_nocolor_aless_zless },
{ ZB_fillTriangleFlat_xx_zoff_nocolor_aless_zless,
ZB_fillTriangleMappingFlat_xx_zoff_nocolor_aless_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_nocolor_aless_zless },
{ ZB_fillTriangleSmooth_xx_zoff_nocolor_aless_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_nocolor_aless_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_nocolor_aless_zless },
},
},
{ // alpha test amore
{
{ ZB_fillTriangleFlat_xx_zoff_nocolor_amore_znone,
ZB_fillTriangleMapping_xx_zoff_nocolor_amore_znone,
ZB_fillTriangleMappingPerspective_xx_zoff_nocolor_amore_znone },
{ ZB_fillTriangleFlat_xx_zoff_nocolor_amore_znone,
ZB_fillTriangleMappingFlat_xx_zoff_nocolor_amore_znone,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_nocolor_amore_znone },
{ ZB_fillTriangleSmooth_xx_zoff_nocolor_amore_znone,
ZB_fillTriangleMappingSmooth_xx_zoff_nocolor_amore_znone,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_nocolor_amore_znone },
},
{
{ ZB_fillTriangleFlat_xx_zoff_nocolor_amore_zless,
ZB_fillTriangleMapping_xx_zoff_nocolor_amore_zless,
ZB_fillTriangleMappingPerspective_xx_zoff_nocolor_amore_zless },
{ ZB_fillTriangleFlat_xx_zoff_nocolor_amore_zless,
ZB_fillTriangleMappingFlat_xx_zoff_nocolor_amore_zless,
ZB_fillTriangleMappingPerspectiveFlat_xx_zoff_nocolor_amore_zless },
{ ZB_fillTriangleSmooth_xx_zoff_nocolor_amore_zless,
ZB_fillTriangleMappingSmooth_xx_zoff_nocolor_amore_zless,
ZB_fillTriangleMappingPerspectiveSmooth_xx_zoff_nocolor_amore_zless },
},
},
},
},
};
static inline void tgl_vertex_transform(GLContext * c, GLVertex * v)
{
float *m;
V4 *n;
if (c->lighting_enabled) {
/* eye coordinates needed for lighting */
m = &c->matrix_stack_ptr[0]->m[0][0];
v->ec.X = (v->coord.X * m[0] + v->coord.Y * m[1] +
v->coord.Z * m[2] + m[3]);
v->ec.Y = (v->coord.X * m[4] + v->coord.Y * m[5] +
v->coord.Z * m[6] + m[7]);
v->ec.Z = (v->coord.X * m[8] + v->coord.Y * m[9] +
v->coord.Z * m[10] + m[11]);
v->ec.W = (v->coord.X * m[12] + v->coord.Y * m[13] +
v->coord.Z * m[14] + m[15]);
/* projection coordinates */
m = &c->matrix_stack_ptr[1]->m[0][0];
v->pc.X = (v->ec.X * m[0] + v->ec.Y * m[1] +
v->ec.Z * m[2] + v->ec.W * m[3]);
v->pc.Y = (v->ec.X * m[4] + v->ec.Y * m[5] +
v->ec.Z * m[6] + v->ec.W * m[7]);
v->pc.Z = (v->ec.X * m[8] + v->ec.Y * m[9] +
v->ec.Z * m[10] + v->ec.W * m[11]);
v->pc.W = (v->ec.X * m[12] + v->ec.Y * m[13] +
v->ec.Z * m[14] + v->ec.W * m[15]);
m = &c->matrix_model_view_inv.m[0][0];
n = &c->current_normal;
v->normal.X = (n->X * m[0] + n->Y * m[1] + n->Z * m[2]);
v->normal.Y = (n->X * m[4] + n->Y * m[5] + n->Z * m[6]);
v->normal.Z = (n->X * m[8] + n->Y * m[9] + n->Z * m[10]);
if (c->normalize_enabled) {
gl_V3_Norm(&v->normal);
}
} else {
/* no eye coordinates needed, no normal */
/* NOTE: W = 1 is assumed */
m = &c->matrix_model_projection.m[0][0];
v->pc.X = (v->coord.X * m[0] + v->coord.Y * m[1] +
v->coord.Z * m[2] + m[3]);
v->pc.Y = (v->coord.X * m[4] + v->coord.Y * m[5] +
v->coord.Z * m[6] + m[7]);
v->pc.Z = (v->coord.X * m[8] + v->coord.Y * m[9] +
v->coord.Z * m[10] + m[11]);
if (c->matrix_model_projection_no_w_transform) {
v->pc.W = m[15];
} else {
v->pc.W = (v->coord.X * m[12] + v->coord.Y * m[13] +
v->coord.Z * m[14] + m[15]);
}
}
v->clip_code = gl_clipcode(v->pc.X, v->pc.Y, v->pc.Z, v->pc.W);
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
TinyGraphicsStateGuardian::
TinyGraphicsStateGuardian(GraphicsPipe *pipe,
TinyGraphicsStateGuardian *share_with) :
GraphicsStateGuardian(CS_yup_right, pipe)
{
_c = NULL;
_vertices = NULL;
_vertices_size = 0;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
TinyGraphicsStateGuardian::
~TinyGraphicsStateGuardian() {
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::reset
// Access: Public, Virtual
// Description: Resets all internal state as if the gsg were newly
// created.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
reset() {
free_pointers();
GraphicsStateGuardian::reset();
glInit(_current_frame_buffer);
_c = gl_get_context();
_c->draw_triangle_front = gl_draw_triangle_fill;
_c->draw_triangle_back = gl_draw_triangle_fill;
_supported_geom_rendering =
Geom::GR_point |
Geom::GR_indexed_other |
Geom::GR_flat_last_vertex;
_max_texture_dimension = 256;
// Count the max number of lights
GLint max_lights;
glGetIntegerv(GL_MAX_LIGHTS, &max_lights);
_max_lights = max_lights;
_color_scale_via_lighting = false;
_alpha_scale_via_texture = false;
_runtime_color_scale = true;
// Now that the GSG has been initialized, make it available for
// optimizations.
add_gsg(this);
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::free_pointers
// Access: Protected, Virtual
// Description: Frees some memory that was explicitly allocated
// within the glgsg.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
free_pointers() {
if (_vertices != (GLVertex *)NULL) {
PANDA_FREE_ARRAY(_vertices);
_vertices = NULL;
}
_vertices_size = 0;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::depth_offset_decals
// Access: Public, Virtual
// Description: Returns true if this GSG can implement decals using a
// DepthOffsetAttrib, or false if that is unreliable
// and the three-step rendering process should be used
// instead.
////////////////////////////////////////////////////////////////////
bool TinyGraphicsStateGuardian::
depth_offset_decals() {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::make_geom_munger
// Access: Public, Virtual
// Description: Creates a new GeomMunger object to munge vertices
// appropriate to this GSG for the indicated state.
////////////////////////////////////////////////////////////////////
PT(GeomMunger) TinyGraphicsStateGuardian::
make_geom_munger(const RenderState *state, Thread *current_thread) {
PT(TinyGeomMunger) munger = new TinyGeomMunger(this, state);
return GeomMunger::register_munger(munger, current_thread);
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::clear
// Access: Public
// Description: Clears the framebuffer within the current
// DisplayRegion, according to the flags indicated by
// the given DrawableRegion object.
//
// This does not set the DisplayRegion first. You
// should call prepare_display_region() to specify the
// region you wish the clear operation to apply to.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
clear(DrawableRegion *clearable) {
PStatTimer timer(_clear_pcollector);
if ((!clearable->get_clear_color_active())&&
(!clearable->get_clear_depth_active())&&
(!clearable->get_clear_stencil_active())) {
return;
}
set_state_and_transform(RenderState::make_empty(), _internal_transform);
bool clear_color = false;
int r, g, b, a;
if (clearable->get_clear_color_active()) {
Colorf v = clearable->get_clear_color();
r = (int)(v[0] * 0xffff);
g = (int)(v[1] * 0xffff);
b = (int)(v[2] * 0xffff);
a = (int)(v[3] * 0xffff);
clear_color = true;
}
bool clear_z = false;
int z;
if (clearable->get_clear_depth_active()) {
// We ignore the specified depth clear value, since we don't
// support alternate depth compare functions anyway.
z = 0;
clear_z = true;
}
ZB_clear_viewport(_c->zb, clear_z, z,
clear_color, r, g, b, a,
_c->viewport.xmin, _c->viewport.ymin,
_c->viewport.xsize, _c->viewport.ysize);
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::prepare_display_region
// Access: Public, Virtual
// Description: Prepare a display region for rendering (set up
// scissor region and viewport)
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
prepare_display_region(DisplayRegionPipelineReader *dr,
Lens::StereoChannel stereo_channel) {
nassertv(dr != (DisplayRegionPipelineReader *)NULL);
GraphicsStateGuardian::prepare_display_region(dr, stereo_channel);
int l, b, w, h;
dr->get_region_pixels(l, b, w, h);
int xmin = GLint(l);
int ymin = GLint(b);
int xsize = GLsizei(w);
int ysize = GLsizei(h);
int xsize_req = xmin + xsize;
int ysize_req = ymin + ysize;
if (_c->gl_resize_viewport &&
_c->gl_resize_viewport(_c, &xsize_req, &ysize_req) != 0) {
gl_fatal_error("glViewport: error while resizing display");
}
xsize = xsize_req - xmin;
ysize = ysize_req - ymin;
if (xsize <= 0 || ysize <= 0) {
gl_fatal_error("glViewport: size too small");
}
_c->viewport.xmin = xmin;
_c->viewport.ymin = ymin;
_c->viewport.xsize = xsize;
_c->viewport.ysize = ysize;
gl_eval_viewport(_c);
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::calc_projection_mat
// Access: Public, Virtual
// Description: Given a lens, calculates the appropriate projection
// matrix for use with this gsg. Note that the
// projection matrix depends a lot upon the coordinate
// system of the rendering API.
//
// The return value is a TransformState if the lens is
// acceptable, NULL if it is not.
////////////////////////////////////////////////////////////////////
CPT(TransformState) TinyGraphicsStateGuardian::
calc_projection_mat(const Lens *lens) {
if (lens == (Lens *)NULL) {
return NULL;
}
if (!lens->is_linear()) {
return NULL;
}
// 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 result =
LMatrix4f::convert_mat(CS_yup_right, _current_lens->get_coordinate_system()) *
lens->get_projection_mat(_current_stereo_channel);
if (_scene_setup->get_inverted()) {
// If the scene is supposed to be inverted, then invert the
// projection matrix.
result *= LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
}
return TransformState::make_mat(result);
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::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 TinyGraphicsStateGuardian::
prepare_lens() {
_transform_stale = true;
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 TinyGraphicsStateGuardian::
begin_frame(Thread *current_thread) {
if (!GraphicsStateGuardian::begin_frame(current_thread)) {
return false;
}
_c->zb = _current_frame_buffer;
#ifdef DO_PSTATS
_vertices_immediate_pcollector.clear_level();
#endif
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::begin_scene
// Access: Public, Virtual
// Description: Called between begin_frame() and end_frame() to mark
// the beginning of drawing commands for a "scene"
// (usually a particular DisplayRegion) within a frame.
// All 3-D drawing commands, except the clear operation,
// must be enclosed within begin_scene() .. end_scene().
//
// The return value is true if successful (in which case
// the scene will be drawn and end_scene() will be
// called later), or false if unsuccessful (in which
// case nothing will be drawn and end_scene() will not
// be called).
////////////////////////////////////////////////////////////////////
bool TinyGraphicsStateGuardian::
begin_scene() {
return GraphicsStateGuardian::begin_scene();
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::end_scene
// Access: Protected, Virtual
// Description: Called between begin_frame() and end_frame() to mark
// the end of drawing commands for a "scene" (usually a
// particular DisplayRegion) within a frame. All 3-D
// drawing commands, except the clear operation, must be
// enclosed within begin_scene() .. end_scene().
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
end_scene() {
GraphicsStateGuardian::end_scene();
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::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 TinyGraphicsStateGuardian::
end_frame(Thread *current_thread) {
GraphicsStateGuardian::end_frame(current_thread);
// Flush any PCollectors specific to this kind of GSG.
_vertices_immediate_pcollector.flush_level();
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::begin_draw_primitives
// Access: Public, Virtual
// Description: Called before a sequence of draw_primitive()
// functions are called, this should prepare the vertex
// data for rendering. It returns true if the vertices
// are ok, false to abort this group of primitives.
////////////////////////////////////////////////////////////////////
bool TinyGraphicsStateGuardian::
begin_draw_primitives(const GeomPipelineReader *geom_reader,
const GeomMunger *munger,
const GeomVertexDataPipelineReader *data_reader,
bool force) {
#ifndef NDEBUG
if (tinydisplay_cat.is_spam()) {
tinydisplay_cat.spam() << "begin_draw_primitives: " << *(data_reader->get_object()) << "\n";
}
#endif // NDEBUG
if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader, force)) {
return false;
}
nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);
// Set up the proper transform.
if (_data_reader->is_vertex_transformed()) {
// If the vertex data claims to be already transformed into clip
// coordinates, wipe out the current projection and modelview
// matrix (so we don't attempt to transform it again).
const TransformState *ident = TransformState::make_identity();
load_matrix(_c->matrix_stack_ptr[0], ident);
load_matrix(_c->matrix_stack_ptr[1], ident);
load_matrix(&_c->matrix_model_view_inv, ident);
load_matrix(&_c->matrix_model_projection, ident);
_c->matrix_model_projection_no_w_transform = 1;
_transform_stale = true;
} else if (_transform_stale) {
// Load the actual transform.
if (_c->lighting_enabled) {
// With the lighting equation, we need to keep the modelview and
// projection matrices separate.
load_matrix(_c->matrix_stack_ptr[0], _internal_transform);
load_matrix(_c->matrix_stack_ptr[1], _projection_mat);
/* precompute inverse modelview */
M4 tmp;
gl_M4_Inv(&tmp, _c->matrix_stack_ptr[0]);
gl_M4_Transpose(&_c->matrix_model_view_inv, &tmp);
}
// Compose the modelview and projection matrices.
load_matrix(&_c->matrix_model_projection,
_projection_mat->compose(_internal_transform));
/* test to accelerate computation */
_c->matrix_model_projection_no_w_transform = 0;
float *m = &_c->matrix_model_projection.m[0][0];
if (m[12] == 0.0 && m[13] == 0.0 && m[14] == 0.0) {
_c->matrix_model_projection_no_w_transform = 1;
}
_transform_stale = false;
}
/* test if the texture matrix is not Identity */
// _c->apply_texture_matrix = !gl_M4_IsId(_c->matrix_stack_ptr[2]);
// Figure out the subset of vertices we will be using in this
// operation.
int num_vertices = data_reader->get_num_rows();
_min_vertex = num_vertices;
_max_vertex = 0;
int num_prims = geom_reader->get_num_primitives();
int i;
for (i = 0; i < num_prims; ++i) {
CPT(GeomPrimitive) prim = geom_reader->get_primitive(i);
int nv = prim->get_min_vertex();
_min_vertex = min(_min_vertex, nv);
int xv = prim->get_max_vertex();
_max_vertex = max(_max_vertex, xv);
}
if (_min_vertex > _max_vertex) {
return false;
}
// Now copy all of those vertices into our working table,
// transforming into screen space them as we go.
int num_used_vertices = _max_vertex - _min_vertex + 1;
if (_vertices_size < num_used_vertices) {
if (_vertices_size == 0) {
_vertices_size = 1;
}
while (_vertices_size < num_used_vertices) {
_vertices_size *= 2;
}
if (_vertices != (GLVertex *)NULL) {
PANDA_FREE_ARRAY(_vertices);
}
_vertices = (GLVertex *)PANDA_MALLOC_ARRAY(_vertices_size * sizeof(GLVertex));
}
GeomVertexReader rtexcoord, rcolor, rnormal;
// We only support single-texturing, so only bother with the first
// texture stage.
bool needs_texcoord = false;
bool needs_texmat = false;
LMatrix4f texmat;
const InternalName *texcoord_name = InternalName::get_texcoord();
int max_stage_index = _effective_texture->get_num_on_ff_stages();
if (max_stage_index > 0) {
TextureStage *stage = _effective_texture->get_on_ff_stage(0);
rtexcoord = GeomVertexReader(data_reader, stage->get_texcoord_name());
rtexcoord.set_row(_min_vertex);
needs_texcoord = rtexcoord.has_column();
if (needs_texcoord && _target._tex_matrix->has_stage(stage)) {
needs_texmat = true;
texmat = _target._tex_matrix->get_mat(stage);
}
}
bool needs_color = false;
if (_vertex_colors_enabled) {
rcolor = GeomVertexReader(data_reader, InternalName::get_color());
rcolor.set_row(_min_vertex);
needs_color = rcolor.has_column();
}
if (!needs_color) {
if (_has_scene_graph_color) {
const Colorf &d = _scene_graph_color;
_c->current_color.X = d[0];
_c->current_color.Y = d[1];
_c->current_color.Z = d[2];
_c->current_color.W = d[3];
} else {
_c->current_color.X = 1.0f;
_c->current_color.Y = 1.0f;
_c->current_color.Z = 1.0f;
_c->current_color.W = 1.0f;
}
}
bool needs_normal = false;
if (_c->lighting_enabled) {
rnormal = GeomVertexReader(data_reader, InternalName::get_normal());
rnormal.set_row(_min_vertex);
needs_normal = rnormal.has_column();
}
GeomVertexReader rvertex(data_reader, InternalName::get_vertex());
rvertex.set_row(_min_vertex);
if (!needs_color) {
if (_c->color_material_enabled) {
float *d = _c->current_color.v;
GLParam q[7];
q[0].op = OP_Material;
q[1].i = _c->current_color_material_mode;
q[2].i = _c->current_color_material_type;
q[3].f = d[0];
q[4].f = d[1];
q[5].f = d[2];
q[6].f = d[3];
glopMaterial(_c, q);
}
}
for (i = 0; i < num_used_vertices; ++i) {
GLVertex *v = &_vertices[i];
const LVecBase4f &d = rvertex.get_data4f();
v->coord.X = d[0];
v->coord.Y = d[1];
v->coord.Z = d[2];
v->coord.W = d[3];
if (needs_texmat) {
// Transform texcoords as a four-component vector for most generality.
LVecBase4f d = rtexcoord.get_data4f() * texmat;
v->tex_coord.X = d[0];
v->tex_coord.Y = d[1];
} else if (needs_texcoord) {
// No need to transform, so just extract as two-component.
const LVecBase2f &d = rtexcoord.get_data2f();
v->tex_coord.X = d[0];
v->tex_coord.Y = d[1];
}
if (needs_color) {
const Colorf &d = rcolor.get_data4f();
_c->current_color.X = d[0];
_c->current_color.Y = d[1];
_c->current_color.Z = d[2];
_c->current_color.W = d[3];
if (_c->color_material_enabled) {
GLParam q[7];
q[0].op = OP_Material;
q[1].i = _c->current_color_material_mode;
q[2].i = _c->current_color_material_type;
q[3].f = d[0];
q[4].f = d[1];
q[5].f = d[2];
q[6].f = d[3];
glopMaterial(_c, q);
}
}
v->color = _c->current_color;
if (needs_normal) {
const LVecBase3f &d = rnormal.get_data3f();
_c->current_normal.X = d[0];
_c->current_normal.Y = d[1];
_c->current_normal.Z = d[2];
_c->current_normal.W = 0.0f;
}
tgl_vertex_transform(_c, v);
if (_c->lighting_enabled) {
gl_shade_vertex(_c, v);
}
if (v->clip_code == 0) {
gl_transform_to_viewport(_c, v);
}
v->edge_flag = 1;
}
// Set up the appropriate function callback for filling triangles,
// according to the current state.
int depth_write_state = 0;
if (_target._depth_write->get_mode() != DepthWriteAttrib::M_on) {
depth_write_state = 1;
}
int color_write_state = 0;
switch (_target._transparency->get_mode()) {
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_dual:
color_write_state = 1;
break;
default:
break;
}
unsigned int color_channels =
_target._color_write->get_channels() & _color_write_mask;
if (color_channels == ColorWriteAttrib::C_off) {
color_write_state = 2;
}
int alpha_test_state = 0;
switch (_target._alpha_test->get_mode()) {
case AlphaTestAttrib::M_none:
case AlphaTestAttrib::M_never:
case AlphaTestAttrib::M_always:
case AlphaTestAttrib::M_equal:
case AlphaTestAttrib::M_not_equal:
alpha_test_state = 0;
break;
case AlphaTestAttrib::M_less:
case AlphaTestAttrib::M_less_equal:
alpha_test_state = 1;
_c->zb->reference_alpha = (unsigned int)_target._alpha_test->get_reference_alpha() * 0xff00;
break;
case AlphaTestAttrib::M_greater:
case AlphaTestAttrib::M_greater_equal:
alpha_test_state = 2;
_c->zb->reference_alpha = (unsigned int)_target._alpha_test->get_reference_alpha() * 0xff00;
break;
}
int depth_test_state = 1;
_c->depth_test = 1; // set this for ZB_line
if (_target._depth_test->get_mode() == DepthTestAttrib::M_none) {
depth_test_state = 0;
_c->depth_test = 0;
}
ShadeModelAttrib::Mode shade_model = _target._shade_model->get_mode();
if (!needs_normal && !needs_color) {
// With no per-vertex lighting, and no per-vertex colors, we might
// as well use the flat shading model.
shade_model = ShadeModelAttrib::M_flat;
}
int shading_state = 2; // smooth
if (shade_model == ShadeModelAttrib::M_flat) {
shading_state = 1; // flat
if (_c->current_color.X == 1.0f &&
_c->current_color.Y == 1.0f &&
_c->current_color.Z == 1.0f &&
_c->current_color.W == 1.0f) {
shading_state = 0; // white
}
}
int texturing_state = 0; // untextured
if (_c->texture_2d_enabled) {
texturing_state = 2; // perspective-correct textures
if (_c->matrix_model_projection_no_w_transform) {
texturing_state = 1; // non-perspective-correct textures
}
}
_c->zb_fill_tri = fill_tri_funcs[depth_write_state][color_write_state][alpha_test_state][depth_test_state][shading_state][texturing_state];
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::draw_triangles
// Access: Public, Virtual
// Description: Draws a series of disconnected triangles.
////////////////////////////////////////////////////////////////////
bool TinyGraphicsStateGuardian::
draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
#ifndef NDEBUG
if (tinydisplay_cat.is_spam()) {
tinydisplay_cat.spam() << "draw_triangles: " << *(reader->get_object()) << "\n";
}
#endif // NDEBUG
int num_vertices = reader->get_num_vertices();
_vertices_immediate_pcollector.add_level(num_vertices);
if (reader->is_indexed()) {
for (int i = 0; i < num_vertices; i += 3) {
GLVertex *v0 = &_vertices[reader->get_vertex(i) - _min_vertex];
GLVertex *v1 = &_vertices[reader->get_vertex(i + 1) - _min_vertex];
GLVertex *v2 = &_vertices[reader->get_vertex(i + 2) - _min_vertex];
gl_draw_triangle(_c, v0, v1, v2);
}
} else {
int delta = reader->get_first_vertex() - _min_vertex;
for (int vi = 0; vi < num_vertices; vi += 3) {
GLVertex *v0 = &_vertices[vi + delta];
GLVertex *v1 = &_vertices[vi + delta + 1];
GLVertex *v2 = &_vertices[vi + delta + 2];
gl_draw_triangle(_c, v0, v1, v2);
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::draw_lines
// Access: Public, Virtual
// Description: Draws a series of disconnected line segments.
////////////////////////////////////////////////////////////////////
bool TinyGraphicsStateGuardian::
draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
#ifndef NDEBUG
if (tinydisplay_cat.is_spam()) {
tinydisplay_cat.spam() << "draw_lines: " << *(reader->get_object()) << "\n";
}
#endif // NDEBUG
int num_vertices = reader->get_num_vertices();
_vertices_immediate_pcollector.add_level(num_vertices);
if (reader->is_indexed()) {
for (int i = 0; i < num_vertices; i += 2) {
GLVertex *v0 = &_vertices[reader->get_vertex(i) - _min_vertex];
GLVertex *v1 = &_vertices[reader->get_vertex(i + 1) - _min_vertex];
gl_draw_line(_c, v0, v1);
}
} else {
int delta = reader->get_first_vertex() - _min_vertex;
for (int vi = 0; vi < num_vertices; vi += 2) {
GLVertex *v0 = &_vertices[vi + delta];
GLVertex *v1 = &_vertices[vi + delta + 1];
gl_draw_line(_c, v0, v1);
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::draw_points
// Access: Public, Virtual
// Description: Draws a series of disconnected points.
////////////////////////////////////////////////////////////////////
bool TinyGraphicsStateGuardian::
draw_points(const GeomPrimitivePipelineReader *reader, bool force) {
PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
#ifndef NDEBUG
if (tinydisplay_cat.is_spam()) {
tinydisplay_cat.spam() << "draw_points: " << *(reader->get_object()) << "\n";
}
#endif // NDEBUG
int num_vertices = reader->get_num_vertices();
_vertices_immediate_pcollector.add_level(num_vertices);
if (reader->is_indexed()) {
for (int i = 0; i < num_vertices; ++i) {
GLVertex *v0 = &_vertices[reader->get_vertex(i) - _min_vertex];
gl_draw_point(_c, v0);
}
} else {
int delta = reader->get_first_vertex() - _min_vertex;
for (int vi = 0; vi < num_vertices; ++vi) {
GLVertex *v0 = &_vertices[vi + delta];
gl_draw_point(_c, v0);
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::end_draw_primitives()
// Access: Public, Virtual
// Description: Called after a sequence of draw_primitive()
// functions are called, this should do whatever cleanup
// is appropriate.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
end_draw_primitives() {
GraphicsStateGuardian::end_draw_primitives();
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::framebuffer_copy_to_texture
// Access: Public, Virtual
// Description: Copy the pixels within the indicated display
// region from the framebuffer into texture memory.
//
// If z > -1, it is the cube map index into which to
// copy.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb) {
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::framebuffer_copy_to_ram
// Access: Public, Virtual
// Description: Copy the pixels within the indicated display region
// from the framebuffer into system memory, not texture
// memory. Returns true on success, false on failure.
//
// This completely redefines the ram image of the
// indicated texture.
////////////////////////////////////////////////////////////////////
bool TinyGraphicsStateGuardian::
framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::set_state_and_transform
// Access: Public, Virtual
// Description: Simultaneously resets the render state and the
// transform state.
//
// This transform specified is the "internal" net
// transform, already converted into the GSG's internal
// coordinate space by composing it to
// get_cs_transform(). (Previously, this used to be the
// "external" net transform, with the assumption that
// that GSG would convert it internally, but that is no
// longer the case.)
//
// Special case: if (state==NULL), then the target
// state is already stored in _target.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
set_state_and_transform(const RenderState *target,
const TransformState *transform) {
#ifndef NDEBUG
if (tinydisplay_cat.is_spam()) {
tinydisplay_cat.spam()
<< "Setting GSG state to " << (void *)target << ":\n";
target->write(tinydisplay_cat.spam(false), 2);
}
#endif
_state_pcollector.add_level(1);
PStatTimer timer1(_draw_set_state_pcollector);
if (transform != _internal_transform) {
PStatTimer timer(_draw_set_state_transform_pcollector);
_state_pcollector.add_level(1);
_internal_transform = transform;
do_issue_transform();
}
if (target == _state_rs) {
return;
}
_target_rs = target;
_target.clear_to_defaults();
target->store_into_slots(&_target);
_state_rs = 0;
if (_target._color != _state._color ||
_target._color_scale != _state._color_scale) {
PStatTimer timer(_draw_set_state_color_pcollector);
do_issue_color();
do_issue_color_scale();
_state._color = _target._color;
_state._color_scale = _target._color_scale;
}
if (_target._cull_face != _state._cull_face) {
PStatTimer timer(_draw_set_state_cull_face_pcollector);
do_issue_cull_face();
_state._cull_face = _target._cull_face;
}
if (_target._render_mode != _state._render_mode) {
PStatTimer timer(_draw_set_state_render_mode_pcollector);
do_issue_render_mode();
_state._render_mode = _target._render_mode;
}
if ((_target._transparency != _state._transparency)||
(_target._color_write != _state._color_write)||
(_target._color_blend != _state._color_blend)) {
PStatTimer timer(_draw_set_state_blending_pcollector);
do_issue_blending();
_state._transparency = _target._transparency;
_state._color_write = _target._color_write;
_state._color_blend = _target._color_blend;
}
if (_target._texture != _state._texture) {
PStatTimer timer(_draw_set_state_texture_pcollector);
determine_effective_texture();
do_issue_texture();
_state._texture = _target._texture;
}
if (_target._material != _state._material) {
PStatTimer timer(_draw_set_state_material_pcollector);
do_issue_material();
_state._material = _target._material;
}
if (_target._light != _state._light) {
PStatTimer timer(_draw_set_state_light_pcollector);
do_issue_light();
_state._light = _target._light;
}
_state_rs = _target_rs;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::prepare_texture
// Access: Public, Virtual
// Description: Creates whatever structures the GSG requires to
// represent the texture internally, and returns a
// newly-allocated TextureContext object with this data.
// 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 *TinyGraphicsStateGuardian::
prepare_texture(Texture *tex) {
if (tex->get_texture_type() != Texture::TT_2d_texture) {
tinydisplay_cat.info()
<< "not loading texture " << tex->get_name() << ": "
<< tex->get_texture_type() << "\n";
return NULL;
}
if (tex->get_ram_image_compression() != Texture::CM_off) {
tinydisplay_cat.info()
<< "not loading texture " << tex->get_name() << ": "
<< tex->get_ram_image_compression() << "\n";
return NULL;
}
if (tex->get_component_type() != Texture::T_unsigned_byte) {
tinydisplay_cat.info()
<< "not loading texture " << tex->get_name() << ": "
<< tex->get_component_type() << "\n";
return NULL;
}
TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex);
gtc->_gltex = (GLTexture *)gl_zalloc(sizeof(GLTexture));
apply_texture(gtc);
return gtc;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::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 TinyGraphicsStateGuardian::
release_texture(TextureContext *tc) {
TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
if (_c->current_texture == gtc->_gltex) {
_c->current_texture = NULL;
_c->zb->current_texture = NULL;
_c->texture_2d_enabled = false;
}
for (int i = 0; i < MAX_TEXTURE_LEVELS; i++) {
GLImage *im = &gtc->_gltex->images[i];
if (im->pixmap != NULL) {
gl_free(im->pixmap);
}
}
gl_free(gtc->_gltex);
gtc->_gltex = NULL;
delete gtc;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::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 do_issue_light() according to whether any
// lights are in use or not.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
enable_lighting(bool enable) {
static PStatCollector _draw_set_state_light_enable_lighting_pcollector("Draw:Set State:Light:Enable lighting");
PStatTimer timer(_draw_set_state_light_enable_lighting_pcollector);
if (enable) {
glEnable(GL_LIGHTING);
} else {
glDisable(GL_LIGHTING);
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::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 do_issue_light() after
// all other lights have been enabled or disabled.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
set_ambient_light(const Colorf &color) {
static PStatCollector _draw_set_state_light_ambient_pcollector("Draw:Set State:Light:Ambient");
PStatTimer timer(_draw_set_state_light_ambient_pcollector);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (float *)color.get_data());
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::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 TinyGraphicsStateGuardian::
enable_light(int light_id, bool enable) {
static PStatCollector _draw_set_state_light_enable_light_pcollector("Draw:Set State:Light:Enable light");
PStatTimer timer(_draw_set_state_light_enable_light_pcollector);
if (enable) {
glEnable(get_light_id(light_id));
} else {
glDisable(get_light_id(light_id));
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::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 TinyGraphicsStateGuardian::
begin_bind_lights() {
static PStatCollector _draw_set_state_light_begin_bind_pcollector("Draw:Set State:Light:Begin bind");
PStatTimer timer(_draw_set_state_light_begin_bind_pcollector);
// 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 _internal_transform->invert_compose(render_transform).
// But I think loading a completely new matrix is simpler.)
CPT(TransformState) render_transform =
_cs_transform->compose(_scene_setup->get_world_transform());
/*
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf(render_transform->get_mat().get_data());
*/
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::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 TinyGraphicsStateGuardian::
end_bind_lights() {
static PStatCollector _draw_set_state_light_end_bind_pcollector("Draw:Set State:Light:End bind");
PStatTimer timer(_draw_set_state_light_end_bind_pcollector);
/*
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
*/
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::do_issue_transform
// Access: Protected
// Description: Sends the indicated transform matrix to the graphics
// API to be applied to future vertices.
//
// This transform is the internal_transform, already
// converted into the GSG's internal coordinate system.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
do_issue_transform() {
_transform_state_pcollector.add_level(1);
_transform_stale = true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::do_issue_render_mode
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
do_issue_render_mode() {
const RenderModeAttrib *attrib = _target._render_mode;
switch (attrib->get_mode()) {
case RenderModeAttrib::M_unchanged:
case RenderModeAttrib::M_filled:
_c->draw_triangle_front = gl_draw_triangle_fill;
_c->draw_triangle_back = gl_draw_triangle_fill;
break;
case RenderModeAttrib::M_wireframe:
_c->draw_triangle_front = gl_draw_triangle_line;
_c->draw_triangle_back = gl_draw_triangle_line;
break;
case RenderModeAttrib::M_point:
_c->draw_triangle_front = gl_draw_triangle_point;
_c->draw_triangle_back = gl_draw_triangle_point;
break;
default:
tinydisplay_cat.error()
<< "Unknown render mode " << (int)attrib->get_mode() << endl;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::do_issue_cull_face
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
do_issue_cull_face() {
const CullFaceAttrib *attrib = _target._cull_face;
CullFaceAttrib::Mode mode = attrib->get_effective_mode();
switch (mode) {
case CullFaceAttrib::M_cull_none:
glDisable(GL_CULL_FACE);
break;
case CullFaceAttrib::M_cull_clockwise:
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
break;
case CullFaceAttrib::M_cull_counter_clockwise:
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
break;
default:
tinydisplay_cat.error()
<< "invalid cull face mode " << (int)mode << endl;
break;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::do_issue_material
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
do_issue_material() {
static Material empty;
const Material *material;
if (_target._material == (MaterialAttrib *)NULL ||
_target._material->is_off()) {
material = &empty;
} else {
material = _target._material->get_material();
}
GLenum face = material->get_twoside() ? GL_FRONT_AND_BACK : GL_FRONT;
glMaterialfv(face, GL_SPECULAR, (GLfloat *)material->get_specular().get_data());
glMaterialfv(face, GL_EMISSION, (GLfloat *)material->get_emission().get_data());
glMaterialf(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().
glDisable(GL_COLOR_MATERIAL);
glMaterialfv(face, GL_AMBIENT, (GLfloat *)material->get_ambient().get_data());
glMaterialfv(face, GL_DIFFUSE, (GLfloat *)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.
glMaterialfv(face, GL_AMBIENT, (GLfloat *)material->get_ambient().get_data());
if (_has_material_force_color) {
glDisable(GL_COLOR_MATERIAL);
glMaterialfv(face, GL_DIFFUSE, (GLfloat *)_material_force_color.get_data());
} else {
glColorMaterial(face, GL_DIFFUSE);
glEnable(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.
glMaterialfv(face, GL_DIFFUSE, (GLfloat *)material->get_diffuse().get_data());
if (_has_material_force_color) {
glDisable(GL_COLOR_MATERIAL);
glMaterialfv(face, GL_AMBIENT, (GLfloat *)_material_force_color.get_data());
} else {
glColorMaterial(face, GL_AMBIENT);
glEnable(GL_COLOR_MATERIAL);
}
} else {
// The material specifies neither a diffuse nor an ambient
// component. Both components come from the object's color.
if (_has_material_force_color) {
glDisable(GL_COLOR_MATERIAL);
glMaterialfv(face, GL_AMBIENT, (GLfloat *)_material_force_color.get_data());
glMaterialfv(face, GL_DIFFUSE, (GLfloat *)_material_force_color.get_data());
} else {
glColorMaterial(face, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
}
}
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, material->get_local());
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::do_issue_texture
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
do_issue_texture() {
_c->texture_2d_enabled = false;
int num_stages = _effective_texture->get_num_on_ff_stages();
if (num_stages == 0) {
// No texturing.
return;
}
nassertv(num_stages == 1);
TextureStage *stage = _effective_texture->get_on_ff_stage(0);
Texture *texture = _effective_texture->get_on_texture(stage);
nassertv(texture != (Texture *)NULL);
TextureContext *tc = texture->prepare_now(_prepared_objects, this);
if (tc == (TextureContext *)NULL) {
// Something wrong with this texture; skip it.
return;
}
// Then, turn on the current texture mode.
apply_texture(tc);
/*
GLint glmode = get_texture_apply_mode_type(stage->get_mode());
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, glmode);
*/
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::do_issue_blending
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
do_issue_blending() {
/*
// Handle the color_write attrib. If color_write is off, then
// all the other blending-related stuff doesn't matter. If the
// device doesn't support color-write, we use blending tricks
// to effectively disable color write.
unsigned int color_channels =
_target._color_write->get_channels() & _color_write_mask;
if (color_channels == ColorWriteAttrib::C_off) {
if (_target._color_write != _state._color_write) {
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(false);
if (CLP(color_mask)) {
enable_blend(false);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
} else {
enable_blend(true);
_glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ZERO, GL_ONE);
}
}
return;
} else {
if (_target._color_write != _state._color_write) {
if (CLP(color_mask)) {
glColorMask((color_channels & ColorWriteAttrib::C_red) != 0,
(color_channels & ColorWriteAttrib::C_green) != 0,
(color_channels & ColorWriteAttrib::C_blue) != 0,
(color_channels & ColorWriteAttrib::C_alpha) != 0);
}
}
}
CPT(ColorBlendAttrib) color_blend = _target._color_blend;
ColorBlendAttrib::Mode color_blend_mode = _target._color_blend->get_mode();
TransparencyAttrib::Mode transparency_mode = _target._transparency->get_mode();
_color_blend_involves_color_scale = color_blend->involves_color_scale();
// 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));
glBlendFunc(get_blend_func(color_blend->get_operand_a()),
get_blend_func(color_blend->get_operand_b()));
if (_color_blend_involves_color_scale) {
// Apply the current color scale to the blend mode.
_glBlendColor(_current_color_scale[0], _current_color_scale[1],
_current_color_scale[2], _current_color_scale[3]);
} else {
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);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return;
case TransparencyAttrib::M_multisample:
// We need to enable *both* of these in M_multisample case.
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:
tinydisplay_cat.error()
<< "invalid transparency mode " << (int)transparency_mode << endl;
break;
}
if (_line_smooth_enabled || _point_smooth_enabled) {
// If we have either of these turned on, we also need to have
// blend mode enabled in order to see it.
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(false);
enable_blend(true);
_glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return;
}
// For best polygon smoothing, we need:
// (1) a frame buffer that supports alpha
// (2) sort polygons front-to-back
// (3) glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE);
//
// Since these modes have other implications for the application, we
// don't attempt to do this by default. If you really want good
// polygon smoothing (and you don't have multisample support), do
// all this yourself.
// Nothing's set, so disable blending.
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(false);
enable_blend(false);
*/
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::apply_texture
// Access: Protected
// Description: Updates TinyGL with the current information for this
// texture, and makes it the current texture available
// for rendering.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
apply_texture(TextureContext *tc) {
TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
gtc->set_active(true);
_c->current_texture = gtc->_gltex;
_c->texture_2d_enabled = true;
if (gtc->was_image_modified()) {
// If the texture image was modified, reload the texture.
if (!upload_texture(gtc)) {
_c->texture_2d_enabled = false;
}
gtc->mark_loaded();
} else if (gtc->was_properties_modified()) {
// If only the properties have been modified, we don't need to
// reload the texture.
gtc->mark_loaded();
}
_c->zb->current_texture = (PIXEL *)gtc->_gltex->images[0].pixmap;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::upload_texture
// Access: Protected
// Description: Uploads the texture image to TinyGL.
//
// The return value is true if successful, or false if
// the texture has no image.
////////////////////////////////////////////////////////////////////
bool TinyGraphicsStateGuardian::
upload_texture(TinyTextureContext *gtc) {
Texture *tex = gtc->get_texture();
PStatTimer timer(_load_texture_pcollector);
if (tinydisplay_cat.is_debug()) {
tinydisplay_cat.debug()
<< "loading texture " << tex->get_name() << "\n";
}
#ifdef DO_PSTATS
_data_transferred_pcollector.add_level(tex->get_ram_image_size());
#endif
// Internal texture size is always 256 x 256 x 4.
static const int iwidth = 256;
static const int iheight = 256;
static const int ibytecount = iwidth * iheight * 4;
GLImage *im = &gtc->_gltex->images[0];
im->xsize = iwidth;
im->ysize = iheight;
if (im->pixmap == NULL) {
im->pixmap = gl_malloc(ibytecount);
}
switch (tex->get_format()) {
case Texture::F_rgb:
case Texture::F_rgb5:
case Texture::F_rgb8:
case Texture::F_rgb12:
case Texture::F_rgb332:
copy_rgb_image(im, tex);
break;
case Texture::F_rgba:
case Texture::F_rgbm:
case Texture::F_rgba4:
case Texture::F_rgba5:
case Texture::F_rgba8:
case Texture::F_rgba12:
case Texture::F_rgba16:
case Texture::F_rgba32:
copy_rgba_image(im, tex);
break;
case Texture::F_luminance:
copy_lum_image(im, tex);
break;
case Texture::F_red:
copy_one_channel_image(im, tex, 0);
break;
case Texture::F_green:
copy_one_channel_image(im, tex, 1);
break;
case Texture::F_blue:
copy_one_channel_image(im, tex, 2);
break;
case Texture::F_alpha:
copy_alpha_image(im, tex);
break;
case Texture::F_luminance_alphamask:
case Texture::F_luminance_alpha:
copy_la_image(im, tex);
break;
}
#ifdef DO_PSTATS
gtc->update_data_size_bytes(ibytecount);
#endif
tex->texture_uploaded();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::copy_lum_image
// Access: Private, Static
// Description: Copies and scales the one-channel luminance image
// from the texture into the indicated GLImage pixmap.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
copy_lum_image(GLImage *im, Texture *tex) {
nassertv(tex->get_num_components() == 1);
int xsize_src = tex->get_x_size();
int ysize_src = tex->get_y_size();
CPTA_uchar src_image = tex->get_ram_image();
nassertv(!src_image.is_null());
const unsigned char *src = src_image.p();
// Component width, and offset to the high-order byte.
int cw = tex->get_component_width();
#ifdef WORDS_BIGENDIAN
// Big-endian: the high-order byte is always first.
static const int co = 0;
#else
// Little-endian: the high-order byte is last.
int co = cw - 1;
#endif
int xsize_dest = im->xsize;
int ysize_dest = im->xsize;
unsigned char *dest = (unsigned char *)im->pixmap;
nassertv(dest != NULL);
int sx_inc = (int)((float)(xsize_src) / (float)(xsize_dest));
int sy_inc = (int)((float)(ysize_src) / (float)(ysize_dest));
unsigned char *dpix = dest;
int syn = 0;
for (int dy = 0; dy < ysize_dest; dy++) {
int sy = syn / ysize_dest;
int sxn = 0;
for (int dx = 0; dx < xsize_dest; dx++) {
int sx = sxn / xsize_dest;
const unsigned char *spix = src + (sy * xsize_src + sx) * 1 * cw;
dpix[0] = spix[co];
dpix[1] = spix[co];
dpix[2] = spix[co];
dpix[3] = 0xff;
dpix += 4;
sxn += xsize_src;
}
syn += ysize_src;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::copy_alpha_image
// Access: Private, Static
// Description: Copies and scales the one-channel alpha image
// from the texture into the indicated GLImage pixmap.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
copy_alpha_image(GLImage *im, Texture *tex) {
nassertv(tex->get_num_components() == 1);
int xsize_src = tex->get_x_size();
int ysize_src = tex->get_y_size();
CPTA_uchar src_image = tex->get_ram_image();
nassertv(!src_image.is_null());
const unsigned char *src = src_image.p();
// Component width, and offset to the high-order byte.
int cw = tex->get_component_width();
#ifdef WORDS_BIGENDIAN
// Big-endian: the high-order byte is always first.
static const int co = 0;
#else
// Little-endian: the high-order byte is last.
int co = cw - 1;
#endif
int xsize_dest = im->xsize;
int ysize_dest = im->xsize;
unsigned char *dest = (unsigned char *)im->pixmap;
nassertv(dest != NULL);
int sx_inc = (int)((float)(xsize_src) / (float)(xsize_dest));
int sy_inc = (int)((float)(ysize_src) / (float)(ysize_dest));
unsigned char *dpix = dest;
int syn = 0;
for (int dy = 0; dy < ysize_dest; dy++) {
int sy = syn / ysize_dest;
int sxn = 0;
for (int dx = 0; dx < xsize_dest; dx++) {
int sx = sxn / xsize_dest;
const unsigned char *spix = src + (sy * xsize_src + sx) * 1 * cw;
dpix[0] = 0xff;
dpix[1] = 0xff;
dpix[2] = 0xff;
dpix[3] = spix[co];
dpix += 4;
sxn += xsize_src;
}
syn += ysize_src;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::copy_one_channel_image
// Access: Private, Static
// Description: Copies and scales the one-channel image (with a
// single channel, e.g. red, green, or blue) from
// the texture into the indicated GLImage pixmap.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
copy_one_channel_image(GLImage *im, Texture *tex, int channel) {
nassertv(tex->get_num_components() == 1);
int xsize_src = tex->get_x_size();
int ysize_src = tex->get_y_size();
CPTA_uchar src_image = tex->get_ram_image();
nassertv(!src_image.is_null());
const unsigned char *src = src_image.p();
// Component width, and offset to the high-order byte.
int cw = tex->get_component_width();
#ifdef WORDS_BIGENDIAN
// Big-endian: the high-order byte is always first.
static const int co = 0;
#else
// Little-endian: the high-order byte is last.
int co = cw - 1;
#endif
int xsize_dest = im->xsize;
int ysize_dest = im->xsize;
unsigned char *dest = (unsigned char *)im->pixmap;
nassertv(dest != NULL);
int sx_inc = (int)((float)(xsize_src) / (float)(xsize_dest));
int sy_inc = (int)((float)(ysize_src) / (float)(ysize_dest));
unsigned char *dpix = dest;
int syn = 0;
for (int dy = 0; dy < ysize_dest; dy++) {
int sy = syn / ysize_dest;
int sxn = 0;
for (int dx = 0; dx < xsize_dest; dx++) {
int sx = sxn / xsize_dest;
const unsigned char *spix = src + (sy * xsize_src + sx) * 1 * cw;
dpix[0] = 0;
dpix[1] = 0;
dpix[2] = 0;
dpix[3] = 0xff;
dpix[channel] = spix[co];
dpix += 4;
sxn += xsize_src;
}
syn += ysize_src;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::copy_la_image
// Access: Private, Static
// Description: Copies and scales the two-channel luminance-alpha
// image from the texture into the indicated GLImage
// pixmap.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
copy_la_image(GLImage *im, Texture *tex) {
nassertv(tex->get_num_components() == 2);
int xsize_src = tex->get_x_size();
int ysize_src = tex->get_y_size();
CPTA_uchar src_image = tex->get_ram_image();
nassertv(!src_image.is_null());
const unsigned char *src = src_image.p();
// Component width, and offset to the high-order byte.
int cw = tex->get_component_width();
#ifdef WORDS_BIGENDIAN
// Big-endian: the high-order byte is always first.
static const int co = 0;
#else
// Little-endian: the high-order byte is last.
int co = cw - 1;
#endif
int xsize_dest = im->xsize;
int ysize_dest = im->xsize;
unsigned char *dest = (unsigned char *)im->pixmap;
nassertv(dest != NULL);
int sx_inc = (int)((float)(xsize_src) / (float)(xsize_dest));
int sy_inc = (int)((float)(ysize_src) / (float)(ysize_dest));
unsigned char *dpix = dest;
int syn = 0;
for (int dy = 0; dy < ysize_dest; dy++) {
int sy = syn / ysize_dest;
int sxn = 0;
for (int dx = 0; dx < xsize_dest; dx++) {
int sx = sxn / xsize_dest;
const unsigned char *spix = src + (sy * xsize_src + sx) * 2 * cw;
dpix[0] = spix[co];
dpix[1] = spix[co];
dpix[2] = spix[co];
dpix[3] = spix[cw + co];
dpix += 4;
sxn += xsize_src;
}
syn += ysize_src;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::copy_rgb_image
// Access: Private, Static
// Description: Copies and scales the three-channel RGB image from
// the texture into the indicated GLImage pixmap.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
copy_rgb_image(GLImage *im, Texture *tex) {
nassertv(tex->get_num_components() == 3);
int xsize_src = tex->get_x_size();
int ysize_src = tex->get_y_size();
CPTA_uchar src_image = tex->get_ram_image();
nassertv(!src_image.is_null());
const unsigned char *src = src_image.p();
// Component width, and offset to the high-order byte.
int cw = tex->get_component_width();
#ifdef WORDS_BIGENDIAN
// Big-endian: the high-order byte is always first.
static const int co = 0;
#else
// Little-endian: the high-order byte is last.
int co = cw - 1;
#endif
int xsize_dest = im->xsize;
int ysize_dest = im->xsize;
unsigned char *dest = (unsigned char *)im->pixmap;
nassertv(dest != NULL);
int sx_inc = (int)((float)(xsize_src) / (float)(xsize_dest));
int sy_inc = (int)((float)(ysize_src) / (float)(ysize_dest));
unsigned char *dpix = dest;
int syn = 0;
for (int dy = 0; dy < ysize_dest; dy++) {
int sy = syn / ysize_dest;
int sxn = 0;
for (int dx = 0; dx < xsize_dest; dx++) {
int sx = sxn / xsize_dest;
const unsigned char *spix = src + (sy * xsize_src + sx) * 3 * cw;
dpix[0] = spix[co];
dpix[1] = spix[cw + co];
dpix[2] = spix[cw + cw + co];
dpix[3] = 0xff;
dpix += 4;
sxn += xsize_src;
}
syn += ysize_src;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::copy_rgba_image
// Access: Private, Static
// Description: Copies and scales the four-channel RGBA image from
// the texture into the indicated GLImage pixmap.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
copy_rgba_image(GLImage *im, Texture *tex) {
nassertv(tex->get_num_components() == 4);
int xsize_src = tex->get_x_size();
int ysize_src = tex->get_y_size();
CPTA_uchar src_image = tex->get_ram_image();
nassertv(!src_image.is_null());
const unsigned char *src = src_image.p();
// Component width, and offset to the high-order byte.
int cw = tex->get_component_width();
#ifdef WORDS_BIGENDIAN
// Big-endian: the high-order byte is always first.
static const int co = 0;
#else
// Little-endian: the high-order byte is last.
int co = cw - 1;
#endif
int xsize_dest = im->xsize;
int ysize_dest = im->xsize;
unsigned char *dest = (unsigned char *)im->pixmap;
nassertv(dest != NULL);
int sx_inc = (int)((float)(xsize_src) / (float)(xsize_dest));
int sy_inc = (int)((float)(ysize_src) / (float)(ysize_dest));
unsigned char *dpix = dest;
int syn = 0;
for (int dy = 0; dy < ysize_dest; dy++) {
int sy = syn / ysize_dest;
int sxn = 0;
for (int dx = 0; dx < xsize_dest; dx++) {
int sx = sxn / xsize_dest;
const unsigned char *spix = src + (sy * xsize_src + sx) * 4 * cw;
dpix[0] = spix[co];
dpix[1] = spix[cw + co];
dpix[2] = spix[cw + cw + co];
dpix[3] = spix[cw + cw + cw + co];
dpix += 4;
sxn += xsize_src;
}
syn += ysize_src;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyGraphicsStateGuardian::load_matrix
// Access: Private, Static
// Description: Copies the Panda matrix stored in the indicated
// TransformState object into the indicated TinyGL
// matrix.
////////////////////////////////////////////////////////////////////
void TinyGraphicsStateGuardian::
load_matrix(M4 *matrix, const TransformState *transform) {
const LMatrix4f &pm = transform->get_mat();
for (int i = 0; i < 4; ++i) {
matrix->m[0][i] = pm.get_cell(i, 0);
matrix->m[1][i] = pm.get_cell(i, 1);
matrix->m[2][i] = pm.get_cell(i, 2);
matrix->m[3][i] = pm.get_cell(i, 3);
}
}