diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index cfe260277e..a3979a6c2c 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -814,17 +814,12 @@ dispatch_compute(int num_groups_x, int num_groups_y, int num_groups_z) { //////////////////////////////////////////////////////////////////// PT(GeomMunger) GraphicsStateGuardian:: get_geom_munger(const RenderState *state, Thread *current_thread) { - // We can cast the RenderState to a non-const object because we are - // only updating a cache within the RenderState, not really changing - // any of its properties. - RenderState *nc_state = ((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 (!nc_state->_mungers.empty()) { - RenderState::Mungers::const_iterator mi = nc_state->_last_mi; + if (!state->_mungers.empty()) { + RenderState::Mungers::const_iterator mi = state->_last_mi; if (!(*mi).first.was_deleted() && (*mi).first == this) { if ((*mi).second->is_registered()) { return (*mi).second; @@ -833,23 +828,23 @@ get_geom_munger(const RenderState *state, Thread *current_thread) { } // Nope, we have to look it up in the map. - RenderState::Mungers::iterator mi = nc_state->_mungers.find(this); - if (mi != nc_state->_mungers.end() && !(*mi).first.was_deleted()) { + RenderState::Mungers::iterator mi = state->_mungers.find(this); + if (mi != state->_mungers.end() && !(*mi).first.was_deleted()) { if ((*mi).second->is_registered()) { - nc_state->_last_mi = mi; + state->_last_mi = mi; return (*mi).second; } // This GeomMunger is no longer registered. Remove it from the // map. - nc_state->_mungers.erase(mi); + state->_mungers.erase(mi); } // Nothing in the map; create a new entry. - PT(GeomMunger) munger = make_geom_munger(nc_state, current_thread); + PT(GeomMunger) munger = make_geom_munger(state, current_thread); nassertr(munger != (GeomMunger *)NULL && munger->is_registered(), munger); - mi = nc_state->_mungers.insert(RenderState::Mungers::value_type(this, munger)).first; - nc_state->_last_mi = mi; + mi = state->_mungers.insert(RenderState::Mungers::value_type(this, munger)).first; + state->_last_mi = mi; return munger; } diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx index 5dc6c03d13..85ddd0f3e1 100644 --- a/panda/src/display/standardMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -131,7 +131,8 @@ munge_data_impl(const GeomVertexData *data) { } GeomVertexAnimationSpec animation = new_data->get_format()->get_animation(); - if (_shader_skinning) { + if (_shader_skinning || (_auto_shader && hardware_animated_vertices && + !basic_shaders_only && animation.get_animation_type() == AT_panda)) { animation.set_hardware(4, true); } else if (hardware_animated_vertices && @@ -387,7 +388,17 @@ munge_state_impl(const RenderState *state) { } if (shader_state->_generated_shader == NULL) { // Cache the generated ShaderAttrib on the shader state. - shader_state->_generated_shader = shader_generator->synthesize_shader(shader_state); + GeomVertexAnimationSpec spec; + + // Currently we overload this flag to request vertex animation + // for the shader generator. + const ShaderAttrib *sattr; + shader_state->get_attrib_def(sattr); + if (sattr->get_flag(ShaderAttrib::F_hardware_skinning)) { + spec.set_hardware(4, true); + } + + shader_state->_generated_shader = shader_generator->synthesize_shader(shader_state, spec); } munged_state = munged_state->set_attrib(shader_state->_generated_shader); } diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index 69f40aca60..47fc97f586 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -41,6 +41,11 @@ CLP(CgShaderContext):: CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext(s) { _glgsg = glgsg; _cg_program = 0; + _glsl_program = 0; + _has_divisor = false; + _color_attrib_index = CA_color; + _transform_table_param = 0; + _slider_table_param = 0; nassertv(s->get_language() == Shader::SL_Cg); @@ -53,6 +58,21 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte return; } + _transform_table_param = cgGetNamedParameter(_cg_program, "tbl_transforms"); + if (_transform_table_param) { + _transform_table_size = cgGetArraySize(_transform_table_param, 0); + } + + _slider_table_param = cgGetNamedParameter(_cg_program, "tbl_sliders"); + if (_slider_table_param) { + _slider_table_size = cgGetArraySize(_slider_table_param, 0); + } + + if (GLCAT.is_debug()) { + GLCAT.debug() + << "Loading Cg shader " << s->get_filename() << "\n"; + } + // Load the program. if (_cg_program == 0) { const char *str = cgGetErrorString(cgGetError()); @@ -73,6 +93,214 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte } } + if (_cg_program != 0) { + if (cgGetProgramProfile(_cg_program) == CG_PROFILE_GLSLC) { + _glsl_program = cgGLGetProgramID(_cg_program); + + // Sometimes it fails to link, and Cg fails to propagate the error. + GLint link_status; + _glgsg->_glGetProgramiv(_glsl_program, GL_LINK_STATUS, &link_status); + if (link_status != GL_TRUE) { + release_resources(); + } + } + } + + if (_cg_program == 0) { + return; + } + + // We don't use cgGLSetParameterPointer to set the vertex attributes any + // longer, since it is buggy on non-NVIDIA hardware and doesn't allow explicit + // control over some parameters. Instead, we have to figure out ourselves how + // to map the input varyings to OpenGL vertex attributes. + // + // We use positive indices to indicate generic vertex attributes, and negative + // indices to indicate conventional vertex attributes (ie. glVertexPointer). + int nvarying = _shader->_var_spec.size(); + for (int i = 0; i < nvarying; ++i) { + Shader::ShaderVarSpec &bind = _shader->_var_spec[i]; + CGparameter p = _cg_parameter_map[i]; + if (p == 0) { + bind._id._seqno = CA_unknown; + continue; + } + + GLint loc = CA_unknown; + CGresource res = cgGetParameterResource(p); + + if (cgGetParameterBaseResource(p) == CG_ATTR0) { + // The Cg toolkit claims that it is bound to a generic vertex attribute. + if (_glsl_program != 0) { + // This is where the Cg glslv compiler lies, making the stupid assumption + // that we're using an NVIDIA card where generic attributes are aliased + // with conventional vertex attributes. Instead, it always uses + // conventional attributes in this case. Correct this. + int index = cgGetParameterResourceIndex(p); + switch (index) { + case 0: // gl_Vertex + loc = CA_vertex; + break; + case 2: // gl_Normal + loc = CA_normal; + break; + case 3: // gl_Color + loc = CA_color; + break; + case 4: // gl_SecondaryColor + loc = CA_secondary_color; + break; + case 1: // glWeightPointerARB? + case 5: // gl_FogCoord + case 6: // PSIZE? + case 7: // BLENDINDICES + GLCAT.error() + << "Cg varying " << cgGetParameterName(p) << " is bound to " + "unrecognized attribute " << index << "\n"; + loc = CA_unknown; + break; + default: + loc = CA_texcoord + (index - 8); + break; + } + } else { + loc = cgGetParameterResourceIndex(p); + + if (loc != 0 && bind._id._name == "vtx_position") { + // We really have to bind the vertex position to attribute 0, since + // OpenGL will hide the model if attribute 0 is not enabled, and we + // can only ever be sure that vtx_position is bound. + GLCAT.warning() + << "CG varying vtx_position is bound to generic attribute " << loc + << "instead of 0. Use ATTR0 semantic to prevent this.\n"; + } + } + + } else if (res == CG_GLSL_ATTRIB || _glsl_program != 0) { + // With cg-glsl-version 130 and higher, no conventional attributes are + // used, but it instead uses specially named variables. + // A bit of guesswork is involved here; Cg seems to mostly use the + // semantics as attribute names in GLSL, with a few exceptions. + const char *attribname = NULL; + switch (res) { + case CG_POSITION0: + attribname = "cg_Vertex"; + break; + case CG_NORMAL0: + attribname = "NORMAL"; + break; + case CG_COLOR0: + case CG_DIFFUSE0: + attribname = "COLOR"; + break; + case CG_COLOR1: + case CG_SPECULAR0: + attribname = "SPECULAR"; + break; + default: + // Everything else appears to be named after the semantic string. + attribname = cgGetParameterSemantic(p); + } + loc = _glgsg->_glGetAttribLocation(_glsl_program, attribname); + + if (bind._id._name == "vtx_color") { + _color_attrib_index = loc; + } + + if (loc == -1) { + const char *resource = cgGetParameterResourceName(p); + if (!resource) { + resource = "unknown"; + } + GLCAT.error() + << "Could not find Cg varying " << cgGetParameterName(p); + if (attribname) { + GLCAT.error(false) << " : " << attribname; + } + GLCAT.error(false) << " (" << resource << ") in the compiled GLSL program.\n"; + + } else if (loc != 0 && bind._id._name == "vtx_position") { + // We really have to bind the vertex position to attribute 0, since + // OpenGL will hide the model if attribute 0 is not enabled, and we + // can only ever be sure that vtx_position is bound. + GLCAT.warning() + << "CG varying vtx_position is bound to generic attribute " << loc + << "instead of 0. Use ATTR0 semantic to prevent this.\n"; + } + + } else if (cgGetParameterBaseResource(p) == CG_TEXCOORD0) { + // A conventional texture coordinate set. + loc = CA_texcoord + cgGetParameterResourceIndex(p); + + } else { + // Some other conventional vertex attribute. + switch (res) { + case CG_POSITION0: + loc = CA_vertex; + break; + case CG_NORMAL0: + loc = CA_normal; + break; + case CG_COLOR0: + case CG_DIFFUSE0: + loc = CA_color; + break; + case CG_COLOR1: + case CG_SPECULAR0: + loc = CA_secondary_color; + break; + default: + GLCAT.error() + << "Cg varying " << cgGetParameterName(p); + if (cgGetParameterSemantic(p)) { + GLCAT.error(false) << " : " << cgGetParameterSemantic(p); + } + GLCAT.error(false) << " has an unrecognized resource"; + if (cgGetParameterResourceName(p)) { + GLCAT.error(false) << " (" << cgGetParameterResourceName(p) << ")"; + } + GLCAT.error(false) << ".\n"; + loc = CA_unknown; + } + } + +#ifndef NDEBUG + if (GLCAT.is_debug()) { + GLCAT.debug() + << "Cg varying " << cgGetParameterName(p); + + const char *semantic = cgGetParameterSemantic(p); + if (semantic) { + GLCAT.debug(false) << " : " << semantic; + } + + if (loc == CA_unknown) { + GLCAT.debug(false) + << " is not bound to a vertex attribute\n"; + + } else if (loc >= 0) { + GLCAT.debug(false) + << " is bound to generic attribute " << loc << "\n"; + + } else { + const char *resource = cgGetParameterResourceName(p); + if (!resource) { + resource = "unknown"; + } + GLCAT.debug(false) + << " is bound to a conventional attribute (" << resource << ")\n"; + } + } + if (loc == CA_unknown) { + // Suggest fix to developer. + GLCAT.error() << "Try using a different semantic.\n"; + } +#endif + + // Abuse the seqno field to store the GLSL attribute location. + bind._id._seqno = loc; + } + _glgsg->report_my_gl_errors(); } @@ -385,6 +613,58 @@ issue_parameters(int altered) { _glgsg->report_my_gl_errors(); } +//////////////////////////////////////////////////////////////////// +// Function: CgGLShaderContext::update_transform_table +// Access: Public +// Description: Changes the active transform table, used for hardware +// skinning. +//////////////////////////////////////////////////////////////////// +void CLP(CgShaderContext):: +update_transform_table(const TransformTable *table) { + LMatrix4f *matrices = (LMatrix4f *)alloca(_transform_table_size * 64); + + int i = 0; + if (table != NULL) { + int num_transforms = min(_transform_table_size, (long)table->get_num_transforms()); + for (; i < num_transforms; ++i) { +#ifdef STDFLOAT_DOUBLE + LMatrix4 matrix; + table->get_transform(i)->get_matrix(matrix); + matrices[i] = LCAST(float, matrix); +#else + table->get_transform(i)->get_matrix(matrices[i]); +#endif + } + } + for (; i < _transform_table_size; ++i) { + matrices[i] = LMatrix4f::ident_mat(); + } + + cgGLSetMatrixParameterArrayfc(_transform_table_param, 0, + _transform_table_size, (float *)matrices); +} + +//////////////////////////////////////////////////////////////////// +// Function: CgGLShaderContext::update_slider_table +// Access: Public +// Description: Changes the active slider table, used for hardware +// skinning. +//////////////////////////////////////////////////////////////////// +void CLP(CgShaderContext):: +update_slider_table(const SliderTable *table) { + float *sliders = (float *)alloca(_slider_table_size * 4); + memset(sliders, 0, _slider_table_size * 4); + + if (table != NULL) { + int num_sliders = min(_slider_table_size, (long)table->get_num_sliders()); + for (int i = 0; i < num_sliders; ++i) { + sliders[i] = table->get_slider(i)->get_slider(); + } + } + + cgGLSetParameterArray4f(_slider_table_param, 0, _slider_table_size, sliders); +} + //////////////////////////////////////////////////////////////////// // Function: GLCgShaderContext::disable_shader_vertex_arrays // Access: Public @@ -396,27 +676,35 @@ disable_shader_vertex_arrays() { return; } - for (int i=0; i<(int)_shader->_var_spec.size(); i++) { - CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno]; - if (p == 0) continue; + for (int i = 0; i < (int)_shader->_var_spec.size(); ++i) { + GLint p = _shader->_var_spec[i]._id._seqno; - if (cgGetParameterBaseResource(p) == CG_ATTR0) { - int index = cgGetParameterResourceIndex(p); - if (index >= 8) { - _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (index - 8)); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - } else if (index == 0) { - glDisableClientState(GL_VERTEX_ARRAY); - - } else if (index == 2) { - glDisableClientState(GL_NORMAL_ARRAY); - - } else if (index == 3) { - glDisableClientState(GL_COLOR_ARRAY); + if (p >= 0) { + _glgsg->_glDisableVertexAttribArray(p); + if (_has_divisor) { + _glgsg->_glVertexAttribDivisor(p, 0); } } else { - cgGLDisableClientState(p); + switch (p) { + case CA_unknown: + break; + case CA_vertex: + glDisableClientState(GL_VERTEX_ARRAY); + break; + case CA_normal: + glDisableClientState(GL_NORMAL_ARRAY); + break; + case CA_color: + glDisableClientState(GL_COLOR_ARRAY); + break; + case CA_secondary_color: + glDisableClientState(GL_SECONDARY_COLOR_ARRAY); + break; + default: + _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (p - CA_texcoord)); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + break; + } } } @@ -458,12 +746,9 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { int start, stride, num_values; int nvarying = _shader->_var_spec.size(); for (int i = 0; i < nvarying; ++i) { - if (_cg_parameter_map[_shader->_var_spec[i]._id._seqno] == 0) { - continue; - } - - InternalName *name = _shader->_var_spec[i]._name; - int texslot = _shader->_var_spec[i]._append_uv; + const Shader::ShaderVarSpec &bind = _shader->_var_spec[i]; + InternalName *name = bind._name; + int texslot = bind._append_uv; if (texslot >= 0 && texslot < _glgsg->_state_texture->get_num_on_stages()) { TextureStage *stage = _glgsg->_state_texture->get_on_stage(texslot); InternalName *texname = stage->get_texcoord_name(); @@ -474,84 +759,146 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { name = name->append(texname->get_basename()); } } - if (_glgsg->_data_reader->get_array_info(name, - array_reader, num_values, numeric_type, - start, stride)) { + GLint p = bind._id._seqno; + + // Don't apply vertex colors if they are disabled with a ColorAttrib. + int num_elements, element_stride, divisor; + bool normalized; + if ((p != _color_attrib_index || _glgsg->_vertex_colors_enabled) && + _glgsg->_data_reader->get_array_info(name, array_reader, + num_values, numeric_type, + normalized, start, stride, divisor, + num_elements, element_stride)) { const unsigned char *client_pointer; if (!_glgsg->setup_array_data(client_pointer, array_reader, force)) { return false; } + client_pointer += start; - CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno]; + // We don't use cgGLSetParameterPointer because it is very buggy and + // limited in the options we can set.start + GLenum type = _glgsg->get_numeric_type(numeric_type); + if (p >= 0) { + _glgsg->_glEnableVertexAttribArray(p); - if (numeric_type == GeomEnums::NT_packed_dabc) { - // Yes, this is a thing. - num_values = GL_BGRA; - } + if (bind._integer) { + _glgsg->_glVertexAttribIPointer(p, num_values, type, + stride, client_pointer); + } else if (numeric_type == GeomEnums::NT_packed_dabc) { + // GL_BGRA is a special accepted value available since OpenGL 3.2. + // It requires us to pass GL_TRUE for normalized. + _glgsg->_glVertexAttribPointer(p, GL_BGRA, GL_UNSIGNED_BYTE, + GL_TRUE, stride, client_pointer); + } else { + _glgsg->_glVertexAttribPointer(p, num_values, type, + normalized, stride, client_pointer); + } - // cgGLSetParameterPointer is just stupidly bugged on every level. - // Sigh. This seems to work on both NVIDIA and AMD cards now. - if (cgGetParameterBaseResource(p) == CG_ATTR0) { - int index = cgGetParameterResourceIndex(p); - switch (index) { - case 0: // gl_Vertex - glVertexPointer(num_values, _glgsg->get_numeric_type(numeric_type), - stride, client_pointer + start); + if (_glgsg->_supports_vertex_attrib_divisor) { + _glgsg->_glVertexAttribDivisor(p, divisor); + _has_divisor = true; + } + + } else { + // It's a conventional vertex attribute. Ugh. + switch (p) { + case CA_unknown: + break; + + case CA_vertex: + glVertexPointer(num_values, type, stride, client_pointer); glEnableClientState(GL_VERTEX_ARRAY); break; - case 2: // gl_Normal - glNormalPointer(_glgsg->get_numeric_type(numeric_type), - stride, client_pointer + start); + case CA_normal: + glNormalPointer(type, stride, client_pointer); glEnableClientState(GL_NORMAL_ARRAY); break; - case 3: // gl_Color - glColorPointer(num_values, _glgsg->get_numeric_type(numeric_type), - stride, client_pointer + start); + case CA_color: + glColorPointer(num_values, type, stride, client_pointer); glEnableClientState(GL_COLOR_ARRAY); break; - case 4: // gl_SecondaryColor - //glSecondaryColorPointer(num_values, _glgsg->get_numeric_type(numeric_type), - // stride, client_pointer + start); - //glEnableClientState(GL_SECONDARY_COLOR_ARRAY); - //break; - case 5: // gl_FogCoord - case 6: // PSIZE? - case 7: // BLENDINDICES? - case 1: // glWeightPointerARB? - GLCAT.error() - << "Unable to bind " << *name << " to " - << cgGetParameterResourceName(p) << "\n"; + case CA_secondary_color: + _glgsg->_glSecondaryColorPointer(num_values, type, + stride, client_pointer); + glEnableClientState(GL_SECONDARY_COLOR_ARRAY); break; default: - _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (index - 8)); - glTexCoordPointer(num_values, _glgsg->get_numeric_type(numeric_type), - stride, client_pointer + start); + _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (p - CA_texcoord)); + glTexCoordPointer(num_values, type, stride, client_pointer); glEnableClientState(GL_TEXTURE_COORD_ARRAY); break; } - } else { - if (name == InternalName::get_normal() && num_values == 4) { - // In some cases, the normals are aligned to 4 values. We tell - // it to use three values exactly, otherwise we get the error: - // An unsupported GL extension was required to perform this operation. - num_values = 3; - } - cgGLSetParameterPointer(p, num_values, - _glgsg->get_numeric_type(numeric_type), - stride, client_pointer + start); - cgGLEnableClientState(p); } } else { - CGparameter p = _cg_parameter_map[_shader->_var_spec[i]._id._seqno]; - cgGLDisableClientState(p); + // There is no vertex column with this name; disable the attribute array. + if (p == 0) { + //NOTE: if we disable attribute 0 in compatibility profile, the object + // will disappear. In GLSL we fix this by forcing the vertex column + // to be at 0, but we don't have control over that with Cg. So, we + // work around this by just binding something silly to 0. + // This breaks flat colors, but it's better than invisible objects? + _glgsg->_glEnableVertexAttribArray(0); + if (bind._integer) { + _glgsg->_glVertexAttribIPointer(0, 4, GL_INT, 0, 0); + } else { + _glgsg->_glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); + } + + } else if (p > 0) { + _glgsg->_glDisableVertexAttribArray(p); + + if (p == _color_attrib_index) { +#ifdef STDFLOAT_DOUBLE + _glgsg->_glVertexAttrib4dv(p, _glgsg->_scene_graph_color.get_data()); +#else + _glgsg->_glVertexAttrib4fv(p, _glgsg->_scene_graph_color.get_data()); +#endif + } + } else { + switch (p) { + case CA_unknown: + break; + case CA_vertex: + glDisableClientState(GL_VERTEX_ARRAY); + break; + case CA_normal: + glDisableClientState(GL_NORMAL_ARRAY); + break; + case CA_color: + glDisableClientState(GL_COLOR_ARRAY); +#ifdef STDFLOAT_DOUBLE + glColor4dv(_glgsg->_scene_graph_color.get_data()); +#else + glColor4fv(_glgsg->_scene_graph_color.get_data()); +#endif + break; + case CA_secondary_color: + glDisableClientState(GL_SECONDARY_COLOR_ARRAY); + break; + default: + _glgsg->_glClientActiveTexture(GL_TEXTURE0 + (p - CA_texcoord)); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + break; + } + } } } } + if (_transform_table_param) { + const TransformTable *table = _glgsg->_data_reader->get_transform_table(); + update_transform_table(table); + } + + if (_slider_table_param) { + const SliderTable *table = _glgsg->_data_reader->get_slider_table(); + update_slider_table(table); + } + cg_report_errors(); _glgsg->report_my_gl_errors(); diff --git a/panda/src/glstuff/glCgShaderContext_src.h b/panda/src/glstuff/glCgShaderContext_src.h index 16dd9bcdb2..da1f0bea78 100644 --- a/panda/src/glstuff/glCgShaderContext_src.h +++ b/panda/src/glstuff/glCgShaderContext_src.h @@ -41,6 +41,8 @@ public: void bind(bool reissue_parameters = true); void unbind(); void issue_parameters(int altered); + void update_transform_table(const TransformTable *table); + void update_slider_table(const SliderTable *table); void disable_shader_vertex_arrays(); bool update_shader_vertex_arrays(ShaderContext *prev, bool force); void disable_shader_texture_bindings(); @@ -50,13 +52,32 @@ public: INLINE bool uses_custom_vertex_arrays(void); INLINE bool uses_custom_texture_bindings(void); + // Special values for location to indicate conventional attrib slots. + enum ConventionalAttrib { + CA_unknown = -1, + CA_vertex = -2, + CA_normal = -3, + CA_color = -4, + CA_secondary_color = -5, + CA_texcoord = -32, + }; + private: CGprogram _cg_program; + GLuint _glsl_program; + + GLint _color_attrib_index; + CGparameter _transform_table_param; + CGparameter _slider_table_param; + long _transform_table_size; + long _slider_table_size; pvector _cg_parameter_map; CLP(GraphicsStateGuardian) *_glgsg; + bool _has_divisor; + void release_resources(); public: diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index c330776394..47a6910216 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -599,6 +599,17 @@ reset() { } #endif +#ifndef OPENGLES + if (is_at_least_gl_version(1, 4)) { + _glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC) + get_extension_func("glSecondaryColorPointer"); + + } else if (has_extension("GL_EXT_secondary_color")) { + _glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC) + get_extension_func("glSecondaryColorPointerEXT"); + } +#endif + #ifdef OPENGLES_2 _supports_vertex_blend = false; #else @@ -7121,6 +7132,7 @@ get_extension_func(const char *name) { #endif #ifdef EXPECT_GL_VERSION_1_4 { "glPointParameterfv", (void *)&glPointParameterfv }, + { "glSecondaryColorPointer", (void *)&glSecondaryColorPointer }, #endif #ifdef EXPECT_GL_VERSION_1_5 { "glBeginQuery", (void *)&glBeginQuery }, diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index d65c6477af..08753fd7fb 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -103,6 +103,7 @@ typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); @@ -662,6 +663,10 @@ public: bool _explicit_primitive_restart; #endif +#ifndef OPENGLES + PFNGLSECONDARYCOLORPOINTERPROC _glSecondaryColorPointer; +#endif + bool _supports_vertex_blend; PFNGLWEIGHTPOINTERARBPROC _glWeightPointer; PFNGLVERTEXBLENDARBPROC _glVertexBlend; diff --git a/panda/src/gobj/shader.I b/panda/src/gobj/shader.I index 1c00d40197..eccb05d79f 100644 --- a/panda/src/gobj/shader.I +++ b/panda/src/gobj/shader.I @@ -49,7 +49,7 @@ get_filename(const ShaderType &type) const { } else { // Um, better than nothing? - return _filename._vertex; + return _filename._fragment; } } diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index 68bf6ebd1c..8f1cc70d33 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -553,6 +553,7 @@ cg_recurse_parameters(CGparameter parameter, const ShaderType &type, ShaderArgClass arg_subclass = arg_class; CGenum vbl = cgGetParameterVariability(parameter); + CGtype base_type = cgGetParameterBaseType(parameter); if ((vbl==CG_VARYING)||(vbl==CG_UNIFORM)) { switch (cgGetParameterType(parameter)) { @@ -567,16 +568,28 @@ cg_recurse_parameters(CGparameter parameter, const ShaderType &type, arg_dim[0] = cgGetArraySize(parameter, 0); + // Fall through default: { arg_dim[1] = cgGetParameterRows(parameter); arg_dim[2] = cgGetParameterColumns(parameter); - ShaderArgId id; - id._name = cgGetParameterName(parameter); - id._type = type; - id._seqno = -1; - success &= compile_parameter(id, arg_class, arg_subclass, arg_type, - arg_dir, (vbl == CG_VARYING), arg_dim, shader_cat.get_safe_ptr()); break; + ShaderArgInfo p; + p._id._name = cgGetParameterName(parameter); + p._id._type = type; + p._id._seqno = -1; + p._class = arg_class; + p._subclass = arg_subclass; + p._type = arg_type; + p._direction = arg_dir; + p._varying = (vbl == CG_VARYING); + p._integer = (base_type == CG_UINT || base_type == CG_INT || + base_type == CG_ULONG || base_type == CG_LONG || + base_type == CG_USHORT || base_type == CG_SHORT || + base_type == CG_UCHAR || base_type == CG_CHAR); + p._cat = shader_cat.get_safe_ptr(); + + success &= compile_parameter(p, arg_dim); + break; } } } @@ -601,24 +614,7 @@ cg_recurse_parameters(CGparameter parameter, const ShaderType &type, // an error message onto the error messages. //////////////////////////////////////////////////////////////////// bool Shader:: -compile_parameter(const ShaderArgId &arg_id, - const ShaderArgClass &arg_class, - const ShaderArgClass &arg_subclass, - const ShaderArgType &arg_type, - const ShaderArgDir &arg_direction, - bool arg_varying, - int *arg_dim, - NotifyCategory *arg_cat) -{ - ShaderArgInfo p; - p._id = arg_id; - p._class = arg_class; - p._subclass = arg_subclass; - p._type = arg_type; - p._direction = arg_direction; - p._varying = arg_varying; - p._cat = arg_cat; - +compile_parameter(ShaderArgInfo &p, int *arg_dim) { if (p._id._name.size() == 0) return true; if (p._id._name[0] == '$') return true; @@ -650,9 +646,9 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderVarSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._append_uv = -1; - bind._integer = false; + bind._integer = p._integer; if (pieces.size() == 2) { if (pieces[1] == "position") { @@ -685,6 +681,24 @@ compile_parameter(const ShaderArgId &arg_id, _var_spec.push_back(bind); return true; } + } else if (pieces.size() == 3) { + if (pieces[1] == "transform") { + if (pieces[2] == "blend") { + bind._name = InternalName::get_transform_blend(); + _var_spec.push_back(bind); + return true; + } + if (pieces[2] == "index") { + bind._name = InternalName::get_transform_index(); + _var_spec.push_back(bind); + return true; + } + if (pieces[2] == "weight") { + bind._name = InternalName::get_transform_weight(); + _var_spec.push_back(bind); + return true; + } + } } bind._name = InternalName::get_root(); @@ -793,7 +807,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._func = SMF_compose; int next = 1; @@ -856,7 +870,7 @@ compile_parameter(const ShaderArgId &arg_id, if (!cp_errchk_parameter_float(p,16,16)) { return false; } - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_transpose; bind._func = SMF_first; bind._part[0] = SMO_attr_material; @@ -867,7 +881,7 @@ compile_parameter(const ShaderArgId &arg_id, if (!cp_errchk_parameter_float(p,3,4)) { return false; } - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_attr_color; @@ -878,7 +892,7 @@ compile_parameter(const ShaderArgId &arg_id, if (!cp_errchk_parameter_float(p,3,4)) { return false; } - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_attr_colorscale; @@ -889,7 +903,7 @@ compile_parameter(const ShaderArgId &arg_id, if (!cp_errchk_parameter_float(p,3,4)) { return false; } - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_attr_fog; @@ -900,7 +914,7 @@ compile_parameter(const ShaderArgId &arg_id, if (!cp_errchk_parameter_float(p,3,4)) { return false; } - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_attr_fogcolor; @@ -940,7 +954,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_alight_x; @@ -961,7 +975,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_satten_x; @@ -981,7 +995,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_transpose; int next = 1; pieces.push_back(""); @@ -1023,7 +1037,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_whole; bind._func = SMF_first; bind._part[0] = SMO_texmat_x; @@ -1044,7 +1058,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_plane_x; @@ -1065,7 +1079,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_clipplane_x; @@ -1087,7 +1101,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[1] = SMO_identity; @@ -1136,7 +1150,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderTexSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._name = 0; bind._stage = atoi(pieces[1].c_str()); switch (p._type) { @@ -1168,7 +1182,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_texpad_x; @@ -1188,7 +1202,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; } ShaderMatSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._piece = SMP_row3; bind._func = SMF_first; bind._part[0] = SMO_texpix_x; @@ -1200,6 +1214,11 @@ compile_parameter(const ShaderArgId &arg_id, return true; } + if (pieces[0] == "tbl") { + // Handled elsewhere. + return true; + } + if (pieces[0] == "l") { // IMPLEMENT THE ERROR CHECKING return true; // Cg handles this automatically. @@ -1235,7 +1254,7 @@ compile_parameter(const ShaderArgId &arg_id, return false; ShaderPtrSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._arg = kinputname; bind._info = p; bind._dep[0] = SSD_general | SSD_shaderinputs; @@ -1253,7 +1272,7 @@ compile_parameter(const ShaderArgId &arg_id, switch (p._type) { case SAT_sampler1d: { ShaderTexSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._name = kinputname; bind._desired_type = Texture::TT_1d_texture; _tex_spec.push_back(bind); @@ -1261,7 +1280,7 @@ compile_parameter(const ShaderArgId &arg_id, } case SAT_sampler2d: { ShaderTexSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._name = kinputname; bind._desired_type = Texture::TT_2d_texture; _tex_spec.push_back(bind); @@ -1269,7 +1288,7 @@ compile_parameter(const ShaderArgId &arg_id, } case SAT_sampler3d: { ShaderTexSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._name = kinputname; bind._desired_type = Texture::TT_3d_texture; _tex_spec.push_back(bind); @@ -1277,7 +1296,7 @@ compile_parameter(const ShaderArgId &arg_id, } case SAT_sampler2dArray: { ShaderTexSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._name = kinputname; bind._desired_type = Texture::TT_2d_texture_array; _tex_spec.push_back(bind); @@ -1285,7 +1304,7 @@ compile_parameter(const ShaderArgId &arg_id, } case SAT_samplercube: { ShaderTexSpec bind; - bind._id = arg_id; + bind._id = p._id; bind._name = kinputname; bind._desired_type = Texture::TT_cube_map; _tex_spec.push_back(bind); @@ -1385,7 +1404,8 @@ cg_parameter_type(CGparameter p) { default: return SAT_unknown; } case CG_PARAMETERCLASS_ARRAY: return SAT_unknown; - default: return SAT_unknown; + default: + return SAT_unknown; } } @@ -1531,6 +1551,12 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps, if (shader_cat.is_debug()) { shader_cat.debug() << "Compilation with active profile failed: " << cgGetErrorString(err) << "\n"; + if (err == CG_COMPILER_ERROR) { + const char *listing = cgGetLastListing(context); + if (listing != NULL) { + shader_cat.debug(false) << listing; + } + } } } @@ -1632,19 +1658,19 @@ cg_compile_shader(const ShaderCaps &caps, CGcontext context) { shader_cat.debug() << "Cg vertex profile: " << cgGetProfileString((CGprofile)_cg_vprofile) << "\n"; vertex_program = cgGetProgramString(_cg_vprogram, CG_COMPILED_PROGRAM); - shader_cat.debug() << vertex_program << "\n"; + shader_cat.spam() << vertex_program << "\n"; } if (_cg_fprogram != 0) { shader_cat.debug() << "Cg fragment profile: " << cgGetProfileString((CGprofile)_cg_fprofile) << "\n"; fragment_program = cgGetProgramString(_cg_fprogram, CG_COMPILED_PROGRAM); - shader_cat.debug() << fragment_program << "\n"; + shader_cat.spam() << fragment_program << "\n"; } if (_cg_gprogram != 0) { shader_cat.debug() << "Cg geometry profile: " << cgGetProfileString((CGprofile)_cg_gprofile) << "\n"; geometry_program = cgGetProgramString(_cg_gprogram, CG_COMPILED_PROGRAM); - shader_cat.debug() << geometry_program << "\n"; + shader_cat.spam() << geometry_program << "\n"; } } @@ -1745,17 +1771,18 @@ cg_analyze_shader(const ShaderCaps &caps) { } } - // Assign sequence numbers to all parameters. + // Assign sequence numbers to all parameters. GLCgShaderContext relies + // on the fact that the varyings start at seqno 0. int seqno = 0; + for (int i=0; i<(int)_var_spec.size(); i++) { + _var_spec[i]._id._seqno = seqno++; + } for (int i=0; i<(int)_mat_spec.size(); i++) { _mat_spec[i]._id._seqno = seqno++; } for (int i=0; i<(int)_tex_spec.size(); i++) { _tex_spec[i]._id._seqno = seqno++; } - for (int i=0; i<(int)_var_spec.size(); i++) { - _var_spec[i]._id._seqno = seqno++; - } for (int i=0; i<(int)_ptr_spec.size(); i++) { _ptr_spec[i]._id._seqno = seqno++; @@ -1955,7 +1982,7 @@ cg_compile_for(const ShaderCaps &caps, CGcontext context, const char *resource = cgGetParameterResourceName(p); if (resource != NULL) { shader_cat.debug() << "Texture parameter " << id._name - << " is bound to resource " << resource << "\n"; + << " is bound to resource " << resource << "\n"; } } map[id._seqno] = p; @@ -1967,48 +1994,18 @@ cg_compile_for(const ShaderCaps &caps, CGcontext context, const char *resource = cgGetParameterResourceName(p); if (shader_cat.is_debug() && resource != NULL) { - shader_cat.debug() - << "Varying parameter " << id._name << " is bound to resource " - << cgGetParameterResourceName(p) << "\n"; + if (cgGetParameterResource(p) == CG_GLSL_ATTRIB) { + shader_cat.debug() + << "Varying parameter " << id._name << " is bound to GLSL attribute " + << resource << "\n"; + } else { + shader_cat.debug() + << "Varying parameter " << id._name << " is bound to resource " + << resource << " (" << cgGetParameterResource(p) + << ", index " << cgGetParameterResourceIndex(p) << ")\n"; + } } - if (cgGetParameterBaseResource(p) == CG_UNDEFINED) { - // I really don't know what this means, but it happens when I - // use the NORMAL0 semantic instead of NORMAL, or POSITION0 - // instead of POSITION, etc. Not catching this results in a - // continuous stream of errors at the renderer side. - shader_cat.error() - << "Varying parameter " << id._name; - - const char *semantic = cgGetParameterSemantic(p); - if (semantic != NULL) { - shader_cat.error(false) << " : " << semantic; - } - if (resource != NULL) { - shader_cat.error(false) << " (bound to resource " << resource << ")"; - } - shader_cat.error(false) << " is invalid!\n"; - -#ifndef NDEBUG - // Let's try to give the developer a hint... - if (semantic != NULL) { - if (strcmp(semantic, "POSITION0") == 0) { - shader_cat.error() << "Try using the semantic POSITION instead of POSITION0.\n"; - } else if (strcmp(semantic, "NORMAL0") == 0) { - shader_cat.error() << "Try using the semantic NORMAL instead of NORMAL0.\n"; - } else if (strcmp(semantic, "DIFFUSE0") == 0) { - shader_cat.error() << "Try using the semantic DIFFUSE instead of DIFFUSE0.\n"; - } else if (strcmp(semantic, "SPECULAR0") == 0) { - shader_cat.error() << "Try using the semantic SPECULAR instead of SPECULAR0.\n"; - } else if (strcmp(semantic, "FOGCOORD0") == 0) { - shader_cat.error() << "Try using the semantic FOGCOORD instead of FOGCOORD0.\n"; - } else if (strcmp(semantic, "PSIZE0") == 0) { - shader_cat.error() << "Try using the semantic PSIZE instead of PSIZE0.\n"; - } - } -#endif // NDEBUG - p = 0; - } map[id._seqno] = p; } @@ -2059,9 +2056,9 @@ Shader(ShaderLanguage lang) : _cg_fprofile = CG_PROFILE_UNKNOWN; _cg_gprofile = CG_PROFILE_UNKNOWN; if (_default_caps._ultimate_vprofile == 0 || _default_caps._ultimate_vprofile == CG_PROFILE_UNKNOWN) { - _default_caps._active_vprofile = CG_PROFILE_UNKNOWN; - _default_caps._active_fprofile = CG_PROFILE_UNKNOWN; - _default_caps._active_gprofile = CG_PROFILE_UNKNOWN; + _default_caps._active_vprofile = CG_PROFILE_GENERIC; + _default_caps._active_fprofile = CG_PROFILE_GENERIC; + _default_caps._active_gprofile = CG_PROFILE_GENERIC; _default_caps._ultimate_vprofile = cgGetProfile("glslv"); _default_caps._ultimate_fprofile = cgGetProfile("glslf"); _default_caps._ultimate_gprofile = cgGetProfile("glslg"); diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index 45bcab70f9..e56960b1f5 100644 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -297,6 +297,7 @@ public: ShaderArgType _type; ShaderArgDir _direction; bool _varying; + bool _integer; NotifyCategory *_cat; }; @@ -478,14 +479,7 @@ public: bool &success); #endif - bool compile_parameter(const ShaderArgId &arg_id, - const ShaderArgClass &arg_class, - const ShaderArgClass &arg_subclass, - const ShaderArgType &arg_type, - const ShaderArgDir &arg_direction, - bool arg_varying, - int *arg_dim, - NotifyCategory *arg_cat); + bool compile_parameter(ShaderArgInfo &p, int *arg_dim); void clear_parameters(); diff --git a/panda/src/pgraph/cullableObject.cxx b/panda/src/pgraph/cullableObject.cxx index 14a6bad1d0..b842850db7 100644 --- a/panda/src/pgraph/cullableObject.cxx +++ b/panda/src/pgraph/cullableObject.cxx @@ -18,6 +18,7 @@ #include "colorAttrib.h" #include "texGenAttrib.h" #include "textureAttrib.h" +#include "shaderAttrib.h" #include "renderState.h" #include "clockObject.h" #include "cullTraverser.h" @@ -128,6 +129,17 @@ munge_geom(GraphicsStateGuardianBase *gsg, return false; } + // If we have prepared it for skinning via the shader generator, + // mark a flag on the state so that the shader generator will do this. + // We should probably find a cleaner way to do this. + const ShaderAttrib *sattr; + if (_state->get_attrib(sattr) && sattr->auto_shader()) { + GeomVertexDataPipelineReader data_reader(_munged_data, current_thread); + if (data_reader.get_format()->get_animation().get_animation_type() == Geom::AT_hardware) { + _state = _state->set_attrib(sattr->set_flag(ShaderAttrib::F_hardware_skinning, true)); + } + } + StateMunger *state_munger; DCAST_INTO_R(state_munger, munger, false); _state = state_munger->munge_state(_state); diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index e1bc57d632..4a59fb5302 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -271,8 +271,8 @@ private: // The code to manage this map lives in // GraphicsStateGuardian::get_geom_munger(). typedef pmap Mungers; - Mungers _mungers; - Mungers::const_iterator _last_mi; + mutable Mungers _mungers; + mutable Mungers::const_iterator _last_mi; // This is used to mark nodes as we visit them to detect cycles. UpdateSeq _cycle_detect; diff --git a/panda/src/pgraph/shaderAttrib.cxx b/panda/src/pgraph/shaderAttrib.cxx index 1cfb112b93..82f05cfbad 100644 --- a/panda/src/pgraph/shaderAttrib.cxx +++ b/panda/src/pgraph/shaderAttrib.cxx @@ -702,6 +702,7 @@ get_auto_shader_attrib_impl(const RenderState *state) const { attrib->_auto_gloss_on = _auto_gloss_on; attrib->_auto_ramp_on = _auto_ramp_on; attrib->_auto_shadow_on = _auto_shadow_on; + attrib->_flags = _flags; return return_new(attrib); } diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 8e4a931076..488fe84c95 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -92,19 +92,25 @@ reset_register_allocator() { //////////////////////////////////////////////////////////////////// const char *ShaderGenerator:: alloc_vreg() { + // The ATTR# input sseem to map to generic vertex attributes in + // both arbvp1 and glslv, which behave more consistently. switch (_vtregs_used) { - case 0: _vtregs_used += 1; return "TEXCOORD0"; - case 1: _vtregs_used += 1; return "TEXCOORD1"; - case 2: _vtregs_used += 1; return "TEXCOORD2"; - case 3: _vtregs_used += 1; return "TEXCOORD3"; - case 4: _vtregs_used += 1; return "TEXCOORD4"; - case 5: _vtregs_used += 1; return "TEXCOORD5"; - case 6: _vtregs_used += 1; return "TEXCOORD6"; - case 7: _vtregs_used += 1; return "TEXCOORD7"; + case 0: _vtregs_used += 1; return "ATTR8"; + case 1: _vtregs_used += 1; return "ATTR9"; + case 2: _vtregs_used += 1; return "ATTR10"; + case 3: _vtregs_used += 1; return "ATTR11"; + case 4: _vtregs_used += 1; return "ATTR12"; + case 5: _vtregs_used += 1; return "ATTR13"; + case 6: _vtregs_used += 1; return "ATTR14"; + case 7: _vtregs_used += 1; return "ATTR15"; } switch (_vcregs_used) { - case 0: _vcregs_used += 1; return "COLOR0"; - case 1: _vcregs_used += 1; return "COLOR1"; + case 0: _vcregs_used += 1; return "ATTR3"; + case 1: _vcregs_used += 1; return "ATTR4"; + case 2: _vcregs_used += 1; return "ATTR5"; + case 3: _vcregs_used += 1; return "ATTR6"; + case 4: _vcregs_used += 1; return "ATTR7"; + case 5: _vcregs_used += 1; return "ATTR1"; } // These don't exist in arbvp1, though they're reportedly // supported by other profiles. @@ -615,6 +621,7 @@ update_shadow_buffer(NodePath light_np) { // - texmatrix // - 1D/2D/3D textures, cube textures, 2D tex arrays // - linear/exp/exp2 fog +// - animation // // Not yet supported: // - dot3_rgb and dot3_rgba combine modes @@ -624,7 +631,7 @@ update_shadow_buffer(NodePath light_np) { // //////////////////////////////////////////////////////////////////// CPT(ShaderAttrib) ShaderGenerator:: -synthesize_shader(const RenderState *rs) { +synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) { analyze_renderstate(rs); reset_register_allocator(); @@ -706,7 +713,7 @@ synthesize_shader(const RenderState *rs) { } } if (_vertex_colors) { - text << "\t in float4 vtx_color : COLOR0,\n"; + text << "\t in float4 vtx_color : ATTR3,\n"; text << "\t out float4 l_color : COLOR0,\n"; } if (_need_world_position || _need_world_normal) { @@ -733,7 +740,7 @@ synthesize_shader(const RenderState *rs) { text << "\t out float4 l_eye_normal : " << eye_normal_freg << ",\n"; } if (_map_index_height >= 0 || _need_world_normal || _need_eye_normal) { - text << "\t in float3 vtx_normal : NORMAL,\n"; + text << "\t in float3 vtx_normal : ATTR2,\n"; } if (_map_index_height >= 0) { text << "\t uniform float4 mspos_view,\n"; @@ -765,11 +772,50 @@ synthesize_shader(const RenderState *rs) { hpos_freg = alloc_freg(); text << "\t out float4 l_hpos : " << hpos_freg << ",\n"; } - text << "\t float4 vtx_position : POSITION,\n"; + if (anim.get_animation_type() == GeomEnums::AT_hardware && + anim.get_num_transforms() > 0) { + int num_transforms; + if (anim.get_indexed_transforms()) { + num_transforms = 120; + } else { + num_transforms = anim.get_num_transforms(); + } + text << "\t uniform float4x4 tbl_transforms[" << num_transforms << "],\n"; + text << "\t in float4 vtx_transform_weight : ATTR1,\n"; + if (anim.get_indexed_transforms()) { + text << "\t in uint4 vtx_transform_index : ATTR7,\n"; + } + } + text << "\t in float4 vtx_position : ATTR0,\n"; text << "\t out float4 l_position : POSITION,\n"; text << "\t uniform float4x4 mat_modelproj\n"; text << ") {\n"; + if (anim.get_animation_type() == GeomEnums::AT_hardware && + anim.get_num_transforms() > 0) { + + if (!anim.get_indexed_transforms()) { + text << "\t const uint4 vtx_transform_index = uint4(0, 1, 2, 3);\n"; + } + + text << "\t float4x4 matrix = tbl_transforms[vtx_transform_index.x] * vtx_transform_weight.x"; + if (anim.get_num_transforms() > 1) { + text << "\n\t + tbl_transforms[vtx_transform_index.y] * vtx_transform_weight.y"; + } + if (anim.get_num_transforms() > 2) { + text << "\n\t + tbl_transforms[vtx_transform_index.z] * vtx_transform_weight.z"; + } + if (anim.get_num_transforms() > 3) { + text << "\n\t + tbl_transforms[vtx_transform_index.w] * vtx_transform_weight.w"; + } + text << ";\n"; + + text << "\t vtx_position = mul(matrix, vtx_position);\n"; + if (_need_world_normal || _need_eye_normal) { + text << "\t vtx_normal = mul((float3x3)matrix, vtx_normal);\n"; + } + } + text << "\t l_position = mul(mat_modelproj, vtx_position);\n"; if (_fog) { text << "\t l_hpos = l_position;\n"; diff --git a/panda/src/pgraphnodes/shaderGenerator.h b/panda/src/pgraphnodes/shaderGenerator.h index 8d414812de..146f115d9a 100644 --- a/panda/src/pgraphnodes/shaderGenerator.h +++ b/panda/src/pgraphnodes/shaderGenerator.h @@ -33,6 +33,7 @@ class DirectionalLight; class PointLight; class Spotlight; class LightAttrib; +class GeomVertexAnimationSpec; //////////////////////////////////////////////////////////////////// // Class : ShaderGenerator @@ -71,7 +72,8 @@ class EXPCL_PANDA_PGRAPHNODES ShaderGenerator : public TypedReferenceCount { PUBLISHED: ShaderGenerator(GraphicsStateGuardianBase *gsg, GraphicsOutputBase *host); virtual ~ShaderGenerator(); - virtual CPT(ShaderAttrib) synthesize_shader(const RenderState *rs); + virtual CPT(ShaderAttrib) synthesize_shader(const RenderState *rs, + const GeomVertexAnimationSpec &anim); protected: CPT(RenderAttrib) create_shader_attrib(const string &txt);