213 lines
6.6 KiB
C++
213 lines
6.6 KiB
C++
// convert3ds.cpp
|
|
//
|
|
// Copyright (C) 2004-2010, Chris Laurel <claurel@gmail.com>
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// Functions for converting a 3DS scene into a Celestia model (cmod)
|
|
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <Eigen/Core>
|
|
|
|
#include <celmodel/material.h>
|
|
#include <celmodel/mesh.h>
|
|
|
|
#include "convert3ds.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
cmod::Material
|
|
convert3dsMaterial(const M3DMaterial* material3ds, cmod::HandleGetter& handleGetter)
|
|
{
|
|
cmod::Material newMaterial;
|
|
|
|
M3DColor diffuse = material3ds->getDiffuseColor();
|
|
newMaterial.diffuse = cmod::Color(diffuse.red, diffuse.green, diffuse.blue);
|
|
newMaterial.opacity = material3ds->getOpacity();
|
|
|
|
M3DColor specular = material3ds->getSpecularColor();
|
|
newMaterial.specular = cmod::Color(specular.red, specular.green, specular.blue);
|
|
|
|
float shininess = material3ds->getShininess();
|
|
|
|
// Map the 3DS file's shininess from percentage (0-100) to
|
|
// range that OpenGL uses for the specular exponent. The
|
|
// current equation is just a guess at the mapping that
|
|
// 3DS actually uses.
|
|
newMaterial.specularPower = std::pow(2.0f, 1.0f + 0.1f * shininess);
|
|
if (newMaterial.specularPower > 128.0f)
|
|
newMaterial.specularPower = 128.0f;
|
|
|
|
if (!material3ds->getTextureMap().empty())
|
|
{
|
|
newMaterial.setMap(cmod::TextureSemantic::DiffuseMap, handleGetter(material3ds->getTextureMap()));
|
|
|
|
}
|
|
|
|
return newMaterial;
|
|
}
|
|
|
|
} // end unnamed namespace
|
|
|
|
|
|
void
|
|
Convert3DSMesh(cmod::Model& model,
|
|
const M3DTriangleMesh& mesh3ds,
|
|
const M3DScene& scene,
|
|
std::string&& meshName)
|
|
{
|
|
int nVertices = mesh3ds.getVertexCount();
|
|
int nTexCoords = mesh3ds.getTexCoordCount();
|
|
bool hasTexCoords = (nTexCoords >= nVertices);
|
|
int vertexSize = 3;
|
|
if (hasTexCoords)
|
|
{
|
|
vertexSize += 2;
|
|
}
|
|
|
|
std::vector<cmod::VWord> vertices(mesh3ds.getVertexCount() * vertexSize);
|
|
|
|
// Build the vertex list
|
|
for (int i = 0; i < mesh3ds.getVertexCount(); ++i)
|
|
{
|
|
int k = i * vertexSize;
|
|
Eigen::Vector3f pos = mesh3ds.getVertex(i);
|
|
std::memcpy(vertices.data() + k, pos.data(), sizeof(float) * 3);
|
|
if (hasTexCoords)
|
|
{
|
|
Eigen::Vector2f texCoord = mesh3ds.getTexCoord(i);
|
|
std::memcpy(vertices.data() + k + 3, texCoord.data(), sizeof(float) * 2);
|
|
}
|
|
}
|
|
|
|
std::vector<cmod::VertexAttribute> attributes;
|
|
attributes.reserve(2);
|
|
std::uint32_t offset = 0;
|
|
|
|
// Position attribute is always present
|
|
attributes.emplace_back(cmod::VertexAttributeSemantic::Position,
|
|
cmod::VertexAttributeFormat::Float3,
|
|
0);
|
|
offset += 3;
|
|
|
|
if (hasTexCoords)
|
|
{
|
|
attributes.emplace_back(cmod::VertexAttributeSemantic::Texture0,
|
|
cmod::VertexAttributeFormat::Float2,
|
|
offset);
|
|
offset += 2;
|
|
}
|
|
|
|
// Create the Celestia mesh
|
|
cmod::Mesh mesh;
|
|
mesh.setVertexDescription(cmod::VertexDescription(std::move(attributes)));
|
|
mesh.setVertices(nVertices, std::move(vertices));
|
|
|
|
mesh.setName(std::move(meshName));
|
|
|
|
if (mesh3ds.getMeshMaterialGroupCount() == 0)
|
|
{
|
|
// No material groups in the 3DS file. This is allowed. We'll create a single
|
|
// primitive group with the default material.
|
|
unsigned int faceCount = mesh3ds.getFaceCount();
|
|
std::vector<cmod::Index32> indices;
|
|
indices.reserve(faceCount * 3);
|
|
|
|
for (unsigned int i = 0; i < faceCount; i++)
|
|
{
|
|
std::uint16_t v0 = 0, v1 = 0, v2 = 0;
|
|
mesh3ds.getFace(i, v0, v1, v2);
|
|
indices.push_back(v0);
|
|
indices.push_back(v1);
|
|
indices.push_back(v2);
|
|
}
|
|
|
|
mesh.addGroup(cmod::PrimitiveGroupType::TriList, ~0, std::move(indices));
|
|
}
|
|
else
|
|
{
|
|
// We have at least one material group. Create a cmod primitive group for
|
|
// each material group in th 3ds mesh.
|
|
for (std::uint32_t groupIndex = 0; groupIndex < mesh3ds.getMeshMaterialGroupCount(); ++groupIndex)
|
|
{
|
|
const M3DMeshMaterialGroup* matGroup = mesh3ds.getMeshMaterialGroup(groupIndex);
|
|
|
|
std::uint32_t nMatGroupFaces = matGroup->faces.size();
|
|
std::vector<cmod::Index32> indices;
|
|
indices.reserve(nMatGroupFaces * 3);
|
|
|
|
for (unsigned int i = 0; i < nMatGroupFaces; i++)
|
|
{
|
|
std::uint16_t v0 = 0, v1 = 0, v2 = 0;
|
|
std::uint16_t faceIndex = matGroup->faces[i];
|
|
mesh3ds.getFace(faceIndex, v0, v1, v2);
|
|
indices.push_back(v0);
|
|
indices.push_back(v1);
|
|
indices.push_back(v2);
|
|
}
|
|
|
|
// Get the material index
|
|
unsigned int materialIndex = ~0u;
|
|
std::string material3dsName = matGroup->materialName;
|
|
if (!material3dsName.empty())
|
|
{
|
|
for (unsigned int i = 0; i < scene.getMaterialCount(); i++)
|
|
{
|
|
const M3DMaterial* material3ds = scene.getMaterial(i);
|
|
if (material3dsName == material3ds->getName())
|
|
{
|
|
materialIndex = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
mesh.addGroup(cmod::PrimitiveGroupType::TriList, materialIndex, std::move(indices));
|
|
}
|
|
}
|
|
|
|
model.addMesh(std::move(mesh));
|
|
}
|
|
|
|
|
|
std::unique_ptr<cmod::Model>
|
|
Convert3DSModel(const M3DScene& scene, cmod::HandleGetter handleGetter)
|
|
{
|
|
auto model = std::make_unique<cmod::Model>();
|
|
|
|
// Convert materials
|
|
for (unsigned int i = 0; i < scene.getMaterialCount(); i++)
|
|
{
|
|
const M3DMaterial* material = scene.getMaterial(i);
|
|
model->addMaterial(convert3dsMaterial(material, handleGetter));
|
|
}
|
|
|
|
// Convert meshes
|
|
for (unsigned int i = 0; i < scene.getModelCount(); i++)
|
|
{
|
|
const M3DModel* model3ds = scene.getModel(i);
|
|
if (model3ds != nullptr)
|
|
{
|
|
for (unsigned int j = 0; j < model3ds->getTriMeshCount(); j++)
|
|
{
|
|
const M3DTriangleMesh* mesh = model3ds->getTriMesh(j);
|
|
|
|
if (mesh != nullptr && mesh->getFaceCount() > 0)
|
|
{
|
|
Convert3DSMesh(*model, *mesh, scene, model3ds->getName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return model;
|
|
}
|
|
|