*** empty log message ***

This commit is contained in:
David Rose 2000-12-15 20:21:17 +00:00
parent 0eb216f274
commit a6505665ed
37 changed files with 1410 additions and 140 deletions

View File

@ -175,6 +175,13 @@
#defer HAVE_TIFF $[libtest $[TIFF_LPATH],$[TIFF_LIBS]]
// Is libfftw installed, and where?
#define FFTW_IPATH /usr/local/include
#define FFTW_LPATH /usr/local/lib
#define FFTW_LIBS rfftw fftw
#defer HAVE_FFTW $[libtest $[FFTW_LPATH],$[FFTW_LIBS]]
// Is VRPN installed, and where?
#define VRPN_IPATH
#define VRPN_LPATH

View File

@ -40,6 +40,9 @@ $[cdefine HAVE_JPEG]
/* Define if we have libtiff installed. */
$[cdefine HAVE_TIFF]
/* Define if we have libfftw installed. */
$[cdefine HAVE_FFTW]
/* Define if we have VRPN installed. */
$[cdefine HAVE_VRPN]

View File

@ -145,6 +145,13 @@
#define tiff_libs $[TIFF_LIBS]
#endif
#if $[HAVE_FFTW]
#define fftw_ipath $[wildcard $[FFTW_IPATH]]
#define fftw_lpath $[wildcard $[FFTW_LPATH]]
#define fftw_cflags $[FFTW_CFLAGS]
#define fftw_libs $[FFTW_LIBS]
#endif
#if $[HAVE_VRPN]
#define vrpn_ipath $[wildcard $[VRPN_IPATH]]
#define vrpn_lpath $[wildcard $[VRPN_LPATH]]
@ -211,6 +218,7 @@
$[or $[not $[DIRECTORY_IF_SGIGL]],$[HAVE_SGIGL]], \
$[or $[not $[DIRECTORY_IF_JPEG]],$[HAVE_JPEG]], \
$[or $[not $[DIRECTORY_IF_TIFF]],$[HAVE_TIFF]], \
$[or $[not $[DIRECTORY_IF_FFTW]],$[HAVE_FFTW]], \
$[or $[not $[DIRECTORY_IF_VRPN]],$[HAVE_VRPN]], \
$[or $[not $[DIRECTORY_IF_GTKMM]],$[HAVE_GTKMM]], \
$[or $[not $[DIRECTORY_IF_MAYA]],$[HAVE_MAYA]], \
@ -239,6 +247,7 @@
$[or $[not $[TARGET_IF_SGIGL]],$[HAVE_SGIGL]], \
$[or $[not $[TARGET_IF_JPEG]],$[HAVE_JPEG]], \
$[or $[not $[TARGET_IF_TIFF]],$[HAVE_TIFF]], \
$[or $[not $[TARGET_IF_FFTW]],$[HAVE_FFTW]], \
$[or $[not $[TARGET_IF_VRPN]],$[HAVE_VRPN]], \
$[or $[not $[TARGET_IF_GTKMM]],$[HAVE_GTKMM]], \
$[or $[not $[TARGET_IF_MAYA]],$[HAVE_MAYA]], \
@ -274,6 +283,7 @@
$[if $[HAVE_CRYPTO],$[IF_CRYPTO_SOURCES]] \
$[if $[HAVE_JPEG],$[IF_JPEG_SOURCES]] \
$[if $[HAVE_TIFF],$[IF_TIFF_SOURCES]] \
$[if $[HAVE_FFTW],$[IF_FFTW_SOURCES]] \
$[if $[HAVE_ZLIB],$[IF_ZLIB_SOURCES]] \
$[if $[HAVE_IPC],$[IF_IPC_SOURCES]] \
$[if $[HAVE_NET],$[IF_NET_SOURCES]] \
@ -284,6 +294,7 @@
$[IF_CRYPTO_SOURCES] \
$[IF_JPEG_SOURCES] \
$[IF_TIFF_SOURCES] \
$[IF_FFTW_SOURCES] \
$[IF_ZLIB_SOURCES] \
$[IF_IPC_SOURCES] \
$[IF_NET_SOURCES] \
@ -359,6 +370,9 @@
#if $[ne $[USE_TIFF] $[components $[USE_TIFF],$[active_component_libs]],]
#set alt_cflags $[alt_cflags] $[tiff_cflags]
#endif
#if $[ne $[USE_FFTW] $[components $[USE_FFTW],$[active_component_libs]],]
#set alt_cflags $[alt_cflags] $[fftw_cflags]
#endif
#if $[ne $[USE_VRPN] $[components $[USE_VRPN],$[active_component_libs]],]
#set alt_cflags $[alt_cflags] $[vrpn_cflags]
#endif
@ -418,6 +432,9 @@
#if $[ne $[USE_TIFF] $[components $[USE_TIFF],$[active_component_libs]],]
#set alt_ipath $[alt_ipath] $[tiff_ipath]
#endif
#if $[ne $[USE_FFTW] $[components $[USE_FFTW],$[active_component_libs]],]
#set alt_ipath $[alt_ipath] $[fftw_ipath]
#endif
#if $[ne $[USE_VRPN] $[components $[USE_VRPN],$[active_component_libs]],]
#set alt_ipath $[alt_ipath] $[vrpn_ipath]
#endif
@ -477,6 +494,9 @@
#if $[ne $[USE_TIFF] $[components $[USE_TIFF],$[active_component_libs]],]
#set alt_lpath $[alt_lpath] $[tiff_lpath]
#endif
#if $[ne $[USE_FFTW] $[components $[USE_FFTW],$[active_component_libs]],]
#set alt_lpath $[alt_lpath] $[fftw_lpath]
#endif
#if $[ne $[USE_VRPN] $[components $[USE_VRPN],$[active_component_libs]],]
#set alt_lpath $[alt_lpath] $[vrpn_lpath]
#endif
@ -537,6 +557,9 @@
#if $[ne $[USE_TIFF] $[components $[USE_TIFF],$[active_component_libs]],]
#set alt_libs $[alt_libs] $[tiff_libs]
#endif
#if $[ne $[USE_FFTW] $[components $[USE_FFTW],$[active_component_libs]],]
#set alt_libs $[alt_libs] $[fftw_libs]
#endif
#if $[ne $[USE_VRPN] $[components $[USE_VRPN],$[active_component_libs]],]
#set alt_libs $[alt_libs] $[vrpn_libs]
#endif

View File

@ -5,8 +5,41 @@
#include "animChannel.h"
#include <compose_matrix.h>
// Tell GCC that we'll take care of the instantiation explicitly here.
#ifdef __GNUC__
#pragma implementation
#endif
////////////////////////////////////////////////////////////////////
// Function: ACMatrixSwitchType::output_value
// Access: Public, Static
// Description: Outputs a very brief description of a matrix.
////////////////////////////////////////////////////////////////////
void ACMatrixSwitchType::
output_value(ostream &out, const ACMatrixSwitchType::ValueType &value) {
LVecBase3f scale, hpr, translate;
if (decompose_matrix(value, scale, hpr, translate)) {
if (!scale.almost_equal(LVecBase3f(1.0, 1.0, 1.0))) {
if (IS_NEARLY_EQUAL(scale[0], scale[1]) &&
IS_NEARLY_EQUAL(scale[1], scale[2])) {
out << " scale " << scale[0];
} else {
out << " scale " << scale;
}
}
if (!hpr.almost_equal(LVecBase3f(0.0, 0.0, 0.0))) {
out << " hpr " << hpr;
}
if (!translate.almost_equal(LVecBase3f(0.0, 0.0, 0.0))) {
out << " trans " << translate;
}
} else {
out << " mat " << value;
}
}

View File

