// Filename: graphicsStateGuardian.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 "graphicsStateGuardian.h" #include "config_display.h" #include "textureContext.h" #include "vertexBufferContext.h" #include "indexBufferContext.h" #include "renderBuffer.h" #include "attribSlots.h" #include "light.h" #include "planeNode.h" #include "ambientLight.h" #include "throw_event.h" #include "clockObject.h" #include "pStatTimer.h" #include "geomTristrips.h" #include "geomTrifans.h" #include "geomLinestrips.h" #include "shader.h" #include #include PStatCollector GraphicsStateGuardian::_total_texusage_pcollector("Texture usage"); PStatCollector GraphicsStateGuardian::_active_texusage_pcollector("Texture usage:Active"); PStatCollector GraphicsStateGuardian::_texture_count_pcollector("Prepared Textures"); PStatCollector GraphicsStateGuardian::_active_texture_count_pcollector("Prepared Textures:Active"); PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Vertex buffer switch:Vertex"); PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Vertex buffer switch:Index"); PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer"); PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer"); PStatCollector GraphicsStateGuardian::_load_texture_pcollector("Draw:Transfer data:Texture"); PStatCollector GraphicsStateGuardian::_data_transferred_pcollector("Data transferred"); PStatCollector GraphicsStateGuardian::_total_geom_pcollector("Prepared Geoms"); PStatCollector GraphicsStateGuardian::_active_geom_pcollector("Prepared Geoms:Active"); PStatCollector GraphicsStateGuardian::_total_buffers_pcollector("Vertex buffer size"); PStatCollector GraphicsStateGuardian::_active_vertex_buffers_pcollector("Vertex buffer size:Active vertex"); PStatCollector GraphicsStateGuardian::_active_index_buffers_pcollector("Vertex buffer size:Active index"); PStatCollector GraphicsStateGuardian::_total_geom_node_pcollector("Prepared GeomNodes"); PStatCollector GraphicsStateGuardian::_active_geom_node_pcollector("Prepared GeomNodes:Active"); PStatCollector GraphicsStateGuardian::_total_texmem_pcollector("Texture memory"); PStatCollector GraphicsStateGuardian::_used_texmem_pcollector("Texture memory:In use"); PStatCollector GraphicsStateGuardian::_texmgrmem_total_pcollector("Texture manager"); PStatCollector GraphicsStateGuardian::_texmgrmem_resident_pcollector("Texture manager:Resident"); PStatCollector GraphicsStateGuardian::_primitive_batches_pcollector("Primitive batches"); PStatCollector GraphicsStateGuardian::_primitive_batches_tristrip_pcollector("Primitive batches:Triangle strips"); PStatCollector GraphicsStateGuardian::_primitive_batches_trifan_pcollector("Primitive batches:Triangle fans"); PStatCollector GraphicsStateGuardian::_primitive_batches_tri_pcollector("Primitive batches:Triangles"); PStatCollector GraphicsStateGuardian::_primitive_batches_other_pcollector("Primitive batches:Other"); PStatCollector GraphicsStateGuardian::_vertices_tristrip_pcollector("Vertices:Triangle strips"); PStatCollector GraphicsStateGuardian::_vertices_trifan_pcollector("Vertices:Triangle fans"); PStatCollector GraphicsStateGuardian::_vertices_tri_pcollector("Vertices:Triangles"); PStatCollector GraphicsStateGuardian::_vertices_other_pcollector("Vertices:Other"); PStatCollector GraphicsStateGuardian::_vertices_indexed_tristrip_pcollector("Vertices:Indexed triangle strips"); PStatCollector GraphicsStateGuardian::_state_pcollector("State changes"); PStatCollector GraphicsStateGuardian::_transform_state_pcollector("State changes:Transforms"); PStatCollector GraphicsStateGuardian::_texture_state_pcollector("State changes:Textures"); PStatCollector GraphicsStateGuardian::_draw_primitive_pcollector("Draw:Primitive"); PStatCollector GraphicsStateGuardian::_clear_pcollector("Draw:Clear"); PStatCollector GraphicsStateGuardian::_flush_pcollector("Draw:Flush"); GraphicsStateGuardian *GraphicsStateGuardian::_global_gsg = NULL; TypeHandle GraphicsStateGuardian::_type_handle; //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// GraphicsStateGuardian:: GraphicsStateGuardian(const FrameBufferProperties &properties, CoordinateSystem internal_coordinate_system) : _internal_coordinate_system(internal_coordinate_system), _properties(properties) { _coordinate_system = CS_invalid; _external_transform = TransformState::make_identity(); _internal_transform = TransformState::make_identity(); set_coordinate_system(get_default_coordinate_system()); _current_display_region = (DisplayRegion*)0L; _current_lens = (Lens *)NULL; _needs_reset = true; _is_valid = false; _closing_gsg = false; _active = true; _prepared_objects = new PreparedGraphicsObjects; _prefers_triangle_strips = false; _max_vertices_per_array = INT_MAX; _max_vertices_per_primitive = INT_MAX; // Initially, we set this to 1 (the default--no multitexturing // supported). A derived GSG may set this differently if it // supports multitexturing. _max_texture_stages = 1; // Also initially, we assume there are no limits on texture sizes, // and that 3-d and cube-map textures are not supported. _max_texture_dimension = -1; _max_3d_texture_dimension = 0; _max_cube_map_dimension = 0; // Assume we don't support these fairly advanced texture combiner // modes. _supports_texture_combine = false; _supports_texture_saved_result = false; _supports_texture_dot3 = false; _supports_3d_texture = false; _supports_cube_map = false; // Assume no limits on number of lights or clip planes. _max_lights = -1; _max_clip_planes = -1; // Assume no vertex blending capability. _max_vertex_transforms = 0; _max_vertex_transform_indices = 0; // Initially, we set this to false; a GSG that knows it has this // property should set it to true. _copy_texture_inverted = false; // Similarly with these capabilities flags. _supports_multisample = false; _supports_generate_mipmap = false; _supports_render_texture = false; _supported_geom_rendering = 0; // If this is true, then we can apply a color and/or color scale by // twiddling the material and/or ambient light (which could mean // enabling lighting even without a LightAttrib). _color_scale_via_lighting = color_scale_via_lighting; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::Destructor // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// GraphicsStateGuardian:: ~GraphicsStateGuardian() { if (_global_gsg == this) { _global_gsg = NULL; } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_supports_multisample // Access: Published, Virtual // Description: Returns true if this particular GSG supports using // the multisample bits to provide antialiasing, and // also supports M_multisample and M_multisample_mask // transparency modes. If this is not true for a // particular GSG, Panda will map the M_multisample // modes to M_binary. // // This method is declared virtual solely so that it can // be queried from cullResult.cxx. //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: get_supports_multisample() const { return _supports_multisample; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_supported_geom_rendering // Access: Published, Virtual // Description: Returns the union of Geom::GeomRendering values that // this particular GSG can support directly. If a Geom // needs to be rendered that requires some additional // properties, the StandardMunger and/or the // CullableObject will convert it as needed. // // This method is declared virtual solely so that it can // be queried from cullableObject.cxx. //////////////////////////////////////////////////////////////////// int GraphicsStateGuardian:: get_supported_geom_rendering() const { return _supported_geom_rendering; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::set_coordinate_system // Access: Published // Description: Changes the coordinate system in effect on this // particular gsg. This is also called the "external" // coordinate system, since it is the coordinate system // used by the scene graph, external to to GSG. // // Normally, this will be the default coordinate system, // but it might be set differently at runtime. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: set_coordinate_system(CoordinateSystem cs) { _coordinate_system = cs; // Changing the external coordinate system changes the cs_transform. if (_internal_coordinate_system == CS_default || _internal_coordinate_system == _coordinate_system) { _cs_transform = TransformState::make_identity(); _inv_cs_transform = TransformState::make_identity(); } else { _cs_transform = TransformState::make_mat (LMatrix4f::convert_mat(_coordinate_system, _internal_coordinate_system)); _inv_cs_transform = TransformState::make_mat (LMatrix4f::convert_mat(_internal_coordinate_system, _coordinate_system)); } _internal_transform = _cs_transform->compose(_external_transform); _transform_stale = true; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_internal_coordinate_system // Access: Published, Virtual // Description: Returns the coordinate system used internally by the // GSG. This may be the same as the external coordinate // system reported by get_coordinate_system(), or it may // be something different. // // In any case, vertices that have been transformed // before being handed to the GSG (that is, vertices // with a contents value of C_clip_point) will be // expected to be in this coordinate system. //////////////////////////////////////////////////////////////////// CoordinateSystem GraphicsStateGuardian:: get_internal_coordinate_system() const { return _internal_coordinate_system; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::reset // Access: Public, Virtual // Description: Resets all internal state as if the gsg were newly // created. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: reset() { _needs_reset = false; _is_valid = false; _display_region_stack_level = 0; _frame_buffer_stack_level = 0; _lens_stack_level = 0; _state_rs = NULL; _target_rs = NULL; _state.clear_to_zero(); _target.clear_to_defaults(); _external_transform = TransformState::make_identity(); _internal_transform = _cs_transform; _scene_null = new SceneSetup; _scene_setup = _scene_null; _buffer_mask = 0; _color_clear_value.set(0.0f, 0.0f, 0.0f, 0.0f); _depth_clear_value = 1.0f; _stencil_clear_value = 0.0f; _accum_clear_value.set(0.0f, 0.0f, 0.0f, 0.0f); _force_normals = 0; _has_scene_graph_color = false; _transform_stale = true; _color_blend_involves_color_scale = false; _texture_involves_color_scale = false; _vertex_colors_enabled = true; _lighting_enabled = false; _lighting_enabled_this_frame = false; _clip_planes_enabled = false; _clip_planes_enabled_this_frame = false; _color_scale_enabled = false; _current_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f); _has_material_force_color = false; _material_force_color.set(1.0f, 1.0f, 1.0f, 1.0f); _light_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f); _tex_gen_modifies_mat = false; _last_max_stage_index = 0; _is_valid = true; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::set_state_and_transform // Access: Public // Description: Simultaneously resets the render state and the // transform state. // // This transform specified is the "external" net // transform, expressed in the external coordinate // space; internally, it will be pretransformed by // get_cs_transform() to express it in the GSG's // internal coordinate space. // // Special case: if (state==NULL), then the target // state is already stored in _target. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: set_state_and_transform(const RenderState *state, const TransformState *trans) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_render_buffer // Access: Public // Description: Returns a RenderBuffer object suitable for operating // on the requested set of buffers. buffer_type is the // union of all the desired RenderBuffer::Type values. //////////////////////////////////////////////////////////////////// RenderBuffer GraphicsStateGuardian:: get_render_buffer(int buffer_type) { return RenderBuffer(this, buffer_type & _buffer_mask); } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_prepared_objects // Access: Public, Virtual // Description: Returns the set of texture and geom objects that have // been prepared with this GSG (and possibly other GSG's // that share objects). //////////////////////////////////////////////////////////////////// PreparedGraphicsObjects *GraphicsStateGuardian:: get_prepared_objects() { return _prepared_objects; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::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 *GraphicsStateGuardian:: prepare_texture(Texture *) { return (TextureContext *)NULL; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::release_texture // Access: Public, Virtual // Description: Frees the resources previously allocated via a call // to prepare_texture(), including deleting the // TextureContext itself, if it is non-NULL. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: release_texture(TextureContext *) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::prepare_geom // Access: Public, Virtual // Description: Prepares the indicated Geom for retained-mode // rendering, by creating whatever structures are // necessary in the GSG (for instance, vertex buffers). // Returns the newly-allocated GeomContext that can be // used to render the geom. //////////////////////////////////////////////////////////////////// GeomContext *GraphicsStateGuardian:: prepare_geom(Geom *) { return (GeomContext *)NULL; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::release_geom // Access: Public, Virtual // Description: Frees the resources previously allocated via a call // to prepare_geom(), including deleting the GeomContext // itself, if it is non-NULL. // // This function should not be called directly to // prepare a Geom. Instead, call Geom::prepare(). //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: release_geom(GeomContext *) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::prepare_shader // Access: Public, Virtual // Description: Compile a vertex/fragment shader body. //////////////////////////////////////////////////////////////////// ShaderContext *GraphicsStateGuardian:: prepare_shader(ShaderExpansion *shader) { return (ShaderContext *)NULL; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::release_shader // Access: Public, Virtual // Description: Releases the resources allocated by prepare_shader //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: release_shader(ShaderContext *sc) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::prepare_vertex_buffer // Access: Public, Virtual // Description: Prepares the indicated buffer for retained-mode // rendering. //////////////////////////////////////////////////////////////////// VertexBufferContext *GraphicsStateGuardian:: prepare_vertex_buffer(GeomVertexArrayData *) { return (VertexBufferContext *)NULL; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::release_vertex_buffer // Access: Public, Virtual // Description: Frees the resources previously allocated via a call // to prepare_data(), including deleting the // VertexBufferContext itself, if necessary. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: release_vertex_buffer(VertexBufferContext *) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::prepare_index_buffer // Access: Public, Virtual // Description: Prepares the indicated buffer for retained-mode // rendering. //////////////////////////////////////////////////////////////////// IndexBufferContext *GraphicsStateGuardian:: prepare_index_buffer(GeomPrimitive *) { return (IndexBufferContext *)NULL; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::release_index_buffer // Access: Public, Virtual // Description: Frees the resources previously allocated via a call // to prepare_data(), including deleting the // IndexBufferContext itself, if necessary. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: release_index_buffer(IndexBufferContext *) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_geom_munger // Access: Public // Description: Looks up or creates a GeomMunger object to munge // vertices appropriate to this GSG for the indicated // state. //////////////////////////////////////////////////////////////////// PT(GeomMunger) GraphicsStateGuardian:: get_geom_munger(const RenderState *state) { // Before we even look up the map, see if the _last_mi value points // to this GSG. This is likely because we tend to visit the same // state multiple times during a frame. Also, this might well be // the only GSG in the world anyway. if (!state->_mungers.empty()) { RenderState::Mungers::const_iterator mi = state->_last_mi; if (!(*mi).first.was_deleted() && (*mi).first == this) { return (*mi).second; } } // Nope, we have to loop up in the map. RenderState::Mungers::const_iterator mi = state->_mungers.find(this); if (mi != state->_mungers.end() && !(*mi).first.was_deleted()) { ((RenderState *)state)->_last_mi = mi; return (*mi).second; } // Nothing in the map; create a new entry. PT(GeomMunger) munger = make_geom_munger(state); // Cast the RenderState to a non-const object. We can do this // because we are only updating a cache within the RenderState, not // really changing any of its properties. RenderState *nc_state = (RenderState *)state; mi = nc_state->_mungers.insert(RenderState::Mungers::value_type(this, munger)).first; nc_state->_last_mi = mi; return munger; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::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) GraphicsStateGuardian:: make_geom_munger(const RenderState *state) { // The default implementation returns no munger at all, but // presumably, every kind of GSG needs some special munging action, // so real GSG's will override this to return something more // useful. return NULL; } //////////////////////////////////////////////////////////////////// // Function: 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 eye coordinates, // from the camera plane. //////////////////////////////////////////////////////////////////// float GraphicsStateGuardian:: compute_distance_to(const LPoint3f &point) const { switch (_coordinate_system) { case CS_zup_right: return point[1]; case CS_yup_right: return -point[2]; case CS_zup_left: return -point[1]; case CS_yup_left: return point[2]; default: gsg_cat.error() << "Invalid coordinate system in compute_distance_to: " << (int)_coordinate_system << "\n"; return 0.0f; } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::set_color_clear_value // Access: Public // Description: Sets the color that the next do_clear() command will set // the color buffer to //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: set_color_clear_value(const Colorf& value) { _color_clear_value = value; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::set_depth_clear_value // Access: Public // Description: Sets the depth that the next do_clear() command will set // the depth buffer to //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: set_depth_clear_value(const float value) { _depth_clear_value = value; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::clear // Access: Public // Description: Clears the framebuffer within the current // DisplayRegion, according to the flags indicated by // the given DrawableRegion object. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: clear(DrawableRegion *clearable) { PStatTimer timer(_clear_pcollector); int clear_buffer_type = 0; if (clearable->get_clear_color_active()) { clear_buffer_type |= clearable->get_draw_buffer_type(); set_color_clear_value(clearable->get_clear_color()); } if (clearable->get_clear_depth_active()) { clear_buffer_type |= RenderBuffer::T_depth; set_depth_clear_value(clearable->get_clear_depth()); } if (clear_buffer_type != 0) { prepare_display_region(); do_clear(get_render_buffer(clear_buffer_type)); } } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: prepare_lens() { return false; } //////////////////////////////////////////////////////////////////// // 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 GraphicsStateGuardian:: begin_frame() { // Now we know the GSG is the currently active context, so this is a // good time to release any textures or geoms that had been queued // up to release in the past frame, and load up any newly requested // textures. _prepared_objects->update(this); #ifdef DO_PSTATS // For Pstats to track our current texture memory usage, we have to // reset the set of current textures each frame. init_frame_pstats(); #endif // We should reset the state to the default at the beginning of // every frame. Although this will incur additional overhead, // particularly in a simple scene, it helps ensure that states that // have changed properties since last time without changing // attribute pointers--like textures, lighting, or fog--will still // be accurately updated. _state_rs = 0; _state.clear_to_zero(); 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 GraphicsStateGuardian:: begin_scene() { return true; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::end_scene // Access: Public, 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 GraphicsStateGuardian:: end_scene() { // We should clear this pointer now, so that we don't keep unneeded // reference counts dangling. We keep around a "null" scene setup // object instead of using a null pointer to avoid special-case code // in set_state_and_transform. _scene_setup = _scene_null; // Undo any lighting we had enabled last scene, to force the lights // to be reissued, in case their parameters or positions have // changed between scenes. if (_lighting_enabled_this_frame) { for (int i = 0; i < (int)_light_info.size(); i++) { if (_light_info[i]._enabled) { enable_light(i, false); _light_info[i]._enabled = false; } _light_info[i]._light = NodePath(); } // Also force the lighting state to unlit, so that issue_light() // will be guaranteed to be called next frame even if we have the // same set of light pointers we had this frame. // modify_state(get_unlit_state()); _lighting_enabled_this_frame = false; } // Ditto for the clipping planes. if (_clip_planes_enabled_this_frame) { for (int i = 0; i < (int)_clip_plane_info.size(); i++) { if (_clip_plane_info[i]._enabled) { enable_clip_plane(i, false); _clip_plane_info[i]._enabled = false; } _clip_plane_info[i]._plane = (PlaneNode *)NULL; } // modify_state(get_unclipped_state()); _clip_planes_enabled_this_frame = false; } // Put the state into the 'unknown' state, forcing a reload. _state_rs = 0; _state.clear_to_zero(); } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: end_frame() { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::wants_normals // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: wants_normals() const { return (_lighting_enabled || (_force_normals != 0)); } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::wants_texcoords // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: wants_texcoords() const { return false; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::wants_colors // Access: Public, Virtual // Description: Returns true if the GSG should issue geometry color // commands, false otherwise. //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: wants_colors() const { return _vertex_colors_enabled; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::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 GraphicsStateGuardian:: depth_offset_decals() { return true; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::begin_decal_base_first // Access: Public, Virtual // Description: Called during draw to begin a three-step rendering // phase to draw decals. The first step, // begin_decal_base_first(), is called prior to drawing the // base geometry. It should set up whatever internal // state is appropriate, as well as returning a // RenderState object that should be applied to the base // geometry for rendering. //////////////////////////////////////////////////////////////////// CPT(RenderState) GraphicsStateGuardian:: begin_decal_base_first() { // Turn off writing the depth buffer to render the base geometry. static CPT(RenderState) decal_base_first; if (decal_base_first == (const RenderState *)NULL) { decal_base_first = RenderState::make (DepthWriteAttrib::make(DepthWriteAttrib::M_off), RenderState::get_max_priority()); } return decal_base_first; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::begin_decal_nested // Access: Public, Virtual // Description: Called during draw to begin a three-step rendering // phase to draw decals. The second step, // begin_decal_nested(), is called after drawing the // base geometry and prior to drawing any of the nested // decal geometry that is to be applied to the base // geometry. //////////////////////////////////////////////////////////////////// CPT(RenderState) GraphicsStateGuardian:: begin_decal_nested() { // We should keep the depth buffer off during this operation, so // that decals on decals will render properly. static CPT(RenderState) decal_nested; if (decal_nested == (const RenderState *)NULL) { decal_nested = RenderState::make (DepthWriteAttrib::make(DepthWriteAttrib::M_off), RenderState::get_max_priority()); } return decal_nested; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::begin_decal_base_second // Access: Public, Virtual // Description: Called during draw to begin a three-step rendering // phase to draw decals. The third step, // begin_decal_base_second(), is called after drawing the // base geometry and the nested decal geometry, and // prior to drawing the base geometry one more time (if // needed). // // It should return a RenderState object appropriate for // rendering the base geometry the second time, or NULL // if it is not necessary to re-render the base // geometry. //////////////////////////////////////////////////////////////////// CPT(RenderState) GraphicsStateGuardian:: begin_decal_base_second() { // Now let the depth buffer go back on, but turn off writing the // color buffer to render the base geometry after the second pass. // Also, turn off texturing since there's no need for it now. static CPT(RenderState) decal_base_second; if (decal_base_second == (const RenderState *)NULL) { decal_base_second = RenderState::make (ColorWriteAttrib::make(ColorWriteAttrib::C_off), // On reflection, we need to leave texturing on so the alpha // test mechanism can work (if it is enabled, e.g. we are // rendering an object with M_dual transparency). // TextureAttrib::make_off(), RenderState::get_max_priority()); } return decal_base_second; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::finish_decal // Access: Public, Virtual // Description: Called during draw to clean up after decals are // finished. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: finish_decal() { // No need to do anything special here. } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::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 GraphicsStateGuardian:: begin_draw_primitives(const Geom *, const GeomMunger *munger, const GeomVertexData *data) { _munger = munger; _vertex_data = data; return _vertex_data->has_vertex(); } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::draw_triangles // Access: Public, Virtual // Description: Draws a series of disconnected triangles. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: draw_triangles(const GeomTriangles *) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::draw_tristrips // Access: Public, Virtual // Description: Draws a series of triangle strips. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: draw_tristrips(const GeomTristrips *primitive) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::draw_trifans // Access: Public, Virtual // Description: Draws a series of triangle fans. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: draw_trifans(const GeomTrifans *primitive) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::draw_lines // Access: Public, Virtual // Description: Draws a series of disconnected line segments. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: draw_lines(const GeomLines *) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::draw_linestrips // Access: Public, Virtual // Description: Draws a series of line strips. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: draw_linestrips(const GeomLinestrips *primitive) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::draw_points // Access: Public, Virtual // Description: Draws a series of disconnected points. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: draw_points(const GeomPoints *) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::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 GraphicsStateGuardian:: end_draw_primitives() { _munger = NULL; _vertex_data = NULL; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::framebuffer_bind_to_texture // Access: Public, Virtual // Description: Works in lieu of copy_texture() to bind the primary // render buffer of the framebuffer to the indicated // texture (which must have been created via // GraphicsOutput::setup_render_texture()). // // If supported by the graphics backend, this will make // the framebuffer memory directly accessible within the // texture, but the frame cannot be rendered again until // framebuffer_release_texture() is called. // // The return value is true if successful, false on // failure. //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: framebuffer_bind_to_texture(GraphicsOutput *, Texture *) { return false; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::framebuffer_release_texture // Access: Public, Virtual // Description: Undoes a previous call to // framebuffer_bind_to_texture(). The framebuffer may // again be rendered into, and the contents of the // texture is undefined. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: framebuffer_release_texture(GraphicsOutput *, Texture *) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::do_issue_color_scale // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: do_issue_color_scale() { const ColorScaleAttrib *attrib = _target._color_scale; _color_scale_enabled = attrib->has_scale(); _current_color_scale = attrib->get_scale(); if (_color_blend_involves_color_scale) { _state_rs = 0; _state._transparency = 0; } if (_texture_involves_color_scale) { _state_rs = 0; _state._texture = 0; } if (_color_scale_via_lighting) { _state_rs = 0; _state._light = 0; _state._material = 0; determine_light_color_scale(); } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::do_issue_color // Access: Public // Description: This method is defined in the base class because it // is likely that this functionality will be used for // all (or at least most) kinds of // GraphicsStateGuardians--it's not specific to any one // rendering backend. // // The ColorAttribute just changes the interpretation of // the color on the vertices, and fiddles with // _vertex_colors_enabled, etc. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: do_issue_color() { const ColorAttrib *attrib = _target._color; switch (attrib->get_color_type()) { case ColorAttrib::T_flat: // Color attribute flat: it specifies a scene graph color that // overrides the vertex color. _scene_graph_color = attrib->get_color(); _has_scene_graph_color = true; _vertex_colors_enabled = false; break; case ColorAttrib::T_off: // Color attribute off: it specifies that no scene graph color is // in effect, and vertex color is not important either. _has_scene_graph_color = false; _vertex_colors_enabled = false; break; case ColorAttrib::T_vertex: // Color attribute vertex: it specifies that vertex color should // be revealed. _has_scene_graph_color = false; _vertex_colors_enabled = true; break; } if (_color_scale_via_lighting) { _state_rs = 0; _state._light = 0; _state._material = 0; determine_light_color_scale(); } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::do_issue_clip_plane // Access: Public // Description: This is fundametically similar to do_issue_light(), with // calls to apply_clip_plane() and enable_clip_planes(), // as appropriate. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: do_issue_clip_plane() { const ClipPlaneAttrib *attrib = _target._clip_plane; int i; int cur_max_planes = (int)_clip_plane_info.size(); for (i = 0; i < cur_max_planes; i++) { _clip_plane_info[i]._next_enabled = false; } CPT(ClipPlaneAttrib) new_attrib = attrib->filter_to_max(_max_clip_planes); bool any_bound = false; int num_enabled = 0; int num_on_planes = new_attrib->get_num_on_planes(); for (int li = 0; li < num_on_planes; li++) { NodePath plane = new_attrib->get_on_plane(li); nassertv(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type())); num_enabled++; // Clipping should be enabled before we apply any planes. enable_clip_planes(true); _clip_planes_enabled = true; _clip_planes_enabled_this_frame = true; // Check to see if this plane has already been bound to an id int cur_plane_id = -1; for (i = 0; i < cur_max_planes; i++) { if (_clip_plane_info[i]._plane == plane) { // Plane has already been bound to an id, we only need to // enable the plane, not reapply it. cur_plane_id = -2; enable_clip_plane(i, true); _clip_plane_info[i]._enabled = true; _clip_plane_info[i]._next_enabled = true; break; } } // See if there are any unbound plane ids if (cur_plane_id == -1) { for (i = 0; i < cur_max_planes; i++) { if (_clip_plane_info[i]._plane.is_empty()) { _clip_plane_info[i]._plane = plane; cur_plane_id = i; break; } } } // If there were no unbound plane ids, see if we can replace // a currently unused but previously bound id if (cur_plane_id == -1) { for (i = 0; i < cur_max_planes; i++) { if (!new_attrib->has_on_plane(_clip_plane_info[i]._plane)) { _clip_plane_info[i]._plane = plane; cur_plane_id = i; break; } } } // If we *still* don't have a plane id, slot a new one. if (cur_plane_id == -1) { if (_max_clip_planes < 0 || cur_max_planes < _max_clip_planes) { cur_plane_id = cur_max_planes; _clip_plane_info.push_back(ClipPlaneInfo()); cur_max_planes++; nassertv(cur_max_planes == (int)_clip_plane_info.size()); } } if (cur_plane_id >= 0) { enable_clip_plane(cur_plane_id, true); _clip_plane_info[cur_plane_id]._enabled = true; _clip_plane_info[cur_plane_id]._next_enabled = true; if (!any_bound) { begin_bind_clip_planes(); any_bound = true; } // This is the first time this frame that this plane has been // bound to this particular id. bind_clip_plane(plane, cur_plane_id); } else if (cur_plane_id == -1) { gsg_cat.warning() << "Failed to bind " << plane << " to id.\n"; } } // Disable all unused planes for (i = 0; i < cur_max_planes; i++) { if (!_clip_plane_info[i]._next_enabled) { enable_clip_plane(i, false); _clip_plane_info[i]._enabled = false; } } // If no planes were enabled, disable clip planes in general. if (num_enabled == 0) { enable_clip_planes(false); _clip_planes_enabled = false; } if (any_bound) { end_bind_clip_planes(); } } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: bind_light(PointLight *light_obj, const NodePath &light, int light_id) { } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) { } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: bind_light(Spotlight *light_obj, const NodePath &light, int light_id) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::do_issue_light // Access: Protected // Description: This implementation of do_issue_light() assumes // we have a limited number of hardware lights // available. This function assigns each light to a // different hardware light id, trying to keep each // light associated with the same id where possible, but // reusing id's when necessary. When it is no longer // possible to reuse existing id's (e.g. all id's are in // use), the next sequential id is assigned (if // available). // // It will call apply_light() each time a light is // assigned to a particular id for the first time in a // given frame, and it will subsequently call // enable_light() to enable or disable each light as the // frame is rendered, as well as enable_lighting() to // enable or disable overall lighting. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: do_issue_light() { // Initialize the current ambient light total and newly enabled // light list Colorf cur_ambient_light(0.0f, 0.0f, 0.0f, 0.0f); int i; int cur_max_lights = (int)_light_info.size(); for (i = 0; i < cur_max_lights; i++) { _light_info[i]._next_enabled = false; } bool any_bound = false; int num_enabled = 0; if (_target._light != (LightAttrib *)NULL) { CPT(LightAttrib) new_light = _target._light->filter_to_max(_max_lights); int num_on_lights = new_light->get_num_on_lights(); for (int li = 0; li < num_on_lights; li++) { NodePath light = new_light->get_on_light(li); nassertv(!light.is_empty() && light.node()->as_light() != (Light *)NULL); Light *light_obj = light.node()->as_light(); num_enabled++; // Lighting should be enabled before we apply any lights. enable_lighting(true); _lighting_enabled = true; _lighting_enabled_this_frame = true; if (light_obj->get_type() == AmbientLight::get_class_type()) { // Ambient lights don't require specific light ids; simply add // in the ambient contribution to the current total cur_ambient_light += light_obj->get_color(); } else { // Check to see if this light has already been bound to an id int cur_light_id = -1; for (i = 0; i < cur_max_lights; i++) { if (_light_info[i]._light == light) { // Light has already been bound to an id; reuse the same id. cur_light_id = -2; enable_light(i, true); _light_info[i]._enabled = true; _light_info[i]._next_enabled = true; if (!any_bound) { begin_bind_lights(); any_bound = true; } light_obj->bind(this, light, i); break; } } // See if there are any unbound light ids if (cur_light_id == -1) { for (i = 0; i < cur_max_lights; i++) { if (_light_info[i]._light.is_empty()) { _light_info[i]._light = light; cur_light_id = i; break; } } } // If there were no unbound light ids, see if we can replace // a currently unused but previously bound id if (cur_light_id == -1) { for (i = 0; i < cur_max_lights; i++) { if (!new_light->has_on_light(_light_info[i]._light)) { _light_info[i]._light = light; cur_light_id = i; break; } } } // If we *still* don't have a light id, slot a new one. if (cur_light_id == -1) { if (_max_lights < 0 || cur_max_lights < _max_lights) { cur_light_id = cur_max_lights; _light_info.push_back(LightInfo()); cur_max_lights++; nassertv(cur_max_lights == (int)_light_info.size()); } } if (cur_light_id >= 0) { enable_light(cur_light_id, true); _light_info[cur_light_id]._enabled = true; _light_info[cur_light_id]._next_enabled = true; if (!any_bound) { begin_bind_lights(); any_bound = true; } // This is the first time this frame that this light has been // bound to this particular id. light_obj->bind(this, light, cur_light_id); } else if (cur_light_id == -1) { gsg_cat.warning() << "Failed to bind " << light << " to id.\n"; } } } } // Disable all unused lights for (i = 0; i < cur_max_lights; i++) { if (!_light_info[i]._next_enabled) { enable_light(i, false); _light_info[i]._enabled = false; } } // If no lights were enabled, disable lighting if (num_enabled == 0) { if (_color_scale_via_lighting && (_has_material_force_color || _light_color_scale != LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f))) { // Unless we need lighting anyway to apply a color or color // scale. enable_lighting(true); _lighting_enabled = true; _lighting_enabled_this_frame = true; set_ambient_light(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); } else { enable_lighting(false); _lighting_enabled = false; } } else { set_ambient_light(cur_ambient_light); } if (any_bound) { end_bind_lights(); } } //////////////////////////////////////////////////////////////////// // Function: 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 do_issue_light() according to whether any // lights are in use or not. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: enable_lighting(bool enable) { } //////////////////////////////////////////////////////////////////// // Function: 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 do_issue_light() after // all other lights have been enabled or disabled. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: set_ambient_light(const Colorf &color) { } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: enable_light(int light_id, bool enable) { } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: begin_bind_lights() { } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: end_bind_lights() { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::enable_clip_planes // Access: Protected, Virtual // Description: Intended to be overridden by a derived class to // enable or disable the use of clipping planes overall. // This is called by do_issue_clip_plane() according to // whether any planes are in use or not. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: enable_clip_planes(bool enable) { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::enable_clip_plane // Access: Protected, Virtual // Description: Intended to be overridden by a derived class to // enable the indicated plane id. A specific PlaneNode // will already have been bound to this id via // bind_clip_plane(). //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: enable_clip_plane(int plane_id, bool enable) { } //////////////////////////////////////////////////////////////////// // Function: 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 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 GraphicsStateGuardian:: begin_bind_clip_planes() { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::bind_clip_plane // Access: Public, Virtual // Description: Called the first time a particular clipping plane has been // bound to a given id within a frame, this should set // up the associated hardware (or API) clipping plane // with the plane's properties. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: bind_clip_plane(const NodePath &plane, int plane_id) { } //////////////////////////////////////////////////////////////////// // Function: 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 GraphicsStateGuardian:: end_bind_clip_planes() { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::free_pointers // Access: Protected, Virtual // Description: Frees some memory that was explicitly allocated // within the glgsg. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: free_pointers() { } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::close_gsg // Access: Protected, Virtual // Description: This is called by the associated GraphicsWindow when // close_window() is called. It should null out the // _win pointer and possibly free any open resources // associated with the GSG. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: close_gsg() { _closing_gsg = true; free_pointers(); // If we're not sharing the prepared objects list with any other // GSG, go ahead and release all the textures and geoms now. This // isn't really a reliable test of whether we are sharing this list, // but it's not too important if we get this wrong since this ought // to be an optional cleanup anyway. (Presumably, the underlying // graphics API will properly clean up outstanding textures and // geoms when the last context using them is released.) if (_prepared_objects->get_ref_count() == 1) { release_all_textures(); release_all_geoms(); release_all_vertex_buffers(); release_all_index_buffers(); } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::panic_deactivate // Access: Protected // Description: This is called internally when it is determined that // things are just fubar. It temporarily deactivates // the GSG just so things don't get out of hand, and // throws an event so the application can deal with this // if it needs to. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: panic_deactivate() { if (_active) { display_cat.error() << "Deactivating " << get_type() << ".\n"; set_active(false); throw_event("panic-deactivate-gsg", this); } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::determine_light_color_scale // Access: Protected // Description: Called whenever the color or color scale is changed, // if _color_scale_via_lighting is true. This will // rederive _material_force_color and _light_color_scale // appropriately. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: determine_light_color_scale() { if (_has_scene_graph_color) { // If we have a scene graph color, it, plus the color scale, goes // directly into the material; we don't color scale the // lights--this allows an alpha color scale to work properly. _has_material_force_color = true; _material_force_color = _scene_graph_color; _light_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f); if (!_color_blend_involves_color_scale && _color_scale_enabled) { _material_force_color.set(_scene_graph_color[0] * _current_color_scale[0], _scene_graph_color[1] * _current_color_scale[1], _scene_graph_color[2] * _current_color_scale[2], _scene_graph_color[3] * _current_color_scale[3]); } } else { // Otherise, leave the materials alone, but we might still scale // the lights. _has_material_force_color = false; _light_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f); if (!_color_blend_involves_color_scale && _color_scale_enabled) { _light_color_scale = _current_color_scale; } } } #ifdef DO_PSTATS //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::init_frame_pstats // Access: Protected // Description: Initializes the relevant PStats data at the beginning // of the frame. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: init_frame_pstats() { if (PStatClient::is_connected()) { _current_textures.clear(); _current_geoms.clear(); _current_vertex_buffers.clear(); _current_index_buffers.clear(); _active_texusage_pcollector.clear_level(); _data_transferred_pcollector.clear_level(); _active_geom_pcollector.clear_level(); _active_geom_node_pcollector.clear_level(); _active_vertex_buffers_pcollector.clear_level(); _active_index_buffers_pcollector.clear_level(); _vertex_buffer_switch_pcollector.clear_level(); _index_buffer_switch_pcollector.clear_level(); _primitive_batches_pcollector.clear_level(); _primitive_batches_tristrip_pcollector.clear_level(); _primitive_batches_trifan_pcollector.clear_level(); _primitive_batches_tri_pcollector.clear_level(); _primitive_batches_other_pcollector.clear_level(); _vertices_tristrip_pcollector.clear_level(); _vertices_trifan_pcollector.clear_level(); _vertices_tri_pcollector.clear_level(); _vertices_other_pcollector.clear_level(); _vertices_indexed_tristrip_pcollector.clear_level(); _state_pcollector.clear_level(); _transform_state_pcollector.clear_level(); _texture_state_pcollector.clear_level(); } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::add_to_texture_record // Access: Protected // Description: Records that the indicated texture has been applied // this frame, and thus must be present in current // texture memory. This function is only used to update // the PStats current_texmem collector; it gets compiled // out if we aren't using PStats. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: add_to_texture_record(TextureContext *tc) { if (PStatClient::is_connected()) { if (_current_textures.insert(tc).second) { _active_texusage_pcollector.add_level(tc->estimate_texture_memory()); } } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::add_to_geom_record // Access: Protected // Description: Records that the indicated Geom has been drawn this // frame. This function is only used to update the // PStats active_geom collector; it gets compiled out // if we aren't using PStats. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: add_to_geom_record(GeomContext *gc) { if (PStatClient::is_connected()) { if (gc != (GeomContext *)NULL && _current_geoms.insert(gc).second) { _active_geom_pcollector.add_level(1); } } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::add_to_vertex_buffer_record // Access: Protected // Description: Records that the indicated data array has been drawn // this frame. This function is only used to update the // PStats active_vertex_buffers collector; it gets // compiled out if we aren't using PStats. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: add_to_vertex_buffer_record(VertexBufferContext *vbc) { if (vbc != (VertexBufferContext *)NULL) { if (PStatClient::is_connected()) { _vertex_buffer_switch_pcollector.add_level(1); if (_current_vertex_buffers.insert(vbc).second) { _active_vertex_buffers_pcollector.add_level(vbc->get_data()->get_data_size_bytes()); } } } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::add_to_index_buffer_record // Access: Protected // Description: Records that the indicated data array has been drawn // this frame. This function is only used to update the // PStats active_index_buffers collector; it gets compiled out // if we aren't using PStats. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: add_to_index_buffer_record(IndexBufferContext *ibc) { if (ibc != (IndexBufferContext *)NULL) { if (PStatClient::is_connected()) { _index_buffer_switch_pcollector.add_level(1); if (_current_index_buffers.insert(ibc).second) { _active_index_buffers_pcollector.add_level(ibc->get_data()->get_data_size_bytes()); } } } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::add_to_total_buffer_record // Access: Protected // Description: Records that the indicated data array has been loaded // this frame. This function is only used to update the // PStats total_buffers collector; it gets // compiled out if we aren't using PStats. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: add_to_total_buffer_record(VertexBufferContext *vbc) { if (vbc != (VertexBufferContext *)NULL) { int delta = vbc->get_data()->get_data_size_bytes() - vbc->get_data_size_bytes(); _total_buffers_pcollector.add_level(delta); } } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::add_to_total_buffer_record // Access: Protected // Description: Records that the indicated data array has been loaded // this frame. This function is only used to update the // PStats total_buffers collector; it gets // compiled out if we aren't using PStats. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: add_to_total_buffer_record(IndexBufferContext *ibc) { if (ibc != (IndexBufferContext *)NULL) { int delta = ibc->get_data()->get_data_size_bytes() - ibc->get_data_size_bytes(); _total_buffers_pcollector.add_level(delta); } } #endif // DO_PSTATS //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_unlit_state // Access: Protected, Static // Description: //////////////////////////////////////////////////////////////////// CPT(RenderState) GraphicsStateGuardian:: get_unlit_state() { static CPT(RenderState) state = NULL; if (state == (const RenderState *)NULL) { state = RenderState::make(LightAttrib::make_all_off()); } return state; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_unclipped_state // Access: Protected, Static // Description: //////////////////////////////////////////////////////////////////// CPT(RenderState) GraphicsStateGuardian:: get_unclipped_state() { static CPT(RenderState) state = NULL; if (state == (const RenderState *)NULL) { state = RenderState::make(ClipPlaneAttrib::make_all_off()); } return state; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_untextured_state // Access: Protected, Static // Description: //////////////////////////////////////////////////////////////////// CPT(RenderState) GraphicsStateGuardian:: get_untextured_state() { static CPT(RenderState) state = NULL; if (state == (const RenderState *)NULL) { state = RenderState::make(TextureAttrib::make_off()); } return state; } //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::traverse_prepared_textures // Access: Public // Description: Calls the indicated function on all // currently-prepared textures, or until the callback // function returns false. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg) { PreparedGraphicsObjects::Textures::const_iterator ti; for (ti = _prepared_objects->_prepared_textures.begin(); ti != _prepared_objects->_prepared_textures.end(); ++ti) { bool bResult=(*pertex_callbackfn)(*ti,callback_arg); if(!bResult) return; } }