panda3d/panda/src/glstuff/glGeomMunger_src.cxx
2015-06-21 20:28:31 +02:00

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);
}