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