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));
A class to retrieve the individual data elements previously stored in a Datagram.
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
int16_t get_int16()
Extracts a signed 16-bit integer.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
int8_t get_int8()
Extracts a signed 8-bit integer.
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
bool get_bool()
Extracts a boolean value.
int32_t get_int32()
Extracts a signed 32-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
void add_int16(int16_t value)
Adds a signed 16-bit integer to the datagram.
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
void add_stdfloat(PN_stdfloat value)
Adds either a 32-bit or a 64-bit floating-point number, according to set_stdfloat_double().
void add_bool(bool value)
Adds a boolean value to the datagram.
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
void add_int8(int8_t value)
Adds a signed 8-bit integer to the datagram.
static void free_storage()
Frees memory that has been allocated during past runs of the FFTCompressor.
bool read_reals(DatagramIterator &di, vector_stdfloat &array)
Reads an array of floating-point numbers.
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...
void set_transpose_quats(bool flag)
Sets the transpose_quats flag.
FFTCompressor()
Constructs a new compressor object with default parameters.
bool read_hprs(DatagramIterator &di, pvector< LVecBase3 > &array, bool new_hpr)
Reads an array of HPR angles.
bool get_use_error_threshold() const
Returns whether the error threshold measurement is enabled.
void set_quality(int quality)
Sets the quality factor for the compression.
bool read_header(DatagramIterator &di, int bam_minor_version)
Reads the compression header that was written previously.
int get_quality() const
Returns the quality number that was previously set via set_quality().
static bool is_compression_available()
Returns true if the FFTW library is compiled in, so that this class is actually capable of doing usef...
void write_header(Datagram &datagram)
Writes the compression parameters to the indicated datagram.
void write_reals(Datagram &datagram, const PN_stdfloat *array, int length)
Writes an array of floating-point numbers to the indicated datagram.
void write_hprs(Datagram &datagram, const LVecBase3 *array, int length)
Writes an array of HPR angles to the indicated datagram.
bool get_transpose_quats() const
Returns the transpose_quats flag.
This is our own Panda specialization on the default STL map.
This is our own Panda specialization on the default STL vector.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.