#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
#include "COBLoader.h"
#include "COBScene.h"
#include "StreamReader.h"
#include "ParsingUtils.h"
#include "fast_atof.h"
#include "LineSplitter.h"
#include "TinyFormatter.h"
#include <memory>
#include <assimp/IOSystem.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/scene.h>
#include <assimp/importerdesc.h>
using namespace Assimp;
using namespace Assimp::COB;
using namespace Assimp::Formatter;
static const float units[] = {
1000.f,
100.f,
1.f,
0.001f,
1.f/0.0254f,
1.f/0.3048f,
1.f/0.9144f,
1.f/1609.344f
};
static const aiImporterDesc desc = {
"TrueSpace Object Importer",
"",
"",
"little-endian files only",
aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
"cob scn"
};
COBImporter::COBImporter()
{}
COBImporter::~COBImporter()
{}
bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
const std::string& extension = GetExtension(pFile);
if (extension == "cob" || extension == "scn") {
return true;
}
else if ((!extension.length() || checkSig) && pIOHandler) {
const char* tokens[] = {"Caligary"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
}
const aiImporterDesc* COBImporter::GetInfo () const
{
return &desc;
}
void COBImporter::SetupProperties(const Importer* )
{
}
AI_WONT_RETURN void COBImporter::ThrowException(const std::string& msg)
{
throw DeadlyImportError("COB: "+msg);
}
void COBImporter::InternReadFile( const std::string& pFile,
aiScene* pScene, IOSystem* pIOHandler)
{
COB::Scene scene;
std::unique_ptr<StreamReaderLE> stream(new StreamReaderLE( pIOHandler->Open(pFile,"rb")) );
char head[32];
stream->CopyAndAdvance(head,32);
if (strncmp(head,"Caligari ",9)) {
ThrowException("Could not found magic id: `Caligari`");
}
DefaultLogger::get()->info("File format tag: "+std::string(head+9,6));
if (head[16]!='L') {
ThrowException("File is big-endian, which is not supported");
}
if (head[15]=='A') {
ReadAsciiFile(scene, stream.get());
}
else {
ReadBinaryFile(scene, stream.get());
}
if(scene.nodes.empty()) {
ThrowException("No nodes loaded");
}
for(std::shared_ptr< Node >& n : scene.nodes) {
if (n->type == Node::TYPE_MESH) {
Mesh& mesh = (Mesh&)(*n.get());
for(Face& f : mesh.faces) {
mesh.temp_map[f.material].push_back(&f);
}
}
}
for(std::shared_ptr< Node >& n : scene.nodes) {
if (n->type == Node::TYPE_MESH) {
Mesh& mesh = (Mesh&)(*n.get());
if (mesh.vertex_positions.size() && mesh.texture_coords.size()) {
pScene->mNumMeshes += static_cast<unsigned int>(mesh.temp_map.size());
}
}
}
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]();
pScene->mNumMeshes = 0;
for(std::shared_ptr< Node >& n : scene.nodes) {
if (n->type == Node::TYPE_LIGHT) {
++pScene->mNumLights;
}
else if (n->type == Node::TYPE_CAMERA) {
++pScene->mNumCameras;
}
}
if (pScene->mNumLights) {
pScene->mLights = new aiLight*[pScene->mNumLights]();
}
if (pScene->mNumCameras) {
pScene->mCameras = new aiCamera*[pScene->mNumCameras]();
}
pScene->mNumLights = pScene->mNumCameras = 0;
std::unique_ptr<Node> root(new Group());
for(size_t n = 0; n < scene.nodes.size(); ++n) {
const Node& nn = *scene.nodes[n].get();
if(nn.parent_id==0) {
root->temp_children.push_back(&nn);
}
for(size_t m = n; m < scene.nodes.size(); ++m) {
const Node& mm = *scene.nodes[m].get();
if (mm.parent_id == nn.id) {
nn.temp_children.push_back(&mm);
}
}
}
pScene->mRootNode = BuildNodes(*root.get(),scene,pScene);
}
void ConvertTexture(std::shared_ptr< Texture > tex, aiMaterial* out, aiTextureType type)
{
const aiString path( tex->path );
out->AddProperty(&path,AI_MATKEY_TEXTURE(type,0));
out->AddProperty(&tex->transform,1,AI_MATKEY_UVTRANSFORM(type,0));
}
aiNode* COBImporter::BuildNodes(const Node& root,const Scene& scin,aiScene* fill)
{
aiNode* nd = new aiNode();
nd->mName.Set(root.name);
nd->mTransformation = root.transform;
if (Node::TYPE_MESH == root.type) {
const Mesh& ndmesh = (const Mesh&)(root);
if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) {
typedef std::pair<unsigned int,Mesh::FaceRefList> Entry;
for(const Entry& reflist : ndmesh.temp_map) {
{ size_t n = 0;
for(Face* f : reflist.second) {
n += f->indices.size();
}
if (!n) {
continue;
}
aiMesh* outmesh = fill->mMeshes[fill->mNumMeshes++] = new aiMesh();
++nd->mNumMeshes;
outmesh->mVertices = new aiVector3D[n];
outmesh->mTextureCoords[0] = new aiVector3D[n];
outmesh->mFaces = new aiFace[reflist.second.size()]();
for(Face* f : reflist.second) {
if (f->indices.empty()) {
continue;
}
aiFace& fout = outmesh->mFaces[outmesh->mNumFaces++];
fout.mIndices = new unsigned int[f->indices.size()];
for(VertexIndex& v : f->indices) {
if (v.pos_idx >= ndmesh.vertex_positions.size()) {
ThrowException("Position index out of range");
}
if (v.uv_idx >= ndmesh.texture_coords.size()) {
ThrowException("UV index out of range");
}
outmesh->mVertices[outmesh->mNumVertices] = ndmesh.vertex_positions[ v.pos_idx ];
outmesh->mTextureCoords[0][outmesh->mNumVertices] = aiVector3D(
ndmesh.texture_coords[ v.uv_idx ].x,
ndmesh.texture_coords[ v.uv_idx ].y,
0.f
);
fout.mIndices[fout.mNumIndices++] = outmesh->mNumVertices++;
}
}
outmesh->mMaterialIndex = fill->mNumMaterials;
}{ const Material* min = NULL;
for(const Material& m : scin.materials) {
if (m.parent_id == ndmesh.id && m.matnum == reflist.first) {
min = &m;
break;
}
}
std::unique_ptr<const Material> defmat;
if(!min) {
DefaultLogger::get()->debug(format()<<"Could not resolve material index "
<<reflist.first<<" - creating default material for this slot");
defmat.reset(min=new Material());
}
aiMaterial* mat = new aiMaterial();
fill->mMaterials[fill->mNumMaterials++] = mat;
const aiString s(format("#mat_")<<fill->mNumMeshes<<"_"<<min->matnum);
mat->AddProperty(&s,AI_MATKEY_NAME);
if(int tmp = ndmesh.draw_flags & Mesh::WIRED ? 1 : 0) {
mat->AddProperty(&tmp,1,AI_MATKEY_ENABLE_WIREFRAME);
}
{ int shader;
switch(min->shader)
{
case Material::FLAT:
shader = aiShadingMode_Gouraud;
break;
case Material::PHONG:
shader = aiShadingMode_Phong;
break;
case Material::METAL:
shader = aiShadingMode_CookTorrance;
break;
default:
ai_assert(false); }
mat->AddProperty(&shader,1,AI_MATKEY_SHADING_MODEL);
if(shader != aiShadingMode_Gouraud) {
mat->AddProperty(&min->exp,1,AI_MATKEY_SHININESS);
}
}
mat->AddProperty(&min->ior,1,AI_MATKEY_REFRACTI);
mat->AddProperty(&min->rgb,1,AI_MATKEY_COLOR_DIFFUSE);
aiColor3D c = aiColor3D(min->rgb)*min->ks;
mat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR);
c = aiColor3D(min->rgb)*min->ka;
mat->AddProperty(&c,1,AI_MATKEY_COLOR_AMBIENT);
if(min->tex_color) {
ConvertTexture(min->tex_color,mat,aiTextureType_DIFFUSE);
}
if(min->tex_env) {
ConvertTexture(min->tex_env ,mat,aiTextureType_UNKNOWN);
}
if(min->tex_bump) {
ConvertTexture(min->tex_bump ,mat,aiTextureType_HEIGHT);
}
}
}
}
}
else if (Node::TYPE_LIGHT == root.type) {
const Light& ndlight = (const Light&)(root);
aiLight* outlight = fill->mLights[fill->mNumLights++] = new aiLight();
outlight->mName.Set(ndlight.name);
outlight->mColorDiffuse = outlight->mColorAmbient = outlight->mColorSpecular = ndlight.color;
outlight->mAngleOuterCone = AI_DEG_TO_RAD(ndlight.angle);
outlight->mAngleInnerCone = AI_DEG_TO_RAD(ndlight.inner_angle);
outlight->mType = ndlight.ltype==Light::SPOT ? aiLightSource_SPOT : aiLightSource_DIRECTIONAL;
}
else if (Node::TYPE_CAMERA == root.type) {
const Camera& ndcam = (const Camera&)(root);
aiCamera* outcam = fill->mCameras[fill->mNumCameras++] = new aiCamera();
outcam->mName.Set(ndcam.name);
}
if (nd->mNumMeshes) { nd->mMeshes = new unsigned int[nd->mNumMeshes];
for(unsigned int i = 0; i < nd->mNumMeshes;++i) {
nd->mMeshes[i] = fill->mNumMeshes-i-1;
}
}
nd->mChildren = new aiNode*[root.temp_children.size()]();
for(const Node* n : root.temp_children) {
(nd->mChildren[nd->mNumChildren++] = BuildNodes(*n,scin,fill))->mParent = nd;
}
return nd;
}
void COBImporter::ReadAsciiFile(Scene& out, StreamReaderLE* stream)
{
ChunkInfo ci;
for(LineSplitter splitter(*stream);splitter;++splitter) {
if (splitter.match_start("PolH ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadPolH_Ascii(out,splitter,ci);
}
if (splitter.match_start("BitM ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadBitM_Ascii(out,splitter,ci);
}
if (splitter.match_start("Mat1 ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadMat1_Ascii(out,splitter,ci);
}
if (splitter.match_start("Grou ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadGrou_Ascii(out,splitter,ci);
}
if (splitter.match_start("Lght ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadLght_Ascii(out,splitter,ci);
}
if (splitter.match_start("Came ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadCame_Ascii(out,splitter,ci);
}
if (splitter.match_start("Bone ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadBone_Ascii(out,splitter,ci);
}
if (splitter.match_start("Chan ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadChan_Ascii(out,splitter,ci);
}
if (splitter.match_start("Unit ")) {
ReadChunkInfo_Ascii(ci,splitter);
ReadUnit_Ascii(out,splitter,ci);
}
if (splitter.match_start("END ")) {
return;
}
}
}
void COBImporter::ReadChunkInfo_Ascii(ChunkInfo& out, const LineSplitter& splitter)
{
const char* all_tokens[8];
splitter.get_tokens(all_tokens);
out.version = (all_tokens[1][1]-'0')*100+(all_tokens[1][3]-'0')*10+(all_tokens[1][4]-'0');
out.id = strtoul10(all_tokens[3]);
out.parent_id = strtoul10(all_tokens[5]);
out.size = strtol10(all_tokens[7]);
}
void COBImporter::UnsupportedChunk_Ascii(LineSplitter& splitter, const ChunkInfo& nfo, const char* name)
{
const std::string error = format("Encountered unsupported chunk: ") << name <<
" [version: "<<nfo.version<<", size: "<<nfo.size<<"]";
if(nfo.size != static_cast<unsigned int>(-1)) {
DefaultLogger::get()->error(error);
splitter.get_stream().IncPtr(nfo.size);
splitter.swallow_next_increment();
}
else ThrowException(error);
}
void COBImporter::LogWarn_Ascii(const LineSplitter& splitter, const format& message) {
LogWarn_Ascii(message << " [at line "<< splitter.get_index()<<"]");
}
void COBImporter::LogError_Ascii(const LineSplitter& splitter, const format& message) {
LogError_Ascii(message << " [at line "<< splitter.get_index()<<"]");
}
void COBImporter::LogInfo_Ascii(const LineSplitter& splitter, const format& message) {
LogInfo_Ascii(message << " [at line "<< splitter.get_index()<<"]");
}
void COBImporter::LogDebug_Ascii(const LineSplitter& splitter, const format& message) {
LogDebug_Ascii(message << " [at line "<< splitter.get_index()<<"]");
}
void COBImporter::LogWarn_Ascii(const Formatter::format& message) {
DefaultLogger::get()->warn(std::string("COB: ")+=message);
}
void COBImporter::LogError_Ascii(const Formatter::format& message) {
DefaultLogger::get()->error(std::string("COB: ")+=message);
}
void COBImporter::LogInfo_Ascii(const Formatter::format& message) {
DefaultLogger::get()->info(std::string("COB: ")+=message);
}
void COBImporter::LogDebug_Ascii(const Formatter::format& message) {
DefaultLogger::get()->debug(std::string("COB: ")+=message);
}
void COBImporter::ReadBasicNodeInfo_Ascii(Node& msh, LineSplitter& splitter, const ChunkInfo& )
{
for(;splitter;++splitter) {
if (splitter.match_start("Name")) {
msh.name = std::string(splitter[1]);
std::replace(msh.name.begin(),msh.name.end(),
',','_');
}
else if (splitter.match_start("Transform")) {
for(unsigned int y = 0; y < 4 && ++splitter; ++y) {
const char* s = splitter->c_str();
for(unsigned int x = 0; x < 4; ++x) {
SkipSpaces(&s);
msh.transform[y][x] = fast_atof(&s);
}
}
return;
}
}
}
template <typename T>
void COBImporter::ReadFloat3Tuple_Ascii(T& fill, const char** in)
{
const char* rgb = *in;
for(unsigned int i = 0; i < 3; ++i) {
SkipSpaces(&rgb);
if (*rgb == ',')++rgb;
SkipSpaces(&rgb);
fill[i] = fast_atof(&rgb);
}
*in = rgb;
}
void COBImporter::ReadMat1_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 8) {
return UnsupportedChunk_Ascii(splitter,nfo,"Mat1");
}
++splitter;
if (!splitter.match_start("mat# ")) {
LogWarn_Ascii(splitter,format()<<
"Expected `mat#` line in `Mat1` chunk "<<nfo.id);
return;
}
out.materials.push_back(Material());
Material& mat = out.materials.back();
mat = nfo;
mat.matnum = strtoul10(splitter[1]);
++splitter;
if (!splitter.match_start("shader: ")) {
LogWarn_Ascii(splitter,format()<<
"Expected `mat#` line in `Mat1` chunk "<<nfo.id);
return;
}
std::string shader = std::string(splitter[1]);
shader = shader.substr(0,shader.find_first_of(" \t"));
if (shader == "metal") {
mat.shader = Material::METAL;
}
else if (shader == "phong") {
mat.shader = Material::PHONG;
}
else if (shader != "flat") {
LogWarn_Ascii(splitter,format()<<
"Unknown value for `shader` in `Mat1` chunk "<<nfo.id);
}
++splitter;
if (!splitter.match_start("rgb ")) {
LogWarn_Ascii(splitter,format()<<
"Expected `rgb` line in `Mat1` chunk "<<nfo.id);
}
const char* rgb = splitter[1];
ReadFloat3Tuple_Ascii(mat.rgb,&rgb);
++splitter;
if (!splitter.match_start("alpha ")) {
LogWarn_Ascii(splitter,format()<<
"Expected `alpha` line in `Mat1` chunk "<<nfo.id);
}
const char* tokens[10];
splitter.get_tokens(tokens);
mat.alpha = fast_atof( tokens[1] );
mat.ka = fast_atof( tokens[3] );
mat.ks = fast_atof( tokens[5] );
mat.exp = fast_atof( tokens[7] );
mat.ior = fast_atof( tokens[9] );
}
void COBImporter::ReadUnit_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 1) {
return UnsupportedChunk_Ascii(splitter,nfo,"Unit");
}
++splitter;
if (!splitter.match_start("Units ")) {
LogWarn_Ascii(splitter,format()<<
"Expected `Units` line in `Unit` chunk "<<nfo.id);
return;
}
for(std::shared_ptr< Node >& nd : out.nodes) {
if (nd->id == nfo.parent_id) {
const unsigned int t=strtoul10(splitter[1]);
nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
LogWarn_Ascii(splitter,format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
,1.f):units[t];
return;
}
}
LogWarn_Ascii(splitter,format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
<<nfo.parent_id<<" which does not exist");
}
void COBImporter::ReadChan_Ascii(Scene& , LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 8) {
return UnsupportedChunk_Ascii(splitter,nfo,"Chan");
}
}
void COBImporter::ReadLght_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 8) {
return UnsupportedChunk_Ascii(splitter,nfo,"Lght");
}
out.nodes.push_back(std::shared_ptr<Light>(new Light()));
Light& msh = (Light&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
if (splitter.match_start("Infinite ")) {
msh.ltype = Light::INFINITE;
}
else if (splitter.match_start("Local ")) {
msh.ltype = Light::LOCAL;
}
else if (splitter.match_start("Spot ")) {
msh.ltype = Light::SPOT;
}
else {
LogWarn_Ascii(splitter,format()<<
"Unknown kind of light source in `Lght` chunk "<<nfo.id<<" : "<<*splitter);
msh.ltype = Light::SPOT;
}
++splitter;
if (!splitter.match_start("color ")) {
LogWarn_Ascii(splitter,format()<<
"Expected `color` line in `Lght` chunk "<<nfo.id);
}
const char* rgb = splitter[1];
ReadFloat3Tuple_Ascii(msh.color ,&rgb);
SkipSpaces(&rgb);
if (strncmp(rgb,"cone angle",10)) {
LogWarn_Ascii(splitter,format()<<
"Expected `cone angle` entity in `color` line in `Lght` chunk "<<nfo.id);
}
SkipSpaces(rgb+10,&rgb);
msh.angle = fast_atof(&rgb);
SkipSpaces(&rgb);
if (strncmp(rgb,"inner angle",11)) {
LogWarn_Ascii(splitter,format()<<
"Expected `inner angle` entity in `color` line in `Lght` chunk "<<nfo.id);
}
SkipSpaces(rgb+11,&rgb);
msh.inner_angle = fast_atof(&rgb);
}
void COBImporter::ReadCame_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 2) {
return UnsupportedChunk_Ascii(splitter,nfo,"Came");
}
out.nodes.push_back(std::shared_ptr<Camera>(new Camera()));
Camera& msh = (Camera&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
++splitter;
}
void COBImporter::ReadBone_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 5) {
return UnsupportedChunk_Ascii(splitter,nfo,"Bone");
}
out.nodes.push_back(std::shared_ptr<Bone>(new Bone()));
Bone& msh = (Bone&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
}
void COBImporter::ReadGrou_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 1) {
return UnsupportedChunk_Ascii(splitter,nfo,"Grou");
}
out.nodes.push_back(std::shared_ptr<Group>(new Group()));
Group& msh = (Group&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
}
void COBImporter::ReadPolH_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 8) {
return UnsupportedChunk_Ascii(splitter,nfo,"PolH");
}
out.nodes.push_back(std::shared_ptr<Mesh>(new Mesh()));
Mesh& msh = (Mesh&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
for(;splitter;++splitter) {
if (splitter.match_start("World Vertices")) {
const unsigned int cnt = strtoul10(splitter[2]);
msh.vertex_positions.resize(cnt);
for(unsigned int cur = 0;cur < cnt && ++splitter;++cur) {
const char* s = splitter->c_str();
aiVector3D& v = msh.vertex_positions[cur];
SkipSpaces(&s);
v.x = fast_atof(&s);
SkipSpaces(&s);
v.y = fast_atof(&s);
SkipSpaces(&s);
v.z = fast_atof(&s);
}
}
else if (splitter.match_start("Texture Vertices")) {
const unsigned int cnt = strtoul10(splitter[2]);
msh.texture_coords.resize(cnt);
for(unsigned int cur = 0;cur < cnt && ++splitter;++cur) {
const char* s = splitter->c_str();
aiVector2D& v = msh.texture_coords[cur];
SkipSpaces(&s);
v.x = fast_atof(&s);
SkipSpaces(&s);
v.y = fast_atof(&s);
}
}
else if (splitter.match_start("Faces")) {
const unsigned int cnt = strtoul10(splitter[1]);
msh.faces.reserve(cnt);
for(unsigned int cur = 0; cur < cnt && ++splitter ;++cur) {
if (splitter.match_start("Hole")) {
LogWarn_Ascii(splitter,"Skipping unsupported `Hole` line");
continue;
}
if (!splitter.match_start("Face")) {
ThrowException("Expected Face line");
}
msh.faces.push_back(Face());
Face& face = msh.faces.back();
face.indices.resize(strtoul10(splitter[2]));
face.flags = strtoul10(splitter[4]);
face.material = strtoul10(splitter[6]);
const char* s = (++splitter)->c_str();
for(size_t i = 0; i < face.indices.size(); ++i) {
if(!SkipSpaces(&s)) {
ThrowException("Expected EOL token in Face entry");
}
if ('<' != *s++) {
ThrowException("Expected < token in Face entry");
}
face.indices[i].pos_idx = strtoul10(s,&s);
if (',' != *s++) {
ThrowException("Expected , token in Face entry");
}
face.indices[i].uv_idx = strtoul10(s,&s);
if ('>' != *s++) {
ThrowException("Expected < token in Face entry");
}
}
}
if (nfo.version <= 4) {
break;
}
}
else if (splitter.match_start("DrawFlags")) {
msh.draw_flags = strtoul10(splitter[1]);
break;
}
}
}
void COBImporter::ReadBitM_Ascii(Scene& , LineSplitter& splitter, const ChunkInfo& nfo)
{
if(nfo.version > 1) {
return UnsupportedChunk_Ascii(splitter,nfo,"BitM");
}
const unsigned int head = strtoul10((++splitter)[1]);
if (head != sizeof(Bitmap::BitmapHeader)) {
LogWarn_Ascii(splitter,"Unexpected ThumbNailHdrSize, skipping this chunk");
return;
}
}
void COBImporter::ReadString_Binary(std::string& out, StreamReaderLE& reader)
{
out.resize( reader.GetI2());
for(char& c : out) {
c = reader.GetI1();
}
}
void COBImporter::ReadBasicNodeInfo_Binary(Node& msh, StreamReaderLE& reader, const ChunkInfo& )
{
const unsigned int dupes = reader.GetI2();
ReadString_Binary(msh.name,reader);
msh.name = format(msh.name)<<'_'<<dupes;
reader.IncPtr(48);
msh.transform = aiMatrix4x4();
for(unsigned int y = 0; y < 3; ++y) {
for(unsigned int x =0; x < 4; ++x) {
msh.transform[y][x] = reader.GetF4();
}
}
}
void COBImporter::UnsupportedChunk_Binary( StreamReaderLE& reader, const ChunkInfo& nfo, const char* name)
{
const std::string error = format("Encountered unsupported chunk: ") << name <<
" [version: "<<nfo.version<<", size: "<<nfo.size<<"]";
if(nfo.size != static_cast<unsigned int>(-1)) {
DefaultLogger::get()->error(error);
reader.IncPtr(nfo.size);
}
else ThrowException(error);
}
class chunk_guard {
public:
chunk_guard(const COB::ChunkInfo& nfo, StreamReaderLE& reader)
: nfo(nfo)
, reader(reader)
, cur(reader.GetCurrentPos()) {
}
~chunk_guard() {
if(nfo.size != static_cast<unsigned int>(-1)) {
try {
reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur );
} catch ( DeadlyImportError e ) {
reader.IncPtr( reader.GetReadLimit() );
}
}
}
private:
const COB::ChunkInfo& nfo;
StreamReaderLE& reader;
long cur;
};
void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader)
{
while(1) {
std::string type;
type += reader -> GetI1()
,type += reader -> GetI1()
,type += reader -> GetI1()
,type += reader -> GetI1()
;
ChunkInfo nfo;
nfo.version = reader -> GetI2()*10;
nfo.version += reader -> GetI2();
nfo.id = reader->GetI4();
nfo.parent_id = reader->GetI4();
nfo.size = reader->GetI4();
if (type == "PolH") {
ReadPolH_Binary(out,*reader,nfo);
}
else if (type == "BitM") {
ReadBitM_Binary(out,*reader,nfo);
}
else if (type == "Grou") {
ReadGrou_Binary(out,*reader,nfo);
}
else if (type == "Lght") {
ReadLght_Binary(out,*reader,nfo);
}
else if (type == "Came") {
ReadCame_Binary(out,*reader,nfo);
}
else if (type == "Mat1") {
ReadMat1_Binary(out,*reader,nfo);
}
else if (type == "Unit") {
ReadUnit_Binary(out,*reader,nfo);
}
else if (type == "OLay") {
if(nfo.size != static_cast<unsigned int>(-1) ) {
reader->IncPtr(nfo.size);
}
else return UnsupportedChunk_Binary(*reader,nfo,type.c_str());
}
else if (type == "END ") {
return;
}
else UnsupportedChunk_Binary(*reader,nfo,type.c_str());
}
}
void COBImporter::ReadPolH_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
{
if(nfo.version > 8) {
return UnsupportedChunk_Binary(reader,nfo,"PolH");
}
const chunk_guard cn(nfo,reader);
out.nodes.push_back(std::shared_ptr<Mesh>(new Mesh()));
Mesh& msh = (Mesh&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Binary(msh,reader,nfo);
msh.vertex_positions.resize(reader.GetI4());
for(aiVector3D& v : msh.vertex_positions) {
v.x = reader.GetF4();
v.y = reader.GetF4();
v.z = reader.GetF4();
}
msh.texture_coords.resize(reader.GetI4());
for(aiVector2D& v : msh.texture_coords) {
v.x = reader.GetF4();
v.y = reader.GetF4();
}
const size_t numf = reader.GetI4();
msh.faces.reserve(numf);
for(size_t i = 0; i < numf; ++i) {
bool hole;
if ((hole = (reader.GetI1() & 0x08) != 0)) {
if (msh.faces.empty()) {
ThrowException(format("A hole is the first entity in the `PolH` chunk with id ") << nfo.id);
}
}
else msh.faces.push_back(Face());
Face& f = msh.faces.back();
const size_t num = reader.GetI2();
f.indices.reserve(f.indices.size() + num);
if(!hole) {
f.material = reader.GetI2();
f.flags = 0;
}
for(size_t x = 0; x < num; ++x) {
f.indices.push_back(VertexIndex());
VertexIndex& v = f.indices.back();
v.pos_idx = reader.GetI4();
v.uv_idx = reader.GetI4();
}
if(hole) {
std::reverse(f.indices.rbegin(),f.indices.rbegin()+num);
}
}
if (nfo.version>4) {
msh.draw_flags = reader.GetI4();
}
nfo.version>5 && nfo.version<8 ? reader.GetI4() : 0;
}
void COBImporter::ReadBitM_Binary(COB::Scene& , StreamReaderLE& reader, const ChunkInfo& nfo)
{
if(nfo.version > 1) {
return UnsupportedChunk_Binary(reader,nfo,"BitM");
}
const chunk_guard cn(nfo,reader);
const uint32_t len = reader.GetI4();
reader.IncPtr(len);
reader.GetI4();
reader.IncPtr(reader.GetI4());
}
void COBImporter::ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
{
if(nfo.version > 8) {
return UnsupportedChunk_Binary(reader,nfo,"Mat1");
}
const chunk_guard cn(nfo,reader);
out.materials.push_back(Material());
Material& mat = out.materials.back();
mat = nfo;
mat.matnum = reader.GetI2();
switch(reader.GetI1()) {
case 'f':
mat.type = Material::FLAT;
break;
case 'p':
mat.type = Material::PHONG;
break;
case 'm':
mat.type = Material::METAL;
break;
default:
LogError_Ascii(format("Unrecognized shader type in `Mat1` chunk with id ")<<nfo.id);
mat.type = Material::FLAT;
}
switch(reader.GetI1()) {
case 'f':
mat.autofacet = Material::FACETED;
break;
case 'a':
mat.autofacet = Material::AUTOFACETED;
break;
case 's':
mat.autofacet = Material::SMOOTH;
break;
default:
LogError_Ascii(format("Unrecognized faceting mode in `Mat1` chunk with id ")<<nfo.id);
mat.autofacet = Material::FACETED;
}
mat.autofacet_angle = static_cast<float>(reader.GetI1());
mat.rgb.r = reader.GetF4();
mat.rgb.g = reader.GetF4();
mat.rgb.b = reader.GetF4();
mat.alpha = reader.GetF4();
mat.ka = reader.GetF4();
mat.ks = reader.GetF4();
mat.exp = reader.GetF4();
mat.ior = reader.GetF4();
char id[2];
id[0] = reader.GetI1(),id[1] = reader.GetI1();
if (id[0] == 'e' && id[1] == ':') {
mat.tex_env.reset(new Texture());
reader.GetI1();
ReadString_Binary(mat.tex_env->path,reader);
id[0] = reader.GetI1(),id[1] = reader.GetI1();
}
if (id[0] == 't' && id[1] == ':') {
mat.tex_color.reset(new Texture());
reader.GetI1();
ReadString_Binary(mat.tex_color->path,reader);
mat.tex_color->transform.mTranslation.x = reader.GetF4();
mat.tex_color->transform.mTranslation.y = reader.GetF4();
mat.tex_color->transform.mScaling.x = reader.GetF4();
mat.tex_color->transform.mScaling.y = reader.GetF4();
id[0] = reader.GetI1(),id[1] = reader.GetI1();
}
if (id[0] == 'b' && id[1] == ':') {
mat.tex_bump.reset(new Texture());
reader.GetI1();
ReadString_Binary(mat.tex_bump->path,reader);
mat.tex_bump->transform.mTranslation.x = reader.GetF4();
mat.tex_bump->transform.mTranslation.y = reader.GetF4();
mat.tex_bump->transform.mScaling.x = reader.GetF4();
mat.tex_bump->transform.mScaling.y = reader.GetF4();
reader.GetF4();
}
reader.IncPtr(-2);
}
void COBImporter::ReadCame_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
{
if(nfo.version > 2) {
return UnsupportedChunk_Binary(reader,nfo,"Came");
}
const chunk_guard cn(nfo,reader);
out.nodes.push_back(std::shared_ptr<Camera>(new Camera()));
Camera& msh = (Camera&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Binary(msh,reader,nfo);
if(nfo.version > 1) {
if (reader.GetI2()==512) {
reader.IncPtr(42);
}
}
}
void COBImporter::ReadLght_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
{
if(nfo.version > 2) {
return UnsupportedChunk_Binary(reader,nfo,"Lght");
}
const chunk_guard cn(nfo,reader);
out.nodes.push_back(std::shared_ptr<Light>(new Light()));
Light& msh = (Light&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Binary(msh,reader,nfo);
}
void COBImporter::ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
{
if(nfo.version > 2) {
return UnsupportedChunk_Binary(reader,nfo,"Grou");
}
const chunk_guard cn(nfo,reader);
out.nodes.push_back(std::shared_ptr<Group>(new Group()));
Group& msh = (Group&)(*out.nodes.back().get());
msh = nfo;
ReadBasicNodeInfo_Binary(msh,reader,nfo);
}
void COBImporter::ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
{
if(nfo.version > 1) {
return UnsupportedChunk_Binary(reader,nfo,"Unit");
}
const chunk_guard cn(nfo,reader);
for(std::shared_ptr< Node >& nd : out.nodes) {
if (nd->id == nfo.parent_id) {
const unsigned int t=reader.GetI2();
nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
LogWarn_Ascii(format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
,1.f):units[t];
return;
}
}
LogWarn_Ascii(format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
<<nfo.parent_id<<" which does not exist");
}
#endif