diff --git a/panda/src/dxgsg9/dxGeomMunger9.cxx b/panda/src/dxgsg9/dxGeomMunger9.cxx index 886ffd2af5..70b5ff8f47 100644 --- a/panda/src/dxgsg9/dxGeomMunger9.cxx +++ b/panda/src/dxgsg9/dxGeomMunger9.cxx @@ -118,7 +118,7 @@ munge_format_impl(const GeomVertexFormat *orig, if (normal_type != (const GeomVertexColumn *)NULL) { new_array_format->add_column - (InternalName::get_normal(), 3, NT_float32, C_vector); + (InternalName::get_normal(), 3, NT_float32, C_normal); new_format->remove_column(normal_type->get_name()); } @@ -227,7 +227,7 @@ premunge_format_impl(const GeomVertexFormat *orig) { if (normal_type != (const GeomVertexColumn *)NULL) { new_array_format->add_column - (InternalName::get_normal(), 3, NT_float32, C_vector); + (InternalName::get_normal(), 3, NT_float32, C_normal); new_format->remove_column(normal_type->get_name()); } diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index b472a104b6..d6b41df09a 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -2240,7 +2240,7 @@ make_vertex_data(const EggRenderState *render_state, if (vertex_pool->has_normals()) { array_format->add_column (InternalName::get_normal(), 3, - Geom::NT_stdfloat, Geom::C_vector); + Geom::NT_stdfloat, Geom::C_normal); } if (!ignore_color) { diff --git a/panda/src/gobj/geomEnums.cxx b/panda/src/gobj/geomEnums.cxx index e105b0c246..9fe394ccb9 100644 --- a/panda/src/gobj/geomEnums.cxx +++ b/panda/src/gobj/geomEnums.cxx @@ -139,6 +139,9 @@ operator << (ostream &out, GeomEnums::Contents contents) { case GeomEnums::C_matrix: return out << "matrix"; + + case GeomEnums::C_normal: + return out << "normal"; } return out << "**invalid contents (" << (int)contents << ")**"; diff --git a/panda/src/gobj/geomEnums.h b/panda/src/gobj/geomEnums.h index 62265cfc23..4cf392eb42 100644 --- a/panda/src/gobj/geomEnums.h +++ b/panda/src/gobj/geomEnums.h @@ -192,7 +192,7 @@ PUBLISHED: C_other, // Arbitrary meaning, leave it alone C_point, // A point in 3-space or 4-space C_clip_point, // A point pre-transformed into clip coordinates - C_vector, // A surface normal, tangent, or binormal + C_vector, // A surface tangent or binormal (see C_normal for normals) C_texcoord, // A texture coordinate C_color, // 3- or 4-component color, ordered R, G, B, [A] C_index, // An index value into some other table @@ -201,6 +201,10 @@ PUBLISHED: // A transformation matrix. This is typically three or four // columns, but we pretend it's only one for convenience. C_matrix, + + // A special version of C_vector that should be used for normal + // vectors, which are scaled differently from other vectors. + C_normal, }; // The type of animation data that is represented by a particular diff --git a/panda/src/gobj/geomVertexArrayFormat.cxx b/panda/src/gobj/geomVertexArrayFormat.cxx index 7f1deaff61..be82d262fd 100644 --- a/panda/src/gobj/geomVertexArrayFormat.cxx +++ b/panda/src/gobj/geomVertexArrayFormat.cxx @@ -394,8 +394,11 @@ align_columns_for_animation() { Columns::const_iterator ci; for (ci = orig_columns.begin(); ci != orig_columns.end(); ++ci) { GeomVertexColumn *column = (*ci); - if ((column->get_contents() == C_point || column->get_contents() == C_vector) && - (column->get_numeric_type() == NT_float32 || column->get_numeric_type() == NT_float64) && + if ((column->get_contents() == C_point || + column->get_contents() == C_vector || + column->get_contents() == C_normal) && + (column->get_numeric_type() == NT_float32 || + column->get_numeric_type() == NT_float64) && column->get_num_components() >= 3) { add_column(column->get_name(), 4, column->get_numeric_type(), column->get_contents(), -1, 16); } else { diff --git a/panda/src/gobj/geomVertexColumn.cxx b/panda/src/gobj/geomVertexColumn.cxx index 1698384d07..345e1660d4 100644 --- a/panda/src/gobj/geomVertexColumn.cxx +++ b/panda/src/gobj/geomVertexColumn.cxx @@ -340,6 +340,11 @@ make_packer() const { break; } return new Packer_color; + case C_normal: + if (get_num_values() != 3) { + gobj_cat.error() + << "GeomVertexColumn with contents C_normal must have 3 components!\n"; + } default: // Otherwise, we just read it as a generic value. switch (get_numeric_type()) { diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 7a265b66a0..95a4c2ab5c 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -1915,10 +1915,37 @@ do_transform_point_column(const GeomVertexFormat *format, GeomVertexRewriter &da //////////////////////////////////////////////////////////////////// void GeomVertexData:: do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &data, - const LMatrix4 &mat, int begin_row, int end_row) { + const LMatrix4 &mat, int begin_row, int end_row) { const GeomVertexColumn *data_column = data.get_column(); int num_values = data_column->get_num_values(); + LMatrix4 xform; + bool normalize = false; + if (data_column->get_contents() == C_normal) { + // This is to preserve perpendicularity to the surface. + LVecBase3 scale, shear, hpr; + if (decompose_matrix(mat.get_upper_3(), scale, shear, hpr) && + IS_NEARLY_EQUAL(scale[0], scale[1]) && + IS_NEARLY_EQUAL(scale[0], scale[2])) { + if (scale[0] == 1) { + // No scale to worry about. + xform = mat; + } else { + // Simply take the uniform scale out of the transformation. + // Not sure if it might be better to just normalize? + compose_matrix(xform, LVecBase3(1, 1, 1), shear, hpr, LVecBase3::zero()); + } + } else { + // There is a non-uniform scale, so we need to do all this to + // preserve orthogonality to the surface. + xform.invert_from(mat); + xform.transpose_in_place(); + normalize = true; + } + } else { + xform = mat; + } + if ((num_values == 3 || num_values == 4) && data_column->get_numeric_type() == NT_float32) { // The table of vectors is a table of LVector3f's or LVector4f's. @@ -1929,9 +1956,11 @@ do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &d size_t num_rows = end_row - begin_row; unsigned char *datat = data_handle->get_write_pointer(); datat += data_column->get_start() + begin_row * stride; - LMatrix4f matf = LCAST(float, mat); + LMatrix4f matf = LCAST(float, xform); - if (num_values == 3) { + if (normalize) { + table_xform_normal3f(datat, num_rows, stride, matf); + } else if (num_values == 3) { table_xform_vector3f(datat, num_rows, stride, matf); } else { table_xform_vecbase4f(datat, num_rows, stride, matf); @@ -1939,11 +1968,20 @@ do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &d } else { // Use the GeomVertexRewriter to transform the vectors. - data.set_row_unsafe(begin_row); - for (int j = begin_row; j < end_row; ++j) { - LVector3 vertex = data.get_data3(); - data.set_data3(vertex * mat); + + if (normalize) { + for (int j = begin_row; j < end_row; ++j) { + LVector3 vector = data.get_data3(); + vector *= xform; + vector.normalize(); + data.set_data3(vector); + } + } else { + for (int j = begin_row; j < end_row; ++j) { + LVector3 vector = data.get_data3(); + data.set_data3(vector * xform); + } } } } @@ -1965,6 +2003,25 @@ table_xform_point3f(unsigned char *datat, size_t num_rows, size_t stride, } } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::table_xform_normal3f +// Access: Private, Static +// Description: Transforms each of the LVector3f objects in the +// indicated table by the indicated matrix, and also +// normalizes them. +//////////////////////////////////////////////////////////////////// +void GeomVertexData:: +table_xform_normal3f(unsigned char *datat, size_t num_rows, size_t stride, + const LMatrix4f &matf) { + // We don't bother checking for the unaligned case here, because in + // practice it doesn't matter with a 3-component vector. + for (size_t i = 0; i < num_rows; ++i) { + LNormalf &vertex = *(LNormalf *)(&datat[i * stride]); + vertex *= matf; + vertex.normalize(); + } +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexData::table_xform_vector3f // Access: Private, Static diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index 9bcdd7daec..acb51b37cd 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -334,6 +334,8 @@ private: const LMatrix4 &mat, int begin_row, int end_row); static void table_xform_point3f(unsigned char *datat, size_t num_rows, size_t stride, const LMatrix4f &matf); + static void table_xform_normal3f(unsigned char *datat, size_t num_rows, + size_t stride, const LMatrix4f &matf); static void table_xform_vector3f(unsigned char *datat, size_t num_rows, size_t stride, const LMatrix4f &matf); static void table_xform_vecbase4f(unsigned char *datat, size_t num_rows, diff --git a/panda/src/gobj/geomVertexFormat.cxx b/panda/src/gobj/geomVertexFormat.cxx index b0fd53515c..ec5a29436e 100644 --- a/panda/src/gobj/geomVertexFormat.cxx +++ b/panda/src/gobj/geomVertexFormat.cxx @@ -825,6 +825,7 @@ do_register() { break; case C_vector: + case C_normal: // It's a vector. _vectors.push_back(column->get_name()); break; @@ -1017,7 +1018,7 @@ make_standard_formats() { (InternalName::get_vertex(), 3, NT_stdfloat, C_point, InternalName::get_normal(), 3, - NT_stdfloat, C_vector)); + NT_stdfloat, C_normal)); _v3t2 = register_format(new GeomVertexArrayFormat (InternalName::get_vertex(), 3, @@ -1029,7 +1030,7 @@ make_standard_formats() { (InternalName::get_vertex(), 3, NT_stdfloat, C_point, InternalName::get_normal(), 3, - NT_stdfloat, C_vector, + NT_stdfloat, C_normal, InternalName::get_texcoord(), 2, NT_stdfloat, C_texcoord)); @@ -1044,7 +1045,7 @@ make_standard_formats() { (InternalName::get_vertex(), 3, NT_stdfloat, C_point, InternalName::get_normal(), 3, - NT_stdfloat, C_vector, + NT_stdfloat, C_normal, InternalName::get_color(), 1, NT_packed_dabc, C_color)); @@ -1060,7 +1061,7 @@ make_standard_formats() { (InternalName::get_vertex(), 3, NT_stdfloat, C_point, InternalName::get_normal(), 3, - NT_stdfloat, C_vector, + NT_stdfloat, C_normal, InternalName::get_color(), 1, NT_packed_dabc, C_color, InternalName::get_texcoord(), 2, @@ -1079,7 +1080,7 @@ make_standard_formats() { (InternalName::get_vertex(), 3, NT_stdfloat, C_point, InternalName::get_normal(), 3, - NT_stdfloat, C_vector, + NT_stdfloat, C_normal, InternalName::get_color(), 4, NT_uint8, C_color)); @@ -1095,7 +1096,7 @@ make_standard_formats() { (InternalName::get_vertex(), 3, NT_stdfloat, C_point, InternalName::get_normal(), 3, - NT_stdfloat, C_vector, + NT_stdfloat, C_normal, InternalName::get_color(), 4, NT_uint8, C_color, InternalName::get_texcoord(), 2, diff --git a/panda/src/grutil/geoMipTerrain.cxx b/panda/src/grutil/geoMipTerrain.cxx index 145ba37131..c236564172 100644 --- a/panda/src/grutil/geoMipTerrain.cxx +++ b/panda/src/grutil/geoMipTerrain.cxx @@ -63,7 +63,7 @@ generate_block(unsigned short mx, array->add_column(InternalName::get_texcoord(), 2, Geom::NT_stdfloat, Geom::C_texcoord); array->add_column(InternalName::get_normal(), 3, - Geom::NT_stdfloat, Geom::C_vector); + Geom::NT_stdfloat, Geom::C_normal); PT(GeomVertexFormat) format = new GeomVertexFormat(); format->add_array(array); diff --git a/panda/src/grutil/pfmVizzer.cxx b/panda/src/grutil/pfmVizzer.cxx index 1304d59bea..1d84cd6331 100644 --- a/panda/src/grutil/pfmVizzer.cxx +++ b/panda/src/grutil/pfmVizzer.cxx @@ -1062,7 +1062,7 @@ make_array_format(const VisColumns &vis_columns) const { case CT_normal3: num_components = 3; numeric_type = GeomEnums::NT_float32; - contents = GeomEnums::C_vector; + contents = GeomEnums::C_normal; break; case CT_blend1: @@ -1072,7 +1072,7 @@ make_array_format(const VisColumns &vis_columns) const { break; } nassertr(num_components != 0, NULL); - + array_format->add_column(name, num_components, numeric_type, contents); } diff --git a/panda/src/parametrics/ropeNode.cxx b/panda/src/parametrics/ropeNode.cxx index eaf0ef48ed..871931cd1b 100644 --- a/panda/src/parametrics/ropeNode.cxx +++ b/panda/src/parametrics/ropeNode.cxx @@ -282,7 +282,7 @@ get_format(bool support_normals) const { if (support_normals && get_normal_mode() == NM_vertex) { array_format->add_column (InternalName::get_normal(), 3, Geom::NT_stdfloat, - Geom::C_vector); + Geom::C_normal); } if (get_use_vertex_color()) { array_format->add_column