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  */
146 write_datagram(BamWriter *manager, Datagram &me) {
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
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.
An animation channel that issues a scalar each frame, read from a table such as might have been read ...
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().
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
virtual void get_value(int frame, PN_stdfloat &value)
Gets the value of the channel at the indicated frame.
set_table
Assigns the data table.
static void register_with_read_factory()
Factory method to generate a AnimChannelScalarTable object.
static TypedWritable * make_AnimChannelScalarTable(const FactoryParams &params)
Factory method to generate a AnimChannelScalarTable object.
virtual void write(std::ostream &out, int indent_level) const
Writes a brief description of the table and all of its descendants.
This template class is the parent class for all kinds of AnimChannels that return different values.
Definition: animChannel.h:28
This is the base class for AnimChannel and AnimBundle.
Definition: animGroup.h:33
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
Similar to PointerToArray, except that its contents may not be modified.
A class to retrieve the individual data elements previously stored in a Datagram.
uint8_t get_uint8()
Extracts an unsigned 8-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.
bool get_bool()
Extracts a boolean value.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
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 add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
This class manages a lossy compression and decompression of a stream of floating-point numbers to a d...
Definition: fftCompressor.h:40
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_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.
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.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
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
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.