36 static fftw_plan get_real_compress_plan(
int length);
37 static fftw_plan get_real_decompress_plan(
int length);
40 static RealPlans _real_compress_plans;
41 static RealPlans _real_decompress_plans;
50 _bam_minor_version = 0;
52 _use_error_threshold =
false;
53 _transpose_quats =
false;
92 if (_quality <= 100) {
93 mathutil_cat.warning()
94 <<
"FFTW library is not available; generating uncompressed output.\n";
104 _fft_offset = fft_offset;
105 _fft_factor = fft_factor;
106 _fft_exponent = fft_exponent;
108 }
else if (_quality < 40) {
111 double t = (double)_quality / 40.0;
112 _fft_offset = interpolate(t, 1.0, 0.001);
116 }
else if (_quality < 95) {
119 double t = (double)(_quality - 40) / 55.0;
121 _fft_factor = interpolate(t, 1.0, 0.1);
127 double t = (double)(_quality - 95) / 5.0;
129 _fft_factor = interpolate(t, 0.1, 0.0);
153 _use_error_threshold = use_error_threshold;
162 return _use_error_threshold;
172 _transpose_quats = flag;
180 return _transpose_quats;
205 if (_quality > 100) {
207 for (
int i = 0; i < length; i++) {
233 bool reject_compression =
false;
248 datagram.
add_bool(reject_compression);
249 if (reject_compression) {
250 if (mathutil_cat.is_debug()) {
252 <<
"Writing stream of " << length <<
" numbers uncompressed.\n";
254 for (
int i = 0; i < length; i++) {
261 int fft_length = length / 2 + 1;
262 fftw_complex *fft_bins = (fftw_complex *)alloca(fft_length *
sizeof(fftw_complex));
267 double *data = &fft_bins[0][0];
269 for (i = 0; i < length; i++) {
274 fftw_plan plan = get_real_compress_plan(length);
275 fftw_execute_dft_r2c(plan, data, fft_bins);
288 RunWidth run_width = RW_invalid;
291 for (i = 0; i < length; i++) {
292 static const double max_range_32 = 2147483647.0;
293 static const double max_range_16 = 32767.0;
294 static const double max_range_8 = 127.0;
298 if (i < fft_length) {
306 double scale_factor = get_scale_factor(bin, fft_length);
307 double num = cfloor(fft_bins[bin][j] / scale_factor + 0.5);
310 double a = fabs(num);
316 }
else if (a <= max_range_8) {
319 }
else if (a <= max_range_16) {
322 }
else if (a <= max_range_32) {
326 num_width = RW_double;
332 if (run_width == RW_8 && num_width == RW_0) {
333 if (run.back() != 0) {
338 if (num_width != run_width) {
344 bool reclaimed_zero = (run_width == RW_8 && num_width == RW_0 &&
346 if (reclaimed_zero) {
350 num_written += write_run(datagram, run_width, run);
352 run_width = num_width;
354 if (reclaimed_zero) {
362 num_written += write_run(datagram, run_width, run);
363 nassertv(num_written == length);
373 if (_quality >= 104) {
376 vector_stdfloat h, p, r;
382 for (
int i = 0; i < length; i++) {
383 h.push_back(array[i][0]);
384 p.push_back(array[i][1]);
385 r.push_back(array[i][2]);
399 if (_quality >= 103) {
406 for (
int i = 0; i < length; i++) {
408 compose_matrix(mat, LVecBase3(1.0, 1.0, 1.0), LVecBase3(0.0, 0.0, 0.0),
410 m00.push_back(mat(0, 0));
411 m01.push_back(mat(0, 1));
412 m02.push_back(mat(0, 2));
413 m10.push_back(mat(1, 0));
414 m11.push_back(mat(1, 1));
415 m12.push_back(mat(1, 2));
416 m20.push_back(mat(2, 0));
417 m21.push_back(mat(2, 1));
418 m22.push_back(mat(2, 2));
454 vector_stdfloat qr, qi, qj, qk;
461 for (
int i = 0; i < length; i++) {
463 compose_matrix(mat, LVecBase3(1.0, 1.0, 1.0), LVecBase3(0.0, 0.0, 0.0),
465 if (_transpose_quats) {
466 mat.transpose_in_place();
469 LOrientation rot(mat);
472 if (rot.get_r() < 0) {
482 rot.set(-rot.get_r(), -rot.get_i(), -rot.get_j(), -rot.get_k());
486 if (mathutil_cat.is_warning()) {
488 rot.extract_to_matrix(mat2);
489 if (!mat.almost_equal(mat2, 0.0001)) {
490 LVecBase3 hpr1, hpr2;
491 LVecBase3 scale, shear;
492 decompose_matrix(mat, scale, shear, hpr1);
493 decompose_matrix(mat2, scale, shear, hpr2);
494 mathutil_cat.warning()
495 <<
"Converted hpr to quaternion incorrectly!\n" 496 <<
" Source hpr: " << array[i] <<
", or " << hpr1 <<
"\n";
497 mathutil_cat.warning(
false)
498 <<
" Quaternion: " << rot <<
"\n" 499 <<
" Which represents: hpr " << hpr2 <<
" scale " 505 qr.push_back(rot.get_r());
506 qi.push_back(rot.get_i());
507 qj.push_back(rot.get_j());
508 qk.push_back(rot.get_k());
514 if (_quality >= 102) {
542 _bam_minor_version = bam_minor_version;
545 if (mathutil_cat.is_debug()) {
547 <<
"Found compressed data at quality level " << _quality <<
"\n";
551 if (_quality <= 100) {
553 <<
"FFTW library is not available; cannot read compressed data.\n";
579 if (_quality > 100) {
580 array.reserve(array.size() + length);
583 for (
int i = 0; i < length; i++) {
611 bool reject_compression = di.
get_bool();
612 if (reject_compression) {
613 array.reserve(array.size() + length);
614 for (
int i = 0; i < length; i++) {
620 vector_double half_complex;
621 half_complex.reserve(length);
623 while (num_read < length) {
624 num_read += read_run(di, half_complex);
626 nassertr(num_read == length,
false);
627 nassertr((
int)half_complex.size() == length,
false);
629 int fft_length = length / 2 + 1;
630 fftw_complex *fft_bins = (fftw_complex *)alloca(fft_length *
sizeof(fftw_complex));
633 for (i = 0; i < fft_length; i++) {
634 double scale_factor = get_scale_factor(i, fft_length);
639 fft_bins[i][0] = half_complex[i] * scale_factor;
642 fft_bins[i][1] = 0.0;
643 }
else if ((i == fft_length - 1) && !(length & 1)) {
645 fft_bins[i][1] = 0.0;
647 fft_bins[i][1] = half_complex[length - i] * scale_factor;
654 double *data = &fft_bins[0][0];
657 fftw_plan plan = get_real_decompress_plan(length);
658 fftw_execute_dft_c2r(plan, fft_bins, data);
660 double scale = 1.0 / (double)length;
661 array.reserve(array.size() + length);
662 for (i = 0; i < length; i++) {
663 array.push_back(data[i] * scale);
683 if (_quality >= 104) {
686 vector_stdfloat h, p, r;
694 nassertr(h.size() == p.size() && p.size() == r.size(),
false);
695 for (
int i = 0; i < (int)h.size(); i++) {
696 array.push_back(LVecBase3(h[i], p[i], r[i]));
702 if (_quality >= 103) {
722 for (
int i = 0; i < (int)m00.size(); i++) {
723 LMatrix3 mat(m00[i], m01[i], m02[i],
724 m10[i], m11[i], m12[i],
725 m20[i], m21[i], m22[i]);
726 LVecBase3 scale, shear, hpr;
728 decompose_matrix(mat, scale, shear, hpr);
730 decompose_matrix_old_hpr(mat, scale, shear, hpr);
732 array.push_back(hpr);
740 vector_stdfloat qr, qi, qj, qk;
745 if (_quality >= 102) {
757 nassertr(qi.size() == qj.size() && qj.size() == qk.size(),
false);
759 array.reserve(array.size() + qi.size());
760 for (
int i = 0; i < (int)qi.size(); i++) {
764 PN_stdfloat qr2 = 1.0 - (qi[i] * qi[i] + qj[i] * qj[i] + qk[i] * qk[i]);
765 PN_stdfloat qr1 = qr2 < 0.0 ? 0.0 : sqrtf(qr2);
767 rot.set(qr1, qi[i], qj[i], qk[i]);
770 if (_quality >= 102) {
774 if (!IS_THRESHOLD_EQUAL(qr[i], qr1, 0.001)) {
775 mathutil_cat.warning()
776 <<
"qr[" << i <<
"] = " << qr[i] <<
", qr1 = " << qr1
777 <<
", diff is " << qr1 - qr[i] <<
"\n";
785 rot.extract_to_matrix(mat);
786 if (_transpose_quats) {
787 mat.transpose_in_place();
789 LVecBase3 scale, shear, hpr;
791 decompose_matrix(mat, scale, shear, hpr);
793 decompose_matrix_old_hpr(mat, scale, shear, hpr);
795 array.push_back(hpr);
822 RealPlans::iterator pi;
823 for (pi = _real_compress_plans.begin();
824 pi != _real_compress_plans.end();
826 fftw_destroy_plan((*pi).second);
828 _real_compress_plans.clear();
830 for (pi = _real_decompress_plans.begin();
831 pi != _real_decompress_plans.end();
833 fftw_destroy_plan((*pi).second);
835 _real_decompress_plans.clear();
844 write_run(
Datagram &datagram, FFTCompressor::RunWidth run_width,
845 const vector_double &run) {
849 nassertr(run_width != RW_invalid, 0);
851 if (run_width != RW_double) {
855 if (run.size() <= RW_length_mask &&
856 ((int)run_width | run.size()) != RW_double) {
860 datagram.
add_uint8((
int)run_width | run.size());
868 nassertr(run.size() < 65536, 0);
869 nassertr(run.size() != 0, 0);
876 vector_double::const_iterator ri;
883 for (ri = run.begin(); ri != run.end(); ++ri) {
889 for (ri = run.begin(); ri != run.end(); ++ri) {
895 for (ri = run.begin(); ri != run.end(); ++ri) {
901 for (ri = run.begin(); ri != run.end(); ++ri) {
905 datagram.
add_int8((int8_t)RW_double);
929 if ((start & 0xff) == RW_double) {
932 run_width = RW_double;
936 run_width = (RunWidth)(start & RW_width_mask);
937 length = start & RW_length_mask;
945 nassertr(length != 0, 0);
947 run.reserve(run.size() + length);
952 for (i = 0; i < length; i++) {
958 for (i = 0; i < length; i++) {
959 run.push_back((
double)(
int)di.
get_int8());
964 for (i = 0; i < length; i++) {
965 run.push_back((
double)(
int)di.
get_int16());
970 for (i = 0; i < length; i++) {
971 run.push_back((
double)(
int)di.
get_int32());
976 for (i = 0; i < length; i++) {
995 double FFTCompressor::
996 get_scale_factor(
int i,
int length)
const {
997 nassertr(i < length, 1.0);
1000 _fft_factor * pow((
double)(length - i) / (
double)(length), _fft_exponent);
1007 double FFTCompressor::
1008 interpolate(
double t,
double a,
double b) {
1009 return a + t * (b - a);
1017 PN_stdfloat FFTCompressor::
1018 get_compressability(
const PN_stdfloat *data,
int length)
const {
1027 PN_stdfloat sum = 0.0;
1028 PN_stdfloat sum2 = 0.0;
1029 for (
int i = 1; i < length; i++) {
1030 PN_stdfloat delta = data[i] - data[i - 1];
1033 sum2 += delta * delta;
1035 PN_stdfloat variance = (sum2 - (sum * sum) / (length - 1)) / (length - 2);
1036 if (variance < 0.0) {
1041 PN_stdfloat std_deviation = csqrt(variance);
1043 return std_deviation;
1055 get_real_compress_plan(
int length) {
1056 RealPlans::iterator pi;
1057 pi = _real_compress_plans.find(length);
1058 if (pi != _real_compress_plans.end()) {
1059 return (*pi).second;
1063 plan = fftw_plan_dft_r2c_1d(length,
nullptr,
nullptr, FFTW_ESTIMATE);
1064 _real_compress_plans.insert(RealPlans::value_type(length, plan));
1074 get_real_decompress_plan(
int length) {
1075 RealPlans::iterator pi;
1076 pi = _real_decompress_plans.find(length);
1077 if (pi != _real_decompress_plans.end()) {
1078 return (*pi).second;
1082 plan = fftw_plan_dft_c2r_1d(length,
nullptr,
nullptr, FFTW_ESTIMATE);
1083 _real_decompress_plans.insert(RealPlans::value_type(length, plan));
void add_int16(int16_t value)
Adds a signed 16-bit integer to the datagram.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool read_reals(DatagramIterator &di, vector_stdfloat &array)
Reads an array of floating-point numbers.
bool read_hprs(DatagramIterator &di, pvector< LVecBase3 > &array, bool new_hpr)
Reads an array of HPR angles.
This is our own Panda specialization on the default STL map.
void set_transpose_quats(bool flag)
Sets the transpose_quats flag.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_quality(int quality)
Sets the quality factor for the compression.
bool get_bool()
Extracts a boolean value.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool read_header(DatagramIterator &di, int bam_minor_version)
Reads the compression header that was written previously.
static void free_storage()
Frees memory that has been allocated during past runs of the FFTCompressor.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool get_use_error_threshold() const
Returns whether the error threshold measurement is enabled.
int32_t get_int32()
Extracts a signed 32-bit integer.
void add_stdfloat(PN_stdfloat value)
Adds either a 32-bit or a 64-bit floating-point number, according to set_stdfloat_double().
This is our own Panda specialization on the default STL vector.
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
void add_int8(int8_t value)
Adds a signed 8-bit integer to the datagram.
void add_bool(bool value)
Adds a boolean value to the datagram.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void write_header(Datagram &datagram)
Writes the compression parameters to the indicated datagram.
int get_quality() const
Returns the quality number that was previously set via set_quality().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void write_reals(Datagram &datagram, const PN_stdfloat *array, int length)
Writes an array of floating-point numbers to the indicated datagram.
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
bool get_transpose_quats() const
Returns the transpose_quats flag.
void write_hprs(Datagram &datagram, const LVecBase3 *array, int length)
Writes an array of HPR angles to the indicated datagram.
FFTCompressor()
Constructs a new compressor object with default parameters.
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
int16_t get_int16()
Extracts a signed 16-bit integer.
A class to retrieve the individual data elements previously stored in a Datagram.
void set_use_error_threshold(bool use_error_threshold)
Enables or disables the use of the error threshold measurement to put a cap on the amount of damage d...
int8_t get_int8()
Extracts a signed 8-bit integer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
static bool is_compression_available()
Returns true if the FFTW library is compiled in, so that this class is actually capable of doing usef...