// Filename: animChannelScalarTable.cxx // Created by: drose (22Feb99) // //////////////////////////////////////////////////////////////////// #include "animChannelScalarTable.h" #include "animBundle.h" #include "config_chan.h" #include #include #include #include #include #include TypeHandle AnimChannelScalarTable::_type_handle; //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// AnimChannelScalarTable:: AnimChannelScalarTable(AnimGroup *parent, const string &name) : AnimChannelScalar(parent, name) { } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// AnimChannelScalarTable:: AnimChannelScalarTable(void){ } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::has_changed // Access: Public, Virtual // Description: Returns true if the value has changed since the last // call to has_changed(). last_frame is the frame // number of the last call; this_frame is the current // frame number. //////////////////////////////////////////////////////////////////// bool AnimChannelScalarTable:: has_changed(int last_frame, int this_frame) { if (_table.size() > 1) { if (_table[last_frame % _table.size()] != _table[this_frame % _table.size()]) { return true; } } return false; } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::get_value // Access: Public, Virtual // Description: Gets the value of the channel at the indicated frame. //////////////////////////////////////////////////////////////////// void AnimChannelScalarTable:: get_value(int frame, float &value) { if (_table.empty()) { value = 0.0; } else { value = _table[frame % _table.size()]; } } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::set_table // Access: Public // Description: Assigns the data table. //////////////////////////////////////////////////////////////////// void AnimChannelScalarTable:: set_table(const CPTA_float &table) { int num_frames = _root->get_num_frames(); if (table.size() > 1 && (int)table.size() < num_frames) { // The new table has an invalid number of frames--it doesn't match // the bundle's requirement. return; } _table = table; } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::write // Access: Public, Virtual // Description: Writes a brief description of the table and all of // its descendants. //////////////////////////////////////////////////////////////////// void AnimChannelScalarTable:: write(ostream &out, int indent_level) const { indent(out, indent_level) << get_type() << " " << get_name() << " " << _table.size(); if (!_children.empty()) { out << " {\n"; write_descendants(out, indent_level + 2); indent(out, indent_level) << "}"; } out << "\n"; } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::write_datagram // Access: Public // Description: Function to write the important information in // the particular object to a Datagram //////////////////////////////////////////////////////////////////// void AnimChannelScalarTable:: write_datagram(BamWriter *manager, Datagram &me) { AnimChannelScalar::write_datagram(manager, me); if (compress_channels && !FFTCompressor::is_compression_available()) { chan_cat.error() << "Compression is not available; writing uncompressed channels.\n"; compress_channels = false; } me.add_bool(compress_channels); if (!compress_channels) { // Write out everything the old way, as floats. me.add_uint16(_table.size()); for(int i = 0; i < (int)_table.size(); i++) { me.add_float32(_table[i]); } } else { // Some channels, particularly blink channels, may involve only a // small number of discrete values. If we come across one of // those, write it out losslessly, since the lossy compression // could damage it significantly (and we can achieve better // compression directly anyway). We consider the channel value // only to the nearest 1000th for this purpose, because floats // aren't very good at being precisely equal to each other. static const int max_values = 16; static const float scale = 1000.0; map index; int i; for (i = 0; i < (int)_table.size() && (int)index.size() <= max_values; i++) { int value = (int)floor(_table[i] * scale + 0.5); index.insert(map::value_type(value, index.size())); } int index_length = index.size(); if (index_length <= max_values) { // All right, here's a blink channel. Now we write out the // index table, and then a table of all the index values, two // per byte. me.add_uint8(index_length); if (index_length > 0) { // We need to write the index in order by its index number; for // this, we need to invert the index. vector_float reverse_index(index_length); map::iterator mi; for (mi = index.begin(); mi != index.end(); ++mi) { float f = (float)(*mi).first / scale; int i = (*mi).second; nassertv(i >= 0 && i < (int)reverse_index.size()); reverse_index[i] = f; } for (i = 0; i < index_length; i++) { me.add_float32(reverse_index[i]); } // Now write out the actual channels. We write these two at a // time, in the high and low nibbles of each byte. int table_length = _table.size(); me.add_uint16(table_length); if (index_length == 1) { // In fact, we don't even need to write the channels at all, // if there weren't at least two different values. } else { for (i = 0; i < table_length - 1; i+= 2) { int value1 = (int)floor(_table[i] * scale + 0.5); int value2 = (int)floor(_table[i + 1] * scale + 0.5); int i1 = index[value1]; int i2 = index[value2]; me.add_uint8((i1 << 4) | i2); } // There might be one odd value. if (i < table_length) { int value1 = (int)floor(_table[i] * scale + 0.5); int i1 = index[value1]; me.add_uint8(i1 << 4); } } } } else { // No, we have continuous channels. Write them out using lossy // compression. me.add_uint8(0xff); FFTCompressor compressor; compressor.set_quality(compress_chan_quality); compressor.write_header(me); compressor.write_reals(me, _table, _table.size()); } } } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::fillin // Access: Protected // Description: Function that reads out of the datagram (or asks // manager to read) all of the data that is needed to // re-create this object and stores it in the appropiate // place //////////////////////////////////////////////////////////////////// void AnimChannelScalarTable:: fillin(DatagramIterator& scan, BamReader* manager) { AnimChannelScalar::fillin(scan, manager); bool wrote_compressed = scan.get_bool(); PTA_float temp_table(0); if (!wrote_compressed) { // Regular floats. int size = scan.get_uint16(); for(int i = 0; i < size; i++) { temp_table.push_back(scan.get_float32()); } } else { // Compressed channels. if (manager->get_file_minor_ver() < 1) { chan_cat.error() << "Cannot read old-style quantized channels.\n"; return; } // Did we write them as discrete or continuous channel values? int index_length = scan.get_uint8(); if (index_length < 0xff) { // Discrete. Read in the index. if (index_length > 0) { float *index = (float *)alloca(index_length * sizeof(float)); int i; for (i = 0; i < index_length; i++) { index[i] = scan.get_float32(); } // Now read in the channel values. int table_length = scan.get_uint16(); if (index_length == 1) { // With only one index value, we can infer the table. for (i = 0; i < table_length; i++) { temp_table.push_back(index[0]); } } else { // Otherwise, we must read it. for (i = 0; i < table_length - 1; i+= 2) { int num = scan.get_uint8(); int i1 = (num >> 4) & 0xf; int i2 = num & 0xf; temp_table.push_back(index[i1]); temp_table.push_back(index[i2]); } // There might be one odd value. if (i < table_length) { int num = scan.get_uint8(); int i1 = (num >> 4) & 0xf; temp_table.push_back(index[i1]); } } } } else { // Continuous channels. FFTCompressor compressor; compressor.read_header(scan); compressor.read_reals(scan, temp_table.v()); } } _table = temp_table; } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::make_AnimChannelScalarTable // Access: Protected // Description: Factory method to generate a AnimChannelScalarTable object //////////////////////////////////////////////////////////////////// TypedWriteable* AnimChannelScalarTable:: make_AnimChannelScalarTable(const FactoryParams ¶ms) { AnimChannelScalarTable *me = new AnimChannelScalarTable; BamReader *manager; Datagram packet; parse_params(params, manager, packet); DatagramIterator scan(packet); me->fillin(scan, manager); return me; } //////////////////////////////////////////////////////////////////// // Function: AnimChannelScalarTable::register_with_factory // Access: Public, Static // Description: Factory method to generate a AnimChannelScalarTable object //////////////////////////////////////////////////////////////////// void AnimChannelScalarTable:: register_with_read_factory(void) { BamReader::get_factory()->register_factory(get_class_type(), make_AnimChannelScalarTable); }