@ -72,6 +72,8 @@ public:
static const char *get_channel_type_name() { return "AnimChannelMatrix"; }
static const char *get_fixed_channel_type_name() { return "AnimChannelMatrixFixed"; }
static const char *get_part_type_name() { return "MovingPart<LMatrix4f>"; }
static void output_value(ostream &out, const ValueType &value);
static void write_datagram(Datagram &dest, ValueType& me)
{
me.write_datagram(dest);
@ -92,6 +94,9 @@ public:
static const char *get_channel_type_name() { return "AnimChannelScalar"; }
static const char *get_fixed_channel_type_name() { return "AnimChannelScalarFixed"; }
static const char *get_part_type_name() { return "MovingPart<float>"; }
static void output_value(ostream &out, ValueType value) {
out << value;
}
static void write_datagram(Datagram &dest, ValueType& me)
{
dest.add_float32(me);

View File

@ -30,6 +30,21 @@ has_table(char table_id) const {
return !(_tables[table_index] == NULL);
}
////////////////////////////////////////////////////////////////////
// Function: AnimChannelMatrixXfmTable::get_table
// Access: Public
// Description: Returns a pointer to the indicated subtable's data,
// if it exists, or NULL if it does not.
////////////////////////////////////////////////////////////////////
INLINE CPTA_float AnimChannelMatrixXfmTable::
get_table(char table_id) const {
int table_index = get_table_index(table_id);
if (table_index < 0) {
return CPTA_float();
}
return _tables[table_index];
}
////////////////////////////////////////////////////////////////////
// Function: AnimChannelMatrixXfmTable::clear_table
// Access: Public

View File

@ -13,6 +13,7 @@
#include <datagramIterator.h>
#include <bamReader.h>
#include <bamWriter.h>
#include <fftCompressor.h>
TypeHandle AnimChannelMatrixXfmTable::_type_handle;
@ -226,9 +227,15 @@ write_datagram(BamWriter *manager, Datagram &me)
{
AnimChannelMatrix::write_datagram(manager, me);
me.add_bool(quantize_bam_channels);
if (!quantize_bam_channels) {
// Write out everything the old way, as floats.
if (compress_channels && !FFTCompressor::is_compression_available()) {
chan_cat.error()
<< "Compression is not available; writing uncompressed channels.\n";
compress_channels = false;
}
me.add_bool(compress_channels);
if (!compress_channels) {
// Write out everything uncompressed, as a stream of floats.
for(int i = 0; i < num_tables; i++) {
me.add_uint16(_tables[i].size());
for(int j = 0; j < (int)_tables[i].size(); j++) {
@ -237,32 +244,35 @@ write_datagram(BamWriter *manager, Datagram &me)
}
} else {
// Write out everything the compact way, as quantized integers.
// Write out everything using lossy compression.
// First, write out the scales. These will be in the range 1/256 .. 255.
FFTCompressor compressor;
compressor.set_quality(compress_chan_quality);
compressor.write_header(me);
// First, write out the scales.
int i;
for(i = 0; i < 3; i++) {
me.add_uint16(_tables[i].size());
for(int j = 0; j < (int)_tables[i].size(); j++) {
me.add_uint16((int)max(min(_tables[i][j]*256.0, 65535.0), 0.0));
}
compressor.write_reals(me, _tables[i], _tables[i].size());
}
// Now, write out the joint angles. These are in the range 0 .. 360.
for(i = 3; i < 6; i++) {
me.add_uint16(_tables[i].size());
for(int j = 0; j < (int)_tables[i].size(); j++) {
me.add_uint16((unsigned int)(_tables[i][j] * 65536.0 / 360.0));
}
// Now, write out the joint angles. For these we need to build up
// a HPR array.
vector_LVecBase3f hprs;
int hprs_length =
max(max(_tables[3].size(), _tables[4].size()), _tables[5].size());
hprs.reserve(hprs_length);
for (i = 0; i < hprs_length; i++) {
float h = (i < (int)_tables[3].size()) ? _tables[3][i] : 0.0f;
float p = (i < (int)_tables[4].size()) ? _tables[4][i] : 0.0f;
float r = (i < (int)_tables[5].size()) ? _tables[5][i] : 0.0f;
hprs.push_back(LVecBase3f(h, p, r));
}
compressor.write_hprs(me, &hprs[0], hprs_length);
// And now write out the translations. These are in the range
// -1000 .. 1000.
// And now the translations.
for(i = 6; i < 9; i++) {
me.add_uint16(_tables[i].size());
for(int j = 0; j < (int)_tables[i].size(); j++) {
me.add_int16((int)max(min(_tables[i][j] * 32767.0 / 1000.0, 32767.0), -32767.0));
}
compressor.write_reals(me, _tables[i], _tables[i].size());
}
}
}
@ -280,9 +290,9 @@ fillin(DatagramIterator& scan, BamReader* manager)
{
AnimChannelMatrix::fillin(scan, manager);
bool wrote_quantized = scan.get_bool();
bool wrote_compressed = scan.get_bool();
if (!wrote_quantized) {
if (!wrote_compressed) {
// Regular floats.
for(int i = 0; i < num_tables; i++) {
int size = scan.get_uint16();
@ -294,36 +304,43 @@ fillin(DatagramIterator& scan, BamReader* manager)
}
} else {
// Quantized int16's and expand to floats.
int i;
// Compressed channels.
if (manager->get_file_minor_ver() < 1) {
chan_cat.error()
<< "Cannot read old-style quantized channels.\n";
return;
}
FFTCompressor compressor;
compressor.read_header(scan);
int i;
// First, read in the scales.
for(i = 0; i < 3; i++) {
int size = scan.get_uint16();
PTA_float ind_table;
for(int j = 0; j < size; j++) {
ind_table.push_back((double)scan.get_uint16() / 256.0);
}
PTA_float ind_table(0);
compressor.read_reals(scan, ind_table.v());
_tables[i] = ind_table;
}
// Then, read in the joint angles.
for(i = 3; i < 6; i++) {
int size = scan.get_uint16();
PTA_float ind_table;
for(int j = 0; j < size; j++) {
ind_table.push_back((double)scan.get_uint16() * 360.0 / 65536.0);
}
_tables[i] = ind_table;
// Read in the HPR array and store it back in the joint angles.
vector_LVecBase3f hprs;
compressor.read_hprs(scan, hprs);
PTA_float h_table(hprs.size());
PTA_float p_table(hprs.size());
PTA_float r_table(hprs.size());
for (i = 0; i < (int)hprs.size(); i++) {
h_table[i] = hprs[i][0];
p_table[i] = hprs[i][1];
r_table[i] = hprs[i][2];
}
_tables[3] = h_table;
_tables[4] = p_table;
_tables[5] = r_table;
// Now read in the translations.
for(i = 6; i < 9; i++) {
int size = scan.get_uint16();
PTA_float ind_table;
for(int j = 0; j < size; j++) {
ind_table.push_back((double)scan.get_int16() * 1000.0 / 32767.0);
}
for (i = 6; i < 9; i++) {
PTA_float ind_table(0);
compressor.read_reals(scan, ind_table.v());
_tables[i] = ind_table;
}
}

View File

@ -35,6 +35,7 @@ public:
void clear_all_tables();
void set_table(char table_id, const CPTA_float &table);
INLINE bool has_table(char table_id) const;
INLINE CPTA_float get_table(char table_id) const;
INLINE void clear_table(char table_id);
virtual void write(ostream &out, int indent_level) const;

View File

@ -12,6 +12,7 @@
#include <datagramIterator.h>
#include <bamReader.h>
#include <bamWriter.h>
#include <fftCompressor.h>
TypeHandle AnimChannelScalarTable::_type_handle;
@ -118,8 +119,14 @@ write_datagram(BamWriter *manager, Datagram &me)
{
AnimChannelScalar::write_datagram(manager, me);
me.add_bool(quantize_bam_channels);
if (!quantize_bam_channels) {
if (compress_channels && !FFTCompressor::is_compression_available()) {
chan_cat.error()
<< "Compression is not available; writing uncompressed channels.\n";
compress_channels = false;
}
me.add_bool(compress_channels);
if (!compress_channels) {
// Write out everything the old way, as floats.
me.add_uint16(_table.size());
for(int i = 0; i < (int)_table.size(); i++) {
@ -127,12 +134,86 @@ write_datagram(BamWriter *manager, Datagram &me)
}
} else {
// Write out everything the compact way, as quantized integers.
// Some channels, particularly blink channels, may involve only a
// small number of discrete values. If we come across one of
// those, write it out losslessly, since the lossy compression
// could damage it significantly (and we can achieve better
// compression directly anyway). We consider the channel value
// only to the nearest 1000th for this purpose, because floats
// aren't very good at being precisely equal to each other.
static const int max_values = 16;
static const float scale = 1000.0;
// We quantize morphs within the range -100 .. 100.
me.add_uint16(_table.size());
for(int i = 0; i < (int)_table.size(); i++) {
me.add_int16((int)max(min(_table[i] * 32767.0 / 100.0, 32767.0), -32767.0));
map<int, int> index;
int i;
for (i = 0;
i < (int)_table.size() && (int)index.size() <= max_values;
i++) {
int value = (int)floor(_table[i] * scale + 0.5);
index.insert(map<int, int>::value_type(value, index.size()));
}
int index_length = index.size();
if (index_length <= max_values) {
// All right, here's a blink channel. Now we write out the
// index table, and then a table of all the index values, two
// per byte.
me.add_uint8(index_length);
if (index_length > 0) {
// We need to write the index in order by its index number; for
// this, we need to invert the index.
vector_float reverse_index(index_length);
map<int, int>::iterator mi;
for (mi = index.begin(); mi != index.end(); ++mi) {
float f = (float)(*mi).first / scale;
int i = (*mi).second;
nassertv(i >= 0 && i < (int)reverse_index.size());
reverse_index[i] = f;
}
for (i = 0; i < index_length; i++) {
me.add_float32(reverse_index[i]);
}
// Now write out the actual channels. We write these two at a
// time, in the high and low nibbles of each byte.
int table_length = _table.size();
me.add_uint16(table_length);
if (index_length == 1) {
// In fact, we don't even need to write the channels at all,
// if there weren't at least two different values.
} else {
for (i = 0; i < table_length - 1; i+= 2) {
int value1 = (int)floor(_table[i] * scale + 0.5);
int value2 = (int)floor(_table[i + 1] * scale + 0.5);
int i1 = index[value1];
int i2 = index[value2];
me.add_uint8((i1 << 4) | i2);
}
// There might be one odd value.
if (i < table_length) {
int value1 = (int)floor(_table[i] * scale + 0.5);
int i1 = index[value1];
me.add_uint8(i1 << 4);
}
}
}
} else {
// No, we have continuous channels. Write them out using lossy
// compression.
me.add_uint8(0xff);
FFTCompressor compressor;
compressor.set_quality(compress_chan_quality);
compressor.write_header(me);
compressor.write_reals(me, _table, _table.size());
}
}
}
@ -150,26 +231,71 @@ fillin(DatagramIterator& scan, BamReader* manager)
{
AnimChannelScalar::fillin(scan, manager);
bool wrote_quantized = scan.get_bool();
bool wrote_compressed = scan.get_bool();
if (!wrote_quantized) {
PTA_float temp_table(0);
if (!wrote_compressed) {
// Regular floats.
int size = scan.get_uint16();
PTA_float temp_table;
for(int i = 0; i < size; i++) {
temp_table.push_back(scan.get_float32());
}
_table = temp_table;
} else {
// Quantized int16's and expand to floats.
int size = scan.get_uint16();
PTA_float temp_table;
for(int i = 0; i < size; i++) {
temp_table.push_back((double)scan.get_int16() * 100.0 / 32767.0);
// Compressed channels.
if (manager->get_file_minor_ver() < 1) {
chan_cat.error()
<< "Cannot read old-style quantized channels.\n";
return;
}
// Did we write them as discrete or continuous channel values?
int index_length = scan.get_uint8();
if (index_length < 0xff) {
// Discrete. Read in the index.
if (index_length > 0) {
float index[index_length];
int i;
for (i = 0; i < index_length; i++) {
index[i] = scan.get_float32();
}
// Now read in the channel values.
int table_length = scan.get_uint16();
if (index_length == 1) {
// With only one index value, we can infer the table.
for (i = 0; i < table_length; i++) {
temp_table.push_back(index[0]);
}
} else {
// Otherwise, we must read it.
for (i = 0; i < table_length - 1; i+= 2) {
int num = scan.get_uint8();
int i1 = (num >> 4) & 0xf;
int i2 = num & 0xf;
temp_table.push_back(index[i1]);
temp_table.push_back(index[i2]);
}
// There might be one odd value.
if (i < table_length) {
int num = scan.get_uint8();
int i1 = (num >> 4) & 0xf;
temp_table.push_back(index[i1]);
}
}
}
} else {
// Continuous channels.
FFTCompressor compressor;
compressor.read_header(scan);
compressor.read_reals(scan, temp_table.v());
}
_table = temp_table;
}
_table = temp_table;
}
////////////////////////////////////////////////////////////////////

View File

@ -17,9 +17,7 @@
// Class : AnimChannelScalarTable
// Description : An animation channel that issues a scalar each frame,
// read from a table such as might have been read from
// an egg file. The table actually consists of nine
// sub-tables, each representing one component of the
// transform: scale, rotate, translate.
// an egg file.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA AnimChannelScalarTable : public AnimChannelScalar {
public:

View File

@ -24,10 +24,10 @@
Configure(config_chan);
NotifyCategoryDef(chan, "");
// This is normally set true to quantize animation channels to 16-bit
// integer values when writing to a Bam file; a cheesy way to
// hopefully achieve greater compression ratios.
const bool quantize_bam_channels = config_chan.GetBool("quantize-bam-channels", true);
// Set this true to enable compress of animation channels when writing to
// the bam file. This is an experimental lossy compression.
bool compress_channels = config_chan.GetBool("compress-channels", false);
int compress_chan_quality = config_chan.GetInt("compress-chan-quality", 95);
ConfigureFn(config_chan) {
AnimBundle::init_type();

View File

@ -12,6 +12,7 @@
// Configure variables for chan package.
NotifyCategoryDecl(chan, EXPCL_PANDA, EXPTP_PANDA);
EXPCL_PANDA extern const bool quantize_bam_channels;
EXPCL_PANDA extern bool compress_channels;
EXPCL_PANDA extern int compress_chan_quality;
#endif

View File

@ -87,6 +87,18 @@ make_initial_channel() const {
return new AnimChannelFixed<SwitchType>(get_name(), _initial_value);
}
////////////////////////////////////////////////////////////////////
// Function: MovingPart::output_value
// Access: Public, Virtual
// Description: Outputs a very brief description of the channel's
// current value.
////////////////////////////////////////////////////////////////////
template<class SwitchType>
void MovingPart<SwitchType>::
output_value(ostream &out) const {
SwitchType::output_value(out, _value);
}
////////////////////////////////////////////////////////////////////
// Function: MovingPart::write_datagram
// Access: Public

View File

@ -32,6 +32,7 @@ public:
virtual TypeHandle get_value_type() const;
virtual AnimChannelBase *make_initial_channel() const;
virtual void output_value(ostream &out) const;
ValueType _value;
ValueType _initial_value;

View File

@ -49,6 +49,27 @@ write(ostream &out, int indent_level) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: MovingPartBase::write_with_value
// Access: Public, Virtual
// Description: Writes a brief description of the channel and all of
// its descendants, along with their values.
////////////////////////////////////////////////////////////////////
void MovingPartBase::
write_with_value(ostream &out, int indent_level) const {
indent(out, indent_level) << get_value_type() << " " << get_name() << "\n";
indent(out, indent_level);
output_value(out);
if (_children.empty()) {
out << "\n";
} else {
out << " {\n";
write_descendants_with_value(out, indent_level + 2);
indent(out, indent_level) << "}\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: MovingPartBase::do_update
// Access: Public, Virtual

View File

@ -32,6 +32,8 @@ public:
virtual TypeHandle get_value_type() const=0;
virtual AnimChannelBase *make_initial_channel() const=0;
virtual void write(ostream &out, int indent_level) const;
virtual void write_with_value(ostream &out, int indent_level) const;
virtual void output_value(ostream &out) const=0;
virtual void do_update(PartBundle *root, PartGroup *parent,
bool parent_changed, bool anim_changed);

View File

@ -296,6 +296,20 @@ write(ostream &out, int indent_level) const {
indent(out, indent_level) << "}\n";
}
////////////////////////////////////////////////////////////////////
// Function: PartGroup::write_with_value
// Access: Public, Virtual
// Description: Writes a brief description of the group, showing its
// current value, and that of all of its descendants.
////////////////////////////////////////////////////////////////////
void PartGroup::
write_with_value(ostream &out, int indent_level) const {
indent(out, indent_level)
<< get_type() << " " << get_name() << " {\n";
write_descendants_with_value(out, indent_level + 2);
indent(out, indent_level) << "}\n";
}
////////////////////////////////////////////////////////////////////
// Function: PartGroup::do_update
@ -327,6 +341,21 @@ write_descendants(ostream &out, int indent_level) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: PartGroup::write_descendants_with_value
// Access: Protected
// Description: Writes a brief description of all of the group's
// descendants and their values.
////////////////////////////////////////////////////////////////////
void PartGroup::
write_descendants_with_value(ostream &out, int indent_level) const {
Children::const_iterator ci;
for (ci = _children.begin(); ci != _children.end(); ++ci) {
(*ci)->write_with_value(out, indent_level);
}
}

View File

@ -64,12 +64,14 @@ public:
int hierarchy_match_flags = 0) const;
virtual void write(ostream &out, int indent_level) const;
virtual void write_with_value(ostream &out, int indent_level) const;
virtual void do_update(PartBundle *root, PartGroup *parent,
bool parent_changed, bool anim_changed);
protected:
void write_descendants(ostream &out, int indent_level) const;
void write_descendants_with_value(ostream &out, int indent_level) const;
virtual void pick_channel_index(list<int> &holes, int &next) const;
virtual void bind_hierarchy(AnimGroup *anim, int channel_index);

View File

@ -48,3 +48,27 @@ get_part(int n) const {
nassertr(n >= 0 && n < (int)_parts.size(), NULL);
return _parts[n];
}
////////////////////////////////////////////////////////////////////
// Function: Character::write_parts
// Access: Public
// Description: Writes a list of the character's joints and sliders,
// in their hierchical structure, to the indicated
// output stream.
////////////////////////////////////////////////////////////////////
INLINE void Character::
write_parts(ostream &out) const {
get_bundle()->write(out, 0);
}
////////////////////////////////////////////////////////////////////
// Function: Character::write_part_values
// Access: Public
// Description: Writes a list of the character's joints and sliders,
// along with each current position, in their hierchical
// structure, to the indicated output stream.
////////////////////////////////////////////////////////////////////
INLINE void Character::
write_part_values(ostream &out) const {
get_bundle()->write_with_value(out, 0);
}

View File

@ -93,7 +93,6 @@ safe_to_transform() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: Character::app_traverse
// Access: Public, Virtual

View File

@ -43,6 +43,9 @@ PUBLISHED:
INLINE int get_num_parts() const;
INLINE PartGroup *get_part(int n) const;
INLINE void write_parts(ostream &out) const;
INLINE void write_part_values(ostream &out) const;
public:
virtual void app_traverse();

View File

@ -411,7 +411,7 @@ void event_esc(CPT_Event) {
#ifdef HAVE_NET
if (PStatClient::get_global_pstats()->is_connected()) {
nout << "Disconnecting from stats host" << endl;
framework_cat.info() << "Disconnecting from stats host" << endl;
PStatClient::get_global_pstats()->disconnect();
}
#endif
@ -438,23 +438,23 @@ void event_f(CPT_Event) {
void event_S(CPT_Event) {
#ifdef HAVE_NET
nout << "Connecting to stats host" << endl;
framework_cat.info() << "Connecting to stats host" << endl;
PStatClient::get_global_pstats()->connect();
#else
nout << "Stats host not supported." << endl;
framework_cat.error() << "Stats host not supported." << endl;
#endif
}
void event_A(CPT_Event) {
#ifdef HAVE_NET
if (PStatClient::get_global_pstats()->is_connected()) {
nout << "Disconnecting from stats host" << endl;
framework_cat.info() << "Disconnecting from stats host" << endl;
PStatClient::get_global_pstats()->disconnect();
} else {
nout << "Stats host is already disconnected." << endl;
framework_cat.error() << "Stats host is already disconnected." << endl;
}
#else
nout << "Stats host not supported." << endl;
framework_cat.error() << "Stats host not supported." << endl;
#endif
}
@ -842,7 +842,7 @@ void pause_draw(void) {
return;
run_render.lock();
render_running = false;
nout << "draw thread paused" << endl;
framework_cat.info() << "draw thread paused" << endl;
}
void unpause_draw(void) {
@ -850,7 +850,7 @@ void unpause_draw(void) {
return;
run_render.unlock();
render_running = true;
nout << "draw thread continuing" << endl;
framework_cat.info() << "draw thread continuing" << endl;
}
void draw_loop(void*) {
@ -859,7 +859,7 @@ void draw_loop(void*) {
main_win->update();
handle_framerate();
}
nout << "draw thread exiting" << endl;
framework_cat.info() << "draw thread exiting" << endl;
}
void event_x(CPT_Event) {
@ -925,30 +925,34 @@ int framework_main(int argc, char *argv[]) {
// load display modules
GraphicsPipe::resolve_modules();
nout << "Known pipe types:" << endl;
GraphicsPipe::_factory.write_types(nout, 2);
framework_cat.info() << "Known pipe types:" << endl;
GraphicsPipe::_factory.write_types(framework_cat.info(false), 2);
// Create a window
main_pipe = GraphicsPipe::_factory.
make_instance(InteractiveGraphicsPipe::get_class_type());
if (main_pipe == (GraphicsPipe*)0L) {
nout << "No interactive pipe is available! Check your Configrc!\n";
framework_cat.error()
<< "No interactive pipe is available! Check your Configrc!\n";
exit(1);
}
cout << "Opened a '" << main_pipe->get_type().get_name()
<< "' interactive graphics pipe." << endl;
framework_cat.info()
<< "Opened a '" << main_pipe->get_type().get_name()
<< "' interactive graphics pipe." << endl;
rib_pipe = GraphicsPipe::_factory.
make_instance(NoninteractiveGraphicsPipe::get_class_type());
if (rib_pipe == (GraphicsPipe*)0L)
cout << "Did not open a non-interactive graphics pipe, features related"
<< " to that will\nbe disabled." << endl;
framework_cat.info()
<< "Did not open a non-interactive graphics pipe, features related"
<< " to that will\nbe disabled." << endl;
else
cout << "Opened a '" << rib_pipe->get_type().get_name()
<< "' non-interactive graphics pipe." << endl;
framework_cat.info()
<< "Opened a '" << rib_pipe->get_type().get_name()
<< "' non-interactive graphics pipe." << endl;
ChanCfgOverrides override;
@ -1105,7 +1109,8 @@ int framework_main(int argc, char *argv[]) {
PT_Node node = loader.load_sync(filename);
if (node == (Node *)NULL) {
nout << "Unable to load file " << filename << "\n";
framework_cat.error()
<< "Unable to load file " << filename << "\n";
} else {
new RenderRelation(root, node);
}
@ -1168,7 +1173,7 @@ int framework_main(int argc, char *argv[]) {
}
if (!main_win->supports_update()) {
nout
framework_cat.info()
<< "Window type " << main_win->get_type()
<< " supports only the glut-style main loop interface.\n";
@ -1184,7 +1189,7 @@ int framework_main(int argc, char *argv[]) {
#ifdef USE_IPC
if (forked_draw) {
nout << "forking draw thread" << endl;
framework_cat.info() << "forking draw thread" << endl;
draw_thread = thread::create(draw_loop);
for (;;)
icb.idle();

View File

@ -11,10 +11,18 @@
coordinateSystem.h deg_2_rad.h \
ioPtaDatagramLinMath.I ioPtaDatagramLinMath.cxx \
ioPtaDatagramLinMath.h lmatrix.cxx lmatrix.h luse.I luse.N luse.cxx \
luse.h mathNumbers.cxx mathNumbers.h pta_Colorf.cxx pta_Colorf.h \
luse.h lquaternion.I lquaternion.h lrotation.I lrotation.h \
lvec2_ops.I lvec2_ops.h lvec3_ops.I lvec3_ops.h lvec4_ops.I \
lvec4_ops.h lvecBase2.I lvecBase2.h lvecBase3.I lvecBase3.h \
lvecBase4.I lvecBase4.h lvector2.I lvector2.h lvector3.I lvector3.h \
lvector4.I lvector4.h \
mathNumbers.cxx mathNumbers.h nearly_zero.h \
pta_Colorf.cxx pta_Colorf.h \
pta_Normalf.cxx pta_Normalf.h pta_TexCoordf.cxx pta_TexCoordf.h \
pta_Vertexf.cxx pta_Vertexf.h vector_Colorf.cxx vector_Colorf.h \
vector_LPoint2f.cxx vector_LPoint2f.h vector_Normalf.cxx \
vector_LPoint2f.cxx vector_LPoint2f.h \
vector_LVecBase3f.cxx vector_LVecBase3f.h \
vector_Normalf.cxx \
vector_Normalf.h vector_Vertexf.cxx vector_Vertexf.h
#define INSTALL_HEADERS \
@ -29,7 +37,8 @@
lvecBase4.I lvecBase4.h lvector2.I lvector2.h lvector3.I lvector3.h \
lvector4.I lvector4.h mathNumbers.h nearly_zero.h pta_Colorf.h \
pta_Normalf.h pta_TexCoordf.h pta_Vertexf.h vector_Colorf.h \
vector_LPoint2f.h vector_Normalf.h vector_TexCoordf.h \
vector_LPoint2f.h vector_LVecBase3f.h \
vector_Normalf.h vector_TexCoordf.h \
vector_Vertexf.h
#define IGATESCAN all

View File

@ -25,6 +25,10 @@ INLINE float cabs(float v) {
return fabs(v);
}
INLINE float catan2(float y, float x) {
return atan2f(y, x);
}
INLINE double csqrt(double v) {
return sqrt(v);
}
@ -41,6 +45,10 @@ INLINE double cabs(double v) {
return fabs(v);
}
INLINE double catan2(double y, double x) {
return atan2(y, x);
}
INLINE bool cnan(double v) {
#ifndef _WIN32
return (isnan(v) != 0);

View File

@ -18,11 +18,13 @@ INLINE float csqrt(float v);
INLINE float csin(float v);
INLINE float ccos(float v);
INLINE float cabs(float v);
INLINE float catan2(float y, float x);
INLINE double csqrt(double v);
INLINE double csin(double v);
INLINE double ccos(double v);
INLINE double cabs(double v);
INLINE double catan2(double y, double x);
// Returns true if the number is nan, false if it's a genuine number
// or infinity.

View File

@ -3,10 +3,6 @@
//
////////////////////////////////////////////////////////////////////
#include "lquaternion.h"
#include "nearly_zero.h"
#include <notify.h>
template<class NumType>
TypeHandle LQuaternionBase<NumType>::_type_handle;
@ -341,19 +337,18 @@ INLINE void LQuaternionBase<NumType>::
normalize(void) {
NumType l = csqrt((_r*_r)+(_i*_i)+(_j*_j)+(_k*_k));
if (IS_NEARLY_ZERO(l)) {
if (l == 0.0) {
_r = 0.;
_i = 0.;
_j = 0.;
_k = 0.;
return;
} else {
l = 1. / l;
_r *= l;
_i *= l;
_j *= l;
_k *= l;
}
l = 1. / l;
_r *= l;
_i *= l;
_j *= l;
_k *= l;
}
////////////////////////////////////////////////////////////////////
@ -362,25 +357,25 @@ normalize(void) {
// Description: Do-While Jones.
////////////////////////////////////////////////////////////////////
template<class NumType>
INLINE void LQuaternionBase<NumType>::
void LQuaternionBase<NumType>::
set(const LMatrix3<NumType> &m) {
NumType m00 = m.get_cell(0, 0);
NumType m11 = m.get_cell(1, 1);
NumType m22 = m.get_cell(2, 2);
if (m00 >= 0.0 && ((m11 + m22) >= 0.0)) {
if (m00 != 0.0 && ((m11 + m22) != 0.0)) {
_r = 1.0 + m00 + m11 + m22;
_i = m.get_cell(2, 1) - m.get_cell(1, 2);
_j = m.get_cell(0, 2) - m.get_cell(2, 0);
_k = m.get_cell(1, 0) - m.get_cell(0, 1);
}
else if (m00 >= 0.0 && ((m11 + m22) == 0.0)) {
else if (m00 != 0.0 && ((m11 + m22) == 0.0)) {
_r = m.get_cell(2, 1) - m.get_cell(1, 2);
_i = 1.0 + m00 - m11 - m22;
_j = m.get_cell(1, 0) + m.get_cell(0, 1);
_k = m.get_cell(0, 2) + m.get_cell(2, 0);
}
else if (m00 == 0.0 && ((m11 - m22) >= 0.0)) {
else if (m00 == 0.0 && ((m11 - m22) != 0.0)) {
_r = m.get_cell(0, 2) - m.get_cell(2, 0);
_i = m.get_cell(1, 0) + m.get_cell(0, 1);
_j = 1.0 - m00 + m11 - m22;
@ -411,7 +406,7 @@ set(const LMatrix4<NumType> &m) {
// Description: Do-While Jones paper from cary.
////////////////////////////////////////////////////////////////////
template<class NumType>
INLINE void LQuaternionBase<NumType>::
void LQuaternionBase<NumType>::
extract_to_matrix(LMatrix3<NumType> &m) const {
NumType tx, ty, tz, tq, tk, tk_denom;
@ -451,7 +446,7 @@ extract_to_matrix(LMatrix3<NumType> &m) const {
// Description:
////////////////////////////////////////////////////////////////////
template<class NumType>
INLINE void LQuaternionBase<NumType>::
void LQuaternionBase<NumType>::
extract_to_matrix(LMatrix4<NumType> &m) const {
NumType tx, ty, tz, tq, tk, tk_denom;
@ -485,6 +480,57 @@ extract_to_matrix(LMatrix4<NumType> &m) const {
m.set_cell(2, 1, tk + tq);
}
////////////////////////////////////////////////////////////////////
// Function: set_hpr
// Access: public
// Description: Sets the quaternion as the unit quaternion that
// is equivalent to these Euler angles.
// (from Real-time Rendering, p.49)
////////////////////////////////////////////////////////////////////
template<class NumType>
INLINE void LQuaternionBase<NumType>::
set_hpr(const LVecBase3<NumType> &hpr) {
LQuaternionBase<NumType> quat_h, quat_p, quat_r;
quat_h.set(ccos(hpr[0]), 0, csin(hpr[0]), 0);
quat_p.set(ccos(hpr[1]), csin(hpr[1]), 0, 0);
quat_r.set(ccos(hpr[2]), 0, 0, csin(hpr[2]));
(*this) = quat_h * quat_p * quat_r;
}
////////////////////////////////////////////////////////////////////
// Function: get_hpr
// Access: public
// Description: Extracts the equivalent Euler angles from the unit
// quaternion.
////////////////////////////////////////////////////////////////////
template<class NumType>
INLINE LVecBase3<NumType> LQuaternionBase<NumType>::
get_hpr() const {
NumType sint = (2.0 * _r * _j) - (2.0 * _i * _k);
NumType cost = csqrt(1 - sint * sint);
NumType sinv, cosv, sinf, cosf;
if (cost != 0.0) {
sinv = ((2.0 * _j * _k) + (2.0 * _r * _i)) / cost;
cosv = (1.0 - (2.0 * _i * _i) - (2.0 * _j * _j)) / cost;
sinf = (1.0 - (2.0 * _i * _i) - (2.0 * _j * _j)) / cost;
cosf = (1.0 - (2.0 * _j * _j) - (2.0 * _k * _k)) / cost;
} else {
sinv = ((2.0 * _r * _i) - (2.0 * _j * _k));
cosv = 1.0 - (2.0 * _i * _i) - (2.0 * _k * _k);
sinf = 0.0;
cosf = 1.0;
}
return LVecBase3<NumType>(rad_2_deg(atan2(sinv, cosv)),
rad_2_deg(atan2(sint, cost)),
rad_2_deg(atan2(sinf, cosf)));
}
////////////////////////////////////////////////////////////////////
// Function: operator *(Matrix3, Quat)
// Access: public

View File

@ -7,6 +7,11 @@
#define __LQUATERNION_H__
#include "lmatrix.h"
#include "nearly_zero.h"
#include "cmath.h"
#include "deg_2_rad.h"
#include <notify.h>
////////////////////////////////////////////////////////////////////
// Class : LQuaternionBase
@ -43,11 +48,14 @@ PUBLISHED:
INLINE void set(NumType, NumType, NumType, NumType);
INLINE void set(const LMatrix3<NumType> &);
INLINE void set(const LMatrix4<NumType> &);
void set(const LMatrix3<NumType> &m);
INLINE void set(const LMatrix4<NumType> &m);
INLINE void extract_to_matrix(LMatrix3<NumType> &) const;
INLINE void extract_to_matrix(LMatrix4<NumType> &) const;
void extract_to_matrix(LMatrix3<NumType> &m) const;
void extract_to_matrix(LMatrix4<NumType> &m) const;
INLINE void set_hpr(const LVecBase3<NumType> &hpr);
LVecBase3<NumType> get_hpr() const;
INLINE NumType get_r(void) const;
INLINE NumType get_i(void) const;

View File

@ -3,10 +3,6 @@
//
////////////////////////////////////////////////////////////////////
#include "lrotation.h"
#include <notify.h>
#include <math.h>
template<class NumType>
TypeHandle LRotation<NumType>::_type_handle;
@ -71,12 +67,12 @@ LRotation(const LMatrix4<NumType> &m) {
////////////////////////////////////////////////////////////////////
template<class NumType>
INLINE LRotation<NumType>::
LRotation(const LVector3<NumType> &axis, float angle) {
float radians = angle * ((float) MathNumbers::pi / 180.0f);
float theta_over_2 = radians / 2.0f;
float sin_to2 = sinf(theta_over_2);
LRotation(const LVector3<NumType> &axis, NumType angle) {
NumType radians = angle * ((NumType) MathNumbers::pi / (NumType)180.0);
NumType theta_over_2 = radians / (NumType)2.0;
NumType sin_to2 = csin(theta_over_2);
set_r(cosf(theta_over_2));
set_r(ccos(theta_over_2));
set_i(axis[0] * sin_to2);
set_j(axis[1] * sin_to2);
set_k(axis[2] * sin_to2);
@ -85,18 +81,12 @@ LRotation(const LVector3<NumType> &axis, float angle) {
////////////////////////////////////////////////////////////////////
// Function: LRotation::Constructor
// Access: public
// Description: hpr (Real-time Rendering, p.49)
// Description: Sets the rotation from the given Euler angles.
////////////////////////////////////////////////////////////////////
template<class NumType>
INLINE LRotation<NumType>::
LRotation(float h, float p, float r) {
LQuaternionBase<NumType> quat_h, quat_p, quat_r;
quat_h.set(cosf(h), 0, sinf(h), 0);
quat_p.set(cosf(p), sinf(p), 0, 0);
quat_r.set(cosf(r), 0, 0, sinf(r));
(*this) = quat_h * quat_p * quat_r;
LRotation(NumType h, NumType p, NumType r) {
set_hpr(LVecBase3<NumType>(h, p, r));
}
////////////////////////////////////////////////////////////////////

View File

@ -8,6 +8,9 @@
#include <pandabase.h>
#include "lquaternion.h"
#include "cmath.h"
#include <notify.h>
////////////////////////////////////////////////////////////////////////
// Class : LRotation
@ -19,10 +22,10 @@ PUBLISHED:
INLINE LRotation();
INLINE LRotation(const LQuaternionBase<NumType>&);
INLINE LRotation(NumType, NumType, NumType, NumType);
INLINE LRotation(const LVector3<NumType> &, float);
INLINE LRotation(const LVector3<NumType> &, NumType);
INLINE LRotation(const LMatrix3<NumType> &);
INLINE LRotation(const LMatrix4<NumType> &);
INLINE LRotation(float, float, float);
INLINE LRotation(NumType, NumType, NumType);
virtual ~LRotation();
INLINE LRotation<NumType>

View File

@ -0,0 +1,11 @@
// Filename: vector_LVecBase3f.cxx
// Created by: drose (11Dec00)
//
////////////////////////////////////////////////////////////////////
#include "vector_LVecBase3f.h"
// Tell GCC that we'll take care of the instantiation explicitly here.
#ifdef __GNUC__
#pragma implementation
#endif

View File

@ -0,0 +1,32 @@
// Filename: vector_LVecBase3f.h
// Created by: drose (11Dec00)
//
////////////////////////////////////////////////////////////////////
#ifndef VECTOR_LVECBASE3F_H
#define VECTOR_LVECBASE3F_H
#include <pandabase.h>
#include "luse.h"
#include <vector>
////////////////////////////////////////////////////////////////////
// Class : vector_LVecBase3f
// Description : A vector of LVecBase3fs. This class is defined once here,
// and exported to PANDA.DLL; other packages that want
// to use a vector of this type (whether they need to
// export it or not) should include this header file,
// rather than defining the vector again.
////////////////////////////////////////////////////////////////////
EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, std::vector<LVecBase3f>)
typedef vector<LVecBase3f> vector_LVecBase3f;
// Tell GCC that we'll take care of the instantiation explicitly here.
#ifdef __GNUC__
#pragma interface
#endif
#endif

View File

@ -4,13 +4,17 @@
#define TARGET mathutil
#define LOCAL_LIBS \
linmath putil
#define USE_FFTW yes
#define UNIX_SYS_LIBS m
#define SOURCES \
boundingHexahedron.I boundingHexahedron.cxx boundingHexahedron.h \
boundingLine.I boundingLine.cxx boundingLine.h boundingSphere.I \
boundingSphere.cxx boundingSphere.h boundingVolume.I \
boundingVolume.cxx boundingVolume.h config_mathutil.cxx \
config_mathutil.h finiteBoundingVolume.cxx finiteBoundingVolume.h \
config_mathutil.h \
fftCompressor.cxx fftCompressor.h \
finiteBoundingVolume.cxx finiteBoundingVolume.h \
geometricBoundingVolume.I geometricBoundingVolume.cxx \
geometricBoundingVolume.h look_at.I look_at.cxx look_at.h \
omniBoundingVolume.I omniBoundingVolume.cxx omniBoundingVolume.h \
@ -19,7 +23,9 @@
#define INSTALL_HEADERS \
boundingHexahedron.I boundingHexahedron.h boundingLine.I \
boundingLine.h boundingSphere.I boundingSphere.h boundingVolume.I \
boundingVolume.h config_mathutil.h finiteBoundingVolume.h frustum.I \
boundingVolume.h config_mathutil.h \
fftCompressor.h \
finiteBoundingVolume.h frustum.I \
frustum.h geometricBoundingVolume.I geometricBoundingVolume.h \
look_at.I look_at.h mathHelpers.I mathHelpers.h mathutil.h \
omniBoundingVolume.I omniBoundingVolume.h plane.I plane.h \

View File

@ -16,6 +16,10 @@
Configure(config_mathutil);
NotifyCategoryDef(mathutil, "");
const double fft_offset = config_mathutil.GetDouble("fft-offset", 0.001);
const double fft_factor = config_mathutil.GetDouble("fft-factor", 0.1);
const double fft_exponent = config_mathutil.GetDouble("fft-exponent", 4);
ConfigureFn(config_mathutil) {
BoundingHexahedron::init_type();
BoundingSphere::init_type();

View File

@ -11,6 +11,10 @@
NotifyCategoryDecl(mathutil, EXPCL_PANDA, EXPTP_PANDA);
extern const double fft_offset;
extern const double fft_factor;
extern const double fft_exponent;
#endif

View File

@ -0,0 +1,735 @@
// Filename: fftCompressor.cxx
// Created by: drose (11Dec00)
//
////////////////////////////////////////////////////////////////////
#include "fftCompressor.h"
#include "config_mathutil.h"
#include <datagram.h>
#include <datagramIterator.h>
#include <compose_matrix.h>
#include <math.h>
#ifdef HAVE_FFTW
#include <rfftw.h>
// These FFTW support objects can only be defined if we actually have
// the FFTW library available.
static rfftw_plan get_real_compress_plan(int length);
static rfftw_plan get_real_decompress_plan(int length);
typedef map<int, rfftw_plan> RealPlans;
static RealPlans _real_compress_plans;
static RealPlans _real_decompress_plans;
#endif
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::Constructor
// Access: Public
// Description: Constructs a new compressor object with default
// parameters.
////////////////////////////////////////////////////////////////////
FFTCompressor::
FFTCompressor() {
set_quality(-1);
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::is_compression_available
// Access: Public, Static
// Description: Returns true if the FFTW library is compiled in, so
// that this class is actually capable of doing useful
// compression/decompression work. Returns false
// otherwise, in which case any attempt to write a
// compressed stream will actually write an uncompressed
// stream, and any attempt to read a compressed stream
// will fail.
////////////////////////////////////////////////////////////////////
bool FFTCompressor::
is_compression_available() {
#ifndef HAVE_FFTW
return false;
#else
return true;
#endif
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::set_quality
// Access: Public
// Description: Sets the quality factor for the compression. This is
// an integer in the range 0 - 100 that roughly controls
// how aggressively the reals are compressed; lower
// numbers mean smaller output, and more data loss.
//
// As a special case, a negative quality indicates that
// the individual parameters should be separately
// controlled via config variables, and a quality
// greater than 100 indicates lossless output.
////////////////////////////////////////////////////////////////////
void FFTCompressor::
set_quality(int quality) {
#ifndef HAVE_FFTW
// If we don't actually have FFTW, we can't really compress anything.
if (_quality <= 100) {
mathutil_cat.warning()
<< "FFTW library is not available; generating uncompressed output.\n";
}
_quality = 101;
#else
_quality = quality;
if (_quality < 0) {
// A negative quality indicates to read the important parameters
// from config variables.
_fft_offset = fft_offset;
_fft_factor = fft_factor;
_fft_exponent = fft_exponent;
} else if (_quality < 40) {
// 0 - 40 :
// fft-offset 1.0 - 0.001
// fft-factor 1.0
// fft-exponent 4.0
double t = (double)_quality / 40.0;
_fft_offset = interpolate(t, 1.0, 0.001);
_fft_factor = 1.0;
_fft_exponent = 4.0;
} else if (_quality < 95) {
// 40 - 95:
// fft-offset 0.001
// fft-factor 1.0 - 0.1
// fft-exponent 4.0
double t = (double)(_quality - 40) / 55.0;
_fft_offset = 0.001;
_fft_factor = interpolate(t, 1.0, 0.1);
_fft_exponent = 4.0;
} else {
// 95 - 100:
// fft-offset 0.001
// fft-factor 0.1 - 0.0
// fft-exponent 4.0
double t = (double)(_quality - 95) / 5.0;
_fft_offset = 0.001;
_fft_factor = interpolate(t, 0.1, 0.0);
_fft_exponent = 4.0;
}
#endif
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::get_quality
// Access: Public
// Description: Returns the quality number that was previously set
// via set_quality().
////////////////////////////////////////////////////////////////////
int FFTCompressor::
get_quality() const {
return _quality;
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::write_header
// Access: Public
// Description: Writes the compression parameters to the indicated
// datagram. It is necessary to call this before
// writing anything else to the datagram, since these
// parameters will be necessary to correctly decompress
// the data later.
////////////////////////////////////////////////////////////////////
void FFTCompressor::
write_header(Datagram &datagram) {
datagram.add_int8(_quality);
if (_quality < 0) {
datagram.add_float64(_fft_offset);
datagram.add_float64(_fft_factor);
datagram.add_float64(_fft_exponent);
}
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::write_reals
// Access: Public
// Description: Writes an array of floating-point numbers to the
// indicated datagram.
////////////////////////////////////////////////////////////////////
void FFTCompressor::
write_reals(Datagram &datagram, const float *array, int length) {
datagram.add_int32(length);
if (_quality > 100) {
// Special case: lossless output.
for (int i = 0; i < length; i++) {
datagram.add_float32(array[i]);
}
return;
}
#ifndef HAVE_FFTW
// If we don't have FFTW, we shouldn't get here.
nassertv(false);
#else
if (length == 0) {
// Special case: do nothing.
return;
}
if (length == 1) {
// Special case: just write out the one number.
datagram.add_float32(array[0]);
return;
}
// Normal case: FFT the array, and write that out.
double data[length];
int i;
for (i = 0; i < length; i++) {
data[i] = array[i];
}
double half_complex[length];
rfftw_plan plan = get_real_compress_plan(length);
rfftw_one(plan, data, half_complex);
if (mathutil_cat.is_debug()) {
mathutil_cat.debug()
<< "write_reals :";
for (int i = 0; i < length; i++) {
double scale_factor = get_scale_factor(i, length);
mathutil_cat.debug(false)
// << " " << data[i];
<< " " << floor(half_complex[i] / scale_factor + 0.5);
}
mathutil_cat.debug(false) << "\n";
}
// Now encode the numbers, run-length encoded by size, so we only
// write out the number of bits we need for each number.
vector_double run;
RunWidth run_width = RW_invalid;
int num_written = 0;
for (i = 0; i < length; i++) {
static const double max_range_32 = 2147483647.0;
static const double max_range_16 = 32767.0;
static const double max_range_8 = 127.0;
double scale_factor = get_scale_factor(i, length);
double num = floor(half_complex[i] / scale_factor + 0.5);
// How many bits do we need to encode this integer?
double a = fabs(num);
RunWidth num_width;
if (a == 0.0) {
num_width = RW_0;
} else if (a <= max_range_8) {
num_width = RW_8;
} else if (a <= max_range_16) {
num_width = RW_16;
} else if (a <= max_range_32) {
num_width = RW_32;
} else {
num_width = RW_double;
}
// A special case: if we're writing a string of one-byters and we
// come across a single intervening zero, don't interrupt the run
// just for that.
if (run_width == RW_8 && num_width == RW_0) {
if (i + 1 >= length || half_complex[i + 1] != 0.0) {
num_width = RW_8;
}
}
if (num_width != run_width) {
// Now we need to flush the last run.
num_written += write_run(datagram, run_width, run);
run.clear();
run_width = num_width;
}
run.push_back(num);
}
num_written += write_run(datagram, run_width, run);
nassertv(num_written == length);
#endif
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::write_hprs
// Access: Public
// Description: Writes an array of HPR angles to the indicated
// datagram.
////////////////////////////////////////////////////////////////////
void FFTCompressor::
write_hprs(Datagram &datagram, const LVecBase3f *array, int length) {
// First, convert the HPR's to quats. We expect quats to have
// better FFT consistency, and therefore compress better, even
// though they have an extra component.
// However, because the quaternion will be normalized, we don't even
// have to write out all three components; any three can be used to
// determine the fourth (provided we ensure consistency of sign).
vector_float qi, qj, qk;
for (int i = 0; i < length; i++) {
LMatrix3f mat;
compose_matrix(mat, LVecBase3f(1.0, 1.0, 1.0), array[i]);
LOrientationf rot;
rot.set(mat);
rot.normalize();
if (rot.get_r() < 0) {
// Since rot == -rot, we can flip the quarternion if need be to
// keep the r component positive. This has two advantages.
// One, it makes it possible to infer r completely given i, j,
// and k (since we know it must be >= 0), and two, it helps
// protect against poor continuity caused by inadvertent
// flipping of the quarternion's sign between frames.
// The choice of leaving r implicit rather than any of the other
// three seems to work the best in terms of guaranteeing
// continuity.
rot.set(-rot.get_r(), -rot.get_i(), -rot.get_j(), -rot.get_k());
}
qi.push_back(rot.get_i());
qj.push_back(rot.get_j());
qk.push_back(rot.get_k());
}
write_reals(datagram, &qi[0], length);
write_reals(datagram, &qj[0], length);
write_reals(datagram, &qk[0], length);
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::read_header
// Access: Public
// Description: Reads the compression header that was written
// previously. This fills in the compression parameters
// necessary to correctly decompress the following data.
//
// Returns true if the header is read successfully,
// false otherwise.
////////////////////////////////////////////////////////////////////
bool FFTCompressor::
read_header(DatagramIterator &di) {
_quality = di.get_int8();
#ifndef HAVE_FFTW
if (_quality <= 100) {
mathutil_cat.error()
<< "FFTW library is not available; cannot read compressed data.\n";
return false;
}
#endif
set_quality(_quality);
if (_quality < 0) {
_fft_offset = di.get_float64();
_fft_factor = di.get_float64();
_fft_exponent = di.get_float64();
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::read_reals
// Access: Public
// Description: Reads an array of floating-point numbers. The result
// is pushed onto the end of the indicated vector, which
// is not cleared first; it is the user's responsibility
// to ensure that the array is initially empty. Returns
// true if the data is read correctly, false if there is
// an error.
////////////////////////////////////////////////////////////////////
bool FFTCompressor::
read_reals(DatagramIterator &di, vector_float &array) {
int length = di.get_int32();
if (_quality > 100) {
// Special case: lossless output.
for (int i = 0; i < length; i++) {
array.push_back(di.get_float32());
}
return true;
}
#ifndef HAVE_FFTW
// If we don't have FFTW, we shouldn't get here.
return false;
#else
if (length == 0) {
// Special case: do nothing.
return true;
}
if (length == 1) {
// Special case: just read in the one number.
array.push_back(di.get_float32());
return true;
}
// Normal case: read in the FFT array, and convert it back to
// (nearly) the original numbers.
vector_double half_complex;
half_complex.reserve(length);
int num_read = 0;
while (num_read < length) {
num_read += read_run(di, half_complex);
}
nassertr(num_read == length, false);
nassertr((int)half_complex.size() == length, false);
int i;
for (i = 0; i < length; i++) {
half_complex[i] *= get_scale_factor(i, length);
}
double data[length];
rfftw_plan plan = get_real_decompress_plan(length);
rfftw_one(plan, &half_complex[0], data);
double scale = 1.0 / (double)length;
array.reserve(array.size() + length);
for (i = 0; i < length; i++) {
array.push_back(data[i] * scale);
}
if (mathutil_cat.is_debug()) {
mathutil_cat.debug()
<< "read_reals :";
for (int i = 0; i < length; i++) {
mathutil_cat.debug(false)
<< " " << data[i] * scale;
}
mathutil_cat.debug(false) << "\n";
}
return true;
#endif
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::read_hprs
// Access: Public
// Description: Reads an array of HPR angles. The result is pushed
// onto the end of the indicated vector, which is not
// cleared first; it is the user's responsibility to
// ensure that the array is initially empty.
////////////////////////////////////////////////////////////////////
bool FFTCompressor::
read_hprs(DatagramIterator &di, vector_LVecBase3f &array) {
vector_float qi, qj, qk;
bool okflag = true;
okflag =
read_reals(di, qi) &&
read_reals(di, qj) &&
read_reals(di, qk);
if (okflag) {
nassertr(qi.size() == qj.size() && qj.size() == qk.size(), false);
array.reserve(array.size() + qi.size());
for (int i = 0; i < (int)qi.size(); i++) {
float qr2 = 1.0 - (qi[i] * qi[i] + qj[i] * qj[i] + qk[i] * qk[i]);
float qr = qr2 < 0.0 ? 0.0 : sqrtf(qr2);
LOrientationf rot(qr, qi[i], qj[i], qk[i]);
rot.normalize(); // Just for good measure.
LMatrix3f mat;
rot.extract_to_matrix(mat);
LVecBase3f scale, hpr;
bool success = decompose_matrix(mat, scale, hpr);
nassertr(success, false);
array.push_back(hpr);
}
}
return okflag;
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::free_storage
// Access: Public, Static
// Description: Frees memory that has been allocated during past runs
// of the FFTCompressor. This is an optional call, but
// it may be made from time to time to empty the global
// cache that the compressor objects keep to facilitate
// fast compression/decompression.
////////////////////////////////////////////////////////////////////
void FFTCompressor::
free_storage() {
RealPlans::iterator pi;
for (pi = _real_compress_plans.begin();
pi != _real_compress_plans.end();
++pi) {
rfftw_destroy_plan((*pi).second);
}
_real_compress_plans.clear();
for (pi = _real_decompress_plans.begin();
pi != _real_decompress_plans.end();
++pi) {
rfftw_destroy_plan((*pi).second);
}
_real_decompress_plans.clear();
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::write_run
// Access: Private
// Description: Writes a sequence of integers that all require the
// same number of bits. Returns the number of integers
// written, i.e. run.size().
////////////////////////////////////////////////////////////////////
int FFTCompressor::
write_run(Datagram &datagram, FFTCompressor::RunWidth run_width,
const vector_double &run) {
if (run.empty()) {
return 0;
}
nassertr(run_width != RW_invalid, 0);
if (run_width != RW_double) {
// If the width is anything other than RW_double, we write a
// single byte indicating the width and length of the upcoming
// run.
if (run.size() <= RW_length_mask &&
((int)run_width | run.size()) != RW_double) {
// If there are enough bits remaining in the byte, use them to
// indicate the length of the run. We have to be a little
// careful, however, not to accidentally write a byte that looks
// like an RW_double flag.
datagram.add_uint8((int)run_width | run.size());
} else {
// Otherwise, write zero as the length, to indicate that we'll
// write the actual length in the following 16-bit word.
datagram.add_uint8(run_width);
// Assuming, of course, that the length fits within 16 bits.
nassertr(run.size() < 65536, 0);
nassertr(run.size() != 0, 0);
datagram.add_uint16(run.size());
}
}
// Now write the data itself.
vector_double::const_iterator ri;
switch (run_width) {
case RW_0:
// If it's a string of zeroes, we're done!
break;
case RW_8:
for (ri = run.begin(); ri != run.end(); ++ri) {
datagram.add_int8((int)*ri);
}
break;
case RW_16:
for (ri = run.begin(); ri != run.end(); ++ri) {
datagram.add_int16((int)*ri);
}
break;
case RW_32:
for (ri = run.begin(); ri != run.end(); ++ri) {
datagram.add_int32((int)*ri);
}
break;
case RW_double:
for (ri = run.begin(); ri != run.end(); ++ri) {
// In the case of RW_double, we only write the numbers one at a
// time, each time preceded by the RW_double flag. Hopefully
// this will happen only rarely.
datagram.add_int8((int)RW_double);
datagram.add_float64(*ri);
}
break;
default:
break;
}
return run.size();
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::read_run
// Access: Private
// Description: Reads a sequence of integers that all require the
// same number of bits. Returns the number of integers
// read. It is the responsibility of the user to clear
// the vector before calling this function, or the
// numbers read will be appended to the end.
////////////////////////////////////////////////////////////////////
int FFTCompressor::
read_run(DatagramIterator &di, vector_double &run) {
PN_uint8 start = di.get_uint8();
RunWidth run_width;
int length;
if ((start & 0xff) == RW_double) {
// RW_double is a special case, and requires the whole byte. In
// this case, we don't encode a length, but assume it's only one.
run_width = RW_double;
length = 1;
} else {
run_width = (RunWidth)(start & RW_width_mask);
length = start & RW_length_mask;
}
if (length == 0) {
// If the length was zero, it means the actual length follows as a
// 16-bit word.
length = di.get_uint16();
}
nassertr(length != 0, 0);
run.reserve(run.size() + length);
int i;
switch (run_width) {
case RW_0:
for (i = 0; i < length; i++) {
run.push_back(0.0);
}
break;
case RW_8:
for (i = 0; i < length; i++) {
run.push_back((double)(int)di.get_int8());
}
break;
case RW_16:
for (i = 0; i < length; i++) {
run.push_back((double)(int)di.get_int16());
}
break;
case RW_32:
for (i = 0; i < length; i++) {
run.push_back((double)(int)di.get_int32());
}
break;
case RW_double:
for (i = 0; i < length; i++) {
run.push_back(di.get_float64());
}
break;
default:
break;
}
return length;
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::get_scale_factor
// Access: Private
// Description: Returns the appropriate scaling for the given
// position within the halfcomplex array.
////////////////////////////////////////////////////////////////////
double FFTCompressor::
get_scale_factor(int i, int length) const {
int m = (length / 2) + 1;
int k = (i < m) ? i : length - i;
nassertr(k >= 0 && k < m, 1.0);
return _fft_offset +
_fft_factor * pow((double)(m-1 - k) / (double)(m-1), _fft_exponent);
}
////////////////////////////////////////////////////////////////////
// Function: FFTCompressor::interpolate
// Access: Private, Static
// Description: Returns a number between a and b, inclusive,
// according to the value of t between 0 and 1,
// inclusive.
////////////////////////////////////////////////////////////////////
double FFTCompressor::
interpolate(double t, double a, double b) {
return a + t * (b - a);
}
#ifdef HAVE_FFTW
////////////////////////////////////////////////////////////////////
// Function: get_real_compress_plan
// Description: Returns a FFTW plan suitable for compressing a float
// array of the indicated length.
////////////////////////////////////////////////////////////////////
static rfftw_plan
get_real_compress_plan(int length) {
RealPlans::iterator pi;
pi = _real_compress_plans.find(length);
if (pi != _real_compress_plans.end()) {
return (*pi).second;
}
rfftw_plan plan;
plan = rfftw_create_plan(length, FFTW_REAL_TO_COMPLEX, FFTW_MEASURE);
_real_compress_plans.insert(RealPlans::value_type(length, plan));
return plan;
}
////////////////////////////////////////////////////////////////////
// Function: get_real_decompress_plan
// Description: Returns a FFTW plan suitable for decompressing a float
// array of the indicated length.
////////////////////////////////////////////////////////////////////
static rfftw_plan
get_real_decompress_plan(int length) {
RealPlans::iterator pi;
pi = _real_decompress_plans.find(length);
if (pi != _real_decompress_plans.end()) {
return (*pi).second;
}
rfftw_plan plan;
plan = rfftw_create_plan(length, FFTW_COMPLEX_TO_REAL, FFTW_MEASURE);
_real_decompress_plans.insert(RealPlans::value_type(length, plan));
return plan;
}
#endif

View File

@ -0,0 +1,83 @@
// Filename: fftCompressor.h
// Created by: drose (11Dec00)
//
////////////////////////////////////////////////////////////////////
#ifndef FFTCOMPRESSOR_H
#define FFTCOMPRESSOR_H
#include <pandabase.h>
#include <pointerToArray.h>
#include <vector_float.h>
#include <vector_double.h>
#include <vector_LVecBase3f.h>
class Datagram;
class DatagramIterator;
////////////////////////////////////////////////////////////////////
// Class : FFTCompressor
// Description : This class manages a lossy compression and
// decompression of a stream of floating-point numbers
// to a datagram, based a fourier transform algorithm
// (similar in principle to JPEG compression).
//
// Actually, it doesn't do any real compression on its
// own; it just outputs a stream of integers that should
// compress much tighter via gzip than the original
// stream of floats would have.
//
// This class depends on the external FFTW library;
// without it, it will fall back on lossless output of
// the original data.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA FFTCompressor {
public:
FFTCompressor();
static bool is_compression_available();
void set_quality(int quality);
int get_quality() const;
void write_header(Datagram &datagram);
void write_reals(Datagram &datagram, const float *array, int length);
void write_hprs(Datagram &datagram, const LVecBase3f *array, int length);
bool read_header(DatagramIterator &di);
bool read_reals(DatagramIterator &di, vector_float &array);
bool read_hprs(DatagramIterator &di, vector_LVecBase3f &array);
static void free_storage();
private:
enum RunWidth {
// We write a byte to the datagram at the beginning of each run to
// encode the width and length of the run. The width is indicated
// by the top two bits, while the length fits in the lower six,
// except RW_double, which is a special case.
RW_width_mask = 0xc0,
RW_length_mask = 0x3f,
RW_0 = 0x00,
RW_8 = 0x40,
RW_16 = 0x80,
RW_32 = 0xc0,
RW_double = 0xff,
RW_invalid = 0x01
};
int write_run(Datagram &datagram, RunWidth run_width,
const vector_double &run);
int read_run(DatagramIterator &di, vector_double &run);
double get_scale_factor(int i, int length) const;
static double interpolate(double t, double a, double b);
int _quality;
double _fft_offset;
double _fft_factor;
double _fft_exponent;
};
#endif

View File

@ -15,7 +15,8 @@
config_util.N config_util.cxx config_util.h configurable.cxx \
configurable.h factoryBase.I factoryBase.cxx factoryBase.h \
factoryParam.I factoryParam.cxx factoryParam.h factoryParams.I \
factoryParams.cxx factoryParams.h globPattern.I globPattern.cxx \
factoryParams.cxx factoryParams.h \
globPattern.I globPattern.cxx \
globPattern.h globalPointerRegistry.I globalPointerRegistry.cxx \
globalPointerRegistry.h ioPtaDatagramFloat.cxx ioPtaDatagramFloat.h \
ioPtaDatagramInt.cxx ioPtaDatagramInt.h ioPtaDatagramShort.cxx \
@ -46,7 +47,8 @@
buttonRegistry.h collideMask.h \
config_util.h configurable.h factory.I factory.h \
factoryBase.I factoryBase.h factoryParam.I factoryParam.h \
factoryParams.I factoryParams.h globPattern.I globPattern.h \
factoryParams.I factoryParams.h \
globPattern.I globPattern.h \
globalPointerRegistry.I globalPointerRegistry.h indirectCompareTo.I \
indirectCompareTo.h ioPtaDatagramFloat.h ioPtaDatagramInt.h \
ioPtaDatagramShort.h iterator_types.h keyboardButton.h lineStream.I \