Panda3D
animChannelScalarTable.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file animChannelScalarTable.cxx
10  * @author drose
11  * @date 1999-02-22
12  */
13 
14 #include "animChannelScalarTable.h"
15 #include "animBundle.h"
16 #include "config_chan.h"
17 
18 #include "indent.h"
19 #include "datagram.h"
20 #include "datagramIterator.h"
21 #include "bamReader.h"
22 #include "bamWriter.h"
23 #include "fftCompressor.h"
24 
25 TypeHandle AnimChannelScalarTable::_type_handle;
26 
27 /**
28  *
29  */
30 AnimChannelScalarTable::
31 AnimChannelScalarTable() : _table(get_class_type()) {
32 }
33 
34 /**
35  * Creates a new AnimChannelScalarTable, just like this one, without copying
36  * any children. The new copy is added to the indicated parent. Intended to
37  * be called by make_copy() only.
38  */
39 AnimChannelScalarTable::
40 AnimChannelScalarTable(AnimGroup *parent, const AnimChannelScalarTable &copy) :
41  AnimChannelScalar(parent, copy),
42  _table(copy._table)
43 {
44 }
45 
46 /**
47  *
48  */
49 AnimChannelScalarTable::
50 AnimChannelScalarTable(AnimGroup *parent, const std::string &name) :
51  AnimChannelScalar(parent, name),
52  _table(get_class_type())
53 {
54 }
55 
56 /**
57  * Returns true if the value has changed since the last call to has_changed().
58  * last_frame is the frame number of the last call; this_frame is the current
59  * frame number.
60  */
62 has_changed(int last_frame, double last_frac,
63  int this_frame, double this_frac) {
64  if (_table.size() > 1) {
65  if (last_frame != this_frame) {
66  if (_table[last_frame % _table.size()] !=
67  _table[this_frame % _table.size()]) {
68  return true;
69  }
70  }
71  if (last_frac != this_frac) {
72  // If we have some fractional changes, also check the next subsequent
73  // frame (since we'll be blending with that).
74  if (_table[last_frame % _table.size()] !=
75  _table[(this_frame + 1) % _table.size()]) {
76  return true;
77  }
78  }
79  }
80 
81  return false;
82 }
83 
84 /**
85  * Gets the value of the channel at the indicated frame.
86  */
88 get_value(int frame, PN_stdfloat &value) {
89  if (_table.empty()) {
90  value = 0.0f;
91  } else {
92  value = _table[frame % _table.size()];
93  }
94 }
95 
96 
97 /**
98  * Assigns the data table.
99  */
101 set_table(const CPTA_stdfloat &table) {
102  int num_frames = _root->get_num_frames();
103 
104  if (table.size() > 1 && (int)table.size() < num_frames) {
105  // The new table has an invalid number of frames--it doesn't match the
106  // bundle's requirement.
107  nassert_raise("mismatched number of frames");
108  return;
109  }
110 
111  _table = table;
112 }
113 
114 /**
115  * Writes a brief description of the table and all of its descendants.
116  */
118 write(std::ostream &out, int indent_level) const {
119  indent(out, indent_level)
120  << get_type() << " " << get_name() << " " << _table.size();
121 
122  if (!_children.empty()) {
123  out << " {\n";
124  write_descendants(out, indent_level + 2);
125  indent(out, indent_level) << "}";
126  }
127 
128  out << "\n";
129 }
130 
131 /**
132  * Returns a copy of this object, and attaches it to the indicated parent
133  * (which may be NULL only if this is an AnimBundle). Intended to be called
134  * by copy_subtree() only.
135  */
136 AnimGroup *AnimChannelScalarTable::
137 make_copy(AnimGroup *parent) const {
138  return new AnimChannelScalarTable(parent, *this);
139 }
140 
141 /**
142  * Function to write the important information in the particular object to a
143  * Datagram
144  */
148 
149  if (compress_channels) {
150  chan_cat.warning()
151  << "FFT compression of animations is deprecated. For compatibility "
152  "with future versions of Panda3D, set compress-channels to false.\n";
153 
155  chan_cat.error()
156  << "Compression is not available; writing uncompressed channels.\n";
157  compress_channels = false;
158  }
159  }
160 
161  me.add_bool(compress_channels);
162  if (!compress_channels) {
163  // Write out everything the old way, as floats.
164  me.add_uint16(_table.size());
165  for(int i = 0; i < (int)_table.size(); i++) {
166  me.add_stdfloat(_table[i]);
167  }
168 
169  } else {
170  // Some channels, particularly blink channels, may involve only a small
171  // number of discrete values. If we come across one of those, write it
172  // out losslessly, since the lossy compression could damage it
173  // significantly (and we can achieve better compression directly anyway).
174  // We consider the channel value only to the nearest 1000th for this
175  // purpose, because floats aren't very good at being precisely equal to
176  // each other.
177  static const int max_values = 16;
178  static const PN_stdfloat scale = 1000.0f;
179 
180  pmap<int, int> index;
181  int i;
182  for (i = 0;
183  i < (int)_table.size() && (int)index.size() <= max_values;
184  i++) {
185  int value = (int)cfloor(_table[i] * scale + 0.5f);
186  index.insert(pmap<int, int>::value_type(value, index.size()));
187  }
188  int index_length = index.size();
189  if (index_length <= max_values) {
190  // All right, here's a blink channel. Now we write out the index table,
191  // and then a table of all the index values, two per byte.
192  me.add_uint8(index_length);
193 
194  if (index_length > 0) {
195  // We need to write the index in order by its index number; for this,
196  // we need to invert the index.
197  vector_stdfloat reverse_index(index_length);
199  for (mi = index.begin(); mi != index.end(); ++mi) {
200  PN_stdfloat f = (PN_stdfloat)(*mi).first / scale;
201  int i = (*mi).second;
202  nassertv(i >= 0 && i < (int)reverse_index.size());
203  reverse_index[i] = f;
204  }
205 
206  for (i = 0; i < index_length; i++) {
207  me.add_stdfloat(reverse_index[i]);
208  }
209 
210  // Now write out the actual channels. We write these two at a time,
211  // in the high and low nibbles of each byte.
212  int table_length = _table.size();
213  me.add_uint16(table_length);
214 
215  if (index_length == 1) {
216  // In fact, we don't even need to write the channels at all, if
217  // there weren't at least two different values.
218 
219  } else {
220  for (i = 0; i < table_length - 1; i+= 2) {
221  int value1 = (int)cfloor(_table[i] * scale + 0.5f);
222  int value2 = (int)cfloor(_table[i + 1] * scale + 0.5f);
223  int i1 = index[value1];
224  int i2 = index[value2];
225 
226  me.add_uint8((i1 << 4) | i2);
227  }
228 
229  // There might be one odd value.
230  if (i < table_length) {
231  int value1 = (int)cfloor(_table[i] * scale + 0.5f);
232  int i1 = index[value1];
233 
234  me.add_uint8(i1 << 4);
235  }
236  }
237  }
238 
239  } else {
240  // No, we have continuous channels. Write them out using lossy
241  // compression.
242  me.add_uint8(0xff);
243 
244  FFTCompressor compressor;
245  compressor.set_quality(compress_chan_quality);
246  compressor.set_use_error_threshold(true);
247  compressor.write_header(me);
248 
249  compressor.write_reals(me, _table, _table.size());
250  }
251  }
252 }
253 
254 /**
255  * Function that reads out of the datagram (or asks manager to read) all of
256  * the data that is needed to re-create this object and stores it in the
257  * appropiate place
258  */
259 void AnimChannelScalarTable::
260 fillin(DatagramIterator& scan, BamReader* manager) {
261  AnimChannelScalar::fillin(scan, manager);
262 
263  bool wrote_compressed = scan.get_bool();
264 
265  PTA_stdfloat temp_table = PTA_stdfloat::empty_array(0, get_class_type());
266 
267  if (!wrote_compressed) {
268  // Regular floats.
269  int size = scan.get_uint16();
270  for(int i = 0; i < size; i++) {
271  temp_table.push_back(scan.get_stdfloat());
272  }
273 
274  } else {
275  // Compressed channels. Did we write them as discrete or continuous
276  // channel values?
277  int index_length = scan.get_uint8();
278 
279  if (index_length < 0xff) {
280  // Discrete. Read in the index.
281  if (index_length > 0) {
282  PN_stdfloat *index = (PN_stdfloat *)alloca(index_length * sizeof(PN_stdfloat));
283 
284  int i;
285  for (i = 0; i < index_length; i++) {
286  index[i] = scan.get_stdfloat();
287  }
288 
289  // Now read in the channel values.
290  int table_length = scan.get_uint16();
291  if (index_length == 1) {
292  // With only one index value, we can infer the table.
293  for (i = 0; i < table_length; i++) {
294  temp_table.push_back(index[0]);
295  }
296  } else {
297  // Otherwise, we must read it.
298  for (i = 0; i < table_length - 1; i+= 2) {
299  int num = scan.get_uint8();
300  int i1 = (num >> 4) & 0xf;
301  int i2 = num & 0xf;
302  temp_table.push_back(index[i1]);
303  temp_table.push_back(index[i2]);
304  }
305  // There might be one odd value.
306  if (i < table_length) {
307  int num = scan.get_uint8();
308  int i1 = (num >> 4) & 0xf;
309  temp_table.push_back(index[i1]);
310  }
311  }
312  }
313  } else {
314  // Continuous channels.
315  FFTCompressor compressor;
316  compressor.read_header(scan, manager->get_file_minor_ver());
317  compressor.read_reals(scan, temp_table.v());
318  }
319  }
320 
321  _table = temp_table;
322 }
323 
324 /**
325  * Factory method to generate a AnimChannelScalarTable object
326  */
330  DatagramIterator scan;
331  BamReader *manager;
332 
333  parse_params(params, scan, manager);
334  me->fillin(scan, manager);
335  return me;
336 }
337 
338 /**
339  * Factory method to generate a AnimChannelScalarTable object
340  */
344 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool read_reals(DatagramIterator &di, vector_stdfloat &array)
Reads an array of floating-point numbers.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
bool read_header(DatagramIterator &di, int bam_minor_version)
Reads the compression header that was written previously.
An animation channel that issues a scalar each frame, read from a table such as might have been read ...
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
virtual void write(std::ostream &out, int indent_level) const
Writes a brief description of the table and all of its descendants.
static void register_with_read_factory()
Factory method to generate a AnimChannelScalarTable object.
This class manages a lossy compression and decompression of a stream of floating-point numbers to a d...
Definition: fftCompressor.h:40
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This template class is the parent class for all kinds of AnimChannels that return different values.
Definition: animChannel.h:28
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_stdfloat(PN_stdfloat value)
Adds either a 32-bit or a 64-bit floating-point number, according to set_stdfloat_double().
Definition: datagram.I:133
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
This is the base class for AnimChannel and AnimBundle.
Definition: animGroup.h:33
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
void write_header(Datagram &datagram)
Writes the compression parameters to the indicated datagram.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
set_table
Assigns the data table.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
void write_reals(Datagram &datagram, const PN_stdfloat *array, int length)
Writes an array of floating-point numbers to the indicated datagram.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
virtual bool has_changed(int last_frame, double last_frac, int this_frame, double this_frac)
Returns true if the value has changed since the last call to has_changed().
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
static TypedWritable * make_AnimChannelScalarTable(const FactoryParams &params)
Factory method to generate a AnimChannelScalarTable object.
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...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
virtual void get_value(int frame, PN_stdfloat &value)
Gets the value of the channel at the indicated frame.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static bool is_compression_available()
Returns true if the FFTW library is compiled in, so that this class is actually capable of doing usef...
Similar to PointerToArray, except that its contents may not be modified.