mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
520 lines
21 KiB
C++
520 lines
21 KiB
C++
// Filename: glGeomMunger_src.cxx
|
|
// Created by: drose (10Mar05)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "dcast.h"
|
|
|
|
TypeHandle CLP(GeomMunger)::_type_handle;
|
|
|
|
ALLOC_DELETED_CHAIN_DEF(CLP(GeomMunger));
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CLP(GeomMunger)::
|
|
CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) :
|
|
StandardMunger(gsg, state, 4, NT_uint8, C_color),
|
|
_texture(DCAST(TextureAttrib, state->get_attrib(TextureAttrib::get_class_slot()))),
|
|
_tex_gen(DCAST(TexGenAttrib, state->get_attrib(TexGenAttrib::get_class_slot())))
|
|
{
|
|
// Set a callback to unregister ourselves when either the Texture or
|
|
// the TexGen object gets deleted.
|
|
_texture.set_callback(this);
|
|
_tex_gen.set_callback(this);
|
|
|
|
_flags = 0;
|
|
|
|
if (gl_interleaved_arrays) {
|
|
_flags |= F_interleaved_arrays;
|
|
} else if (gl_parallel_arrays) {
|
|
_flags |= F_parallel_arrays;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::Destructor
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CLP(GeomMunger)::
|
|
~CLP(GeomMunger)() {
|
|
// We need to remove this pointer from all of the geom contexts that
|
|
// reference this object.
|
|
GeomContexts::iterator gci;
|
|
for (gci = _geom_contexts.begin(); gci != _geom_contexts.end(); ++gci) {
|
|
(*gci)->remove_munger(this);
|
|
}
|
|
_geom_contexts.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::wp_callback
|
|
// Access: Public, Virtual
|
|
// Description: This callback is set to be made whenever the
|
|
// associated _texture or _tex_gen attributes are
|
|
// destructed, in which case the GeomMunger is invalid
|
|
// and should no longer be used.
|
|
////////////////////////////////////////////////////////////////////
|
|
void CLP(GeomMunger)::
|
|
wp_callback(void *) {
|
|
unregister_myself();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::munge_format_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Given a source GeomVertexFormat, converts it if
|
|
// necessary to the appropriate format for rendering.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(GeomVertexFormat) CLP(GeomMunger)::
|
|
munge_format_impl(const GeomVertexFormat *orig,
|
|
const GeomVertexAnimationSpec &animation) {
|
|
PT(GeomVertexFormat) new_format = new GeomVertexFormat(*orig);
|
|
new_format->set_animation(animation);
|
|
|
|
CLP(GraphicsStateGuardian) *glgsg;
|
|
DCAST_INTO_R(glgsg, get_gsg(), NULL);
|
|
|
|
#ifndef OPENGLES
|
|
// OpenGL ES 1 does, but regular OpenGL doesn't support GL_BYTE vertices
|
|
// and texture coordinates.
|
|
const GeomVertexColumn *vertex_type = orig->get_vertex_column();
|
|
if (vertex_type != (GeomVertexColumn *)NULL &&
|
|
(vertex_type->get_numeric_type() == NT_int8 ||
|
|
vertex_type->get_numeric_type() == NT_uint8)) {
|
|
int vertex_array = orig->get_array_with(InternalName::get_vertex());
|
|
|
|
PT(GeomVertexArrayFormat) new_array_format = new_format->modify_array(vertex_array);
|
|
|
|
// Replace the existing vertex format with the new format.
|
|
new_array_format->add_column
|
|
(InternalName::get_vertex(), 3, NT_int16,
|
|
C_point, vertex_type->get_start(), vertex_type->get_column_alignment());
|
|
}
|
|
|
|
// Convert packed formats that OpenGL may not understand.
|
|
for (int i = 0; i < orig->get_num_columns(); ++i) {
|
|
const GeomVertexColumn *column = orig->get_column(i);
|
|
int array = orig->get_array_with(column->get_name());
|
|
|
|
if (column->get_numeric_type() == NT_packed_dabc &&
|
|
!glgsg->_supports_packed_dabc) {
|
|
// Unpack the packed ARGB color into its four byte components.
|
|
PT(GeomVertexArrayFormat) array_format = new_format->modify_array(array);
|
|
array_format->add_column(column->get_name(), 4, NT_uint8, C_color,
|
|
column->get_start(), column->get_column_alignment());
|
|
|
|
} else if (column->get_numeric_type() == NT_packed_ufloat &&
|
|
!glgsg->_supports_packed_ufloat) {
|
|
// Unpack to three 32-bit floats. (In future, should try 16-bit float)
|
|
PT(GeomVertexArrayFormat) array_format = new_format->modify_array(array);
|
|
array_format->add_column(column->get_name(), 3, NT_float32,
|
|
column->get_contents(), column->get_start(),
|
|
column->get_column_alignment());
|
|
}
|
|
}
|
|
#endif // !OPENGLES
|
|
|
|
const GeomVertexColumn *color_type = orig->get_color_column();
|
|
if (color_type != (GeomVertexColumn *)NULL &&
|
|
color_type->get_numeric_type() == NT_packed_dabc &&
|
|
!glgsg->_supports_packed_dabc) {
|
|
// We need to convert the color format; OpenGL doesn't support the
|
|
// byte order of DirectX's packed ARGB format.
|
|
int color_array = orig->get_array_with(InternalName::get_color());
|
|
|
|
PT(GeomVertexArrayFormat) new_array_format = new_format->modify_array(color_array);
|
|
|
|
// Replace the existing color format with the new format.
|
|
new_array_format->add_column
|
|
(InternalName::get_color(), 4, NT_uint8, C_color,
|
|
color_type->get_start(), color_type->get_column_alignment());
|
|
}
|
|
|
|
if (animation.get_animation_type() == AT_hardware) {
|
|
// If we want hardware animation, we need to reserve space for the
|
|
// blend weights.
|
|
|
|
// Make sure the old weights and indices are removed, just in
|
|
// case.
|
|
new_format->remove_column(InternalName::get_transform_weight());
|
|
new_format->remove_column(InternalName::get_transform_index());
|
|
|
|
// And we don't need the transform_blend table any more.
|
|
new_format->remove_column(InternalName::get_transform_blend());
|
|
|
|
if (animation.get_num_transforms() > 1) {
|
|
PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
|
|
new_array_format->add_column
|
|
(InternalName::get_transform_weight(), animation.get_num_transforms(),
|
|
NT_stdfloat, C_other);
|
|
|
|
if (animation.get_indexed_transforms()) {
|
|
// Also, if we'll be indexing into the transform table, reserve
|
|
// space for the index.
|
|
|
|
// TODO: We should examine the maximum palette index so we can
|
|
// decide whether we need 16-bit indices. That implies saving
|
|
// the maximum palette index, presumably in the AnimationSpec.
|
|
// At the moment, I don't think any existing hardware supports
|
|
// more than 255 indices anyway.
|
|
new_array_format->add_column
|
|
(InternalName::get_transform_index(), animation.get_num_transforms(),
|
|
NT_uint8, C_index);
|
|
}
|
|
|
|
new_format->add_array(new_array_format);
|
|
}
|
|
}
|
|
|
|
CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(new_format);
|
|
|
|
if ((_flags & F_parallel_arrays) != 0) {
|
|
// Split out the interleaved array into n parallel arrays.
|
|
new_format = new GeomVertexFormat;
|
|
for (int i = 0; i < format->get_num_columns(); ++i) {
|
|
const GeomVertexColumn *column = format->get_column(i);
|
|
PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
|
|
new_array_format->add_column(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
new_format->add_array(new_array_format);
|
|
}
|
|
format = GeomVertexFormat::register_format(new_format);
|
|
|
|
} else if ((_flags & F_interleaved_arrays) != 0) {
|
|
// Combine the primary data columns into a single array.
|
|
new_format = new GeomVertexFormat(*format);
|
|
PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
|
|
|
|
const GeomVertexColumn *column = format->get_vertex_column();
|
|
if (column != (const GeomVertexColumn *)NULL) {
|
|
new_array_format->add_column
|
|
(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
new_format->remove_column(column->get_name());
|
|
}
|
|
|
|
column = format->get_normal_column();
|
|
if (column != (const GeomVertexColumn *)NULL) {
|
|
new_array_format->add_column
|
|
(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
new_format->remove_column(column->get_name());
|
|
}
|
|
|
|
column = format->get_color_column();
|
|
if (column != (const GeomVertexColumn *)NULL) {
|
|
new_array_format->add_column
|
|
(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
new_format->remove_column(column->get_name());
|
|
}
|
|
|
|
// Put only the used texture coordinates into the interleaved
|
|
// array.
|
|
if (_texture != (TextureAttrib *)NULL) {
|
|
typedef pset<const InternalName *> UsedStages;
|
|
UsedStages used_stages;
|
|
|
|
int num_stages = _texture->get_num_on_stages();
|
|
for (int i = 0; i < num_stages; ++i) {
|
|
TextureStage *stage = _texture->get_on_stage(i);
|
|
if (_tex_gen == (TexGenAttrib *)NULL ||
|
|
!_tex_gen->has_stage(stage)) {
|
|
InternalName *name = stage->get_texcoord_name();
|
|
if (used_stages.insert(name).second) {
|
|
// This is the first time we've encountered this texcoord name.
|
|
const GeomVertexColumn *texcoord_type = format->get_column(name);
|
|
|
|
if (texcoord_type != (const GeomVertexColumn *)NULL) {
|
|
new_array_format->add_column
|
|
(name, texcoord_type->get_num_values(), NT_stdfloat, C_texcoord,
|
|
-1, texcoord_type->get_column_alignment());
|
|
} else {
|
|
// We have to add something as a placeholder, even if the
|
|
// texture coordinates aren't defined.
|
|
new_array_format->add_column(name, 2, NT_stdfloat, C_texcoord);
|
|
}
|
|
new_format->remove_column(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
new_format->insert_array(0, new_array_format);
|
|
format = GeomVertexFormat::register_format(new_format);
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::premunge_format_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Given a source GeomVertexFormat, converts it if
|
|
// necessary to the appropriate format for rendering.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(GeomVertexFormat) CLP(GeomMunger)::
|
|
premunge_format_impl(const GeomVertexFormat *orig) {
|
|
PT(GeomVertexFormat) new_format = new GeomVertexFormat(*orig);
|
|
|
|
CLP(GraphicsStateGuardian) *glgsg;
|
|
DCAST_INTO_R(glgsg, get_gsg(), NULL);
|
|
|
|
#ifndef OPENGLES
|
|
// OpenGL ES 1 does, but regular OpenGL doesn't support GL_BYTE vertices
|
|
// and texture coordinates.
|
|
const GeomVertexColumn *vertex_type = orig->get_vertex_column();
|
|
if (vertex_type != (GeomVertexColumn *)NULL &&
|
|
(vertex_type->get_numeric_type() == NT_int8 ||
|
|
vertex_type->get_numeric_type() == NT_uint8)) {
|
|
int vertex_array = orig->get_array_with(InternalName::get_vertex());
|
|
|
|
PT(GeomVertexArrayFormat) new_array_format = new_format->modify_array(vertex_array);
|
|
|
|
// Replace the existing vertex format with the new format.
|
|
new_array_format->add_column
|
|
(InternalName::get_vertex(), 3, NT_int16,
|
|
C_point, vertex_type->get_start(), vertex_type->get_column_alignment());
|
|
}
|
|
|
|
// Convert packed formats that OpenGL may not understand.
|
|
for (int i = 0; i < orig->get_num_columns(); ++i) {
|
|
const GeomVertexColumn *column = orig->get_column(i);
|
|
int array = orig->get_array_with(column->get_name());
|
|
|
|
if (column->get_numeric_type() == NT_packed_dabc &&
|
|
!glgsg->_supports_packed_dabc) {
|
|
// Unpack the packed ARGB color into its four byte components.
|
|
PT(GeomVertexArrayFormat) array_format = new_format->modify_array(array);
|
|
array_format->add_column(column->get_name(), 4, NT_uint8, C_color,
|
|
column->get_start(), column->get_column_alignment());
|
|
|
|
} else if (column->get_numeric_type() == NT_packed_ufloat &&
|
|
!glgsg->_supports_packed_ufloat) {
|
|
// Unpack to three 32-bit floats. (In future, should try 16-bit float)
|
|
PT(GeomVertexArrayFormat) array_format = new_format->modify_array(array);
|
|
array_format->add_column(column->get_name(), 3, NT_float32,
|
|
column->get_contents(), column->get_start(),
|
|
column->get_column_alignment());
|
|
}
|
|
}
|
|
#endif // !OPENGLES
|
|
|
|
CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(new_format);
|
|
|
|
if ((_flags & F_parallel_arrays) != 0) {
|
|
// Split out the interleaved array into n parallel arrays.
|
|
new_format = new GeomVertexFormat;
|
|
for (int i = 0; i < format->get_num_columns(); ++i) {
|
|
const GeomVertexColumn *column = format->get_column(i);
|
|
PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
|
|
new_array_format->add_column(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
new_format->add_array(new_array_format);
|
|
}
|
|
format = GeomVertexFormat::register_format(new_format);
|
|
|
|
} else {
|
|
// Combine the primary data columns into a single array. Unlike
|
|
// the munge case, above, in the premunge case, we do this even if
|
|
// F_interleaved_arrays is not set (unless F_parallel_arrays is
|
|
// set), since the presumption is that you're more willing to pay
|
|
// the overhead of doing this step at load time than you might be
|
|
// at run time.
|
|
new_format = new GeomVertexFormat(*format);
|
|
PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat;
|
|
|
|
const GeomVertexColumn *column = format->get_vertex_column();
|
|
if (column != (const GeomVertexColumn *)NULL) {
|
|
new_array_format->add_column
|
|
(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
new_format->remove_column(column->get_name());
|
|
}
|
|
|
|
column = format->get_normal_column();
|
|
if (column != (const GeomVertexColumn *)NULL) {
|
|
new_array_format->add_column
|
|
(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
new_format->remove_column(column->get_name());
|
|
}
|
|
|
|
column = format->get_color_column();
|
|
if (column != (const GeomVertexColumn *)NULL) {
|
|
new_array_format->add_column
|
|
(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
new_format->remove_column(column->get_name());
|
|
}
|
|
|
|
// Put only the used texture coordinates into the interleaved
|
|
// array. The others will be kept around, but in a parallel
|
|
// array.
|
|
if (_texture != (TextureAttrib *)NULL) {
|
|
typedef pset<const InternalName *> UsedStages;
|
|
UsedStages used_stages;
|
|
|
|
int num_stages = _texture->get_num_on_stages();
|
|
for (int i = 0; i < num_stages; ++i) {
|
|
TextureStage *stage = _texture->get_on_stage(i);
|
|
if (_tex_gen == (TexGenAttrib *)NULL ||
|
|
!_tex_gen->has_stage(stage)) {
|
|
InternalName *name = stage->get_texcoord_name();
|
|
if (used_stages.insert(name).second) {
|
|
// This is the first time we've encountered this texcoord name.
|
|
const GeomVertexColumn *texcoord_type = format->get_column(name);
|
|
|
|
if (texcoord_type != (const GeomVertexColumn *)NULL) {
|
|
new_array_format->add_column
|
|
(name, texcoord_type->get_num_values(), NT_stdfloat, C_texcoord,
|
|
-1, texcoord_type->get_column_alignment());
|
|
} else {
|
|
// We have to add something as a placeholder, even if the
|
|
// texture coordinates aren't defined.
|
|
new_array_format->add_column(name, 2, NT_stdfloat, C_texcoord);
|
|
}
|
|
new_format->remove_column(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now go through the remaining arrays and make sure they are
|
|
// tightly packed (with the column alignment restrictions). If
|
|
// not, repack them.
|
|
for (int i = 0; i < new_format->get_num_arrays(); ++i) {
|
|
CPT(GeomVertexArrayFormat) orig_a = new_format->get_array(i);
|
|
if (orig_a->count_unused_space() != 0) {
|
|
PT(GeomVertexArrayFormat) new_a = new GeomVertexArrayFormat;
|
|
for (int j = 0; j < orig_a->get_num_columns(); ++j) {
|
|
const GeomVertexColumn *column = orig_a->get_column(j);
|
|
new_a->add_column(column->get_name(), column->get_num_components(),
|
|
column->get_numeric_type(), column->get_contents(),
|
|
-1, column->get_column_alignment());
|
|
}
|
|
new_format->set_array(i, new_a);
|
|
}
|
|
}
|
|
|
|
// Finally, insert the interleaved array first in the format.
|
|
new_format->insert_array(0, new_array_format);
|
|
format = GeomVertexFormat::register_format(new_format);
|
|
}
|
|
|
|
return format;
|
|
}
|
|
|
|
#ifdef OPENGLES
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::munge_geom_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Converts a Geom and/or its data as necessary.
|
|
////////////////////////////////////////////////////////////////////
|
|
void CLP(GeomMunger)::
|
|
munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data,
|
|
Thread *current_thread) {
|
|
StandardMunger::munge_geom_impl(geom, vertex_data, current_thread);
|
|
|
|
// OpenGL ES has no polygon mode, so we have to emulate it.
|
|
RenderModeAttrib::Mode render_mode = get_render_mode();
|
|
if (render_mode == RenderModeAttrib::M_point) {
|
|
geom = geom->make_points();
|
|
} else if (render_mode == RenderModeAttrib::M_wireframe) {
|
|
geom = geom->make_lines();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::premunge_geom_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Converts a Geom and/or its data as necessary.
|
|
////////////////////////////////////////////////////////////////////
|
|
void CLP(GeomMunger)::
|
|
premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) {
|
|
StandardMunger::premunge_geom_impl(geom, vertex_data);
|
|
|
|
// OpenGL ES has no polygon mode, so we have to emulate it.
|
|
RenderModeAttrib::Mode render_mode = get_render_mode();
|
|
if (render_mode == RenderModeAttrib::M_point) {
|
|
geom = geom->make_points();
|
|
} else if (render_mode == RenderModeAttrib::M_wireframe) {
|
|
geom = geom->make_lines();
|
|
}
|
|
}
|
|
#endif // OPENGLES
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::compare_to_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Called to compare two GeomMungers who are known to be
|
|
// of the same type, for an apples-to-apples comparison.
|
|
// This will never be called on two pointers of a
|
|
// different type.
|
|
////////////////////////////////////////////////////////////////////
|
|
int CLP(GeomMunger)::
|
|
compare_to_impl(const GeomMunger *other) const {
|
|
const CLP(GeomMunger) *om = DCAST(CLP(GeomMunger), other);
|
|
if (_texture != om->_texture) {
|
|
return _texture < om->_texture ? -1 : 1;
|
|
}
|
|
if (_tex_gen != om->_tex_gen) {
|
|
return _tex_gen < om->_tex_gen ? -1 : 1;
|
|
}
|
|
if (_flags != om->_flags) {
|
|
return _flags < om->_flags ? -1 : 1;
|
|
}
|
|
|
|
return StandardMunger::compare_to_impl(other);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CLP(GeomMunger)::geom_compare_to_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Called to compare two GeomMungers who are known to be
|
|
// of the same type, for an apples-to-apples comparison.
|
|
// This will never be called on two pointers of a
|
|
// different type.
|
|
////////////////////////////////////////////////////////////////////
|
|
int CLP(GeomMunger)::
|
|
geom_compare_to_impl(const GeomMunger *other) const {
|
|
const CLP(GeomMunger) *om = DCAST(CLP(GeomMunger), other);
|
|
#ifdef OPENGLES
|
|
if (get_render_mode() != om->get_render_mode()) {
|
|
return get_render_mode() < om->get_render_mode() ? -1 : 1;
|
|
}
|
|
#endif
|
|
if (_texture != om->_texture) {
|
|
return _texture < om->_texture ? -1 : 1;
|
|
}
|
|
if (_tex_gen != om->_tex_gen) {
|
|
return _tex_gen < om->_tex_gen ? -1 : 1;
|
|
}
|
|
if (_flags != om->_flags) {
|
|
return _flags < om->_flags ? -1 : 1;
|
|
}
|
|
|
|
return StandardMunger::compare_to_impl(other);
|
|
}
|