mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
*** empty log message ***
This commit is contained in:
parent
0eb216f274
commit
a6505665ed
@ -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
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -93,7 +93,6 @@ safe_to_transform() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: Character::app_traverse
|
||||
// Access: Public, Virtual
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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>
|
||||
|
11
panda/src/linmath/vector_LVecBase3f.cxx
Normal file
11
panda/src/linmath/vector_LVecBase3f.cxx
Normal 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
|
32
panda/src/linmath/vector_LVecBase3f.h
Normal file
32
panda/src/linmath/vector_LVecBase3f.h
Normal 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
|
@ -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 \
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
735
panda/src/mathutil/fftCompressor.cxx
Normal file
735
panda/src/mathutil/fftCompressor.cxx
Normal 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
|
83
panda/src/mathutil/fftCompressor.h
Normal file
83
panda/src/mathutil/fftCompressor.h
Normal 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
|
||||
|
@ -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 \
|
||||
|
Loading…
x
Reference in New Issue
Block a user