// Filename: glShaderContext_src.cxx // Created by: jyelon (01Sep05) // //////////////////////////////////////////////////////////////////// // // 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 . // //////////////////////////////////////////////////////////////////// #ifdef HAVE_CG #include "Cg/cgGL.h" #endif #define DEBUG_GL_SHADER 0 TypeHandle CLP(ShaderContext)::_type_handle; //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::Constructor // Access: Public // Description: xyz //////////////////////////////////////////////////////////////////// CLP(ShaderContext):: CLP(ShaderContext)(ShaderExpansion *s, GSG *gsg) : ShaderContext(s) { #ifdef HAVE_CG if (s->get_header() == "//Cg") { // Ask the shader expansion to compile itself for us and // to give us the resulting Cg program objects. if (!s->cg_compile_for(gsg->_shader_caps, _cg_context, _cg_vprogram, _cg_fprogram, _cg_parameter_map)) { return; } // Load the program. cgGLLoadProgram(_cg_vprogram); CGerror verror = cgGetError(); cgGLLoadProgram(_cg_fprogram); CGerror ferror = cgGetError(); if ((verror != CG_NO_ERROR)||(ferror != CG_NO_ERROR)) { GLCAT.error() << "Could not load Cg shader:" << s->get_name() << "\n"; release_resources(); } } #endif } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::Destructor // Access: Public // Description: xyz //////////////////////////////////////////////////////////////////// CLP(ShaderContext):: ~CLP(ShaderContext)() { release_resources(); } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::release_resources // Access: Public // Description: Should deallocate all system resources (such as // vertex program handles or Cg contexts). //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: release_resources() { #ifdef HAVE_CG if (_cg_context) { cgDestroyContext(_cg_context); _cg_context = 0; _cg_vprogram = 0; _cg_fprogram = 0; _cg_parameter_map.clear(); } #endif } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::bind // Access: Public // Description: This function is to be called to enable a new // shader. It also initializes all of the shader's // input parameters. //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: bind(GSG *gsg) { #ifdef HAVE_CG if (_cg_context != 0) { // Pass in k-parameters and transform-parameters issue_parameters(gsg, true); // Bind the shaders. cgGLEnableProfile(cgGetProgramProfile(_cg_vprogram)); cgGLBindProgram(_cg_vprogram); cgGLEnableProfile(cgGetProgramProfile(_cg_fprogram)); cgGLBindProgram(_cg_fprogram); } #endif } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::unbind // Access: Public // Description: This function disables a currently-bound shader. //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: unbind() { #ifdef HAVE_CG if (_cg_context != 0) { cgGLDisableProfile(cgGetProgramProfile(_cg_vprogram)); cgGLDisableProfile(cgGetProgramProfile(_cg_fprogram)); } #endif } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::issue_parameters // Access: Public // Description: This function gets called whenever the RenderState // or TransformState has changed, but the ShaderExpansion // itself has not changed. It loads new values into the // shader's parameters. // // If "altered" is false, that means you promise that // the parameters for this shader context have already // been issued once, and that since the last time the // parameters were issued, no part of the render // state has changed except the external and internal // transforms. //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: issue_parameters(GSG *gsg, bool altered) { #ifdef HAVE_CG if (_cg_context == 0) { return; } for (int i=0; i<(int)_expansion->_mat_spec.size(); i++) { if (altered || _expansion->_mat_spec[i]._trans_dependent) { CGparameter p = _cg_parameter_map[_expansion->_mat_spec[i]._id._seqno]; const LMatrix4f *val = gsg->fetch_specified_value(_expansion->_mat_spec[i], altered); if (val) { const float *data = val->get_data(); switch (_expansion->_mat_spec[i]._piece) { case ShaderExpansion::SMP_whole: cgGLSetMatrixParameterfc(p, data); break; case ShaderExpansion::SMP_transpose: cgGLSetMatrixParameterfr(p, data); break; case ShaderExpansion::SMP_row0: cgGLSetParameter4fv(p, data+ 0); break; case ShaderExpansion::SMP_row1: cgGLSetParameter4fv(p, data+ 4); break; case ShaderExpansion::SMP_row2: cgGLSetParameter4fv(p, data+ 8); break; case ShaderExpansion::SMP_row3: cgGLSetParameter4fv(p, data+12); break; case ShaderExpansion::SMP_col0: cgGLSetParameter4f(p, data[0], data[4], data[ 8], data[12]); break; case ShaderExpansion::SMP_col1: cgGLSetParameter4f(p, data[1], data[5], data[ 9], data[13]); break; case ShaderExpansion::SMP_col2: cgGLSetParameter4f(p, data[2], data[6], data[10], data[14]); break; case ShaderExpansion::SMP_col3: cgGLSetParameter4f(p, data[3], data[7], data[11], data[15]); break; } } } } #endif } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::disable_shader_vertex_arrays // Access: Public // Description: Disable all the vertex arrays used by this shader. //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: disable_shader_vertex_arrays(GSG *gsg) { #ifdef HAVE_CG if (_cg_context == 0) { return; } for (int i=0; i<(int)_expansion->_var_spec.size(); i++) { CGparameter p = _cg_parameter_map[_expansion->_var_spec[i]._id._seqno]; cgGLDisableClientState(p); } #endif } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::update_shader_vertex_arrays // Access: Public // Description: Disables all vertex arrays used by the previous // shader, then enables all the vertex arrays needed // by this shader. Extracts the relevant vertex array // data from the gsg. // The current implementation is inefficient, because // it may unnecessarily disable arrays then immediately // reenable them. We may optimize this someday. //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg) { if (prev) prev->disable_shader_vertex_arrays(gsg); #ifdef HAVE_CG if (_cg_context == 0) { return; } #ifdef SUPPORT_IMMEDIATE_MODE if (gsg->_use_sender) { GLCAT.error() << "immediate mode shaders not implemented yet\n"; } else #endif // SUPPORT_IMMEDIATE_MODE { const GeomVertexArrayDataPipelineReader *array_reader; Geom::NumericType numeric_type; int start, stride, num_values; int nvarying = _expansion->_var_spec.size(); for (int i=0; i_var_spec[i]._id._seqno]; InternalName *name = _expansion->_var_spec[i]._name; int texslot = _expansion->_var_spec[i]._append_uv; if (texslot >= 0) { const Geom::ActiveTextureStages &active_stages = gsg->_state._texture->get_on_stages(); if (texslot < (int)active_stages.size()) { TextureStage *stage = active_stages[texslot]; InternalName *texname = stage->get_texcoord_name(); if (name == InternalName::get_texcoord()) { name = texname; } else if (texname != InternalName::get_texcoord()) { name = name->append(texname->get_basename()); } } } if (gsg->_data_reader->get_array_info(name, array_reader, num_values, numeric_type, start, stride)) { const unsigned char *client_pointer = gsg->setup_array_data(array_reader); cgGLSetParameterPointer(p, num_values, gsg->get_numeric_type(numeric_type), stride, client_pointer + start); cgGLEnableClientState(p); } else { cgGLDisableClientState(p); } } } #endif // HAVE_CG } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::disable_shader_texture_bindings // Access: Public // Description: Disable all the texture bindings used by this shader. //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: disable_shader_texture_bindings(GSG *gsg) { #ifdef HAVE_CG if (_cg_context == 0) { return; } for (int i=0; i<(int)_expansion->_tex_spec.size(); i++) { CGparameter p = _cg_parameter_map[_expansion->_tex_spec[i]._id._seqno]; int texunit = cgGetParameterResourceIndex(p); gsg->_glActiveTexture(GL_TEXTURE0 + texunit); GLP(Disable)(GL_TEXTURE_1D); GLP(Disable)(GL_TEXTURE_2D); if (gsg->_supports_3d_texture) { GLP(Disable)(GL_TEXTURE_3D); } if (gsg->_supports_cube_map) { GLP(Disable)(GL_TEXTURE_CUBE_MAP); } // This is probably faster - but maybe not as safe? // cgGLDisableTextureParameter(p); } #endif } //////////////////////////////////////////////////////////////////// // Function: GLShaderContext::update_shader_texture_bindings // Access: Public // Description: Disables all texture bindings used by the previous // shader, then enables all the texture bindings needed // by this shader. Extracts the relevant vertex array // data from the gsg. // The current implementation is inefficient, because // it may unnecessarily disable textures then immediately // reenable them. We may optimize this someday. //////////////////////////////////////////////////////////////////// void CLP(ShaderContext):: update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg) { if (prev) prev->disable_shader_texture_bindings(gsg); #ifdef HAVE_CG if (_cg_context == 0) { return; } for (int i=0; i<(int)_expansion->_tex_spec.size(); i++) { CGparameter p = _cg_parameter_map[_expansion->_tex_spec[i]._id._seqno]; Texture *tex = 0; InternalName *id = _expansion->_tex_spec[i]._name; if (id != 0) { const ShaderInput *input = gsg->_target._shader->get_shader_input(id); tex = input->get_texture(); } else { if (_expansion->_tex_spec[i]._stage >= gsg->_target._texture->get_num_on_stages()) { continue; } TextureStage *stage = gsg->_target._texture->get_on_stage(_expansion->_tex_spec[i]._stage); tex = gsg->_target._texture->get_on_texture(stage); } if (_expansion->_tex_spec[i]._suffix != 0) { // The suffix feature is inefficient. It is a temporary hack. if (tex == 0) { continue; } tex = tex->load_related(_expansion->_tex_spec[i]._suffix); } if ((tex == 0) || (tex->get_texture_type() != _expansion->_tex_spec[i]._desired_type)) { continue; } TextureContext *tc = tex->prepare_now(gsg->_prepared_objects, gsg); if (tc == (TextureContext*)NULL) { continue; } int texunit = cgGetParameterResourceIndex(p); gsg->_glActiveTexture(GL_TEXTURE0 + texunit); GLenum target = gsg->get_texture_target(tex->get_texture_type()); if (target == GL_NONE) { // Unsupported texture mode. continue; } GLP(Enable)(target); gsg->apply_texture(tc); } #endif }