2018-02-19 12:46:02 +00:00
|
|
|
/*
|
|
|
|
Open Asset Import Library (assimp)
|
|
|
|
----------------------------------------------------------------------
|
|
|
|
|
2019-01-30 08:41:39 +00:00
|
|
|
Copyright (c) 2006-2019, assimp team
|
2018-02-19 12:46:02 +00:00
|
|
|
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use of this software in source and binary forms,
|
|
|
|
with or without modification, are permitted provided that the
|
|
|
|
following conditions are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above
|
|
|
|
copyright notice, this list of conditions and the
|
|
|
|
following disclaimer.
|
|
|
|
|
|
|
|
* Redistributions in binary form must reproduce the above
|
|
|
|
copyright notice, this list of conditions and the
|
|
|
|
following disclaimer in the documentation and/or other
|
|
|
|
materials provided with the distribution.
|
|
|
|
|
|
|
|
* Neither the name of the assimp team, nor the names of its
|
|
|
|
contributors may be used to endorse or promote products
|
|
|
|
derived from this software without specific prior
|
|
|
|
written permission of the assimp team.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
----------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
|
|
|
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
|
|
|
|
|
|
|
|
#include "FBXExportProperty.h"
|
|
|
|
|
|
|
|
#include <assimp/StreamWriter.h> // StreamWriterLE
|
|
|
|
#include <assimp/Exceptional.h> // DeadlyExportError
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2018-03-28 19:40:26 +00:00
|
|
|
#include <ostream>
|
|
|
|
#include <locale>
|
|
|
|
#include <sstream> // ostringstream
|
2018-02-19 12:46:02 +00:00
|
|
|
|
2019-05-09 12:50:22 +00:00
|
|
|
namespace Assimp {
|
2018-02-19 12:46:02 +00:00
|
|
|
// constructors for single element properties
|
|
|
|
|
|
|
|
FBX::Property::Property(bool v)
|
|
|
|
: type('C'), data(1)
|
|
|
|
{
|
|
|
|
data = {uint8_t(v)};
|
|
|
|
}
|
|
|
|
|
|
|
|
FBX::Property::Property(int16_t v) : type('Y'), data(2)
|
|
|
|
{
|
|
|
|
uint8_t* d = data.data();
|
|
|
|
(reinterpret_cast<int16_t*>(d))[0] = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
FBX::Property::Property(int32_t v) : type('I'), data(4)
|
|
|
|
{
|
|
|
|
uint8_t* d = data.data();
|
|
|
|
(reinterpret_cast<int32_t*>(d))[0] = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
FBX::Property::Property(float v) : type('F'), data(4)
|
|
|
|
{
|
|
|
|
uint8_t* d = data.data();
|
|
|
|
(reinterpret_cast<float*>(d))[0] = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
FBX::Property::Property(double v) : type('D'), data(8)
|
|
|
|
{
|
|
|
|
uint8_t* d = data.data();
|
|
|
|
(reinterpret_cast<double*>(d))[0] = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
FBX::Property::Property(int64_t v) : type('L'), data(8)
|
|
|
|
{
|
|
|
|
uint8_t* d = data.data();
|
|
|
|
(reinterpret_cast<int64_t*>(d))[0] = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// constructors for array-type properties
|
|
|
|
|
|
|
|
FBX::Property::Property(const char* c, bool raw)
|
|
|
|
: Property(std::string(c), raw)
|
|
|
|
{}
|
|
|
|
|
|
|
|
// strings can either be saved as "raw" (R) data, or "string" (S) data
|
|
|
|
FBX::Property::Property(const std::string& s, bool raw)
|
|
|
|
: type(raw ? 'R' : 'S'), data(s.size())
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < s.size(); ++i) {
|
|
|
|
data[i] = uint8_t(s[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FBX::Property::Property(const std::vector<uint8_t>& r)
|
|
|
|
: type('R'), data(r)
|
|
|
|
{}
|
|
|
|
|
|
|
|
FBX::Property::Property(const std::vector<int32_t>& va)
|
|
|
|
: type('i'), data(4*va.size())
|
|
|
|
{
|
|
|
|
int32_t* d = reinterpret_cast<int32_t*>(data.data());
|
|
|
|
for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
|
|
|
|
}
|
|
|
|
|
2018-03-26 16:27:15 +00:00
|
|
|
FBX::Property::Property(const std::vector<int64_t>& va)
|
|
|
|
: type('l'), data(8*va.size())
|
|
|
|
{
|
|
|
|
int64_t* d = reinterpret_cast<int64_t*>(data.data());
|
|
|
|
for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
|
|
|
|
}
|
|
|
|
|
|
|
|
FBX::Property::Property(const std::vector<float>& va)
|
|
|
|
: type('f'), data(4*va.size())
|
|
|
|
{
|
|
|
|
float* d = reinterpret_cast<float*>(data.data());
|
|
|
|
for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
|
|
|
|
}
|
|
|
|
|
2018-02-19 12:46:02 +00:00
|
|
|
FBX::Property::Property(const std::vector<double>& va)
|
|
|
|
: type('d'), data(8*va.size())
|
|
|
|
{
|
|
|
|
double* d = reinterpret_cast<double*>(data.data());
|
|
|
|
for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
|
|
|
|
}
|
|
|
|
|
|
|
|
FBX::Property::Property(const aiMatrix4x4& vm)
|
|
|
|
: type('d'), data(8*16)
|
|
|
|
{
|
|
|
|
double* d = reinterpret_cast<double*>(data.data());
|
2018-03-19 17:22:27 +00:00
|
|
|
for (unsigned int c = 0; c < 4; ++c) {
|
|
|
|
for (unsigned int r = 0; r < 4; ++r) {
|
2018-02-19 12:46:02 +00:00
|
|
|
d[4*c+r] = vm[r][c];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// public member functions
|
|
|
|
|
|
|
|
size_t FBX::Property::size()
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L':
|
|
|
|
return data.size() + 1;
|
|
|
|
case 'S': case 'R':
|
|
|
|
return data.size() + 5;
|
|
|
|
case 'i': case 'd':
|
|
|
|
return data.size() + 13;
|
|
|
|
default:
|
|
|
|
throw DeadlyExportError("Requested size on property of unknown type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-28 19:40:26 +00:00
|
|
|
void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s)
|
2018-02-19 12:46:02 +00:00
|
|
|
{
|
|
|
|
s.PutU1(type);
|
2018-03-28 19:40:26 +00:00
|
|
|
uint8_t* d = data.data();
|
2018-02-19 12:46:02 +00:00
|
|
|
size_t N;
|
|
|
|
switch (type) {
|
2018-03-28 19:40:26 +00:00
|
|
|
case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
|
|
|
|
case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
|
|
|
|
case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
|
|
|
|
case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
|
|
|
|
case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
|
|
|
|
case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
|
2018-02-19 12:46:02 +00:00
|
|
|
case 'S':
|
|
|
|
case 'R':
|
2018-03-19 17:22:27 +00:00
|
|
|
s.PutU4(uint32_t(data.size()));
|
2018-02-19 12:46:02 +00:00
|
|
|
for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
|
|
|
|
return;
|
|
|
|
case 'i':
|
|
|
|
N = data.size() / 4;
|
2018-03-19 17:22:27 +00:00
|
|
|
s.PutU4(uint32_t(N)); // number of elements
|
2018-02-19 12:46:02 +00:00
|
|
|
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
|
|
|
// TODO: compress if large?
|
2018-03-19 17:22:27 +00:00
|
|
|
s.PutU4(uint32_t(data.size())); // data size
|
2018-02-19 12:46:02 +00:00
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
|
|
s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
|
|
|
|
}
|
|
|
|
return;
|
2018-03-26 16:27:15 +00:00
|
|
|
case 'l':
|
|
|
|
N = data.size() / 8;
|
|
|
|
s.PutU4(uint32_t(N)); // number of elements
|
|
|
|
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
|
|
|
// TODO: compress if large?
|
|
|
|
s.PutU4(uint32_t(data.size())); // data size
|
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
|
|
s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case 'f':
|
|
|
|
N = data.size() / 4;
|
|
|
|
s.PutU4(uint32_t(N)); // number of elements
|
|
|
|
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
|
|
|
// TODO: compress if large?
|
|
|
|
s.PutU4(uint32_t(data.size())); // data size
|
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
|
|
s.PutF4((reinterpret_cast<float*>(d))[i]);
|
|
|
|
}
|
|
|
|
return;
|
2018-02-19 12:46:02 +00:00
|
|
|
case 'd':
|
|
|
|
N = data.size() / 8;
|
2018-03-19 17:22:27 +00:00
|
|
|
s.PutU4(uint32_t(N)); // number of elements
|
2018-02-19 12:46:02 +00:00
|
|
|
s.PutU4(0); // no encoding (1 would be zip-compressed)
|
|
|
|
// TODO: compress if large?
|
2018-03-19 17:22:27 +00:00
|
|
|
s.PutU4(uint32_t(data.size())); // data size
|
2018-02-19 12:46:02 +00:00
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
|
|
s.PutF8((reinterpret_cast<double*>(d))[i]);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
default:
|
2018-03-28 19:40:26 +00:00
|
|
|
std::ostringstream err;
|
2018-02-19 12:46:02 +00:00
|
|
|
err << "Tried to dump property with invalid type '";
|
|
|
|
err << type << "'!";
|
|
|
|
throw DeadlyExportError(err.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-28 19:40:26 +00:00
|
|
|
void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss.imbue(std::locale::classic());
|
|
|
|
ss.precision(15); // this seems to match official FBX SDK exports
|
|
|
|
DumpAscii(ss, indent);
|
|
|
|
outstream.PutString(ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void FBX::Property::DumpAscii(std::ostream& s, int indent)
|
|
|
|
{
|
|
|
|
// no writing type... or anything. just shove it into the stream.
|
|
|
|
uint8_t* d = data.data();
|
|
|
|
size_t N;
|
|
|
|
size_t swap = data.size();
|
|
|
|
size_t count = 0;
|
|
|
|
switch (type) {
|
|
|
|
case 'C':
|
|
|
|
if (*(reinterpret_cast<uint8_t*>(d))) { s << 'T'; }
|
|
|
|
else { s << 'F'; }
|
|
|
|
return;
|
|
|
|
case 'Y': s << *(reinterpret_cast<int16_t*>(d)); return;
|
|
|
|
case 'I': s << *(reinterpret_cast<int32_t*>(d)); return;
|
|
|
|
case 'F': s << *(reinterpret_cast<float*>(d)); return;
|
|
|
|
case 'D': s << *(reinterpret_cast<double*>(d)); return;
|
|
|
|
case 'L': s << *(reinterpret_cast<int64_t*>(d)); return;
|
|
|
|
case 'S':
|
|
|
|
// first search to see if it has "\x00\x01" in it -
|
|
|
|
// which separates fields which are reversed in the ascii version.
|
|
|
|
// yeah.
|
|
|
|
// FBX, yeah.
|
|
|
|
for (size_t i = 0; i < data.size(); ++i) {
|
|
|
|
if (data[i] == '\0') {
|
|
|
|
swap = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case 'R':
|
|
|
|
s << '"';
|
|
|
|
// we might as well check this now,
|
|
|
|
// probably it will never happen
|
|
|
|
for (size_t i = 0; i < data.size(); ++i) {
|
|
|
|
char c = data[i];
|
|
|
|
if (c == '"') {
|
|
|
|
throw runtime_error("can't handle quotes in property string");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// first write the SWAPPED member (if any)
|
|
|
|
for (size_t i = swap + 2; i < data.size(); ++i) {
|
|
|
|
char c = data[i];
|
|
|
|
s << c;
|
|
|
|
}
|
|
|
|
// then a separator
|
|
|
|
if (swap != data.size()) {
|
|
|
|
s << "::";
|
|
|
|
}
|
|
|
|
// then the initial member
|
|
|
|
for (size_t i = 0; i < swap; ++i) {
|
|
|
|
char c = data[i];
|
|
|
|
s << c;
|
|
|
|
}
|
|
|
|
s << '"';
|
|
|
|
return;
|
|
|
|
case 'i':
|
|
|
|
N = data.size() / 4; // number of elements
|
|
|
|
s << '*' << N << " {\n";
|
|
|
|
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
|
|
|
|
s << "a: ";
|
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
|
|
if (i > 0) { s << ','; }
|
|
|
|
if (count++ > 120) { s << '\n'; count = 0; }
|
|
|
|
s << (reinterpret_cast<int32_t*>(d))[i];
|
|
|
|
}
|
|
|
|
s << '\n';
|
|
|
|
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
|
|
|
s << "} ";
|
|
|
|
return;
|
|
|
|
case 'l':
|
|
|
|
N = data.size() / 8;
|
|
|
|
s << '*' << N << " {\n";
|
|
|
|
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
|
|
|
|
s << "a: ";
|
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
|
|
if (i > 0) { s << ','; }
|
|
|
|
if (count++ > 120) { s << '\n'; count = 0; }
|
|
|
|
s << (reinterpret_cast<int64_t*>(d))[i];
|
|
|
|
}
|
|
|
|
s << '\n';
|
|
|
|
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
|
|
|
s << "} ";
|
|
|
|
return;
|
|
|
|
case 'f':
|
|
|
|
N = data.size() / 4;
|
|
|
|
s << '*' << N << " {\n";
|
|
|
|
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
|
|
|
|
s << "a: ";
|
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
|
|
if (i > 0) { s << ','; }
|
|
|
|
if (count++ > 120) { s << '\n'; count = 0; }
|
|
|
|
s << (reinterpret_cast<float*>(d))[i];
|
|
|
|
}
|
|
|
|
s << '\n';
|
|
|
|
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
|
|
|
s << "} ";
|
|
|
|
return;
|
|
|
|
case 'd':
|
|
|
|
N = data.size() / 8;
|
|
|
|
s << '*' << N << " {\n";
|
|
|
|
for (int i = 0; i < indent + 1; ++i) { s << '\t'; }
|
|
|
|
s << "a: ";
|
|
|
|
// set precision to something that can handle doubles
|
|
|
|
s.precision(15);
|
|
|
|
for (size_t i = 0; i < N; ++i) {
|
|
|
|
if (i > 0) { s << ','; }
|
|
|
|
if (count++ > 120) { s << '\n'; count = 0; }
|
|
|
|
s << (reinterpret_cast<double*>(d))[i];
|
|
|
|
}
|
|
|
|
s << '\n';
|
|
|
|
for (int i = 0; i < indent; ++i) { s << '\t'; }
|
|
|
|
s << "} ";
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
std::ostringstream err;
|
|
|
|
err << "Tried to dump property with invalid type '";
|
|
|
|
err << type << "'!";
|
|
|
|
throw runtime_error(err.str());
|
|
|
|
}
|
|
|
|
}
|
2019-05-09 12:50:22 +00:00
|
|
|
}
|
2018-02-19 12:46:02 +00:00
|
|
|
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
|
|
|
|
#endif // ASSIMP_BUILD_NO_EXPORT
|