Extract inner types from Material and Mesh

- Remove using namspace cmod
- Convert enums to enum classes
pull/1196/head
Andrew Tribick 2021-11-27 15:29:08 +01:00 committed by ajtribick
parent 474057221b
commit b79959979a
37 changed files with 2277 additions and 2277 deletions

View File

@ -44,7 +44,7 @@ public:
* all within this geometry object. This information is used * all within this geometry object. This information is used
* to decide whether multiple rendering passes are required. * to decide whether multiple rendering passes are required.
*/ */
virtual bool usesTextureType(cmod::Material::TextureSemantic) const virtual bool usesTextureType(cmod::TextureSemantic) const
{ {
return false; return false;
} }

View File

@ -11,196 +11,44 @@
// Experimental particle system support // Experimental particle system support
#define PARTICLE_SYSTEM 0 #define PARTICLE_SYSTEM 0
#include <cmath>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <memory>
#include <fmt/ostream.h>
#include <fmt/printf.h>
#include <cel3ds/3dsmodel.h>
#include <cel3ds/3dsread.h>
#include <celmath/randutils.h>
#include <celmodel/material.h>
#include <celmodel/mesh.h>
#include <celmodel/model.h>
#include <celmodel/modelfile.h>
#include <celutil/debug.h>
#include <celutil/filetype.h>
#include <celutil/gettext.h>
#include <celutil/tokenizer.h>
#include "meshmanager.h"
#include "modelgeometry.h"
#include "parser.h"
#include "spheremesh.h"
#include "texmanager.h"
#if PARTICLE_SYSTEM #if PARTICLE_SYSTEM
#include "particlesystem.h" #include "particlesystem.h"
#include "particlesystemfile.h" #include "particlesystemfile.h"
#endif #endif
#include "parser.h" namespace
#include "spheremesh.h"
#include "texmanager.h"
#include "meshmanager.h"
#include "modelgeometry.h"
#include <celutil/tokenizer.h>
#include <cel3ds/3dsmodel.h>
#include <cel3ds/3dsread.h>
#include <celmodel/modelfile.h>
#include <celmath/mathlib.h>
#include <celmath/randutils.h>
#include <celutil/debug.h>
#include <celutil/filetype.h>
#include <celutil/debug.h>
#include <celutil/gettext.h>
#include <iostream>
#include <fstream>
#include <cassert>
#include <utility>
#include <memory>
#include <fmt/ostream.h>
#include <fmt/printf.h>
using namespace cmod;
using namespace Eigen;
using namespace std;
static Model* LoadCelestiaMesh(const fs::path& filename);
static Model* Convert3DSModel(const M3DScene& scene, const fs::path& texPath);
static GeometryManager* geometryManager = nullptr;
constexpr const fs::path::value_type UniqueSuffixChar = '!';
GeometryManager* GetGeometryManager()
{ {
if (geometryManager == nullptr)
geometryManager = new GeometryManager("models");
return geometryManager;
}
fs::path GeometryInfo::resolve(const fs::path& baseDir)
{
// Ensure that models with different centers get resolved to different objects by
// adding a 'uniquifying' suffix to the filename that encodes the center value.
// This suffix is stripped before the file is actually loaded.
fs::path::string_type uniquifyingSuffix;
fs::path::string_type format;
#ifdef _WIN32
format = L"%c%f,%f,%f,%f,%d";
#else
format = "%c%f,%f,%f,%f,%d";
#endif
uniquifyingSuffix = fmt::sprintf(format, UniqueSuffixChar, center.x(), center.y(), center.z(), scale, (int) isNormalized);
if (!path.empty())
{
fs::path filename = path / "models" / source;
ifstream in(filename.string());
if (in.good())
{
resolvedToPath = true;
return filename += uniquifyingSuffix;
}
}
return (baseDir / source) += uniquifyingSuffix;
}
Geometry* GeometryInfo::load(const fs::path& resolvedFilename)
{
// Strip off the uniquifying suffix
fs::path::string_type::size_type uniquifyingSuffixStart = resolvedFilename.native().rfind(UniqueSuffixChar);
fs::path filename = resolvedFilename.native().substr(0, uniquifyingSuffixStart);
clog << fmt::sprintf(_("Loading model: %s\n"), filename);
Model* model = nullptr;
ContentType fileType = DetermineFileType(filename);
if (fileType == Content_3DStudio)
{
std::unique_ptr<M3DScene> scene = Read3DSFile(filename);
if (scene != nullptr)
{
if (resolvedToPath)
model = Convert3DSModel(*scene, path);
else
model = Convert3DSModel(*scene, "");
if (isNormalized)
model->normalize(center);
else
model->transform(center, scale);
}
}
else if (fileType == Content_CelestiaModel)
{
ifstream in(filename.string(), ios::binary);
if (in.good())
{
model = LoadModel(in,
[&](const fs::path& name)
{
return GetTextureManager()
->getHandle(TextureInfo(name, path, TextureInfo::WrapTexture));
});
if (model != nullptr)
{
if (isNormalized)
model->normalize(center);
else
model->transform(center, scale);
}
}
}
else if (fileType == Content_CelestiaMesh)
{
model = LoadCelestiaMesh(filename);
if (model != nullptr)
{
if (isNormalized)
model->normalize(center);
else
model->transform(center, scale);
}
}
#if PARTICLE_SYSTEM
else if (fileType == Content_CelestiaParticleSystem)
{
ifstream in(filename);
if (in.good())
{
return LoadParticleSystem(in, path);
}
}
#endif
// Condition the model for optimal rendering
if (model != nullptr)
{
// Many models tend to have a lot of duplicate materials; eliminate
// them, since unnecessarily setting material parameters can adversely
// impact rendering performance. Ideally uniquification of materials
// would be performed just once when the model was created, but
// that's not the case.
uint32_t originalMaterialCount = model->getMaterialCount();
model->uniquifyMaterials();
// Sort the submeshes roughly by opacity. This will eliminate a
// good number of the errors caused when translucent triangles are
// rendered before geometry that they cover.
model->sortMeshes(Model::OpacityComparator());
model->determineOpacity();
// Display some statics for the model
clog << fmt::sprintf(
_(" Model statistics: %u vertices, %u primitives, %u materials (%u unique)\n"),
model->getVertexCount(),
model->getPrimitiveCount(),
originalMaterialCount,
model->getMaterialCount());
return new ModelGeometry(unique_ptr<cmod::Model>(model));
}
else
{
clog << fmt::sprintf(_("Error loading model '%s'\n"), filename);
return nullptr;
}
}
struct NoiseMeshParameters struct NoiseMeshParameters
{ {
Vector3f size; Eigen::Vector3f size;
Vector3f offset; Eigen::Vector3f offset;
float featureHeight; float featureHeight;
float octaves; float octaves;
float slices; float slices;
@ -208,26 +56,28 @@ struct NoiseMeshParameters
}; };
static float NoiseDisplacementFunc(float u, float v, void* info) float
NoiseDisplacementFunc(float u, float v, void* info)
{ {
float theta = u * (float) PI * 2; float theta = u * static_cast<float>(PI) * 2;
float phi = (v - 0.5f) * (float) PI; float phi = (v - 0.5f) * static_cast<float>(PI);
float x = (float) (cos(phi) * cos(theta));
float y = (float) sin(phi);
float z = (float) (cos(phi) * sin(theta));
// assert(info != nullptr); // assert(info != nullptr);
auto* params = (NoiseMeshParameters*) info; auto* params = static_cast<NoiseMeshParameters*>(info);
Vector3f p = Vector3f(x, y, z) + params->offset; Eigen::Vector3f p = Eigen::Vector3f(std::cos(phi) * std::cos(theta),
std::sin(phi),
std::cos(phi) * std::sin(theta))
+ params->offset;
return celmath::fractalsum(p, params->octaves) * params->featureHeight; return celmath::fractalsum(p, params->octaves) * params->featureHeight;
} }
// TODO: The Celestia mesh format is deprecated // TODO: The Celestia mesh format is deprecated
Model* LoadCelestiaMesh(const fs::path& filename) cmod::Model*
LoadCelestiaMesh(const fs::path& filename)
{ {
ifstream meshFile(filename.string(), ios::in); std::ifstream meshFile(filename.string(), std::ios::in);
if (!meshFile.good()) if (!meshFile.good())
{ {
DPRINTF(LOG_LEVEL_ERROR, "Error opening mesh file: %s\n", filename); DPRINTF(LOG_LEVEL_ERROR, "Error opening mesh file: %s\n", filename);
@ -268,8 +118,8 @@ Model* LoadCelestiaMesh(const fs::path& filename)
NoiseMeshParameters params{}; NoiseMeshParameters params{};
params.size = Vector3f::Ones(); params.size = Eigen::Vector3f::Ones();
params.offset = Vector3f::Constant(10.0f); params.offset = Eigen::Vector3f::Constant(10.0f);
params.featureHeight = 0.0f; params.featureHeight = 0.0f;
params.octaves = 1; params.octaves = 1;
params.slices = 20; params.slices = 20;
@ -284,12 +134,13 @@ Model* LoadCelestiaMesh(const fs::path& filename)
delete meshDefValue; delete meshDefValue;
Model* model = new Model(); cmod::Model* model = new cmod::Model();
SphereMesh* sphereMesh = new SphereMesh(params.size, SphereMesh* sphereMesh = new SphereMesh(params.size,
(int) params.rings, (int) params.slices, static_cast<int>(params.rings),
static_cast<int>(params.slices),
NoiseDisplacementFunc, NoiseDisplacementFunc,
(void*) &params); (void*) &params);
Mesh* mesh = sphereMesh->convertToMesh(); cmod::Mesh* mesh = sphereMesh->convertToMesh();
model->addMesh(mesh); model->addMesh(mesh);
delete sphereMesh; delete sphereMesh;
@ -297,7 +148,7 @@ Model* LoadCelestiaMesh(const fs::path& filename)
} }
static Mesh* cmod::Mesh*
ConvertTriangleMesh(const M3DTriangleMesh& mesh, ConvertTriangleMesh(const M3DTriangleMesh& mesh,
const M3DScene& scene) const M3DScene& scene)
{ {
@ -311,33 +162,39 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
// Create the attribute set. Always include positions and normals, texture coords // Create the attribute set. Always include positions and normals, texture coords
// are optional. // are optional.
Mesh::VertexAttribute attributes[8]; cmod::VertexAttribute attributes[8];
uint32_t nAttributes = 0; std::uint32_t nAttributes = 0;
uint32_t offset = 0; std::uint32_t offset = 0;
// Position attribute are required // Position attribute are required
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Position, Mesh::Float3, 0); attributes[nAttributes] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Position,
cmod::VertexAttributeFormat::Float3,
0);
nAttributes++; nAttributes++;
offset += 12; offset += 12;
// Normals are always generated // Normals are always generated
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Normal, Mesh::Float3, offset); attributes[nAttributes] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Normal,
cmod::VertexAttributeFormat::Float3,
offset);
nAttributes++; nAttributes++;
offset += 12; offset += 12;
if (hasTextureCoords) if (hasTextureCoords)
{ {
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Texture0, Mesh::Float2, offset); attributes[nAttributes] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Texture0,
cmod::VertexAttributeFormat::Float2,
offset);
nAttributes++; nAttributes++;
offset += 8; offset += 8;
} }
uint32_t vertexSize = offset; std::uint32_t vertexSize = offset;
// bool smooth = (mesh.getSmoothingGroupCount() == nFaces); // bool smooth = (mesh.getSmoothingGroupCount() == nFaces);
Vector3f* faceNormals = new Vector3f[nFaces]; Eigen::Vector3f* faceNormals = new Eigen::Vector3f[nFaces];
Vector3f* vertexNormals = new Vector3f[nFaces * 3]; Eigen::Vector3f* vertexNormals = new Eigen::Vector3f[nFaces * 3];
auto* faceCounts = new int[nVertices]; auto* faceCounts = new int[nVertices];
auto** vertexFaces = new int*[nVertices]; auto** vertexFaces = new int*[nVertices];
@ -350,16 +207,16 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
// generate face normals // generate face normals
for (int i = 0; i < nFaces; i++) for (int i = 0; i < nFaces; i++)
{ {
uint16_t v0, v1, v2; std::uint16_t v0, v1, v2;
mesh.getFace(i, v0, v1, v2); mesh.getFace(i, v0, v1, v2);
faceCounts[v0]++; faceCounts[v0]++;
faceCounts[v1]++; faceCounts[v1]++;
faceCounts[v2]++; faceCounts[v2]++;
Vector3f p0 = mesh.getVertex(v0); Eigen::Vector3f p0 = mesh.getVertex(v0);
Vector3f p1 = mesh.getVertex(v1); Eigen::Vector3f p1 = mesh.getVertex(v1);
Vector3f p2 = mesh.getVertex(v2); Eigen::Vector3f p2 = mesh.getVertex(v2);
faceNormals[i] = (p1 - p0).cross(p2 - p1).normalized(); faceNormals[i] = (p1 - p0).cross(p2 - p1).normalized();
} }
@ -385,7 +242,7 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
for (int i = 0; i < nFaces; i++) for (int i = 0; i < nFaces; i++)
{ {
uint16_t v0, v1, v2; std::uint16_t v0, v1, v2;
mesh.getFace(i, v0, v1, v2); mesh.getFace(i, v0, v1, v2);
vertexFaces[v0][faceCounts[v0]--] = i; vertexFaces[v0][faceCounts[v0]--] = i;
vertexFaces[v1][faceCounts[v1]--] = i; vertexFaces[v1][faceCounts[v1]--] = i;
@ -395,11 +252,11 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
// average face normals to compute the vertex normals // average face normals to compute the vertex normals
for (int i = 0; i < nFaces; i++) for (int i = 0; i < nFaces; i++)
{ {
uint16_t v0, v1, v2; std::uint16_t v0, v1, v2;
mesh.getFace(i, v0, v1, v2); mesh.getFace(i, v0, v1, v2);
// uint32_t smoothingGroups = mesh.getSmoothingGroups(i); // uint32_t smoothingGroups = mesh.getSmoothingGroups(i);
Vector3f v = Vector3f::Zero(); Eigen::Vector3f v = Eigen::Vector3f::Zero();
for (int j = 1; j <= vertexFaces[v0][0]; j++) for (int j = 1; j <= vertexFaces[v0][0]; j++)
{ {
int k = vertexFaces[v0][j]; int k = vertexFaces[v0][j];
@ -409,7 +266,7 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
} }
vertexNormals[i * 3] = v.normalized(); vertexNormals[i * 3] = v.normalized();
v = Vector3f::Zero(); v = Eigen::Vector3f::Zero();
for (int j = 1; j <= vertexFaces[v1][0]; j++) for (int j = 1; j <= vertexFaces[v1][0]; j++)
{ {
int k = vertexFaces[v1][j]; int k = vertexFaces[v1][j];
@ -419,7 +276,7 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
} }
vertexNormals[i * 3 + 1] = v.normalized(); vertexNormals[i * 3 + 1] = v.normalized();
v = Vector3f::Zero(); v = Eigen::Vector3f::Zero();
for (int j = 1; j <= vertexFaces[v2][0]; j++) for (int j = 1; j <= vertexFaces[v2][0]; j++)
{ {
int k = vertexFaces[v2][j]; int k = vertexFaces[v2][j];
@ -437,13 +294,13 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
for (int i = 0; i < nFaces; i++) for (int i = 0; i < nFaces; i++)
{ {
uint16_t triVert[3]; std::uint16_t triVert[3];
mesh.getFace(i, triVert[0], triVert[1], triVert[2]); mesh.getFace(i, triVert[0], triVert[1], triVert[2]);
for (unsigned int j = 0; j < 3; j++) for (unsigned int j = 0; j < 3; j++)
{ {
Vector3f position = mesh.getVertex(triVert[j]); Eigen::Vector3f position = mesh.getVertex(triVert[j]);
Vector3f normal = vertexNormals[i * 3 + j]; Eigen::Vector3f normal = vertexNormals[i * 3 + j];
int dataOffset = (i * 3 + j) * floatsPerVertex; int dataOffset = (i * 3 + j) * floatsPerVertex;
vertexData[dataOffset + 0] = position.x(); vertexData[dataOffset + 0] = position.x();
@ -454,7 +311,7 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
vertexData[dataOffset + 5] = normal.z(); vertexData[dataOffset + 5] = normal.z();
if (hasTextureCoords) if (hasTextureCoords)
{ {
Vector2f texCoord = mesh.getTexCoord(triVert[j]); Eigen::Vector2f texCoord = mesh.getTexCoord(triVert[j]);
vertexData[dataOffset + 6] = texCoord.x(); vertexData[dataOffset + 6] = texCoord.x();
vertexData[dataOffset + 7] = texCoord.y(); vertexData[dataOffset + 7] = texCoord.y();
} }
@ -462,8 +319,8 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
} }
// Create the mesh // Create the mesh
Mesh* newMesh = new Mesh(); cmod::Mesh* newMesh = new cmod::Mesh();
newMesh->setVertexDescription(Mesh::VertexDescription(vertexSize, nAttributes, attributes)); newMesh->setVertexDescription(cmod::VertexDescription(vertexSize, nAttributes, attributes));
newMesh->setVertices(nFaces * 3, vertexData); newMesh->setVertices(nFaces * 3, vertexData);
for (uint32_t i = 0; i < mesh.getMeshMaterialGroupCount(); ++i) for (uint32_t i = 0; i < mesh.getMeshMaterialGroupCount(); ++i)
@ -472,20 +329,20 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
// Vertex lists are not indexed, so the conversion to an indexed format is // Vertex lists are not indexed, so the conversion to an indexed format is
// trivial (although much space is wasted storing unnecessary indices.) // trivial (although much space is wasted storing unnecessary indices.)
uint32_t nMatGroupFaces = matGroup->faces.size(); std::uint32_t nMatGroupFaces = matGroup->faces.size();
auto indices = new uint32_t[nMatGroupFaces * 3]; auto indices = new std::uint32_t[nMatGroupFaces * 3];
for (uint32_t j = 0; j < nMatGroupFaces; ++j) for (std::uint32_t j = 0; j < nMatGroupFaces; ++j)
{ {
uint16_t faceIndex = matGroup->faces[j]; std::uint16_t faceIndex = matGroup->faces[j];
indices[j * 3 + 0] = faceIndex * 3 + 0; indices[j * 3 + 0] = faceIndex * 3 + 0;
indices[j * 3 + 1] = faceIndex * 3 + 1; indices[j * 3 + 1] = faceIndex * 3 + 1;
indices[j * 3 + 2] = faceIndex * 3 + 2; indices[j * 3 + 2] = faceIndex * 3 + 2;
} }
// Lookup the material index // Lookup the material index
uint32_t materialIndex = 0; std::uint32_t materialIndex = 0;
for (uint32_t j = 0; j < scene.getMaterialCount(); ++j) for (std::uint32_t j = 0; j < scene.getMaterialCount(); ++j)
{ {
if (matGroup->materialName == scene.getMaterial(j)->getName()) if (matGroup->materialName == scene.getMaterial(j)->getName())
{ {
@ -494,7 +351,7 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
} }
} }
newMesh->addGroup(Mesh::TriList, materialIndex, nMatGroupFaces * 3, indices); newMesh->addGroup(cmod::PrimitiveGroupType::TriList, materialIndex, nMatGroupFaces * 3, indices);
} }
// clean up // clean up
@ -511,32 +368,23 @@ ConvertTriangleMesh(const M3DTriangleMesh& mesh,
} }
#if 0 cmod::Model*
static Material::Color
toMaterialColor(Color c)
{
return {c.red(), c.green(), c.blue()};
}
#endif
static Model*
Convert3DSModel(const M3DScene& scene, const fs::path& texPath) Convert3DSModel(const M3DScene& scene, const fs::path& texPath)
{ {
Model* model = new Model(); cmod::Model* model = new cmod::Model();
// Convert the materials // Convert the materials
for (uint32_t i = 0; i < scene.getMaterialCount(); i++) for (std::uint32_t i = 0; i < scene.getMaterialCount(); i++)
{ {
const M3DMaterial* material = scene.getMaterial(i); const M3DMaterial* material = scene.getMaterial(i);
Material* newMaterial = new Material(); cmod::Material* newMaterial = new cmod::Material();
M3DColor diffuse = material->getDiffuseColor(); M3DColor diffuse = material->getDiffuseColor();
newMaterial->diffuse = Material::Color(diffuse.red, diffuse.green, diffuse.blue); newMaterial->diffuse = cmod::Color(diffuse.red, diffuse.green, diffuse.blue);
newMaterial->opacity = material->getOpacity(); newMaterial->opacity = material->getOpacity();
M3DColor specular = material->getSpecularColor(); M3DColor specular = material->getSpecularColor();
newMaterial->specular = Material::Color(specular.red, specular.green, specular.blue); newMaterial->specular = cmod::Color(specular.red, specular.green, specular.blue);
float shininess = material->getShininess(); float shininess = material->getShininess();
@ -544,14 +392,14 @@ Convert3DSModel(const M3DScene& scene, const fs::path& texPath)
// range that OpenGL uses for the specular exponent. The // range that OpenGL uses for the specular exponent. The
// current equation is just a guess at the mapping that // current equation is just a guess at the mapping that
// 3DS actually uses. // 3DS actually uses.
newMaterial->specularPower = (float) pow(2.0, 1.0 + 0.1 * shininess); newMaterial->specularPower = std::pow(2.0f, 1.0f + 0.1f * shininess);
if (newMaterial->specularPower > 128.0f) if (newMaterial->specularPower > 128.0f)
newMaterial->specularPower = 128.0f; newMaterial->specularPower = 128.0f;
if (!material->getTextureMap().empty()) if (!material->getTextureMap().empty())
{ {
ResourceHandle tex = GetTextureManager()->getHandle(TextureInfo(material->getTextureMap(), texPath, TextureInfo::WrapTexture)); ResourceHandle tex = GetTextureManager()->getHandle(TextureInfo(material->getTextureMap(), texPath, TextureInfo::WrapTexture));
newMaterial->maps[Material::DiffuseMap] = tex; newMaterial->setMap(cmod::TextureSemantic::DiffuseMap, tex);
} }
model->addMaterial(newMaterial); model->addMaterial(newMaterial);
@ -559,7 +407,7 @@ Convert3DSModel(const M3DScene& scene, const fs::path& texPath)
// Convert all models in the scene. Some confusing terminology: a 3ds 'scene' is the same // Convert all models in the scene. Some confusing terminology: a 3ds 'scene' is the same
// as a Celestia model, and a 3ds 'model' is the same as a Celestia mesh. // as a Celestia model, and a 3ds 'model' is the same as a Celestia mesh.
for (uint32_t i = 0; i < scene.getModelCount(); i++) for (std::uint32_t i = 0; i < scene.getModelCount(); i++)
{ {
const M3DModel* model3ds = scene.getModel(i); const M3DModel* model3ds = scene.getModel(i);
if (model3ds) if (model3ds)
@ -569,7 +417,7 @@ Convert3DSModel(const M3DScene& scene, const fs::path& texPath)
const M3DTriangleMesh* mesh = model3ds->getTriMesh(j); const M3DTriangleMesh* mesh = model3ds->getTriMesh(j);
if (mesh) if (mesh)
{ {
Mesh* newMesh = ConvertTriangleMesh(*mesh, scene); cmod::Mesh* newMesh = ConvertTriangleMesh(*mesh, scene);
model->addMesh(newMesh); model->addMesh(newMesh);
} }
} }
@ -578,3 +426,152 @@ Convert3DSModel(const M3DScene& scene, const fs::path& texPath)
return model; return model;
} }
constexpr const fs::path::value_type UniqueSuffixChar = '!';
} // end unnamed namespace
GeometryManager*
GetGeometryManager()
{
static GeometryManager geometryManager("models");
return &geometryManager;
}
fs::path
GeometryInfo::resolve(const fs::path& baseDir)
{
// Ensure that models with different centers get resolved to different objects by
// adding a 'uniquifying' suffix to the filename that encodes the center value.
// This suffix is stripped before the file is actually loaded.
fs::path::string_type uniquifyingSuffix;
fs::path::string_type format;
#ifdef _WIN32
format = L"%c%f,%f,%f,%f,%d";
#else
format = "%c%f,%f,%f,%f,%d";
#endif
uniquifyingSuffix = fmt::sprintf(format, UniqueSuffixChar, center.x(), center.y(), center.z(), scale, (int) isNormalized);
if (!path.empty())
{
fs::path filename = path / "models" / source;
std::ifstream in(filename.string());
if (in.good())
{
resolvedToPath = true;
return filename += uniquifyingSuffix;
}
}
return (baseDir / source) += uniquifyingSuffix;
}
Geometry*
GeometryInfo::load(const fs::path& resolvedFilename)
{
// Strip off the uniquifying suffix
fs::path::string_type::size_type uniquifyingSuffixStart = resolvedFilename.native().rfind(UniqueSuffixChar);
fs::path filename = resolvedFilename.native().substr(0, uniquifyingSuffixStart);
std::clog << fmt::sprintf(_("Loading model: %s\n"), filename);
cmod::Model* model = nullptr;
ContentType fileType = DetermineFileType(filename);
if (fileType == Content_3DStudio)
{
std::unique_ptr<M3DScene> scene = Read3DSFile(filename);
if (scene != nullptr)
{
if (resolvedToPath)
model = Convert3DSModel(*scene, path);
else
model = Convert3DSModel(*scene, "");
if (isNormalized)
model->normalize(center);
else
model->transform(center, scale);
}
}
else if (fileType == Content_CelestiaModel)
{
std::ifstream in(filename.string(), std::ios::binary);
if (in.good())
{
model = cmod::LoadModel(
in,
[&](const fs::path& name)
{
return GetTextureManager()->getHandle(TextureInfo(name, path, TextureInfo::WrapTexture));
});
if (model != nullptr)
{
if (isNormalized)
model->normalize(center);
else
model->transform(center, scale);
}
}
}
else if (fileType == Content_CelestiaMesh)
{
model = LoadCelestiaMesh(filename);
if (model != nullptr)
{
if (isNormalized)
model->normalize(center);
else
model->transform(center, scale);
}
}
#if PARTICLE_SYSTEM
else if (fileType == Content_CelestiaParticleSystem)
{
ifstream in(filename);
if (in.good())
{
return LoadParticleSystem(in, path);
}
}
#endif
// Condition the model for optimal rendering
if (model != nullptr)
{
// Many models tend to have a lot of duplicate materials; eliminate
// them, since unnecessarily setting material parameters can adversely
// impact rendering performance. Ideally uniquification of materials
// would be performed just once when the model was created, but
// that's not the case.
std::uint32_t originalMaterialCount = model->getMaterialCount();
model->uniquifyMaterials();
// Sort the submeshes roughly by opacity. This will eliminate a
// good number of the errors caused when translucent triangles are
// rendered before geometry that they cover.
model->sortMeshes(cmod::Model::OpacityComparator());
model->determineOpacity();
// Display some statics for the model
std::clog << fmt::sprintf(
_(" Model statistics: %u vertices, %u primitives, %u materials (%u unique)\n"),
model->getVertexCount(),
model->getPrimitiveCount(),
originalMaterialCount,
model->getMaterialCount());
return new ModelGeometry(std::unique_ptr<cmod::Model>(model));
}
else
{
std::clog << fmt::sprintf(_("Error loading model '%s'\n"), filename);
return nullptr;
}
}

View File

@ -7,14 +7,13 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CELENGINE_MESHMANAGER_H_ #pragma once
#define _CELENGINE_MESHMANAGER_H_
#include <Eigen/Core>
#include <string>
#include <map>
#include <celutil/resmanager.h>
#include <celengine/geometry.h>
#include <celcompat/filesystem.h> #include <celcompat/filesystem.h>
#include <celutil/resmanager.h>
#include "geometry.h"
class GeometryInfo : public ResourceInfo<Geometry> class GeometryInfo : public ResourceInfo<Geometry>
@ -75,6 +74,3 @@ inline bool operator<(const GeometryInfo& g0, const GeometryInfo& g1)
typedef ResourceManager<GeometryInfo> GeometryManager; typedef ResourceManager<GeometryInfo> GeometryManager;
extern GeometryManager* GetGeometryManager(); extern GeometryManager* GetGeometryManager();
#endif // _CELENGINE_MESHMANAGER_H_

View File

@ -8,18 +8,12 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#include <map>
#include <utility>
#include "glsupport.h"
#include "modelgeometry.h" #include "modelgeometry.h"
#include "rendcontext.h" #include "rendcontext.h"
#include "texmanager.h"
#include <Eigen/Core>
#include <functional>
#include <algorithm>
#include <cassert>
using namespace cmod;
using namespace Eigen;
using namespace std;
using namespace celmath;
// Vertex buffer object support // Vertex buffer object support
@ -51,9 +45,9 @@ public:
/** Create a new ModelGeometry wrapping the specified model. /** Create a new ModelGeometry wrapping the specified model.
* The ModelGeoemtry takes ownership of the model. * The ModelGeoemtry takes ownership of the model.
*/ */
ModelGeometry::ModelGeometry(unique_ptr<cmod::Model>&& model) : ModelGeometry::ModelGeometry(std::unique_ptr<cmod::Model>&& model) :
m_model(move(model)), m_model(std::move(model)),
m_glData(unique_ptr<ModelOpenGLData>(new ModelOpenGLData())) m_glData(std::make_unique<ModelOpenGLData>())
{ {
} }
@ -84,8 +78,8 @@ ModelGeometry::render(RenderContext& rc, double /* t */)
for (unsigned int i = 0; i < m_model->getMeshCount(); ++i) for (unsigned int i = 0; i < m_model->getMeshCount(); ++i)
{ {
Mesh* mesh = m_model->getMesh(i); cmod::Mesh* mesh = m_model->getMesh(i);
const Mesh::VertexDescription& vertexDesc = mesh->getVertexDescription(); const cmod::VertexDescription& vertexDesc = mesh->getVertexDescription();
GLuint vboId = 0; GLuint vboId = 0;
if (mesh->getVertexCount() * vertexDesc.stride > MinVBOSize) if (mesh->getVertexCount() * vertexDesc.stride > MinVBOSize)
@ -105,8 +99,9 @@ ModelGeometry::render(RenderContext& rc, double /* t */)
for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex) for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex)
{ {
const Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex); const cmod::PrimitiveGroup* group = mesh->getGroup(groupIndex);
if (group->vertexOverride != nullptr && group->vertexCountOverride * group->vertexDescriptionOverride.stride > MinVBOSize) if (group->vertexOverride != nullptr
&& group->vertexCountOverride * group->vertexDescriptionOverride.stride > MinVBOSize)
{ {
glGenBuffers(1, &vboId); glGenBuffers(1, &vboId);
if (vboId != 0) if (vboId != 0)
@ -129,7 +124,7 @@ ModelGeometry::render(RenderContext& rc, double /* t */)
// Iterate over all meshes in the model // Iterate over all meshes in the model
for (unsigned int meshIndex = 0; meshIndex < m_model->getMeshCount(); ++meshIndex) for (unsigned int meshIndex = 0; meshIndex < m_model->getMeshCount(); ++meshIndex)
{ {
Mesh* mesh = m_model->getMesh(meshIndex); cmod::Mesh* mesh = m_model->getMesh(meshIndex);
const void* currentData = nullptr; const void* currentData = nullptr;
GLuint currentVboId = 0; GLuint currentVboId = 0;
@ -137,7 +132,7 @@ ModelGeometry::render(RenderContext& rc, double /* t */)
// Iterate over all primitive groups in the mesh // Iterate over all primitive groups in the mesh
for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex) for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex)
{ {
const Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex); const cmod::PrimitiveGroup* group = mesh->getGroup(groupIndex);
bool useOverrideValue = group->vertexOverride != nullptr && rc.shouldDrawLineAsTriangles(); bool useOverrideValue = group->vertexOverride != nullptr && rc.shouldDrawLineAsTriangles();
const void* data = useOverrideValue ? group->vertexOverride : mesh->getVertexData(); const void* data = useOverrideValue ? group->vertexOverride : mesh->getVertexData();
@ -168,7 +163,7 @@ ModelGeometry::render(RenderContext& rc, double /* t */)
rc.updateShader(vertexDescription, group->prim); rc.updateShader(vertexDescription, group->prim);
// Set up the material // Set up the material
const Material* material = nullptr; const cmod::Material* material = nullptr;
unsigned int materialIndex = group->materialIndex; unsigned int materialIndex = group->materialIndex;
if (materialIndex != lastMaterial && materialIndex < materialCount) if (materialIndex != lastMaterial && materialIndex < materialCount)
{ {
@ -203,7 +198,7 @@ ModelGeometry::isNormalized() const
bool bool
ModelGeometry::usesTextureType(Material::TextureSemantic t) const ModelGeometry::usesTextureType(cmod::TextureSemantic t) const
{ {
return m_model->usesTextureType(t); return m_model->usesTextureType(t);
} }

View File

@ -15,11 +15,11 @@
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include <celmodel/model.h> #include <celmodel/model.h>
#include <celutil/resmanager.h>
#include "geometry.h" #include "geometry.h"
class ModelOpenGLData; class ModelOpenGLData;
class RenderContext;
class ModelGeometry : public Geometry class ModelGeometry : public Geometry
{ {
@ -32,14 +32,14 @@ class ModelGeometry : public Geometry
* and set distance; otherwise return false and leave * and set distance; otherwise return false and leave
* distance unmodified. * distance unmodified.
*/ */
virtual bool pick(const Eigen::ParametrizedLine<double, 3>& r, double& distance) const; bool pick(const Eigen::ParametrizedLine<double, 3>& r, double& distance) const override;
//! Render the model in the current OpenGL context //! Render the model in the current OpenGL context
virtual void render(RenderContext&, double t = 0.0); void render(RenderContext&, double t = 0.0) override;
virtual bool usesTextureType(cmod::Material::TextureSemantic) const; bool usesTextureType(cmod::TextureSemantic) const override;
virtual bool isOpaque() const; bool isOpaque() const override;
virtual bool isNormalized() const; bool isNormalized() const override;
void loadTextures(); void loadTextures();

View File

@ -8,22 +8,24 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#include <vector> #include <algorithm>
#include <celmath/geomutil.h> #include <cstddef>
#include <celutil/color.h>
#include "body.h" #include "body.h"
#include "modelgeometry.h" #include "lightenv.h"
#include "rendcontext.h" #include "rendcontext.h"
#include "render.h" #include "render.h"
#include "shadowmap.h"
#include "texmanager.h" #include "texmanager.h"
#include "texture.h"
using namespace cmod;
using namespace Eigen;
using namespace std;
static Material defaultMaterial; namespace
{
static GLenum GLPrimitiveModes[Mesh::PrimitiveTypeMax] = const cmod::Material defaultMaterial;
constexpr GLenum GLPrimitiveModes[static_cast<std::size_t>(cmod::PrimitiveGroupType::PrimitiveTypeMax)] =
{ {
GL_TRIANGLES, GL_TRIANGLES,
GL_TRIANGLE_STRIP, GL_TRIANGLE_STRIP,
@ -34,7 +36,7 @@ static GLenum GLPrimitiveModes[Mesh::PrimitiveTypeMax] =
GL_POINTS, GL_POINTS,
}; };
static GLenum GLComponentTypes[Mesh::FormatMax] = constexpr GLenum GLComponentTypes[static_cast<std::size_t>(cmod::VertexAttributeFormat::FormatMax)] =
{ {
GL_FLOAT, // Float1 GL_FLOAT, // Float1
GL_FLOAT, // Float2 GL_FLOAT, // Float2
@ -43,7 +45,7 @@ static GLenum GLComponentTypes[Mesh::FormatMax] =
GL_UNSIGNED_BYTE, // UByte4 GL_UNSIGNED_BYTE, // UByte4
}; };
static int GLComponentCounts[Mesh::FormatMax] = constexpr int GLComponentCounts[static_cast<std::size_t>(cmod::VertexAttributeFormat::FormatMax)] =
{ {
1, // Float1 1, // Float1
2, // Float2 2, // Float2
@ -53,13 +55,160 @@ static int GLComponentCounts[Mesh::FormatMax] =
}; };
void
setStandardVertexArrays(const cmod::VertexDescription& desc,
const void* vertexData)
{
const cmod::VertexAttribute& position = desc.getAttribute(cmod::VertexAttributeSemantic::Position);
const cmod::VertexAttribute& normal = desc.getAttribute(cmod::VertexAttributeSemantic::Normal);
const cmod::VertexAttribute& color0 = desc.getAttribute(cmod::VertexAttributeSemantic::Color0);
const cmod::VertexAttribute& texCoord0 = desc.getAttribute(cmod::VertexAttributeSemantic::Texture0);
// Can't render anything unless we have positions
if (position.format != cmod::VertexAttributeFormat::Float3)
return;
// Set up the vertex arrays
glEnableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::VertexCoordAttributeIndex,
3, GL_FLOAT, GL_FALSE, desc.stride,
reinterpret_cast<const char*>(vertexData) + position.offset);
// Set up the normal array
switch (normal.format)
{
case cmod::VertexAttributeFormat::Float3:
glEnableVertexAttribArray(CelestiaGLProgram::NormalAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::NormalAttributeIndex,
3, GLComponentTypes[static_cast<std::size_t>(normal.format)],
GL_FALSE, desc.stride,
reinterpret_cast<const char*>(vertexData) + normal.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::NormalAttributeIndex);
break;
}
GLint normalized = GL_TRUE;
// Set up the color array
switch (color0.format)
{
case cmod::VertexAttributeFormat::Float3:
case cmod::VertexAttributeFormat::Float4:
normalized = GL_FALSE;
case cmod::VertexAttributeFormat::UByte4:
glEnableVertexAttribArray(CelestiaGLProgram::ColorAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::ColorAttributeIndex,
GLComponentCounts[static_cast<std::size_t>(color0.format)],
GLComponentTypes[static_cast<std::size_t>(color0.format)],
normalized, desc.stride,
reinterpret_cast<const char*>(vertexData) + color0.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::ColorAttributeIndex);
break;
}
// Set up the texture coordinate array
switch (texCoord0.format)
{
case cmod::VertexAttributeFormat::Float1:
case cmod::VertexAttributeFormat::Float2:
case cmod::VertexAttributeFormat::Float3:
case cmod::VertexAttributeFormat::Float4:
glEnableVertexAttribArray(CelestiaGLProgram::TextureCoord0AttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::TextureCoord0AttributeIndex,
GLComponentCounts[static_cast<std::size_t>(texCoord0.format)],
GLComponentTypes[static_cast<std::size_t>(texCoord0.format)],
GL_FALSE,
desc.stride,
reinterpret_cast<const char*>(vertexData) + texCoord0.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::TextureCoord0AttributeIndex);
break;
}
}
void
setExtendedVertexArrays(const cmod::VertexDescription& desc,
const void* vertexData)
{
const cmod::VertexAttribute& tangent = desc.getAttribute(cmod::VertexAttributeSemantic::Tangent);
const auto* vertices = reinterpret_cast<const char*>(vertexData);
switch (tangent.format)
{
case cmod::VertexAttributeFormat::Float3:
glEnableVertexAttribArray(CelestiaGLProgram::TangentAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::TangentAttributeIndex,
GLComponentCounts[static_cast<std::size_t>(tangent.format)],
GLComponentTypes[static_cast<std::size_t>(tangent.format)],
GL_FALSE,
desc.stride,
vertices + tangent.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::TangentAttributeIndex);
break;
}
const cmod::VertexAttribute& pointsize = desc.getAttribute(cmod::VertexAttributeSemantic::PointSize);
switch (pointsize.format)
{
case cmod::VertexAttributeFormat::Float1:
glEnableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::PointSizeAttributeIndex,
GLComponentCounts[static_cast<std::size_t>(pointsize.format)],
GLComponentTypes[static_cast<std::size_t>(pointsize.format)],
GL_FALSE,
desc.stride,
vertices + pointsize.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex);
break;
}
const cmod::VertexAttribute& nextPos = desc.getAttribute(cmod::VertexAttributeSemantic::NextPosition);
switch (nextPos.format)
{
case cmod::VertexAttributeFormat::Float3:
glEnableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::NextVCoordAttributeIndex,
GLComponentCounts[static_cast<std::size_t>(nextPos.format)],
GLComponentTypes[static_cast<std::size_t>(nextPos.format)],
GL_FALSE,
desc.stride,
vertices + nextPos.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex);
break;
}
const cmod::VertexAttribute& scaleFac = desc.getAttribute(cmod::VertexAttributeSemantic::ScaleFactor);
switch (scaleFac.format)
{
case cmod::VertexAttributeFormat::Float1:
glEnableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::ScaleFactorAttributeIndex,
GLComponentCounts[static_cast<std::size_t>(scaleFac.format)],
GLComponentTypes[static_cast<std::size_t>(scaleFac.format)],
GL_FALSE,
desc.stride,
vertices + scaleFac.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex);
break;
}
}
} // end unnamed namespace
static void
setStandardVertexArrays(const Mesh::VertexDescription& desc,
const void* vertexData);
static void
setExtendedVertexArrays(const Mesh::VertexDescription& desc,
const void* vertexData);
RenderContext::RenderContext(Renderer* _renderer) : RenderContext::RenderContext(Renderer* _renderer) :
@ -69,7 +218,7 @@ RenderContext::RenderContext(Renderer* _renderer) :
} }
RenderContext::RenderContext(const Material* _material) RenderContext::RenderContext(const cmod::Material* _material)
{ {
if (_material == nullptr) if (_material == nullptr)
material = &defaultMaterial; material = &defaultMaterial;
@ -78,7 +227,7 @@ RenderContext::RenderContext(const Material* _material)
} }
const Material* const cmod::Material*
RenderContext::getMaterial() const RenderContext::getMaterial() const
{ {
return material; return material;
@ -86,7 +235,7 @@ RenderContext::getMaterial() const
void void
RenderContext::setMaterial(const Material* newMaterial) RenderContext::setMaterial(const cmod::Material* newMaterial)
{ {
if (!locked) if (!locked)
{ {
@ -103,8 +252,8 @@ RenderContext::setMaterial(const Material* newMaterial)
} }
else if (renderPass == EmissivePass) else if (renderPass == EmissivePass)
{ {
if (material->maps[Material::EmissiveMap] != if (material->getMap(cmod::TextureSemantic::EmissiveMap) !=
newMaterial->maps[Material::EmissiveMap]) newMaterial->getMap(cmod::TextureSemantic::EmissiveMap))
{ {
material = newMaterial; material = newMaterial;
makeCurrent(*material); makeCurrent(*material);
@ -136,13 +285,13 @@ RenderContext::getPointScale() const
void void
RenderContext::setCameraOrientation(const Quaternionf& q) RenderContext::setCameraOrientation(const Eigen::Quaternionf& q)
{ {
cameraOrientation = q; cameraOrientation = q;
} }
Quaternionf Eigen::Quaternionf
RenderContext::getCameraOrientation() const RenderContext::getCameraOrientation() const
{ {
return cameraOrientation; return cameraOrientation;
@ -150,11 +299,11 @@ RenderContext::getCameraOrientation() const
void void
RenderContext::drawGroup(const Mesh::PrimitiveGroup& group, bool useOverride) RenderContext::drawGroup(const cmod::PrimitiveGroup& group, bool useOverride)
{ {
// Skip rendering if this is the emissive pass but there's no // Skip rendering if this is the emissive pass but there's no
// emissive texture. // emissive texture.
ResourceHandle emissiveMap = material->maps[Material::EmissiveMap]; ResourceHandle emissiveMap = material->getMap(cmod::TextureSemantic::EmissiveMap);
if (renderPass == EmissivePass && emissiveMap == InvalidResource) if (renderPass == EmissivePass && emissiveMap == InvalidResource)
{ {
@ -162,10 +311,10 @@ RenderContext::drawGroup(const Mesh::PrimitiveGroup& group, bool useOverride)
} }
bool drawPoints = false; bool drawPoints = false;
if (group.prim == Mesh::SpriteList || group.prim == Mesh::PointList) if (group.prim == cmod::PrimitiveGroupType::SpriteList || group.prim == cmod::PrimitiveGroupType::PointList)
{ {
drawPoints = true; drawPoints = true;
if (group.prim == Mesh::PointList) if (group.prim == cmod::PrimitiveGroupType::PointList)
glVertexAttrib1f(CelestiaGLProgram::PointSizeAttributeIndex, 1.0f); glVertexAttrib1f(CelestiaGLProgram::PointSizeAttributeIndex, 1.0f);
#ifndef GL_ES #ifndef GL_ES
glEnable(GL_POINT_SPRITE); glEnable(GL_POINT_SPRITE);
@ -189,7 +338,7 @@ RenderContext::drawGroup(const Mesh::PrimitiveGroup& group, bool useOverride)
void void
RenderContext::setVertexArrays(const Mesh::VertexDescription& desc, RenderContext::setVertexArrays(const cmod::VertexDescription& desc,
const void* vertexData) const void* vertexData)
{ {
setStandardVertexArrays(desc, vertexData); setStandardVertexArrays(desc, vertexData);
@ -197,18 +346,23 @@ RenderContext::setVertexArrays(const Mesh::VertexDescription& desc,
} }
void void
RenderContext::updateShader(const cmod::Mesh::VertexDescription& desc, Mesh::PrimitiveGroupType primType) RenderContext::updateShader(const cmod::VertexDescription& desc, cmod::PrimitiveGroupType primType)
{ {
// Normally, the shader that will be used depends only on the material. // Normally, the shader that will be used depends only on the material.
// But the presence of point size and normals can also affect the // But the presence of point size and normals can also affect the
// shader, so force an update of the material if those attributes appear // shader, so force an update of the material if those attributes appear
// or disappear in the new set of vertex arrays. // or disappear in the new set of vertex arrays.
bool usePointSizeNow = (desc.getAttribute(Mesh::PointSize).format == Mesh::Float1); bool usePointSizeNow = (desc.getAttribute(cmod::VertexAttributeSemantic::PointSize).format
bool useNormalsNow = (desc.getAttribute(Mesh::Normal).format == Mesh::Float3); == cmod::VertexAttributeFormat::Float1);
bool useColorsNow = (desc.getAttribute(Mesh::Color0).format != Mesh::InvalidFormat); bool useNormalsNow = (desc.getAttribute(cmod::VertexAttributeSemantic::Normal).format
bool useTexCoordsNow = (desc.getAttribute(Mesh::Texture0).format != Mesh::InvalidFormat); == cmod::VertexAttributeFormat::Float3);
bool drawLineNow = (desc.getAttribute(Mesh::NextPosition).format == Mesh::Float3); bool useColorsNow = (desc.getAttribute(cmod::VertexAttributeSemantic::Color0).format
bool useStaticPointSizeNow = primType == Mesh::PointList; != cmod::VertexAttributeFormat::InvalidFormat);
bool useTexCoordsNow = (desc.getAttribute(cmod::VertexAttributeSemantic::Texture0).format
!= cmod::VertexAttributeFormat::InvalidFormat);
bool drawLineNow = (desc.getAttribute(cmod::VertexAttributeSemantic::NextPosition).format
== cmod::VertexAttributeFormat::Float3);
bool useStaticPointSizeNow = primType == cmod::PrimitiveGroupType::PointList;
if (usePointSizeNow != usePointSize || if (usePointSizeNow != usePointSize ||
useStaticPointSizeNow != useStaticPointSize || useStaticPointSizeNow != useStaticPointSize ||
@ -240,168 +394,17 @@ RenderContext::setModelViewMatrix(const Eigen::Matrix4f *m)
modelViewMatrix = m; modelViewMatrix = m;
} }
void
setStandardVertexArrays(const Mesh::VertexDescription& desc,
const void* vertexData)
{
const Mesh::VertexAttribute& position = desc.getAttribute(Mesh::Position);
const Mesh::VertexAttribute& normal = desc.getAttribute(Mesh::Normal);
const Mesh::VertexAttribute& color0 = desc.getAttribute(Mesh::Color0);
const Mesh::VertexAttribute& texCoord0 = desc.getAttribute(Mesh::Texture0);
// Can't render anything unless we have positions
if (position.format != Mesh::Float3)
return;
// Set up the vertex arrays
glEnableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::VertexCoordAttributeIndex,
3, GL_FLOAT, GL_FALSE, desc.stride,
reinterpret_cast<const char*>(vertexData) + position.offset);
// Set up the normal array
switch (normal.format)
{
case Mesh::Float3:
glEnableVertexAttribArray(CelestiaGLProgram::NormalAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::NormalAttributeIndex,
3, GLComponentTypes[(int) normal.format],
GL_FALSE, desc.stride,
reinterpret_cast<const char*>(vertexData) + normal.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::NormalAttributeIndex);
break;
}
GLint normalized = GL_TRUE;
// Set up the color array
switch (color0.format)
{
case Mesh::Float3:
case Mesh::Float4:
normalized = GL_FALSE;
case Mesh::UByte4:
glEnableVertexAttribArray(CelestiaGLProgram::ColorAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::ColorAttributeIndex,
GLComponentCounts[color0.format],
GLComponentTypes[color0.format],
normalized, desc.stride,
reinterpret_cast<const char*>(vertexData) + color0.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::ColorAttributeIndex);
break;
}
// Set up the texture coordinate array
switch (texCoord0.format)
{
case Mesh::Float1:
case Mesh::Float2:
case Mesh::Float3:
case Mesh::Float4:
glEnableVertexAttribArray(CelestiaGLProgram::TextureCoord0AttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::TextureCoord0AttributeIndex,
GLComponentCounts[(int) texCoord0.format],
GLComponentTypes[(int) texCoord0.format],
GL_FALSE,
desc.stride,
reinterpret_cast<const char*>(vertexData) + texCoord0.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::TextureCoord0AttributeIndex);
break;
}
}
void
setExtendedVertexArrays(const Mesh::VertexDescription& desc,
const void* vertexData)
{
const Mesh::VertexAttribute& tangent = desc.getAttribute(Mesh::Tangent);
const auto* vertices = reinterpret_cast<const char*>(vertexData);
switch (tangent.format)
{
case Mesh::Float3:
glEnableVertexAttribArray(CelestiaGLProgram::TangentAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::TangentAttributeIndex,
GLComponentCounts[(int) tangent.format],
GLComponentTypes[(int) tangent.format],
GL_FALSE,
desc.stride,
vertices + tangent.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::TangentAttributeIndex);
break;
}
const Mesh::VertexAttribute& pointsize = desc.getAttribute(Mesh::PointSize);
switch (pointsize.format)
{
case Mesh::Float1:
glEnableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::PointSizeAttributeIndex,
GLComponentCounts[(int) pointsize.format],
GLComponentTypes[(int) pointsize.format],
GL_FALSE,
desc.stride,
vertices + pointsize.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex);
break;
}
const Mesh::VertexAttribute& nextPos = desc.getAttribute(Mesh::NextPosition);
switch (nextPos.format)
{
case Mesh::Float3:
glEnableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::NextVCoordAttributeIndex,
GLComponentCounts[(int) nextPos.format],
GLComponentTypes[(int) nextPos.format],
GL_FALSE,
desc.stride,
vertices + nextPos.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex);
break;
}
const Mesh::VertexAttribute& scaleFac = desc.getAttribute(Mesh::ScaleFactor);
switch (scaleFac.format)
{
case Mesh::Float1:
glEnableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::ScaleFactorAttributeIndex,
GLComponentCounts[(int) scaleFac.format],
GLComponentTypes[(int) scaleFac.format],
GL_FALSE,
desc.stride,
vertices + scaleFac.offset);
break;
default:
glDisableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex);
break;
}
}
/***** GLSL render context ******/ /***** GLSL render context ******/
GLSL_RenderContext::GLSL_RenderContext(Renderer* renderer, GLSL_RenderContext::GLSL_RenderContext(Renderer* renderer,
const LightingState& ls, const LightingState& ls,
float _objRadius, float _objRadius,
const Quaternionf& orientation) : const Eigen::Quaternionf& orientation) :
RenderContext(renderer), RenderContext(renderer),
lightingState(ls), lightingState(ls),
objRadius(_objRadius), objRadius(_objRadius),
objScale(Vector3f::Constant(_objRadius)), objScale(Eigen::Vector3f::Constant(_objRadius)),
objOrientation(orientation) objOrientation(orientation)
{ {
initLightingEnvironment(); initLightingEnvironment();
@ -411,7 +414,7 @@ GLSL_RenderContext::GLSL_RenderContext(Renderer* renderer,
GLSL_RenderContext::GLSL_RenderContext(Renderer* renderer, GLSL_RenderContext::GLSL_RenderContext(Renderer* renderer,
const LightingState& ls, const LightingState& ls,
const Eigen::Vector3f& _objScale, const Eigen::Vector3f& _objScale,
const Quaternionf& orientation) : const Eigen::Quaternionf& orientation) :
RenderContext(renderer), RenderContext(renderer),
lightingState(ls), lightingState(ls),
objRadius(_objScale.maxCoeff()), objRadius(_objScale.maxCoeff()),
@ -438,7 +441,7 @@ GLSL_RenderContext::initLightingEnvironment()
{ {
// Set the light and shadow environment, which is constant for the entire model. // Set the light and shadow environment, which is constant for the entire model.
// The material properties will be set per mesh. // The material properties will be set per mesh.
shaderProps.nLights = min(lightingState.nLights, MaxShaderLights); shaderProps.nLights = std::min(lightingState.nLights, MaxShaderLights);
// Set the shadow information. // Set the shadow information.
// Track the total number of shadows; if there are too many, we'll have // Track the total number of shadows; if there are too many, we'll have
@ -448,7 +451,8 @@ GLSL_RenderContext::initLightingEnvironment()
{ {
if (lightingState.shadows[li] && !lightingState.shadows[li]->empty()) if (lightingState.shadows[li] && !lightingState.shadows[li]->empty())
{ {
unsigned int nShadows = (unsigned int) min((size_t) MaxShaderEclipseShadows, lightingState.shadows[li]->size()); unsigned int nShadows = static_cast<unsigned int>(std::min(static_cast<std::size_t>(MaxShaderEclipseShadows),
lightingState.shadows[li]->size()));
shaderProps.setEclipseShadowCountForLight(li, nShadows); shaderProps.setEclipseShadowCountForLight(li, nShadows);
totalShadows += nShadows; totalShadows += nShadows;
} }
@ -457,7 +461,7 @@ GLSL_RenderContext::initLightingEnvironment()
} }
void void
GLSL_RenderContext::makeCurrent(const Material& m) GLSL_RenderContext::makeCurrent(const cmod::Material& m)
{ {
Texture* textures[4] = { nullptr, nullptr, nullptr, nullptr }; Texture* textures[4] = { nullptr, nullptr, nullptr, nullptr };
unsigned int nTextures = 0; unsigned int nTextures = 0;
@ -490,10 +494,10 @@ GLSL_RenderContext::makeCurrent(const Material& m)
shaderProps.lightModel = ShaderProperties::ParticleDiffuseModel; shaderProps.lightModel = ShaderProperties::ParticleDiffuseModel;
} }
ResourceHandle diffuseMap = m.maps[Material::DiffuseMap]; ResourceHandle diffuseMap = m.getMap(cmod::TextureSemantic::DiffuseMap);
ResourceHandle normalMap = m.maps[Material::NormalMap]; ResourceHandle normalMap = m.getMap(cmod::TextureSemantic::NormalMap);
ResourceHandle specularMap = m.maps[Material::SpecularMap]; ResourceHandle specularMap = m.getMap(cmod::TextureSemantic::SpecularMap);
ResourceHandle emissiveMap = m.maps[Material::EmissiveMap]; ResourceHandle emissiveMap = m.getMap(cmod::TextureSemantic::EmissiveMap);
if (diffuseMap != InvalidResource && (useTexCoords || usePointSize)) if (diffuseMap != InvalidResource && (useTexCoords || usePointSize))
{ {
@ -519,7 +523,7 @@ GLSL_RenderContext::makeCurrent(const Material& m)
} }
} }
if (m.specular != Material::Color(0.0f, 0.0f, 0.0f) && useNormals) if (m.specular != cmod::Color(0.0f, 0.0f, 0.0f) && useNormals)
{ {
shaderProps.lightModel = ShaderProperties::PerPixelSpecularModel; shaderProps.lightModel = ShaderProperties::PerPixelSpecularModel;
specTex = GetTextureManager()->find(specularMap); specTex = GetTextureManager()->find(specularMap);
@ -634,9 +638,9 @@ GLSL_RenderContext::makeCurrent(const Material& m)
#if GL_ONLY_SHADOWS #if GL_ONLY_SHADOWS
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
#endif #endif
Matrix4f shadowBias(Matrix4f::Zero()); Eigen::Matrix4f shadowBias(Eigen::Matrix4f::Zero());
shadowBias.diagonal() = Vector4f(0.5f, 0.5f, 0.5f, 1.0f); shadowBias.diagonal() = Eigen::Vector4f(0.5f, 0.5f, 0.5f, 1.0f);
shadowBias.col(3) = Vector4f(0.5f, 0.5f, 0.5f, 1.0f); shadowBias.col(3) = Eigen::Vector4f(0.5f, 0.5f, 0.5f, 1.0f);
prog->ShadowMatrix0 = shadowBias * (*lightMatrix); prog->ShadowMatrix0 = shadowBias * (*lightMatrix);
prog->floatParam("shadowMapSize") = static_cast<float>(shadowMapWidth); prog->floatParam("shadowMapSize") = static_cast<float>(shadowMapWidth);
} }
@ -698,7 +702,7 @@ GLSL_RenderContext::makeCurrent(const Material& m)
float ringWidth = rings->outerRadius - rings->innerRadius; float ringWidth = rings->outerRadius - rings->innerRadius;
prog->ringRadius = rings->innerRadius / objRadius; prog->ringRadius = rings->innerRadius / objRadius;
prog->ringWidth = objRadius / ringWidth; prog->ringWidth = objRadius / ringWidth;
prog->ringPlane = Hyperplane<float, 3>(lightingState.ringPlaneNormal, lightingState.ringCenter / objRadius).coeffs(); prog->ringPlane = Eigen::Hyperplane<float, 3>(lightingState.ringPlaneNormal, lightingState.ringCenter / objRadius).coeffs();
prog->ringCenter = lightingState.ringCenter / objRadius; prog->ringCenter = lightingState.ringCenter / objRadius;
for (unsigned int lightIndex = 0; lightIndex < lightingState.nLights; ++lightIndex) for (unsigned int lightIndex = 0; lightIndex < lightingState.nLights; ++lightIndex)
@ -710,9 +714,9 @@ GLSL_RenderContext::makeCurrent(const Material& m)
} }
} }
Material::BlendMode newBlendMode = Material::InvalidBlend; cmod::BlendMode newBlendMode = cmod::BlendMode::InvalidBlend;
if (m.opacity != 1.0f || if (m.opacity != 1.0f ||
m.blend == Material::AdditiveBlend || m.blend == cmod::BlendMode::AdditiveBlend ||
(baseTex != nullptr && baseTex->hasAlpha())) (baseTex != nullptr && baseTex->hasAlpha()))
{ {
newBlendMode = m.blend; newBlendMode = m.blend;
@ -723,7 +727,7 @@ GLSL_RenderContext::makeCurrent(const Material& m)
blendMode = newBlendMode; blendMode = newBlendMode;
switch (blendMode) switch (blendMode)
{ {
case Material::NormalBlend: case cmod::BlendMode::NormalBlend:
renderer->enableBlending(); renderer->enableBlending();
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (disableDepthWriteOnBlend) if (disableDepthWriteOnBlend)
@ -731,7 +735,7 @@ GLSL_RenderContext::makeCurrent(const Material& m)
else else
renderer->enableDepthMask(); renderer->enableDepthMask();
break; break;
case Material::AdditiveBlend: case cmod::BlendMode::AdditiveBlend:
renderer->enableBlending(); renderer->enableBlending();
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE); renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE);
if (disableDepthWriteOnBlend) if (disableDepthWriteOnBlend)
@ -739,7 +743,7 @@ GLSL_RenderContext::makeCurrent(const Material& m)
else else
renderer->enableDepthMask(); renderer->enableDepthMask();
break; break;
case Material::PremultipliedAlphaBlend: case cmod::BlendMode::PremultipliedAlphaBlend:
renderer->enableBlending(); renderer->enableBlending();
renderer->setBlendingFactors(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); renderer->setBlendingFactors(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
if (disableDepthWriteOnBlend) if (disableDepthWriteOnBlend)
@ -781,7 +785,7 @@ GLSL_RenderContext::setShadowMap(GLuint _shadowMap, GLuint _width, const Eigen::
GLSLUnlit_RenderContext::GLSLUnlit_RenderContext(Renderer* renderer, float _objRadius) : GLSLUnlit_RenderContext::GLSLUnlit_RenderContext(Renderer* renderer, float _objRadius) :
RenderContext(renderer), RenderContext(renderer),
blendMode(Material::InvalidBlend), blendMode(cmod::BlendMode::InvalidBlend),
objRadius(_objRadius) objRadius(_objRadius)
{ {
initLightingEnvironment(); initLightingEnvironment();
@ -809,7 +813,7 @@ GLSLUnlit_RenderContext::initLightingEnvironment()
void void
GLSLUnlit_RenderContext::makeCurrent(const Material& m) GLSLUnlit_RenderContext::makeCurrent(const cmod::Material& m)
{ {
Texture* textures[4] = { nullptr, nullptr, nullptr, nullptr }; Texture* textures[4] = { nullptr, nullptr, nullptr, nullptr };
unsigned int nTextures = 0; unsigned int nTextures = 0;
@ -820,7 +824,7 @@ GLSLUnlit_RenderContext::makeCurrent(const Material& m)
shaderProps.lightModel = ShaderProperties::EmissiveModel; shaderProps.lightModel = ShaderProperties::EmissiveModel;
shaderProps.texUsage = ShaderProperties::SharedTextureCoords; shaderProps.texUsage = ShaderProperties::SharedTextureCoords;
ResourceHandle diffuseMap = m.maps[Material::DiffuseMap]; ResourceHandle diffuseMap = m.getMap(cmod::TextureSemantic::DiffuseMap);
if (diffuseMap != InvalidResource && (useTexCoords || usePointSize)) if (diffuseMap != InvalidResource && (useTexCoords || usePointSize))
{ {
baseTex = GetTextureManager()->find(diffuseMap); baseTex = GetTextureManager()->find(diffuseMap);
@ -879,9 +883,9 @@ GLSLUnlit_RenderContext::makeCurrent(const Material& m)
prog->lineWidthY = renderer->getLineWidthY(); prog->lineWidthY = renderer->getLineWidthY();
} }
Material::BlendMode newBlendMode = Material::InvalidBlend; cmod::BlendMode newBlendMode = cmod::BlendMode::InvalidBlend;
if (m.opacity != 1.0f || if (m.opacity != 1.0f ||
m.blend == Material::AdditiveBlend || m.blend == cmod::BlendMode::AdditiveBlend ||
(baseTex != nullptr && baseTex->hasAlpha())) (baseTex != nullptr && baseTex->hasAlpha()))
{ {
newBlendMode = m.blend; newBlendMode = m.blend;
@ -892,17 +896,17 @@ GLSLUnlit_RenderContext::makeCurrent(const Material& m)
blendMode = newBlendMode; blendMode = newBlendMode;
switch (blendMode) switch (blendMode)
{ {
case Material::NormalBlend: case cmod::BlendMode::NormalBlend:
renderer->enableBlending(); renderer->enableBlending();
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
renderer->disableDepthMask(); renderer->disableDepthMask();
break; break;
case Material::AdditiveBlend: case cmod::BlendMode::AdditiveBlend:
renderer->enableBlending(); renderer->enableBlending();
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE); renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE);
renderer->disableDepthMask(); renderer->disableDepthMask();
break; break;
case Material::PremultipliedAlphaBlend: case cmod::BlendMode::PremultipliedAlphaBlend:
renderer->enableBlending(); renderer->enableBlending();
renderer->setBlendingFactors(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); renderer->setBlendingFactors(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
renderer->disableDepthMask(); renderer->disableDepthMask();

View File

@ -8,13 +8,21 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CELENGINE_RENDCONTEXT_H_ #pragma once
#define _CELENGINE_RENDCONTEXT_H_
#include "shadermanager.h" #include <Eigen/Core>
#include <celmodel/mesh.h>
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include <celmodel/material.h>
#include <celmodel/mesh.h>
#include "glsupport.h"
#include "shadermanager.h"
class Atmosphere;
class Color;
class LightingState;
class Renderer; class Renderer;
class RenderContext class RenderContext
@ -27,10 +35,10 @@ class RenderContext
virtual ~RenderContext() = default; virtual ~RenderContext() = default;
virtual void makeCurrent(const cmod::Material&) = 0; virtual void makeCurrent(const cmod::Material&) = 0;
virtual void setVertexArrays(const cmod::Mesh::VertexDescription& desc, virtual void setVertexArrays(const cmod::VertexDescription& desc,
const void* vertexData); const void* vertexData);
virtual void updateShader(const cmod::Mesh::VertexDescription& desc, cmod::Mesh::PrimitiveGroupType primType); virtual void updateShader(const cmod::VertexDescription& desc, cmod::PrimitiveGroupType primType);
virtual void drawGroup(const cmod::Mesh::PrimitiveGroup& group, bool useOverride); virtual void drawGroup(const cmod::PrimitiveGroup& group, bool useOverride);
const cmod::Material* getMaterial() const; const cmod::Material* getMaterial() const;
void setMaterial(const cmod::Material*); void setMaterial(const cmod::Material*);
@ -57,13 +65,6 @@ class RenderContext
void setModelViewMatrix(const Eigen::Matrix4f *m); void setModelViewMatrix(const Eigen::Matrix4f *m);
void setProjectionMatrix(const Eigen::Matrix4f *m); void setProjectionMatrix(const Eigen::Matrix4f *m);
private:
const cmod::Material* material{ nullptr };
bool locked{ false };
RenderPass renderPass{ PrimaryPass };
float pointScale{ 1.0f };
Eigen::Quaternionf cameraOrientation; // required for drawing billboards
protected: protected:
Renderer* renderer { nullptr }; Renderer* renderer { nullptr };
bool usePointSize{ false }; bool usePointSize{ false };
@ -74,6 +75,13 @@ class RenderContext
bool drawLine { false }; bool drawLine { false };
const Eigen::Matrix4f *modelViewMatrix; const Eigen::Matrix4f *modelViewMatrix;
const Eigen::Matrix4f *projectionMatrix; const Eigen::Matrix4f *projectionMatrix;
private:
const cmod::Material* material{ nullptr };
bool locked{ false };
RenderPass renderPass{ PrimaryPass };
float pointScale{ 1.0f };
Eigen::Quaternionf cameraOrientation; // required for drawing billboards
}; };
@ -105,14 +113,13 @@ class GLSL_RenderContext : public RenderContext
void setShadowMap(GLuint, GLuint, const Eigen::Matrix4f*); void setShadowMap(GLuint, GLuint, const Eigen::Matrix4f*);
private: private:
void initLightingEnvironment(); void initLightingEnvironment();
void setLightingParameters(CelestiaGLProgram& prog, Color diffuseColor, Color specularColor); void setLightingParameters(CelestiaGLProgram& prog, Color diffuseColor, Color specularColor);
void setShadowParameters(CelestiaGLProgram& prog); void setShadowParameters(CelestiaGLProgram& prog);
private:
const LightingState& lightingState; const LightingState& lightingState;
const Atmosphere* atmosphere{ nullptr }; const Atmosphere* atmosphere{ nullptr };
cmod::Material::BlendMode blendMode{ cmod::Material::InvalidBlend }; cmod::BlendMode blendMode{ cmod::BlendMode::InvalidBlend };
float objRadius; float objRadius;
Eigen::Vector3f objScale; Eigen::Vector3f objScale;
Eigen::Quaternionf objOrientation; Eigen::Quaternionf objOrientation;
@ -139,12 +146,8 @@ class GLSLUnlit_RenderContext : public RenderContext
void initLightingEnvironment(); void initLightingEnvironment();
void setLightingParameters(CelestiaGLProgram& prog, Color diffuseColor, Color specularColor); void setLightingParameters(CelestiaGLProgram& prog, Color diffuseColor, Color specularColor);
private: cmod::BlendMode blendMode;
cmod::Material::BlendMode blendMode;
float objRadius; float objRadius;
ShaderProperties shaderProps; ShaderProperties shaderProps;
}; };
#endif // _CELENGINE_RENDCONTEXT_H_

View File

@ -95,7 +95,6 @@ std::ofstream hdrlog;
#endif #endif
#endif #endif
using namespace cmod;
using namespace Eigen; using namespace Eigen;
using namespace std; using namespace std;
using namespace celestia; using namespace celestia;

View File

@ -8,14 +8,15 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CELENGINE_RENDER_H_ #pragma once
#define _CELENGINE_RENDER_H_
#include <list> #include <list>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <Eigen/Core> #include <Eigen/Core>
#include <celengine/universe.h> #include <celengine/universe.h>
#include <celengine/selection.h> #include <celengine/selection.h>
#include <celengine/starcolors.h> #include <celengine/starcolors.h>
@ -965,6 +966,3 @@ class RendererWatcher
virtual void notifyRenderSettingsChanged(const Renderer*) = 0; virtual void notifyRenderSettingsChanged(const Renderer*) = 0;
}; };
#endif // _CELENGINE_RENDER_H_

View File

@ -10,59 +10,117 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#include <config.h>
#include <algorithm> #include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <memory> #include <memory>
#include <celmath/geomutil.h>
#include <celmath/mathlib.h>
#include <celmodel/material.h>
#include <celutil/color.h>
#include "body.h"
#include "framebuffer.h" #include "framebuffer.h"
#include "geometry.h" #include "geometry.h"
#include "glsupport.h"
#include "lodspheremesh.h" #include "lodspheremesh.h"
#include "meshmanager.h" #include "rendcontext.h"
#include "modelgeometry.h"
#include "render.h" #include "render.h"
#include "renderinfo.h"
#include "renderglsl.h" #include "renderglsl.h"
#include "renderinfo.h"
#include "shadermanager.h" #include "shadermanager.h"
#include "texmanager.h" #include "texture.h"
#include "vecgl.h" #include "vecgl.h"
#include <celengine/astro.h>
#include <celmath/frustum.h>
#include <celmath/distance.h>
#include <celmath/geomutil.h>
#include <celmath/intersect.h>
#include <celutil/utf8.h>
#include <celutil/util.h>
#include "shadowmap.h"
using namespace cmod;
using namespace Eigen;
using namespace std;
using namespace celmath;
using namespace celestia; using namespace celestia;
static
namespace
{
// Calculate the matrix used to render the model from the
// perspective of the light.
Eigen::Matrix4f directionalLightMatrix(const Eigen::Vector3f& lightDirection)
{
const Eigen::Vector3f &viewDir = lightDirection;
Eigen::Vector3f upDir = viewDir.unitOrthogonal();
Eigen::Vector3f rightDir = upDir.cross(viewDir);
Eigen::Matrix4f m = Eigen::Matrix4f::Identity();
m.row(0).head(3) = rightDir;
m.row(1).head(3) = upDir;
m.row(2).head(3) = viewDir;
return m;
}
/*! Render a mesh object
* Parameters:
* tsec : animation clock time in seconds
*/
void renderGeometryShadow_GLSL(Geometry* geometry, void renderGeometryShadow_GLSL(Geometry* geometry,
FramebufferObject* shadowFbo, FramebufferObject* shadowFbo,
const LightingState& ls, const LightingState& ls,
int lightIndex, int lightIndex,
double tsec, double tsec,
Renderer* renderer, Renderer* renderer,
Matrix4f *lightMatrix); Eigen::Matrix4f *lightMatrix)
{
auto *prog = renderer->getShaderManager().getShader("depth");
if (prog == nullptr)
return;
GLint oldFboId;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFboId);
shadowFbo->bind();
glViewport(0, 0, shadowFbo->width(), shadowFbo->height());
// Write only to the depth buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
renderer->enableDepthMask();
renderer->enableDepthTest();
glClear(GL_DEPTH_BUFFER_BIT);
// Render backfaces only in order to reduce self-shadowing artifacts
glCullFace(GL_FRONT);
Shadow_RenderContext rc(renderer);
prog->use();
// Enable poligon offset to decrease "shadow acne"
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(.001f, .001f);
Eigen::Matrix4f projMat = celmath::Ortho(-1.f, 1.f, -1.f, 1.f, -1.f, 1.f);
Eigen::Matrix4f modelViewMat = directionalLightMatrix(ls.lights[lightIndex].direction_obj);
*lightMatrix = projMat * modelViewMat;
prog->setMVPMatrices(projMat, modelViewMat);
geometry->render(rc, tsec);
glDisable(GL_POLYGON_OFFSET_FILL);
// Re-enable the color buffer
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glCullFace(GL_BACK);
shadowFbo->unbind(oldFboId);
}
} // end unnamed namespace
static
Matrix4f directionalLightMatrix(const Vector3f& lightDirection);
// Render a planet sphere with GLSL shaders // Render a planet sphere with GLSL shaders
void renderEllipsoid_GLSL(const RenderInfo& ri, void renderEllipsoid_GLSL(const RenderInfo& ri,
const LightingState& ls, const LightingState& ls,
Atmosphere* atmosphere, Atmosphere* atmosphere,
float cloudTexOffset, float cloudTexOffset,
const Vector3f& semiAxes, const Eigen::Vector3f& semiAxes,
unsigned int textureRes, unsigned int textureRes,
uint64_t renderFlags, std::uint64_t renderFlags,
const Quaternionf& planetOrientation, const Eigen::Quaternionf& planetOrientation,
const Frustum& frustum, const celmath::Frustum& frustum,
const Matrices &m, const Matrices &m,
Renderer* renderer) Renderer* renderer)
{ {
float radius = semiAxes.maxCoeff(); float radius = semiAxes.maxCoeff();
@ -71,7 +129,7 @@ void renderEllipsoid_GLSL(const RenderInfo& ri,
unsigned int nTextures = 0; unsigned int nTextures = 0;
ShaderProperties shadprop; ShaderProperties shadprop;
shadprop.nLights = min(ls.nLights, MaxShaderLights); shadprop.nLights = std::min(ls.nLights, MaxShaderLights);
// Set up the textures used by this object // Set up the textures used by this object
if (ri.baseTex != nullptr) if (ri.baseTex != nullptr)
@ -189,7 +247,8 @@ void renderEllipsoid_GLSL(const RenderInfo& ri,
{ {
if (ls.shadows[li] && !ls.shadows[li]->empty()) if (ls.shadows[li] && !ls.shadows[li]->empty())
{ {
unsigned int nShadows = (unsigned int) min((size_t) MaxShaderEclipseShadows, ls.shadows[li]->size()); unsigned int nShadows = static_cast<unsigned int>(std::min(static_cast<std::size_t>(MaxShaderEclipseShadows),
ls.shadows[li]->size()));
shadprop.setEclipseShadowCountForLight(li, nShadows); shadprop.setEclipseShadowCountForLight(li, nShadows);
totalShadows += nShadows; totalShadows += nShadows;
} }
@ -262,7 +321,7 @@ void renderEllipsoid_GLSL(const RenderInfo& ri,
float ringWidth = ls.shadowingRingSystem->outerRadius - ls.shadowingRingSystem->innerRadius; float ringWidth = ls.shadowingRingSystem->outerRadius - ls.shadowingRingSystem->innerRadius;
prog->ringRadius = ls.shadowingRingSystem->innerRadius / radius; prog->ringRadius = ls.shadowingRingSystem->innerRadius / radius;
prog->ringWidth = radius / ringWidth; prog->ringWidth = radius / ringWidth;
prog->ringPlane = Hyperplane<float, 3>(ls.ringPlaneNormal, ls.ringCenter / radius).coeffs(); prog->ringPlane = Eigen::Hyperplane<float, 3>(ls.ringPlaneNormal, ls.ringCenter / radius).coeffs();
prog->ringCenter = ls.ringCenter / radius; prog->ringCenter = ls.ringCenter / radius;
for (unsigned int lightIndex = 0; lightIndex < ls.nLights; ++lightIndex) for (unsigned int lightIndex = 0; lightIndex < ls.nLights; ++lightIndex)
{ {
@ -311,14 +370,14 @@ void renderGeometry_GLSL(Geometry* geometry,
const LightingState& ls, const LightingState& ls,
const Atmosphere* atmosphere, const Atmosphere* atmosphere,
float geometryScale, float geometryScale,
uint64_t renderFlags, std::uint64_t renderFlags,
const Quaternionf& planetOrientation, const Eigen::Quaternionf& planetOrientation,
double tsec, double tsec,
const Matrices &m, const Matrices &m,
Renderer* renderer) Renderer* renderer)
{ {
auto *shadowBuffer = renderer->getShadowFBO(0); auto *shadowBuffer = renderer->getShadowFBO(0);
Matrix4f lightMatrix(Matrix4f::Identity()); Eigen::Matrix4f lightMatrix(Eigen::Matrix4f::Identity());
if (shadowBuffer != nullptr && shadowBuffer->isValid()) if (shadowBuffer != nullptr && shadowBuffer->isValid())
{ {
@ -407,12 +466,12 @@ void renderGeometry_GLSL(Geometry* geometry,
// override all materials specified in the geometry file. // override all materials specified in the geometry file.
if (texOverride != InvalidResource) if (texOverride != InvalidResource)
{ {
Material m; cmod::Material m;
m.diffuse = Material::Color(ri.color); m.diffuse = cmod::Color(ri.color);
m.specular = Material::Color(ri.specularColor); m.specular = cmod::Color(ri.specularColor);
m.specularPower = ri.specularPower; m.specularPower = ri.specularPower;
m.maps[Material::DiffuseMap] = texOverride; m.setMap(cmod::TextureSemantic::DiffuseMap, texOverride);
rc.setMaterial(&m); rc.setMaterial(&m);
rc.lock(); rc.lock();
geometry->render(rc, tsec); geometry->render(rc, tsec);
@ -432,8 +491,8 @@ void renderGeometry_GLSL_Unlit(Geometry* geometry,
const RenderInfo& ri, const RenderInfo& ri,
ResourceHandle texOverride, ResourceHandle texOverride,
float geometryScale, float geometryScale,
uint64_t /* renderFlags */, std::uint64_t /* renderFlags */,
const Quaternionf& /* planetOrientation */, const Eigen::Quaternionf& /* planetOrientation */,
double tsec, double tsec,
const Matrices &m, const Matrices &m,
Renderer* renderer) Renderer* renderer)
@ -448,12 +507,12 @@ void renderGeometry_GLSL_Unlit(Geometry* geometry,
// override all materials specified in the model file. // override all materials specified in the model file.
if (texOverride != InvalidResource) if (texOverride != InvalidResource)
{ {
Material m; cmod::Material m;
m.diffuse = Material::Color(ri.color); m.diffuse = cmod::Color(ri.color);
m.specular = Material::Color(ri.specularColor); m.specular = cmod::Color(ri.specularColor);
m.specularPower = ri.specularPower; m.specularPower = ri.specularPower;
m.maps[Material::DiffuseMap] = texOverride; m.setMap(cmod::TextureSemantic::DiffuseMap, texOverride);
rc.setMaterial(&m); rc.setMaterial(&m);
rc.lock(); rc.lock();
geometry->render(rc, tsec); geometry->render(rc, tsec);
@ -472,11 +531,11 @@ void renderClouds_GLSL(const RenderInfo& ri,
Texture* cloudTex, Texture* cloudTex,
Texture* cloudNormalMap, Texture* cloudNormalMap,
float texOffset, float texOffset,
const Vector3f& semiAxes, const Eigen::Vector3f& semiAxes,
unsigned int /*textureRes*/, unsigned int /*textureRes*/,
uint64_t renderFlags, std::uint64_t renderFlags,
const Quaternionf& planetOrientation, const Eigen::Quaternionf& planetOrientation,
const Frustum& frustum, const celmath::Frustum& frustum,
const Matrices &m, const Matrices &m,
Renderer* renderer) Renderer* renderer)
{ {
@ -546,7 +605,8 @@ void renderClouds_GLSL(const RenderInfo& ri,
{ {
if (ls.shadows[li] && !ls.shadows[li]->empty()) if (ls.shadows[li] && !ls.shadows[li]->empty())
{ {
unsigned int nShadows = (unsigned int) min((size_t) MaxShaderEclipseShadows, ls.shadows[li]->size()); unsigned int nShadows = static_cast<unsigned int>(std::min(static_cast<std::size_t>(MaxShaderEclipseShadows),
ls.shadows[li]->size()));
shadprop.setEclipseShadowCountForLight(li, nShadows); shadprop.setEclipseShadowCountForLight(li, nShadows);
totalShadows += nShadows; totalShadows += nShadows;
} }
@ -604,8 +664,8 @@ renderAtmosphere_GLSL(const RenderInfo& ri,
const LightingState& ls, const LightingState& ls,
Atmosphere* atmosphere, Atmosphere* atmosphere,
float radius, float radius,
const Quaternionf& /*planetOrientation*/, const Eigen::Quaternionf& /*planetOrientation*/,
const Frustum& frustum, const celmath::Frustum& frustum,
const Matrices &m, const Matrices &m,
Renderer* renderer) Renderer* renderer)
{ {
@ -629,9 +689,9 @@ renderAtmosphere_GLSL(const RenderInfo& ri,
prog->use(); prog->use();
prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black); prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
prog->ambientColor = Vector3f::Zero(); prog->ambientColor = Eigen::Vector3f::Zero();
float atmosphereRadius = radius + -atmosphere->mieScaleHeight * log(AtmosphereExtinctionThreshold); float atmosphereRadius = radius + -atmosphere->mieScaleHeight * std::log(AtmosphereExtinctionThreshold);
float atmScale = atmosphereRadius / radius; float atmScale = atmosphereRadius / radius;
prog->eyePosition = ls.eyePos_obj / atmScale; prog->eyePosition = ls.eyePos_obj / atmScale;
@ -671,19 +731,19 @@ static void renderRingSystem(GLuint *vboId,
GLshort tex[2]; GLshort tex[2];
}; };
constexpr const float angle = 2*static_cast<float>(PI); constexpr const float angle = 2.0f * static_cast<float>(PI);
if (*vboId == 0) if (*vboId == 0)
{ {
struct RingVertex vertex; struct RingVertex vertex;
vector<struct RingVertex> ringCoord; std::vector<struct RingVertex> ringCoord;
ringCoord.reserve(2 * nSections); ringCoord.reserve(2 * nSections);
for (unsigned i = 0; i <= nSections; i++) for (unsigned i = 0; i <= nSections; i++)
{ {
float t = (float) i / (float) nSections; float t = static_cast<float>(i) / static_cast<float>(nSections);
float theta = t * angle; float theta = t * angle;
float s = (float) sin(theta); float s = std::sin(theta);
float c = (float) cos(theta); float c = std::cos(theta);
// inner point // inner point
vertex.pos[0] = c * innerRadius; vertex.pos[0] = c * innerRadius;
@ -717,7 +777,7 @@ static void renderRingSystem(GLuint *vboId,
glVertexAttribPointer(CelestiaGLProgram::TextureCoord0AttributeIndex, glVertexAttribPointer(CelestiaGLProgram::TextureCoord0AttributeIndex,
2, GL_SHORT, GL_FALSE, 2, GL_SHORT, GL_FALSE,
sizeof(struct RingVertex), sizeof(struct RingVertex),
(GLvoid*) offsetof(struct RingVertex, tex)); reinterpret_cast<GLvoid*>(offsetof(struct RingVertex, tex)));
glEnableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex); glEnableVertexAttribArray(CelestiaGLProgram::VertexCoordAttributeIndex);
glVertexAttribPointer(CelestiaGLProgram::VertexCoordAttributeIndex, glVertexAttribPointer(CelestiaGLProgram::VertexCoordAttributeIndex,
@ -768,7 +828,7 @@ void renderRings_GLSL(RingSystem& rings,
// Set up the shader properties for ring rendering // Set up the shader properties for ring rendering
{ {
shadprop.lightModel = ShaderProperties::RingIllumModel; shadprop.lightModel = ShaderProperties::RingIllumModel;
shadprop.nLights = min(ls.nLights, MaxShaderLights); shadprop.nLights = std::min(ls.nLights, MaxShaderLights);
if (renderShadow) if (renderShadow)
{ {
@ -804,8 +864,8 @@ void renderRings_GLSL(RingSystem& rings,
// planet would ever orbit underneath its sun (an orbital // planet would ever orbit underneath its sun (an orbital
// inclination of 90 degrees), but this should be made // inclination of 90 degrees), but this should be made
// more robust anyway. // more robust anyway.
Vector3f axis = Vector3f::UnitY().cross(light.direction_obj); Eigen::Vector3f axis = Eigen::Vector3f::UnitY().cross(light.direction_obj);
float cosAngle = Vector3f::UnitY().dot(light.direction_obj); float cosAngle = Eigen::Vector3f::UnitY().dot(light.direction_obj);
axis.normalize(); axis.normalize();
float tScale = 1.0f; float tScale = 1.0f;
@ -821,20 +881,20 @@ void renderRings_GLSL(RingSystem& rings,
// Calculate the radius of the ellipse at the incident angle of the // Calculate the radius of the ellipse at the incident angle of the
// light on the ring plane + 90 degrees. // light on the ring plane + 90 degrees.
float r = a * (float) sqrt((1.0f - ecc2) / float r = a * std::sqrt((1.0f - ecc2) /
(1.0f - ecc2 * square(cosAngle))); (1.0f - ecc2 * celmath::square(cosAngle)));
tScale *= a / r; tScale *= a / r;
} }
// The s axis is perpendicular to the shadow axis in the plane of the // The s axis is perpendicular to the shadow axis in the plane of the
// of the rings, and the t axis completes the orthonormal basis. // of the rings, and the t axis completes the orthonormal basis.
Vector3f sAxis = axis * 0.5f; Eigen::Vector3f sAxis = axis * 0.5f;
Vector3f tAxis = (axis.cross(light.direction_obj)) * 0.5f * tScale; Eigen::Vector3f tAxis = (axis.cross(light.direction_obj)) * 0.5f * tScale;
Vector4f texGenS; Eigen::Vector4f texGenS;
texGenS.head(3) = sAxis; texGenS.head(3) = sAxis;
texGenS[3] = 0.5f; texGenS[3] = 0.5f;
Vector4f texGenT; Eigen::Vector4f texGenT;
texGenT.head(3) = tAxis; texGenT.head(3) = tAxis;
texGenT[3] = 0.5f; texGenT[3] = 0.5f;
@ -862,11 +922,11 @@ void renderRings_GLSL(RingSystem& rings,
ringsTex->bind(); ringsTex->bind();
if (rings.renderData == nullptr) if (rings.renderData == nullptr)
rings.renderData = shared_ptr<GLRingRenderData>(new GLRingRenderData); rings.renderData = std::make_shared<GLRingRenderData>();
auto data = reinterpret_cast<GLRingRenderData*>(rings.renderData.get()); auto data = reinterpret_cast<GLRingRenderData*>(rings.renderData.get());
unsigned nSections = 180; unsigned nSections = 180;
size_t i = 0; std::size_t i = 0;
for (i = 0; i < data->vboId.size() - 1; i++) for (i = 0; i < data->vboId.size() - 1; i++)
{ {
float s = segmentSizeInPixels * tan(PI / nSections); float s = segmentSizeInPixels * tan(PI / nSections);
@ -878,71 +938,3 @@ void renderRings_GLSL(RingSystem& rings,
renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE); renderer->setBlendingFactors(GL_SRC_ALPHA, GL_ONE);
} }
// Calculate the matrix used to render the model from the
// perspective of the light.
static
Matrix4f directionalLightMatrix(const Vector3f& lightDirection)
{
const Vector3f &viewDir = lightDirection;
Vector3f upDir = viewDir.unitOrthogonal();
Vector3f rightDir = upDir.cross(viewDir);
Matrix4f m = Matrix4f::Identity();
m.row(0).head(3) = rightDir;
m.row(1).head(3) = upDir;
m.row(2).head(3) = viewDir;
return m;
}
/*! Render a mesh object
* Parameters:
* tsec : animation clock time in seconds
*/
static
void renderGeometryShadow_GLSL(Geometry* geometry,
FramebufferObject* shadowFbo,
const LightingState& ls,
int lightIndex,
double tsec,
Renderer* renderer,
Eigen::Matrix4f *lightMatrix)
{
auto *prog = renderer->getShaderManager().getShader("depth");
if (prog == nullptr)
return;
GLint oldFboId;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFboId);
shadowFbo->bind();
glViewport(0, 0, shadowFbo->width(), shadowFbo->height());
// Write only to the depth buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
renderer->enableDepthMask();
renderer->enableDepthTest();
glClear(GL_DEPTH_BUFFER_BIT);
// Render backfaces only in order to reduce self-shadowing artifacts
glCullFace(GL_FRONT);
Shadow_RenderContext rc(renderer);
prog->use();
// Enable poligon offset to decrease "shadow acne"
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(.001f, .001f);
Matrix4f projMat = Ortho(-1.f, 1.f, -1.f, 1.f, -1.f, 1.f);
Matrix4f modelViewMat = directionalLightMatrix(ls.lights[lightIndex].direction_obj);
*lightMatrix = projMat * modelViewMat;
prog->setMVPMatrices(projMat, modelViewMat);
geometry->render(rc, tsec);
glDisable(GL_POLYGON_OFFSET_FILL);
// Re-enable the color buffer
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glCullFace(GL_BACK);
shadowFbo->unbind(oldFboId);
}

View File

@ -10,25 +10,41 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CELENGINE_RENDERGLSL_H_ #pragma once
#define _CELENGINE_RENDERGLSL_H_
#include <celengine/lightenv.h> #include <cstdint>
#include <Eigen/Core>
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include <celutil/reshandle.h>
class Atmosphere;
class Geometry;
class LightingState;
struct Matrices;
class Renderer; class Renderer;
struct RenderInfo;
class RingSystem;
class Texture;
namespace celmath
{
class Frustum;
}
void renderEllipsoid_GLSL(const RenderInfo& ri, void renderEllipsoid_GLSL(const RenderInfo& ri,
const LightingState& ls, const LightingState& ls,
Atmosphere* atmosphere, Atmosphere* atmosphere,
float cloudTexOffset, float cloudTexOffset,
const Eigen::Vector3f& semiAxes, const Eigen::Vector3f& semiAxes,
unsigned int textureRes, unsigned int textureRes,
uint64_t renderFlags, std::uint64_t renderFlags,
const Eigen::Quaternionf& planetOrientation, const Eigen::Quaternionf& planetOrientation,
const celmath::Frustum& frustum, const celmath::Frustum& frustum,
const Matrices &m, const Matrices &m,
Renderer* renderer); Renderer* renderer);
void renderGeometry_GLSL(Geometry* geometry, void renderGeometry_GLSL(Geometry* geometry,
const RenderInfo& ri, const RenderInfo& ri,
@ -36,7 +52,7 @@ void renderGeometry_GLSL(Geometry* geometry,
const LightingState& ls, const LightingState& ls,
const Atmosphere* atmosphere, const Atmosphere* atmosphere,
float geometryScale, float geometryScale,
uint64_t renderFlags, std::uint64_t renderFlags,
const Eigen::Quaternionf& planetOrientation, const Eigen::Quaternionf& planetOrientation,
double tsec, double tsec,
const Matrices &m, const Matrices &m,
@ -50,7 +66,7 @@ void renderClouds_GLSL(const RenderInfo& ri,
float texOffset, float texOffset,
const Eigen::Vector3f& semiAxes, const Eigen::Vector3f& semiAxes,
unsigned int textureRes, unsigned int textureRes,
uint64_t renderFlags, std::uint64_t renderFlags,
const Eigen::Quaternionf& planetOrientation, const Eigen::Quaternionf& planetOrientation,
const celmath::Frustum& frustum, const celmath::Frustum& frustum,
const Matrices &m, const Matrices &m,
@ -80,10 +96,8 @@ void renderGeometry_GLSL_Unlit(Geometry* geometry,
const RenderInfo& ri, const RenderInfo& ri,
ResourceHandle texOverride, ResourceHandle texOverride,
float geometryScale, float geometryScale,
uint64_t renderFlags, std::uint64_t renderFlags,
const Eigen::Quaternionf& planetOrientation, const Eigen::Quaternionf& planetOrientation,
double tsec, double tsec,
const Matrices &m, const Matrices &m,
Renderer* renderer); Renderer* renderer);
#endif // _CELENGINE_RENDERGLSL_H_

View File

@ -7,6 +7,18 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#pragma once
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <celutil/color.h>
class LODSphereMesh;
class Texture;
struct RenderInfo struct RenderInfo
{ {
EIGEN_MAKE_ALIGNED_OPERATOR_NEW EIGEN_MAKE_ALIGNED_OPERATOR_NEW

View File

@ -12,12 +12,11 @@
// It's sole function now is to handle the now-deprecated .cms mesh files; // It's sole function now is to handle the now-deprecated .cms mesh files;
// it will eventually be removed from Celestia. // it will eventually be removed from Celestia.
#include "spheremesh.h"
#include <celmath/mathlib.h>
#include "glsupport.h"
#include <cmath> #include <cmath>
#include <cstdint>
using namespace Eigen; #include <celmodel/mesh.h>
#include "spheremesh.h"
SphereMesh::SphereMesh(float radius, int _nRings, int _nSlices) SphereMesh::SphereMesh(float radius, int _nRings, int _nSlices)
@ -25,13 +24,13 @@ SphereMesh::SphereMesh(float radius, int _nRings, int _nSlices)
createSphere(radius, _nRings, _nSlices); createSphere(radius, _nRings, _nSlices);
} }
SphereMesh::SphereMesh(const Vector3f& size, int _nRings, int _nSlices) SphereMesh::SphereMesh(const Eigen::Vector3f& size, int _nRings, int _nSlices)
{ {
createSphere(1.0f, _nRings, _nSlices); createSphere(1.0f, _nRings, _nSlices);
scale(size); scale(size);
} }
SphereMesh::SphereMesh(const Vector3f& size, SphereMesh::SphereMesh(const Eigen::Vector3f& size,
const DisplacementMap& dispmap, const DisplacementMap& dispmap,
float height) float height)
{ {
@ -42,7 +41,7 @@ SphereMesh::SphereMesh(const Vector3f& size,
fixNormals(); fixNormals();
} }
SphereMesh::SphereMesh(const Vector3f& size, SphereMesh::SphereMesh(const Eigen::Vector3f& size,
int _nRings, int _nSlices, int _nRings, int _nSlices,
DisplacementMapFunc func, DisplacementMapFunc func,
void* info) void* info)
@ -79,27 +78,27 @@ void SphereMesh::createSphere(float radius, int _nRings, int _nSlices)
int i; int i;
for (i = 0; i < nRings; i++) for (i = 0; i < nRings; i++)
{ {
float phi = ((float) i / (float) (nRings - 1) - 0.5f) * (float) PI; float phi = (static_cast<float>(i) / static_cast<float>(nRings - 1) - 0.5f) * static_cast<float>(PI);
for (int j = 0; j <= nSlices; j++) for (int j = 0; j <= nSlices; j++)
{ {
float theta = (float) j / (float) nSlices * (float) PI * 2; float theta = static_cast<float>(j) / static_cast<float>(nSlices) * static_cast<float>(PI * 2.0);
int n = i * (nSlices + 1) + j; int n = i * (nSlices + 1) + j;
auto x = (float) (std::cos(phi) * std::cos(theta)); auto x = std::cos(phi) * std::cos(theta);
auto y = (float) std::sin(phi); auto y = std::sin(phi);
auto z = (float) (std::cos(phi) * std::sin(theta)); auto z = std::cos(phi) * std::sin(theta);
vertices[n * 3] = x * radius; vertices[n * 3] = x * radius;
vertices[n * 3 + 1] = y * radius; vertices[n * 3 + 1] = y * radius;
vertices[n * 3 + 2] = z * radius; vertices[n * 3 + 2] = z * radius;
normals[n * 3] = x; normals[n * 3] = x;
normals[n * 3 + 1] = y; normals[n * 3 + 1] = y;
normals[n * 3 + 2] = z; normals[n * 3 + 2] = z;
texCoords[n * 2] = 1.0f - (float) j / (float) nSlices; texCoords[n * 2] = 1.0f - static_cast<float>(j) / static_cast<float>(nSlices);
texCoords[n * 2 + 1] = 1.0f - (float) i / (float) (nRings - 1); texCoords[n * 2 + 1] = 1.0f - static_cast<float>(i) / static_cast<float>(nRings - 1);
// Compute the tangent--required for bump mapping // Compute the tangent--required for bump mapping
auto tx = (float) (std::sin(phi) * std::sin(theta)); auto tx = std::sin(phi) * std::sin(theta);
auto ty = (float) -std::cos(phi); auto ty = -std::cos(phi);
auto tz = (float) (std::sin(phi) * std::cos(theta)); auto tz = std::sin(phi) * std::cos(theta);
tangents[n * 3] = tx; tangents[n * 3] = tx;
tangents[n * 3 + 1] = ty; tangents[n * 3 + 1] = ty;
tangents[n * 3 + 2] = tz; tangents[n * 3 + 2] = tz;
@ -122,7 +121,7 @@ void SphereMesh::createSphere(float radius, int _nRings, int _nSlices)
void SphereMesh::generateNormals() void SphereMesh::generateNormals()
{ {
int nQuads = nSlices * (nRings - 1); int nQuads = nSlices * (nRings - 1);
Vector3f* faceNormals = new Vector3f[nQuads]; Eigen::Vector3f* faceNormals = new Eigen::Vector3f[nQuads];
int i; int i;
// Compute face normals for the mesh // Compute face normals for the mesh
@ -138,20 +137,20 @@ void SphereMesh::generateNormals()
// Compute the face normal. Watch out for degenerate (zero-length) // Compute the face normal. Watch out for degenerate (zero-length)
// edges. If there are two degenerate edges, the entire face must // edges. If there are two degenerate edges, the entire face must
// be degenerate and we'll handle that later // be degenerate and we'll handle that later
Vector3f v0(p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]); Eigen::Vector3f v0 = Eigen::Map<Eigen::Vector3f>(p1) - Eigen::Map<Eigen::Vector3f>(p0);
Vector3f v1(p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]); Eigen::Vector3f v1 = Eigen::Map<Eigen::Vector3f>(p2) - Eigen::Map<Eigen::Vector3f>(p1);
if (v0.norm() < 1e-6f) if (v0.norm() < 1e-6f)
{ {
v0 = Vector3f(p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]); v0 = Eigen::Map<Eigen::Vector3f>(p2) - Eigen::Map<Eigen::Vector3f>(p1);
v1 = Vector3f(p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2]); v1 = Eigen::Map<Eigen::Vector3f>(p3) - Eigen::Map<Eigen::Vector3f>(p2);
} }
else if (v1.norm() < 1e-6f) else if (v1.norm() < 1e-6f)
{ {
v0 = Vector3f(p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2]); v0 = Eigen::Map<Eigen::Vector3f>(p3) - Eigen::Map<Eigen::Vector3f>(p2);
v1 = Vector3f(p0[0] - p3[0], p0[1] - p3[1], p0[2] - p3[2]); v1 = Eigen::Map<Eigen::Vector3f>(p0) - Eigen::Map<Eigen::Vector3f>(p3);
} }
Vector3f faceNormal = v0.cross(v1); Eigen::Vector3f faceNormal = v0.cross(v1);
float length = faceNormal.norm(); float length = faceNormal.norm();
if (length != 0) if (length != 0)
faceNormal *= (1 / length); faceNormal *= (1 / length);
@ -224,11 +223,11 @@ void SphereMesh::generateNormals()
{ {
if (faceCounts[i] > 0) if (faceCounts[i] > 0)
{ {
float s = 1.0f / (float) faceCounts[i]; float s = 1.0f / static_cast<float>(faceCounts[i]);
float nx = normals[i * 3] * s; float nx = normals[i * 3] * s;
float ny = normals[i * 3 + 1] * s; float ny = normals[i * 3 + 1] * s;
float nz = normals[i * 3 + 2] * s; float nz = normals[i * 3 + 2] * s;
auto length = (float) std::sqrt(nx * nx + ny * ny + nz * nz); auto length = std::sqrt(nx * nx + ny * ny + nz * nz);
if (length > 0) if (length > 0)
{ {
length = 1 / length; length = 1 / length;
@ -251,9 +250,9 @@ void SphereMesh::fixNormals()
{ {
float* v0 = normals + (i * (nSlices + 1)) * 3; float* v0 = normals + (i * (nSlices + 1)) * 3;
float* v1 = normals + ((i + 1) * (nSlices + 1) - 1) * 3; float* v1 = normals + ((i + 1) * (nSlices + 1) - 1) * 3;
Vector3f n0(v0[0], v0[1], v0[2]); Eigen::Map<Eigen::Vector3f> n0(v0);
Vector3f n1(v0[0], v0[1], v0[2]); Eigen::Map<Eigen::Vector3f> n1(v1);
Vector3f normal = n0 + n1; Eigen::Vector3f normal = n0 + n1;
normal.normalize(); normal.normalize();
v0[0] = normal.x(); v0[0] = normal.x();
v0[1] = normal.y(); v0[1] = normal.y();
@ -265,7 +264,7 @@ void SphereMesh::fixNormals()
} }
void SphereMesh::scale(const Vector3f& s) void SphereMesh::scale(const Eigen::Vector3f& s)
{ {
int i; int i;
for (i = 0; i < nVertices; i++) for (i = 0; i < nVertices; i++)
@ -280,11 +279,11 @@ void SphereMesh::scale(const Vector3f& s)
{ {
// TODO: Make a fast special case for uniform scale factors, where // TODO: Make a fast special case for uniform scale factors, where
// renormalization is not required. // renormalization is not required.
Vector3f is = s.cwiseInverse(); Eigen::Vector3f is = s.cwiseInverse();
for (i = 0; i < nVertices; i++) for (i = 0; i < nVertices; i++)
{ {
int n = i * 3; int n = i * 3;
Vector3f normal(normals[n] * is.x(), normals[n + 1] * is.y(), normals[n + 2] * is.z()); Eigen::Vector3f normal = Eigen::Map<Eigen::Vector3f>(normals).cwiseProduct(is);
normal.normalize(); normal.normalize();
normals[n] = normal.x(); normals[n] = normal.x();
normals[n + 1] = normal.y(); normals[n + 1] = normal.y();
@ -297,24 +296,16 @@ void SphereMesh::scale(const Vector3f& s)
void SphereMesh::displace(const DisplacementMap& dispmap, void SphereMesh::displace(const DisplacementMap& dispmap,
float height) float height)
{ {
// assert(dispMap.getWidth() == nSlices);
// assert(dispMap.getHeight() == nRings);
for (int i = 0; i < nRings; i++) for (int i = 0; i < nRings; i++)
{ {
for (int j = 0; j <= nSlices; j++) for (int j = 0; j <= nSlices; j++)
{ {
int n = (i * (nSlices + 1) + j) * 3; int n = (i * (nSlices + 1) + j) * 3;
/*
float theta = (float) j / (float) nSlices * (float) PI * 2; Eigen::Map<Eigen::Vector3f> normal(normals);
float x = (float) (cos(phi) * cos(theta));
float y = (float) sin(phi);
float z = (float) (cos(phi) * sin(theta));
*/
Vector3f normal(normals[n], normals[n + 1], normals[n + 2]);
int k = (j == nSlices) ? 0 : j; int k = (j == nSlices) ? 0 : j;
Vector3f v = normal * dispmap.getDisplacement(k, i) * height; Eigen::Vector3f v = normal * dispmap.getDisplacement(k, i) * height;
vertices[n] += v.x(); vertices[n] += v.x();
vertices[n + 1] += v.y(); vertices[n + 1] += v.y();
vertices[n + 2] += v.z(); vertices[n + 2] += v.z();
@ -332,8 +323,8 @@ void SphereMesh::displace(DisplacementMapFunc func, void* info)
{ {
float u = (float) j / (float) nSlices; float u = (float) j / (float) nSlices;
int n = (i * (nSlices + 1) + j) * 3; int n = (i * (nSlices + 1) + j) * 3;
Vector3f normal(normals[n], normals[n + 1], normals[n + 2]); Eigen::Map<Eigen::Vector3f> normal(normals);
Vector3f vert = normal * func(u, v, info); Eigen::Vector3f vert = normal * func(u, v, info);
vertices[n] += vert.x(); vertices[n] += vert.x();
vertices[n + 1] += vert.y(); vertices[n + 1] += vert.y();
vertices[n + 2] += vert.z(); vertices[n + 2] += vert.z();
@ -344,15 +335,21 @@ void SphereMesh::displace(DisplacementMapFunc func, void* info)
cmod::Mesh* SphereMesh::convertToMesh() const cmod::Mesh* SphereMesh::convertToMesh() const
{ {
uint32_t stride = 32; std::uint32_t stride = 32;
cmod::Mesh::VertexAttribute attributes[3]; cmod::VertexAttribute attributes[3];
attributes[0] = cmod::Mesh::VertexAttribute(cmod::Mesh::Position, cmod::Mesh::Float3, 0); attributes[0] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Position,
attributes[1] = cmod::Mesh::VertexAttribute(cmod::Mesh::Normal, cmod::Mesh::Float3, 12); cmod::VertexAttributeFormat::Float3,
attributes[2] = cmod::Mesh::VertexAttribute(cmod::Mesh::Texture0, cmod::Mesh::Float2, 24); 0);
attributes[1] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Normal,
cmod::VertexAttributeFormat::Float3,
12);
attributes[2] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Texture0,
cmod::VertexAttributeFormat::Float2,
24);
cmod::Mesh* mesh = new cmod::Mesh(); cmod::Mesh* mesh = new cmod::Mesh();
mesh->setVertexDescription(cmod::Mesh::VertexDescription(stride, 3, attributes)); mesh->setVertexDescription(cmod::VertexDescription(stride, 3, attributes));
// Copy the vertex data from the separate position, normal, and texture coordinate // Copy the vertex data from the separate position, normal, and texture coordinate
// arrays into a single array. // arrays into a single array.
@ -375,14 +372,14 @@ cmod::Mesh* SphereMesh::convertToMesh() const
for (int i = 0; i < nRings - 1; i++) for (int i = 0; i < nRings - 1; i++)
{ {
auto* indexData = new uint32_t[(nSlices + 1) * 2]; auto* indexData = new std::uint32_t[(nSlices + 1) * 2];
for (int j = 0; j <= nSlices; j++) for (int j = 0; j <= nSlices; j++)
{ {
indexData[j * 2 + 0] = i * (nSlices + 1) + j; indexData[j * 2 + 0] = i * (nSlices + 1) + j;
indexData[j * 2 + 1] = (i + 1) * (nSlices + 1) + j; indexData[j * 2 + 1] = (i + 1) * (nSlices + 1) + j;
} }
mesh->addGroup(cmod::Mesh::TriStrip, ~0u, (nSlices + 1) * 2, indexData); mesh->addGroup(cmod::PrimitiveGroupType::TriStrip, ~0u, (nSlices + 1) * 2, indexData);
} }
return mesh; return mesh;

View File

@ -8,16 +8,20 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CELENGINE_SPHEREMESH_H_ #pragma once
#define _CELENGINE_SPHEREMESH_H_
// IMPORTANT: This file is a relic from the early days of Celestia. // IMPORTANT: This file is a relic from the early days of Celestia.
// Its sole function now is to handle the now-deprecated .cms mesh files; // Its sole function now is to handle the now-deprecated .cms mesh files;
// it will eventually be removed from Celestia. // it will eventually be removed from Celestia.
#include <celengine/dispmap.h> #include <Eigen/Core>
#include <celmodel/mesh.h>
#include <celengine/dispmap.h>
namespace cmod
{
class Mesh;
}
/*! The SphereMesh class is used to generate displacement mapped /*! The SphereMesh class is used to generate displacement mapped
* spheres when loading the now-deprecated .cms geometry files. * spheres when loading the now-deprecated .cms geometry files.
@ -59,5 +63,3 @@ public:
int nIndices; int nIndices;
unsigned short* indices{ nullptr }; unsigned short* indices{ nullptr };
}; };
#endif // _CELENGINE_SPHEREMESH_H_

View File

@ -14,6 +14,7 @@
namespace cmod namespace cmod
{ {
Material::Material() Material::Material()
{ {
maps.fill(InvalidResource); maps.fill(InvalidResource);
@ -21,7 +22,7 @@ Material::Material()
bool bool
operator<(const Material::Color& c0, const Material::Color& c1) operator<(const Color& c0, const Color& c1)
{ {
return std::tie(c0.m_red, c0.m_green, c0.m_blue) return std::tie(c0.m_red, c0.m_green, c0.m_blue)
< std::tie(c1.m_red, c1.m_green, c1.m_blue); < std::tie(c1.m_red, c1.m_green, c1.m_blue);

View File

@ -10,114 +10,131 @@
#pragma once #pragma once
#include <Eigen/Core>
#include <string>
#include <array> #include <array>
#include <cstdint>
#include <cstddef>
#include <Eigen/Core>
#include <celutil/color.h> #include <celutil/color.h>
#include <celutil/reshandle.h> #include <celutil/reshandle.h>
#include <celcompat/filesystem.h>
namespace cmod namespace cmod
{ {
class Color
{
public:
constexpr Color() :
m_red(0.0f),
m_green(0.0f),
m_blue(0.0f)
{
}
constexpr Color(float r, float g, float b) :
m_red(r),
m_green(g),
m_blue(b)
{
}
Color(const ::Color& color) :
m_red(color.red()),
m_green(color.green()),
m_blue(color.blue())
{
}
constexpr float red() const
{
return m_red;
}
constexpr float green() const
{
return m_green;
}
constexpr float blue() const
{
return m_blue;
}
Eigen::Vector3f toVector3() const
{
return Eigen::Vector3f(m_red, m_green, m_blue);
}
constexpr bool operator==(const Color& other) const
{
return m_red == other.m_red && m_green == other.m_green && m_blue == other.m_blue;
}
constexpr bool operator!=(const Color& other) const
{
return !(*this == other);
}
friend bool operator<(const Color& c0, const Color& c1);
private:
float m_red;
float m_green;
float m_blue;
};
enum class BlendMode : std::int16_t
{
NormalBlend = 0,
AdditiveBlend = 1,
PremultipliedAlphaBlend = 2,
BlendMax = 3,
InvalidBlend = -1,
};
enum class TextureSemantic : std::int16_t
{
DiffuseMap = 0,
NormalMap = 1,
SpecularMap = 2,
EmissiveMap = 3,
TextureSemanticMax = 4,
InvalidTextureSemantic = -1,
};
class Material class Material
{ {
public: public:
Material(); Material();
~Material() = default; ~Material() = default;
class Color inline ResourceHandle getMap(TextureSemantic semantic) const
{ {
public: return maps[static_cast<std::size_t>(semantic)];
constexpr Color() : }
m_red(0.0f),
m_green(0.0f),
m_blue(0.0f)
{
}
constexpr Color(float r, float g, float b) : inline void setMap(TextureSemantic semantic, ResourceHandle handle)
m_red(r),
m_green(g),
m_blue(b)
{
}
Color(const ::Color& color) :
m_red(color.red()),
m_green(color.green()),
m_blue(color.blue())
{
}
constexpr float red() const
{
return m_red;
}
constexpr float green() const
{
return m_green;
}
constexpr float blue() const
{
return m_blue;
}
Eigen::Vector3f toVector3() const
{
return Eigen::Vector3f(m_red, m_green, m_blue);
}
constexpr bool operator==(const Color& other) const
{
return m_red == other.m_red && m_green == other.m_green && m_blue == other.m_blue;
}
constexpr bool operator!=(const Color& other) const
{
return !(*this == other);
}
friend bool operator<(const Color& c0, const Color& c1);
private:
float m_red;
float m_green;
float m_blue;
};
enum BlendMode
{ {
NormalBlend = 0, maps[static_cast<std::size_t>(semantic)] = handle;
AdditiveBlend = 1, }
PremultipliedAlphaBlend = 2,
BlendMax = 3,
InvalidBlend = -1,
};
enum TextureSemantic
{
DiffuseMap = 0,
NormalMap = 1,
SpecularMap = 2,
EmissiveMap = 3,
TextureSemanticMax = 4,
InvalidTextureSemantic = -1,
};
Color diffuse{ 0.0f, 0.0f, 0.0f }; Color diffuse{ 0.0f, 0.0f, 0.0f };
Color emissive{ 0.0f, 0.0f, 0.0f }; Color emissive{ 0.0f, 0.0f, 0.0f };
Color specular{ 0.0f, 0.0f, 0.0f }; Color specular{ 0.0f, 0.0f, 0.0f };
float specularPower{ 1.0f }; float specularPower{ 1.0f };
float opacity{ 1.0f }; float opacity{ 1.0f };
BlendMode blend{ NormalBlend }; BlendMode blend{ BlendMode::NormalBlend };
std::array<ResourceHandle, TextureSemanticMax> maps; std::array<ResourceHandle, static_cast<std::size_t>(TextureSemantic::TextureSemanticMax)> maps;
}; };
bool operator<(const Material::Color& c0, const Material::Color& c1);
bool operator<(const Color& c0, const Color& c1);
// Define an ordering for materials; required for elimination of duplicate // Define an ordering for materials; required for elimination of duplicate
// materials. // materials.

View File

@ -8,34 +8,22 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#include <array>
#include <cstring>
#include "mesh.h" #include "mesh.h"
#include <cassert>
#include <iostream>
#include <algorithm>
#include <Eigen/Core>
#include <Eigen/Geometry>
using namespace cmod;
using namespace Eigen;
using namespace std;
static size_t VertexAttributeFormatSizes[Mesh::FormatMax] = namespace cmod
{ {
4, // Float1
8, // Float2
12, // Float3
16, // Float4,
4, // UByte4
};
VertexDescription::VertexDescription(unsigned int _stride,
Mesh::VertexDescription::VertexDescription(unsigned int _stride, unsigned int _nAttributes,
unsigned int _nAttributes, VertexAttribute* _attributes) :
VertexAttribute* _attributes) : stride(_stride),
stride(_stride), nAttributes(_nAttributes),
nAttributes(_nAttributes), attributes(nullptr)
attributes(nullptr)
{ {
if (nAttributes != 0) if (nAttributes != 0)
{ {
@ -47,7 +35,7 @@ Mesh::VertexDescription::VertexDescription(unsigned int _stride,
} }
Mesh::VertexDescription::VertexDescription(const VertexDescription& desc) : VertexDescription::VertexDescription(const VertexDescription& desc) :
stride(desc.stride), stride(desc.stride),
nAttributes(desc.nAttributes), nAttributes(desc.nAttributes),
attributes(nullptr) attributes(nullptr)
@ -62,8 +50,8 @@ Mesh::VertexDescription::VertexDescription(const VertexDescription& desc) :
} }
Mesh::VertexDescription& VertexDescription&
Mesh::VertexDescription::operator=(const Mesh::VertexDescription& desc) VertexDescription::operator=(const VertexDescription& desc)
{ {
if (this == &desc) if (this == &desc)
return *this; return *this;
@ -88,18 +76,14 @@ Mesh::VertexDescription::operator=(const Mesh::VertexDescription& desc)
// TODO: This should be called in the constructor; we should start using // TODO: This should be called in the constructor; we should start using
// exceptions in Celestia. // exceptions in Celestia.
bool bool
Mesh::VertexDescription::validate() const VertexDescription::validate() const
{ {
for (unsigned int i = 0; i < nAttributes; i++) for (unsigned int i = 0; i < nAttributes; i++)
{ {
VertexAttribute& attr = attributes[i]; VertexAttribute& attr = attributes[i];
// Validate the attribute // Validate the attribute
if (attr.semantic >= SemanticMax || attr.format >= FormatMax) if (attr.offset % 4 != 0 || attr.offset + VertexAttribute::getFormatSize(attr.format) > stride)
return false;
if (attr.offset % 4 != 0)
return false;
if (attr.offset + VertexAttributeFormatSizes[attr.format] > stride)
return false; return false;
// TODO: check for repetition of attributes // TODO: check for repetition of attributes
// if (vertexAttributeMap[attr->semantic].format != InvalidFormat) // if (vertexAttributeMap[attr->semantic].format != InvalidFormat)
@ -109,69 +93,62 @@ Mesh::VertexDescription::validate() const
return true; return true;
} }
Mesh::VertexDescription Mesh::VertexDescription::appendingAttributes(const VertexAttribute* newAttributes, int count) const VertexDescription VertexDescription::appendingAttributes(const VertexAttribute* newAttributes, int count) const
{ {
unsigned int totalAttributeCount = nAttributes + count; unsigned int totalAttributeCount = nAttributes + count;
VertexAttribute* allAttributes = new VertexAttribute[totalAttributeCount]; VertexAttribute* allAttributes = new VertexAttribute[totalAttributeCount];
memcpy(allAttributes, attributes, sizeof(VertexAttribute) * nAttributes); std::memcpy(allAttributes, attributes, sizeof(VertexAttribute) * nAttributes);
unsigned int newStride = stride; unsigned int newStride = stride;
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
VertexAttribute attribute = newAttributes[i]; VertexAttribute attribute = newAttributes[i];
allAttributes[nAttributes + i] = attribute; allAttributes[nAttributes + i] = attribute;
newStride += Mesh::getVertexAttributeSize(attribute.format); newStride += VertexAttribute::getFormatSize(attribute.format);
} }
memcpy(allAttributes + nAttributes, newAttributes, sizeof(VertexAttribute) * count); std::memcpy(allAttributes + nAttributes, newAttributes, sizeof(VertexAttribute) * count);
VertexDescription desc = VertexDescription(newStride, totalAttributeCount, allAttributes); VertexDescription desc = VertexDescription(newStride, totalAttributeCount, allAttributes);
delete[] allAttributes; delete[] allAttributes;
return desc; return desc;
} }
Mesh::VertexDescription::~VertexDescription() VertexDescription::~VertexDescription()
{ {
delete[] attributes; delete[] attributes;
} }
void void
Mesh::VertexDescription::buildSemanticMap() VertexDescription::buildSemanticMap()
{ {
for (unsigned int i = 0; i < nAttributes; i++) for (unsigned int i = 0; i < nAttributes; i++)
semanticMap[attributes[i].semantic] = attributes[i]; semanticMap[static_cast<std::size_t>(attributes[i].semantic)] = attributes[i];
} }
void void
Mesh::VertexDescription::clearSemanticMap() VertexDescription::clearSemanticMap()
{ {
for (auto& i : semanticMap) for (auto& i : semanticMap)
i = VertexAttribute(); i = VertexAttribute();
} }
//Mesh::PrimitiveGroup::~PrimitiveGroup()
//{
// TODO: probably should free index list; need to sort out
// ownership issues.
//}
unsigned int unsigned int
Mesh::PrimitiveGroup::getPrimitiveCount() const PrimitiveGroup::getPrimitiveCount() const
{ {
switch (prim) switch (prim)
{ {
case TriList: case PrimitiveGroupType::TriList:
return nIndices / 3; return nIndices / 3;
case TriStrip: case PrimitiveGroupType::TriStrip:
case TriFan: case PrimitiveGroupType::TriFan:
return nIndices - 2; return nIndices - 2;
case LineList: case PrimitiveGroupType::LineList:
return nIndices / 2; return nIndices / 2;
case LineStrip: case PrimitiveGroupType::LineStrip:
return nIndices - 2; return nIndices - 2;
case PointList: case PrimitiveGroupType::PointList:
case SpriteList: case PrimitiveGroupType::SpriteList:
return nIndices; return nIndices;
default: default:
// Invalid value // Invalid value
@ -188,7 +165,6 @@ Mesh::~Mesh()
// TODO: this is just to cast away void* and shut up GCC warnings; // TODO: this is just to cast away void* and shut up GCC warnings;
// should probably be static_cast<VertexList::VertexPart*> // should probably be static_cast<VertexList::VertexPart*>
delete[] static_cast<char*>(vertices); delete[] static_cast<char*>(vertices);
delete vbResource;
} }
@ -219,13 +195,13 @@ Mesh::setVertexDescription(const VertexDescription& desc)
} }
const Mesh::VertexDescription& Mesh::getVertexDescription() const const VertexDescription& Mesh::getVertexDescription() const
{ {
return vertexDesc; return vertexDesc;
} }
const Mesh::PrimitiveGroup* const PrimitiveGroup*
Mesh::getGroup(unsigned int index) const Mesh::getGroup(unsigned int index) const
{ {
if (index >= groups.size()) if (index >= groups.size())
@ -235,7 +211,7 @@ Mesh::getGroup(unsigned int index) const
} }
Mesh::PrimitiveGroup* PrimitiveGroup*
Mesh::getGroup(unsigned int index) Mesh::getGroup(unsigned int index)
{ {
if (index >= groups.size()) if (index >= groups.size())
@ -253,13 +229,14 @@ Mesh::addGroup(PrimitiveGroup* group)
} }
Mesh::PrimitiveGroup* Mesh::createLinePrimitiveGroup(bool lineStrip, unsigned int nIndices, index32* indices) PrimitiveGroup*
Mesh::createLinePrimitiveGroup(bool lineStrip, unsigned int nIndices, index32* indices)
{ {
// Transform LINE_STRIP/LINES to triangle vertices // Transform LINE_STRIP/LINES to triangle vertices
int transformedVertCount = lineStrip ? (nIndices - 1) * 4 : nIndices * 2; int transformedVertCount = lineStrip ? (nIndices - 1) * 4 : nIndices * 2;
// Get information of the position attributes // Get information of the position attributes
auto positionAttributes = vertexDesc.getAttribute(Position); auto positionAttributes = vertexDesc.getAttribute(VertexAttributeSemantic::Position);
int positionSize = getVertexAttributeSize(positionAttributes.format); int positionSize = VertexAttribute::getFormatSize(positionAttributes.format);
int positionOffset = positionAttributes.offset; int positionOffset = positionAttributes.offset;
int originalStride = vertexDesc.stride; int originalStride = vertexDesc.stride;
@ -289,23 +266,23 @@ Mesh::PrimitiveGroup* Mesh::createLinePrimitiveGroup(bool lineStrip, unsigned in
float *ffn = (float *)origNextVertLoc; float *ffn = (float *)origNextVertLoc;
// Fill the info for the 4 vertices // Fill the info for the 4 vertices
memcpy(ptr, origThisVertLoc, originalStride); std::memcpy(ptr, origThisVertLoc, originalStride);
memcpy(ptr + originalStride, origNextVertLoc + positionOffset, positionSize); std::memcpy(ptr + originalStride, origNextVertLoc + positionOffset, positionSize);
*(float *)&ptr[originalStride + positionSize] = -0.5f; *(float *)&ptr[originalStride + positionSize] = -0.5f;
ptr += stride; ptr += stride;
memcpy(ptr, origThisVertLoc, originalStride); std::memcpy(ptr, origThisVertLoc, originalStride);
memcpy(ptr + originalStride, origNextVertLoc + positionOffset, positionSize); std::memcpy(ptr + originalStride, origNextVertLoc + positionOffset, positionSize);
*(float *)&ptr[originalStride + positionSize] = 0.5f; *(float *)&ptr[originalStride + positionSize] = 0.5f;
ptr += stride; ptr += stride;
memcpy(ptr, origNextVertLoc, originalStride); std::memcpy(ptr, origNextVertLoc, originalStride);
memcpy(ptr + originalStride, origThisVertLoc + positionOffset, positionSize); std::memcpy(ptr + originalStride, origThisVertLoc + positionOffset, positionSize);
*(float *)&ptr[originalStride + positionSize] = -0.5f; *(float *)&ptr[originalStride + positionSize] = -0.5f;
ptr += stride; ptr += stride;
memcpy(ptr, origNextVertLoc, originalStride); std::memcpy(ptr, origNextVertLoc, originalStride);
memcpy(ptr + originalStride, origThisVertLoc + positionOffset, positionSize); std::memcpy(ptr + originalStride, origThisVertLoc + positionOffset, positionSize);
*(float *)&ptr[originalStride + positionSize] = 0.5f; *(float *)&ptr[originalStride + positionSize] = 0.5f;
ptr += stride; ptr += stride;
@ -321,9 +298,9 @@ Mesh::PrimitiveGroup* Mesh::createLinePrimitiveGroup(bool lineStrip, unsigned in
newIndices[lineIndex + 5] = newIndex; newIndices[lineIndex + 5] = newIndex;
} }
array<VertexAttribute, 2> newAttributes = { std::array<VertexAttribute, 2> newAttributes = {
VertexAttribute(NextPosition, positionAttributes.format, originalStride), VertexAttribute(VertexAttributeSemantic::NextPosition, positionAttributes.format, originalStride),
VertexAttribute(ScaleFactor, Float1, originalStride + positionSize), VertexAttribute(VertexAttributeSemantic::ScaleFactor, VertexAttributeFormat::Float1, originalStride + positionSize),
}; };
auto* g = new PrimitiveGroup(); auto* g = new PrimitiveGroup();
g->vertexOverride = data; g->vertexOverride = data;
@ -343,9 +320,9 @@ Mesh::addGroup(PrimitiveGroupType prim,
index32* indices) index32* indices)
{ {
PrimitiveGroup* g; PrimitiveGroup* g;
if (prim == LineStrip || prim == LineList) if (prim == PrimitiveGroupType::LineStrip || prim == PrimitiveGroupType::LineList)
{ {
g = createLinePrimitiveGroup(prim == LineStrip, nIndices, indices); g = createLinePrimitiveGroup(prim == PrimitiveGroupType::LineStrip, nIndices, indices);
} }
else else
{ {
@ -382,7 +359,7 @@ Mesh::clearGroups()
} }
const string& const std::string&
Mesh::getName() const Mesh::getName() const
{ {
return name; return name;
@ -390,14 +367,14 @@ Mesh::getName() const
void void
Mesh::setName(const string& _name) Mesh::setName(const std::string& _name)
{ {
name = _name; name = _name;
} }
void void
Mesh::remapIndices(const vector<index32>& indexMap) Mesh::remapIndices(const std::vector<index32>& indexMap)
{ {
for (auto group : groups) for (auto group : groups)
{ {
@ -410,57 +387,54 @@ Mesh::remapIndices(const vector<index32>& indexMap)
void void
Mesh::remapMaterials(const vector<unsigned int>& materialMap) Mesh::remapMaterials(const std::vector<unsigned int>& materialMap)
{ {
for (auto group : groups) for (auto group : groups)
group->materialIndex = materialMap[group->materialIndex]; group->materialIndex = materialMap[group->materialIndex];
} }
struct PrimitiveGroupComparator
{
bool operator()(const Mesh::PrimitiveGroup* g0, const Mesh::PrimitiveGroup* g1) const
{
return g0->materialIndex < g1->materialIndex;
}
};
void void
Mesh::aggregateByMaterial() Mesh::aggregateByMaterial()
{ {
sort(groups.begin(), groups.end(), PrimitiveGroupComparator()); std::sort(groups.begin(), groups.end(),
[](const PrimitiveGroup* g0, const PrimitiveGroup* g1)
{
return g0->materialIndex < g1->materialIndex;
});
} }
bool bool
Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, PickResult* result) const Mesh::pick(const Eigen::Vector3d& rayOrigin, const Eigen::Vector3d& rayDirection, PickResult* result) const
{ {
double maxDistance = 1.0e30; double maxDistance = 1.0e30;
double closest = maxDistance; double closest = maxDistance;
// Pick will automatically fail without vertex positions--no reasonable // Pick will automatically fail without vertex positions--no reasonable
// mesh should lack these. // mesh should lack these.
if (vertexDesc.getAttribute(Position).semantic != Position || if (vertexDesc.getAttribute(VertexAttributeSemantic::Position).semantic != VertexAttributeSemantic::Position ||
vertexDesc.getAttribute(Position).format != Float3) vertexDesc.getAttribute(VertexAttributeSemantic::Position).format != VertexAttributeFormat::Float3)
{ {
return false; return false;
} }
unsigned int posOffset = vertexDesc.getAttribute(Position).offset; unsigned int posOffset = vertexDesc.getAttribute(VertexAttributeSemantic::Position).offset;
auto* vdata = reinterpret_cast<char*>(vertices); auto* vdata = reinterpret_cast<char*>(vertices);
// Iterate over all primitive groups in the mesh // Iterate over all primitive groups in the mesh
for (const auto group : groups) for (const auto group : groups)
{ {
Mesh::PrimitiveGroupType primType = group->prim; PrimitiveGroupType primType = group->prim;
index32 nIndices = group->nIndices; index32 nIndices = group->nIndices;
// Only attempt to compute the intersection of the ray with triangle // Only attempt to compute the intersection of the ray with triangle
// groups. // groups.
if ((primType == TriList || primType == TriStrip || primType == TriFan) && if ((primType == PrimitiveGroupType::TriList
|| primType == PrimitiveGroupType::TriStrip
|| primType == PrimitiveGroupType::TriFan) &&
(nIndices >= 3) && (nIndices >= 3) &&
!(primType == TriList && nIndices % 3 != 0)) !(primType == PrimitiveGroupType::TriList && nIndices % 3 != 0))
{ {
unsigned int primitiveIndex = 0; unsigned int primitiveIndex = 0;
index32 index = 0; index32 index = 0;
@ -472,14 +446,14 @@ Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, PickResult*
do do
{ {
// Get the triangle vertices v0, v1, and v2 // Get the triangle vertices v0, v1, and v2
Vector3d v0 = Map<Vector3f>(reinterpret_cast<float*>(vdata + i0 * vertexDesc.stride + posOffset)).cast<double>(); Eigen::Vector3d v0 = Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata + i0 * vertexDesc.stride + posOffset)).cast<double>();
Vector3d v1 = Map<Vector3f>(reinterpret_cast<float*>(vdata + i1 * vertexDesc.stride + posOffset)).cast<double>(); Eigen::Vector3d v1 = Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata + i1 * vertexDesc.stride + posOffset)).cast<double>();
Vector3d v2 = Map<Vector3f>(reinterpret_cast<float*>(vdata + i2 * vertexDesc.stride + posOffset)).cast<double>(); Eigen::Vector3d v2 = Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata + i2 * vertexDesc.stride + posOffset)).cast<double>();
// Compute the edge vectors e0 and e1, and the normal n // Compute the edge vectors e0 and e1, and the normal n
Vector3d e0 = v1 - v0; Eigen::Vector3d e0 = v1 - v0;
Vector3d e1 = v2 - v0; Eigen::Vector3d e1 = v2 - v0;
Vector3d n = e0.cross(e1); Eigen::Vector3d n = e0.cross(e1);
// c is the cosine of the angle between the ray and triangle normal // c is the cosine of the angle between the ray and triangle normal
double c = n.dot(rayDirection); double c = n.dot(rayDirection);
@ -499,8 +473,8 @@ Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, PickResult*
double det = m00 * m11 - m01 * m10; double det = m00 * m11 - m01 * m10;
if (det != 0.0) if (det != 0.0)
{ {
Vector3d p = rayOrigin + rayDirection * t; Eigen::Vector3d p = rayOrigin + rayDirection * t;
Vector3d q = p - v0; Eigen::Vector3d q = p - v0;
double q0 = e0.dot(q); double q0 = e0.dot(q);
double q1 = e1.dot(q); double q1 = e1.dot(q);
double d = 1.0 / det; double d = 1.0 / det;
@ -521,7 +495,7 @@ Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, PickResult*
} }
// Get the indices for the next triangle // Get the indices for the next triangle
if (primType == TriList) if (primType == PrimitiveGroupType::TriList)
{ {
index += 3; index += 3;
if (index < nIndices) if (index < nIndices)
@ -531,7 +505,7 @@ Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, PickResult*
i2 = group->indices[index + 2]; i2 = group->indices[index + 2];
} }
} }
else if (primType == TriStrip) else if (primType == PrimitiveGroupType::TriStrip)
{ {
index += 1; index += 1;
if (index < nIndices) if (index < nIndices)
@ -564,7 +538,7 @@ Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, PickResult*
bool bool
Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, double& distance) const Mesh::pick(const Eigen::Vector3d& rayOrigin, const Eigen::Vector3d& rayDirection, double& distance) const
{ {
PickResult result; PickResult result;
bool hit = pick(rayOrigin, rayDirection, &result); bool hit = pick(rayOrigin, rayDirection, &result);
@ -577,38 +551,38 @@ Mesh::pick(const Vector3d& rayOrigin, const Vector3d& rayDirection, double& dist
} }
AlignedBox<float, 3> Eigen::AlignedBox<float, 3>
Mesh::getBoundingBox() const Mesh::getBoundingBox() const
{ {
AlignedBox<float, 3> bbox; Eigen::AlignedBox<float, 3> bbox;
// Return an empty box if there's no position info // Return an empty box if there's no position info
if (vertexDesc.getAttribute(Position).format != Float3) if (vertexDesc.getAttribute(VertexAttributeSemantic::Position).format != VertexAttributeFormat::Float3)
return bbox; return bbox;
char* vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(Position).offset; char* vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(VertexAttributeSemantic::Position).offset;
if (vertexDesc.getAttribute(PointSize).format == Float1) if (vertexDesc.getAttribute(VertexAttributeSemantic::PointSize).format == VertexAttributeFormat::Float1)
{ {
// Handle bounding box calculation for point sprites. Unlike other // Handle bounding box calculation for point sprites. Unlike other
// primitives, point sprite vertices have a non-zero size. // primitives, point sprite vertices have a non-zero size.
int pointSizeOffset = (int) vertexDesc.getAttribute(PointSize).offset - int pointSizeOffset = (int) vertexDesc.getAttribute(VertexAttributeSemantic::PointSize).offset -
(int) vertexDesc.getAttribute(Position).offset; (int) vertexDesc.getAttribute(VertexAttributeSemantic::Position).offset;
for (unsigned int i = 0; i < nVertices; i++, vdata += vertexDesc.stride) for (unsigned int i = 0; i < nVertices; i++, vdata += vertexDesc.stride)
{ {
Vector3f center = Map<Vector3f>(reinterpret_cast<float*>(vdata)); Eigen::Vector3f center = Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata));
float pointSize = (reinterpret_cast<float*>(vdata + pointSizeOffset))[0]; float pointSize = (reinterpret_cast<float*>(vdata + pointSizeOffset))[0];
Vector3f offsetVec = Vector3f::Constant(pointSize); Eigen::Vector3f offsetVec = Eigen::Vector3f::Constant(pointSize);
AlignedBox<float, 3> pointbox(center - offsetVec, center + offsetVec); Eigen::AlignedBox<float, 3> pointbox(center - offsetVec, center + offsetVec);
bbox.extend(pointbox); bbox.extend(pointbox);
} }
} }
else else
{ {
for (unsigned int i = 0; i < nVertices; i++, vdata += vertexDesc.stride) for (unsigned int i = 0; i < nVertices; i++, vdata += vertexDesc.stride)
bbox.extend(Map<Vector3f>(reinterpret_cast<float*>(vdata))); bbox.extend(Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata)));
} }
return bbox; return bbox;
@ -616,19 +590,19 @@ Mesh::getBoundingBox() const
void void
Mesh::transform(const Vector3f& translation, float scale) Mesh::transform(const Eigen::Vector3f& translation, float scale)
{ {
if (vertexDesc.getAttribute(Position).format != Float3) if (vertexDesc.getAttribute(VertexAttributeSemantic::Position).format != VertexAttributeFormat::Float3)
return; return;
char* vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(Position).offset; char* vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(VertexAttributeSemantic::Position).offset;
unsigned int i; unsigned int i;
// Scale and translate the vertex positions // Scale and translate the vertex positions
for (i = 0; i < nVertices; i++, vdata += vertexDesc.stride) for (i = 0; i < nVertices; i++, vdata += vertexDesc.stride)
{ {
const Vector3f tv = (Map<Vector3f>(reinterpret_cast<float*>(vdata)) + translation) * scale; const Eigen::Vector3f tv = (Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata)) + translation) * scale;
Map<Vector3f>(reinterpret_cast<float*>(vdata)) = tv; Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata)) = tv;
} }
// Scale and translate the overriden vertex values // Scale and translate the overriden vertex values
@ -640,22 +614,22 @@ Mesh::transform(const Vector3f& translation, float scale)
continue; continue;
auto vertexDesc = group->vertexDescriptionOverride; auto vertexDesc = group->vertexDescriptionOverride;
int positionOffset = vertexDesc.getAttribute(Position).offset; int positionOffset = vertexDesc.getAttribute(VertexAttributeSemantic::Position).offset;
int nextPositionOffset = vertexDesc.getAttribute(NextPosition).offset; int nextPositionOffset = vertexDesc.getAttribute(VertexAttributeSemantic::NextPosition).offset;
for (unsigned int j = 0; j < group->vertexCountOverride; j++, vdata += vertexDesc.stride) for (unsigned int j = 0; j < group->vertexCountOverride; j++, vdata += vertexDesc.stride)
{ {
Vector3f tv = (Map<Vector3f>(reinterpret_cast<float*>(vdata + positionOffset)) + translation) * scale; Eigen::Vector3f tv = (Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata + positionOffset)) + translation) * scale;
Map<Vector3f>(reinterpret_cast<float*>(vdata + positionOffset)) = tv; Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata + positionOffset)) = tv;
tv = (Map<Vector3f>(reinterpret_cast<float*>(vdata + nextPositionOffset)) + translation) * scale; tv = (Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata + nextPositionOffset)) + translation) * scale;
Map<Vector3f>(reinterpret_cast<float*>(vdata + nextPositionOffset)) = tv; Eigen::Map<Eigen::Vector3f>(reinterpret_cast<float*>(vdata + nextPositionOffset)) = tv;
} }
} }
// Point sizes need to be scaled as well // Point sizes need to be scaled as well
if (vertexDesc.getAttribute(PointSize).format == Float1) if (vertexDesc.getAttribute(VertexAttributeSemantic::PointSize).format == VertexAttributeFormat::Float1)
{ {
vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(PointSize).offset; vdata = reinterpret_cast<char*>(vertices) + vertexDesc.getAttribute(VertexAttributeSemantic::PointSize).offset;
for (i = 0; i < nVertices; i++, vdata += vertexDesc.stride) for (i = 0; i < nVertices; i++, vdata += vertexDesc.stride)
reinterpret_cast<float*>(vdata)[0] *= scale; reinterpret_cast<float*>(vdata)[0] *= scale;
} }
@ -673,104 +647,4 @@ Mesh::getPrimitiveCount() const
return count; return count;
} }
} // end namespace cmod
Mesh::PrimitiveGroupType
Mesh::parsePrimitiveGroupType(const string& name)
{
if (name == "trilist")
return TriList;
if (name == "tristrip")
return TriStrip;
if (name == "trifan")
return TriFan;
if (name == "linelist")
return LineList;
if (name == "linestrip")
return LineStrip;
if (name == "points")
return PointList;
if (name == "sprites")
return SpriteList;
else
return InvalidPrimitiveGroupType;
}
Mesh::VertexAttributeSemantic
Mesh::parseVertexAttributeSemantic(const string& name)
{
if (name == "position")
return Position;
if (name == "normal")
return Normal;
if (name == "color0")
return Color0;
if (name == "color1")
return Color1;
if (name == "tangent")
return Tangent;
if (name == "texcoord0")
return Texture0;
if (name == "texcoord1")
return Texture1;
if (name == "texcoord2")
return Texture2;
if (name == "texcoord3")
return Texture3;
if (name == "pointsize")
return PointSize;
return InvalidSemantic;
}
Mesh::VertexAttributeFormat
Mesh::parseVertexAttributeFormat(const string& name)
{
if (name == "f1")
return Float1;
if (name == "f2")
return Float2;
if (name == "f3")
return Float3;
if (name == "f4")
return Float4;
if (name == "ub4")
return UByte4;
return InvalidFormat;
}
Material::TextureSemantic
Mesh::parseTextureSemantic(const string& name)
{
if (name == "texture0")
return Material::DiffuseMap;
if (name == "normalmap")
return Material::NormalMap;
if (name == "specularmap")
return Material::SpecularMap;
if (name == "emissivemap")
return Material::EmissiveMap;
return Material::InvalidTextureSemantic;
}
unsigned int
Mesh::getVertexAttributeSize(VertexAttributeFormat fmt)
{
switch (fmt)
{
case Float1:
case UByte4:
return 4;
case Float2:
return 8;
case Float3:
return 12;
case Float4:
return 16;
default:
return 0;
}
}

View File

@ -8,144 +8,169 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CELMODEL_MESH_H_ #pragma once
#define _CELMODEL_MESH_H_
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include "material.h"
#include <Eigen/Core> #include <Eigen/Core>
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include <vector>
#include <string> #include "material.h"
namespace cmod namespace cmod
{ {
// 32-bit index type
using index32 = std::uint32_t;
enum class VertexAttributeSemantic : std::int16_t
{
Position = 0,
Color0 = 1,
Color1 = 2,
Normal = 3,
Tangent = 4,
Texture0 = 5,
Texture1 = 6,
Texture2 = 7,
Texture3 = 8,
PointSize = 9,
NextPosition = 10,
ScaleFactor = 11,
SemanticMax = 12,
InvalidSemantic = -1,
};
enum class VertexAttributeFormat : std::int16_t
{
Float1 = 0,
Float2 = 1,
Float3 = 2,
Float4 = 3,
UByte4 = 4,
FormatMax = 5,
InvalidFormat = -1,
};
enum class PrimitiveGroupType : std::int16_t
{
TriList = 0,
TriStrip = 1,
TriFan = 2,
LineList = 3,
LineStrip = 4,
PointList = 5,
SpriteList = 6,
PrimitiveTypeMax = 7,
InvalidPrimitiveGroupType = -1
};
struct VertexAttribute
{
VertexAttribute() :
semantic(VertexAttributeSemantic::InvalidSemantic),
format(VertexAttributeFormat::InvalidFormat),
offset(0)
{
}
VertexAttribute(VertexAttributeSemantic _semantic,
VertexAttributeFormat _format,
unsigned int _offset) :
semantic(_semantic),
format(_format),
offset(_offset)
{
}
static constexpr unsigned int getFormatSize(VertexAttributeFormat fmt)
{
switch (fmt)
{
case VertexAttributeFormat::Float1:
case VertexAttributeFormat::UByte4:
return 4;
case VertexAttributeFormat::Float2:
return 8;
case VertexAttributeFormat::Float3:
return 12;
case VertexAttributeFormat::Float4:
return 16;
default:
return 0;
}
}
VertexAttributeSemantic semantic;
VertexAttributeFormat format;
unsigned int offset;
};
struct VertexDescription
{
VertexDescription(unsigned int _stride,
unsigned int _nAttributes,
VertexAttribute* _attributes);
VertexDescription(const VertexDescription& desc);
~VertexDescription();
inline const VertexAttribute& getAttribute(VertexAttributeSemantic semantic) const
{
return semanticMap[static_cast<std::size_t>(semantic)];
}
VertexDescription appendingAttributes(const VertexAttribute* newAttributes, int count) const;
bool validate() const;
VertexDescription& operator=(const VertexDescription&);
unsigned int stride;
unsigned int nAttributes;
VertexAttribute* attributes;
private:
void clearSemanticMap();
void buildSemanticMap();
// Vertex attributes indexed by semantic
std::array<VertexAttribute, static_cast<std::size_t>(VertexAttributeSemantic::SemanticMax)> semanticMap;
};
class PrimitiveGroup
{
public:
PrimitiveGroup() = default;
~PrimitiveGroup() = default;
unsigned int getPrimitiveCount() const;
PrimitiveGroupType prim;
unsigned int materialIndex;
index32* indices;
unsigned int nIndices;
PrimitiveGroupType primOverride;
void* vertexOverride;
unsigned int vertexCountOverride;
index32* indicesOverride;
unsigned int nIndicesOverride;
VertexDescription vertexDescriptionOverride { 0, 0, nullptr };
};
class Mesh class Mesh
{ {
public: public:
// 32-bit index type
typedef unsigned int index32;
class BufferResource
{
};
enum VertexAttributeSemantic
{
Position = 0,
Color0 = 1,
Color1 = 2,
Normal = 3,
Tangent = 4,
Texture0 = 5,
Texture1 = 6,
Texture2 = 7,
Texture3 = 8,
PointSize = 9,
NextPosition = 10,
ScaleFactor = 11,
SemanticMax = 12,
InvalidSemantic = -1,
};
enum VertexAttributeFormat
{
Float1 = 0,
Float2 = 1,
Float3 = 2,
Float4 = 3,
UByte4 = 4,
FormatMax = 5,
InvalidFormat = -1,
};
struct VertexAttribute
{
VertexAttribute() :
semantic(InvalidSemantic),
format(InvalidFormat),
offset(0)
{
}
VertexAttribute(VertexAttributeSemantic _semantic,
VertexAttributeFormat _format,
unsigned int _offset) :
semantic(_semantic),
format(_format),
offset(_offset)
{
}
VertexAttributeSemantic semantic;
VertexAttributeFormat format;
unsigned int offset;
};
struct VertexDescription
{
VertexDescription(unsigned int _stride,
unsigned int _nAttributes,
VertexAttribute* _attributes);
VertexDescription(const VertexDescription& desc);
~VertexDescription();
const VertexAttribute& getAttribute(VertexAttributeSemantic semantic) const
{
return semanticMap[semantic];
}
VertexDescription appendingAttributes(const VertexAttribute* newAttributes, int count) const;
bool validate() const;
VertexDescription& operator=(const VertexDescription&);
unsigned int stride;
unsigned int nAttributes;
VertexAttribute* attributes;
private:
void clearSemanticMap();
void buildSemanticMap();
// Vertex attributes indexed by semantic
VertexAttribute semanticMap[SemanticMax];
};
enum PrimitiveGroupType
{
TriList = 0,
TriStrip = 1,
TriFan = 2,
LineList = 3,
LineStrip = 4,
PointList = 5,
SpriteList = 6,
PrimitiveTypeMax = 7,
InvalidPrimitiveGroupType = -1
};
class PrimitiveGroup
{
public:
PrimitiveGroup() = default;
~PrimitiveGroup() = default;
unsigned int getPrimitiveCount() const;
PrimitiveGroupType prim;
unsigned int materialIndex;
index32* indices;
unsigned int nIndices;
PrimitiveGroupType primOverride;
void* vertexOverride;
unsigned int vertexCountOverride;
index32* indicesOverride;
unsigned int nIndicesOverride;
VertexDescription vertexDescriptionOverride { 0, 0, nullptr };
};
class PickResult class PickResult
{ {
public: public:
@ -164,7 +189,7 @@ class Mesh
bool setVertexDescription(const VertexDescription& desc); bool setVertexDescription(const VertexDescription& desc);
const VertexDescription& getVertexDescription() const; const VertexDescription& getVertexDescription() const;
PrimitiveGroup* createLinePrimitiveGroup(bool lineStrip, unsigned int nIndices, Mesh::index32* indices); PrimitiveGroup* createLinePrimitiveGroup(bool lineStrip, unsigned int nIndices, index32* indices);
const PrimitiveGroup* getGroup(unsigned int index) const; const PrimitiveGroup* getGroup(unsigned int index) const;
PrimitiveGroup* getGroup(unsigned int index); PrimitiveGroup* getGroup(unsigned int index);
unsigned int addGroup(PrimitiveGroup* group); unsigned int addGroup(PrimitiveGroup* group);
@ -198,21 +223,13 @@ class Mesh
unsigned int getVertexStride() const { return vertexDesc.stride; } unsigned int getVertexStride() const { return vertexDesc.stride; }
unsigned int getPrimitiveCount() const; unsigned int getPrimitiveCount() const;
static PrimitiveGroupType parsePrimitiveGroupType(const std::string&);
static VertexAttributeSemantic parseVertexAttributeSemantic(const std::string&);
static VertexAttributeFormat parseVertexAttributeFormat(const std::string&);
static Material::TextureSemantic parseTextureSemantic(const std::string&);
static unsigned int getVertexAttributeSize(VertexAttributeFormat);
private: private:
void recomputeBoundingBox(); void recomputeBoundingBox();
private:
VertexDescription vertexDesc{ 0, 0, nullptr }; VertexDescription vertexDesc{ 0, 0, nullptr };
unsigned int nVertices{ 0 }; unsigned int nVertices{ 0 };
void* vertices{ nullptr }; void* vertices{ nullptr };
mutable BufferResource* vbResource{ nullptr };
std::vector<PrimitiveGroup*> groups; std::vector<PrimitiveGroup*> groups;
@ -220,6 +237,3 @@ class Mesh
}; };
} // namespace cmod } // namespace cmod
#endif // !_CELMESH_MESH_H_

View File

@ -95,7 +95,7 @@ Model::addMaterial(const Material* m)
// the model, we could potentially end up with false positives--this // the model, we could potentially end up with false positives--this
// won't cause any rendering troubles, but could hurt performance // won't cause any rendering troubles, but could hurt performance
// if it forces multipass rendering when it's not required. // if it forces multipass rendering when it's not required.
for (int i = 0; i < Material::TextureSemanticMax; i++) for (int i = 0; i < static_cast<int>(TextureSemantic::TextureSemanticMax); i++)
{ {
if (m->maps[i] != InvalidResource) if (m->maps[i] != InvalidResource)
{ {
@ -306,7 +306,7 @@ Model::determineOpacity()
for (unsigned int i = 0; i < materials.size(); i++) for (unsigned int i = 0; i < materials.size(); i++)
{ {
if ((materials[i]->opacity > 0.01f && materials[i]->opacity < 1.0f) || if ((materials[i]->opacity > 0.01f && materials[i]->opacity < 1.0f) ||
materials[i]->blend == Material::AdditiveBlend) materials[i]->blend == BlendMode::AdditiveBlend)
{ {
opaque = false; opaque = false;
return; return;
@ -318,9 +318,9 @@ Model::determineOpacity()
bool bool
Model::usesTextureType(Material::TextureSemantic t) const Model::usesTextureType(TextureSemantic t) const
{ {
return textureUsage[static_cast<int>(t)]; return textureUsage[static_cast<std::size_t>(t)];
} }

View File

@ -11,6 +11,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <cstddef>
#include <vector> #include <vector>
#include <Eigen/Core> #include <Eigen/Core>
@ -99,15 +100,15 @@ class Model
* all within a mesh. This information is used to decide * all within a mesh. This information is used to decide
* if multiple rendering passes are required. * if multiple rendering passes are required.
*/ */
virtual bool usesTextureType(Material::TextureSemantic) const; bool usesTextureType(TextureSemantic) const;
/** Return true if the model has no translucent components. */ /** Return true if the model has no translucent components. */
virtual bool isOpaque() const bool isOpaque() const
{ {
return opaque; return opaque;
} }
virtual bool isNormalized() const bool isNormalized() const
{ {
return normalized; return normalized;
} }
@ -118,7 +119,7 @@ class Model
class MeshComparator class MeshComparator
{ {
public: public:
virtual ~MeshComparator() = default; virtual ~MeshComparator() = default;
virtual bool operator()(const Mesh&, const Mesh&) const = 0; virtual bool operator()(const Mesh&, const Mesh&) const = 0;
@ -145,7 +146,7 @@ class Model
*/ */
class OpacityComparator : public MeshComparator class OpacityComparator : public MeshComparator
{ {
public: public:
OpacityComparator() = default; OpacityComparator() = default;
virtual ~OpacityComparator() = default; virtual ~OpacityComparator() = default;
@ -156,7 +157,7 @@ class Model
std::vector<const Material*> materials; std::vector<const Material*> materials;
std::vector<Mesh*> meshes; std::vector<Mesh*> meshes;
std::array<bool, Material::TextureSemanticMax> textureUsage; std::array<bool, static_cast<std::size_t>(TextureSemantic::TextureSemanticMax)> textureUsage;
bool opaque{ true }; bool opaque{ true };
bool normalized{ false }; bool normalized{ false };
}; };

View File

@ -35,12 +35,12 @@ constexpr const char CEL_MODEL_HEADER_ASCII[] = "#celmodel__ascii";
constexpr const char CEL_MODEL_HEADER_BINARY[] = "#celmodel_binary"; constexpr const char CEL_MODEL_HEADER_BINARY[] = "#celmodel_binary";
// Material default values // Material default values
constexpr Material::Color DefaultDiffuse(0.0f, 0.0f, 0.0f); constexpr Color DefaultDiffuse(0.0f, 0.0f, 0.0f);
constexpr Material::Color DefaultSpecular(0.0f, 0.0f, 0.0f); constexpr Color DefaultSpecular(0.0f, 0.0f, 0.0f);
constexpr Material::Color DefaultEmissive(0.0f, 0.0f, 0.0f); constexpr Color DefaultEmissive(0.0f, 0.0f, 0.0f);
constexpr float DefaultSpecularPower = 1.0f; constexpr float DefaultSpecularPower = 1.0f;
constexpr float DefaultOpacity = 1.0f; constexpr float DefaultOpacity = 1.0f;
constexpr Material::BlendMode DefaultBlend = Material::NormalBlend; constexpr BlendMode DefaultBlend = BlendMode::NormalBlend;
// Standard tokens for ASCII model loader // Standard tokens for ASCII model loader
constexpr const char MeshToken[] = "mesh"; constexpr const char MeshToken[] = "mesh";
@ -188,6 +188,88 @@ defined here--they have the obvious definitions.
<material_index> :: <unsigned_int> | -1 <material_index> :: <unsigned_int> | -1
\endcode \endcode
*/ */
PrimitiveGroupType
parsePrimitiveGroupType(const std::string& name)
{
if (name == "trilist")
return PrimitiveGroupType::TriList;
if (name == "tristrip")
return PrimitiveGroupType::TriStrip;
if (name == "trifan")
return PrimitiveGroupType::TriFan;
if (name == "linelist")
return PrimitiveGroupType::LineList;
if (name == "linestrip")
return PrimitiveGroupType::LineStrip;
if (name == "points")
return PrimitiveGroupType::PointList;
if (name == "sprites")
return PrimitiveGroupType::SpriteList;
else
return PrimitiveGroupType::InvalidPrimitiveGroupType;
}
VertexAttributeSemantic
parseVertexAttributeSemantic(const std::string& name)
{
if (name == "position")
return VertexAttributeSemantic::Position;
if (name == "normal")
return VertexAttributeSemantic::Normal;
if (name == "color0")
return VertexAttributeSemantic::Color0;
if (name == "color1")
return VertexAttributeSemantic::Color1;
if (name == "tangent")
return VertexAttributeSemantic::Tangent;
if (name == "texcoord0")
return VertexAttributeSemantic::Texture0;
if (name == "texcoord1")
return VertexAttributeSemantic::Texture1;
if (name == "texcoord2")
return VertexAttributeSemantic::Texture2;
if (name == "texcoord3")
return VertexAttributeSemantic::Texture3;
if (name == "pointsize")
return VertexAttributeSemantic::PointSize;
return VertexAttributeSemantic::InvalidSemantic;
}
VertexAttributeFormat
parseVertexAttributeFormat(const std::string& name)
{
if (name == "f1")
return VertexAttributeFormat::Float1;
if (name == "f2")
return VertexAttributeFormat::Float2;
if (name == "f3")
return VertexAttributeFormat::Float3;
if (name == "f4")
return VertexAttributeFormat::Float4;
if (name == "ub4")
return VertexAttributeFormat::UByte4;
return VertexAttributeFormat::InvalidFormat;
}
TextureSemantic
parseTextureSemantic(const std::string& name)
{
if (name == "texture0")
return TextureSemantic::DiffuseMap;
if (name == "normalmap")
return TextureSemantic::NormalMap;
if (name == "specularmap")
return TextureSemantic::SpecularMap;
if (name == "emissivemap")
return TextureSemantic::EmissiveMap;
return TextureSemantic::InvalidTextureSemantic;
}
class AsciiModelLoader : public ModelLoader class AsciiModelLoader : public ModelLoader
{ {
public: public:
@ -201,9 +283,9 @@ public:
void reportError(const std::string& /*msg*/) override; void reportError(const std::string& /*msg*/) override;
Material* loadMaterial(); Material* loadMaterial();
Mesh::VertexDescription* loadVertexDescription(); VertexDescription* loadVertexDescription();
Mesh* loadMesh(); Mesh* loadMesh();
char* loadVertices(const Mesh::VertexDescription& vertexDesc, char* loadVertices(const VertexDescription& vertexDesc,
unsigned int& vertexCount); unsigned int& vertexCount);
private: private:
@ -239,9 +321,9 @@ AsciiModelLoader::loadMaterial()
while (tok.nextToken() == Tokenizer::TokenName && tok.getNameValue() != EndMaterialToken) while (tok.nextToken() == Tokenizer::TokenName && tok.getNameValue() != EndMaterialToken)
{ {
std::string property = tok.getStringValue(); std::string property = tok.getStringValue();
Material::TextureSemantic texType = Mesh::parseTextureSemantic(property); TextureSemantic texType = parseTextureSemantic(property);
if (texType != Material::InvalidTextureSemantic) if (texType != TextureSemantic::InvalidTextureSemantic)
{ {
if (tok.nextToken() != Tokenizer::TokenString) if (tok.nextToken() != Tokenizer::TokenString)
{ {
@ -253,24 +335,24 @@ AsciiModelLoader::loadMaterial()
std::string textureName = tok.getStringValue(); std::string textureName = tok.getStringValue();
ResourceHandle tex = getHandle(textureName); ResourceHandle tex = getHandle(textureName);
material->maps[texType] = tex; material->setMap(texType, tex);
} }
else if (property == "blend") else if (property == "blend")
{ {
Material::BlendMode blendMode = Material::InvalidBlend; BlendMode blendMode = BlendMode::InvalidBlend;
if (tok.nextToken() == Tokenizer::TokenName) if (tok.nextToken() == Tokenizer::TokenName)
{ {
std::string blendModeName = tok.getStringValue(); std::string blendModeName = tok.getStringValue();
if (blendModeName == "normal") if (blendModeName == "normal")
blendMode = Material::NormalBlend; blendMode = BlendMode::NormalBlend;
else if (blendModeName == "add") else if (blendModeName == "add")
blendMode = Material::AdditiveBlend; blendMode = BlendMode::AdditiveBlend;
else if (blendModeName == "premultiplied") else if (blendModeName == "premultiplied")
blendMode = Material::PremultipliedAlphaBlend; blendMode = BlendMode::PremultipliedAlphaBlend;
} }
if (blendMode == Material::InvalidBlend) if (blendMode == BlendMode::InvalidBlend)
{ {
reportError("Bad blend mode in material"); reportError("Bad blend mode in material");
delete material; delete material;
@ -301,10 +383,12 @@ AsciiModelLoader::loadMaterial()
data[i] = tok.getNumberValue(); data[i] = tok.getNumberValue();
} }
Material::Color colorVal; Color colorVal;
if (nValues == 3) if (nValues == 3)
{ {
colorVal = Material::Color((float) data[0], (float) data[1], (float) data[2]); colorVal = Color(static_cast<float>(data[0]),
static_cast<float>(data[1]),
static_cast<float>(data[2]));
} }
if (property == "diffuse") if (property == "diffuse")
@ -340,7 +424,7 @@ AsciiModelLoader::loadMaterial()
} }
Mesh::VertexDescription* VertexDescription*
AsciiModelLoader::loadVertexDescription() AsciiModelLoader::loadVertexDescription()
{ {
if (tok.nextToken() != Tokenizer::TokenName || tok.getNameValue() != VertexDescToken) if (tok.nextToken() != Tokenizer::TokenName || tok.getNameValue() != VertexDescToken)
@ -352,7 +436,7 @@ AsciiModelLoader::loadVertexDescription()
int maxAttributes = 16; int maxAttributes = 16;
int nAttributes = 0; int nAttributes = 0;
unsigned int offset = 0; unsigned int offset = 0;
auto* attributes = new Mesh::VertexAttribute[maxAttributes]; auto* attributes = new VertexAttribute[maxAttributes];
while (tok.nextToken() == Tokenizer::TokenName && tok.getNameValue() != EndVertexDescToken) while (tok.nextToken() == Tokenizer::TokenName && tok.getNameValue() != EndVertexDescToken)
{ {
@ -384,18 +468,16 @@ AsciiModelLoader::loadVertexDescription()
return nullptr; return nullptr;
} }
Mesh::VertexAttributeSemantic semantic = VertexAttributeSemantic semantic = parseVertexAttributeSemantic(semanticName);
Mesh::parseVertexAttributeSemantic(semanticName); if (semantic == VertexAttributeSemantic::InvalidSemantic)
if (semantic == Mesh::InvalidSemantic)
{ {
reportError(fmt::format("Invalid vertex attribute semantic '{}'", semanticName)); reportError(fmt::format("Invalid vertex attribute semantic '{}'", semanticName));
delete[] attributes; delete[] attributes;
return nullptr; return nullptr;
} }
Mesh::VertexAttributeFormat format = VertexAttributeFormat format = parseVertexAttributeFormat(formatName);
Mesh::parseVertexAttributeFormat(formatName); if (format == VertexAttributeFormat::InvalidFormat)
if (format == Mesh::InvalidFormat)
{ {
reportError(fmt::format("Invalid vertex attribute format '{}'", formatName)); reportError(fmt::format("Invalid vertex attribute format '{}'", formatName));
delete[] attributes; delete[] attributes;
@ -406,7 +488,7 @@ AsciiModelLoader::loadVertexDescription()
attributes[nAttributes].format = format; attributes[nAttributes].format = format;
attributes[nAttributes].offset = offset; attributes[nAttributes].offset = offset;
offset += Mesh::getVertexAttributeSize(format); offset += VertexAttribute::getFormatSize(format);
nAttributes++; nAttributes++;
} }
@ -424,15 +506,14 @@ AsciiModelLoader::loadVertexDescription()
return nullptr; return nullptr;
} }
auto *vertexDesc = auto *vertexDesc = new VertexDescription(offset, nAttributes, attributes);
new Mesh::VertexDescription(offset, nAttributes, attributes);
delete[] attributes; delete[] attributes;
return vertexDesc; return vertexDesc;
} }
char* char*
AsciiModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc, AsciiModelLoader::loadVertices(const VertexDescription& vertexDesc,
unsigned int& vertexCount) unsigned int& vertexCount)
{ {
if (tok.nextToken() != Tokenizer::TokenName && tok.getNameValue() != VerticesToken) if (tok.nextToken() != Tokenizer::TokenName && tok.getNameValue() != VerticesToken)
@ -465,22 +546,22 @@ AsciiModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc,
assert(offset < vertexDataSize); assert(offset < vertexDataSize);
for (unsigned int attr = 0; attr < vertexDesc.nAttributes; attr++) for (unsigned int attr = 0; attr < vertexDesc.nAttributes; attr++)
{ {
Mesh::VertexAttributeFormat fmt = vertexDesc.attributes[attr].format; VertexAttributeFormat vfmt = vertexDesc.attributes[attr].format;
/*unsigned int nBytes = Mesh::getVertexAttributeSize(fmt); Unused*/ /*unsigned int nBytes = Mesh::getVertexAttributeSize(fmt); Unused*/
int readCount = 0; int readCount = 0;
switch (fmt) switch (vfmt)
{ {
case Mesh::Float1: case VertexAttributeFormat::Float1:
readCount = 1; readCount = 1;
break; break;
case Mesh::Float2: case VertexAttributeFormat::Float2:
readCount = 2; readCount = 2;
break; break;
case Mesh::Float3: case VertexAttributeFormat::Float3:
readCount = 3; readCount = 3;
break; break;
case Mesh::Float4: case VertexAttributeFormat::Float4:
case Mesh::UByte4: case VertexAttributeFormat::UByte4:
readCount = 4; readCount = 4;
break; break;
default: default:
@ -504,7 +585,7 @@ AsciiModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc,
} }
unsigned int base = offset + vertexDesc.attributes[attr].offset; unsigned int base = offset + vertexDesc.attributes[attr].offset;
if (fmt == Mesh::UByte4) if (vfmt == VertexAttributeFormat::UByte4)
{ {
for (int k = 0; k < readCount; k++) for (int k = 0; k < readCount; k++)
{ {
@ -535,7 +616,7 @@ AsciiModelLoader::loadMesh()
return nullptr; return nullptr;
} }
Mesh::VertexDescription* vertexDesc = loadVertexDescription(); VertexDescription* vertexDesc = loadVertexDescription();
if (vertexDesc == nullptr) if (vertexDesc == nullptr)
return nullptr; return nullptr;
@ -554,9 +635,8 @@ AsciiModelLoader::loadMesh()
while (tok.nextToken() == Tokenizer::TokenName && tok.getNameValue() != EndMeshToken) while (tok.nextToken() == Tokenizer::TokenName && tok.getNameValue() != EndMeshToken)
{ {
Mesh::PrimitiveGroupType type = PrimitiveGroupType type = parsePrimitiveGroupType(tok.getStringValue());
Mesh::parsePrimitiveGroupType(tok.getStringValue()); if (type == PrimitiveGroupType::InvalidPrimitiveGroupType)
if (type == Mesh::InvalidPrimitiveGroupType)
{ {
reportError("Bad primitive group type: " + tok.getStringValue()); reportError("Bad primitive group type: " + tok.getStringValue());
delete mesh; delete mesh;
@ -589,7 +669,7 @@ AsciiModelLoader::loadMesh()
unsigned int indexCount = (unsigned int) tok.getIntegerValue(); unsigned int indexCount = (unsigned int) tok.getIntegerValue();
auto* indices = new Mesh::index32[indexCount]; auto* indices = new index32[indexCount];
for (unsigned int i = 0; i < indexCount; i++) for (unsigned int i = 0; i < indexCount; i++)
{ {
@ -700,12 +780,12 @@ public:
private: private:
bool writeMesh(const Mesh& /*mesh*/); bool writeMesh(const Mesh& /*mesh*/);
bool writeMaterial(const Material& /*material*/); bool writeMaterial(const Material& /*material*/);
bool writeGroup(const Mesh::PrimitiveGroup& /*group*/); bool writeGroup(const PrimitiveGroup& /*group*/);
bool writeVertexDescription(const Mesh::VertexDescription& /*desc*/); bool writeVertexDescription(const VertexDescription& /*desc*/);
bool writeVertices(const void* vertexData, bool writeVertices(const void* vertexData,
unsigned int nVertices, unsigned int nVertices,
unsigned int stride, unsigned int stride,
const Mesh::VertexDescription& desc); const VertexDescription& desc);
std::ostream& out; std::ostream& out;
}; };
@ -734,23 +814,23 @@ AsciiModelWriter::write(const Model& model)
bool bool
AsciiModelWriter::writeGroup(const Mesh::PrimitiveGroup& group) AsciiModelWriter::writeGroup(const PrimitiveGroup& group)
{ {
switch (group.prim) switch (group.prim)
{ {
case Mesh::TriList: case PrimitiveGroupType::TriList:
fmt::print(out, "trilist"); break; fmt::print(out, "trilist"); break;
case Mesh::TriStrip: case PrimitiveGroupType::TriStrip:
fmt::print(out, "tristrip"); break; fmt::print(out, "tristrip"); break;
case Mesh::TriFan: case PrimitiveGroupType::TriFan:
fmt::print(out, "trifan"); break; fmt::print(out, "trifan"); break;
case Mesh::LineList: case PrimitiveGroupType::LineList:
fmt::print(out, "linelist"); break; fmt::print(out, "linelist"); break;
case Mesh::LineStrip: case PrimitiveGroupType::LineStrip:
fmt::print(out, "linestrip"); break; fmt::print(out, "linestrip"); break;
case Mesh::PointList: case PrimitiveGroupType::PointList:
fmt::print(out, "points"); break; fmt::print(out, "points"); break;
case Mesh::SpriteList: case PrimitiveGroupType::SpriteList:
fmt::print(out, "sprites"); break; fmt::print(out, "sprites"); break;
default: default:
return false; return false;
@ -820,7 +900,7 @@ bool
AsciiModelWriter::writeVertices(const void* vertexData, AsciiModelWriter::writeVertices(const void* vertexData,
unsigned int nVertices, unsigned int nVertices,
unsigned int stride, unsigned int stride,
const Mesh::VertexDescription& desc) const VertexDescription& desc)
{ {
const auto* vertex = reinterpret_cast<const unsigned char*>(vertexData); const auto* vertex = reinterpret_cast<const unsigned char*>(vertexData);
@ -837,23 +917,23 @@ AsciiModelWriter::writeVertices(const void* vertexData,
switch (desc.attributes[attr].format) switch (desc.attributes[attr].format)
{ {
case Mesh::Float1: case VertexAttributeFormat::Float1:
std::memcpy(fdata, ubdata, sizeof(float)); std::memcpy(fdata, ubdata, sizeof(float));
fmt::print(out, "{}", fdata[0]); fmt::print(out, "{}", fdata[0]);
break; break;
case Mesh::Float2: case VertexAttributeFormat::Float2:
std::memcpy(fdata, ubdata, sizeof(float) * 2); std::memcpy(fdata, ubdata, sizeof(float) * 2);
fmt::print(out, "{} {}", fdata[0], fdata[1]); fmt::print(out, "{} {}", fdata[0], fdata[1]);
break; break;
case Mesh::Float3: case VertexAttributeFormat::Float3:
std::memcpy(fdata, ubdata, sizeof(float) * 3); std::memcpy(fdata, ubdata, sizeof(float) * 3);
fmt::print(out, "{} {} {}", fdata[0], fdata[1], fdata[2]); fmt::print(out, "{} {} {}", fdata[0], fdata[1], fdata[2]);
break; break;
case Mesh::Float4: case VertexAttributeFormat::Float4:
std::memcpy(fdata, ubdata, sizeof(float) * 4); std::memcpy(fdata, ubdata, sizeof(float) * 4);
fmt::print(out, "{} {} {} {}", fdata[0], fdata[1], fdata[2], fdata[3]); fmt::print(out, "{} {} {} {}", fdata[0], fdata[1], fdata[2], fdata[3]);
break; break;
case Mesh::UByte4: case VertexAttributeFormat::UByte4:
fmt::print(out, "{} {} {} {}", +ubdata[0], +ubdata[1], +ubdata[2], +ubdata[3]); fmt::print(out, "{} {} {} {}", +ubdata[0], +ubdata[1], +ubdata[2], +ubdata[3]);
break; break;
default: default:
@ -871,7 +951,7 @@ AsciiModelWriter::writeVertices(const void* vertexData,
bool bool
AsciiModelWriter::writeVertexDescription(const Mesh::VertexDescription& desc) AsciiModelWriter::writeVertexDescription(const VertexDescription& desc)
{ {
if (!(out << "vertexdesc\n").good()) { return false; } if (!(out << "vertexdesc\n").good()) { return false; }
for (unsigned int attr = 0; attr < desc.nAttributes; attr++) for (unsigned int attr = 0; attr < desc.nAttributes; attr++)
@ -881,34 +961,34 @@ AsciiModelWriter::writeVertexDescription(const Mesh::VertexDescription& desc)
switch (desc.attributes[attr].semantic) switch (desc.attributes[attr].semantic)
{ {
case Mesh::Position: case VertexAttributeSemantic::Position:
out << "position"; out << "position";
break; break;
case Mesh::Color0: case VertexAttributeSemantic::Color0:
out << "color0"; out << "color0";
break; break;
case Mesh::Color1: case VertexAttributeSemantic::Color1:
out << "color1"; out << "color1";
break; break;
case Mesh::Normal: case VertexAttributeSemantic::Normal:
out << "normal"; out << "normal";
break; break;
case Mesh::Tangent: case VertexAttributeSemantic::Tangent:
out << "tangent"; out << "tangent";
break; break;
case Mesh::Texture0: case VertexAttributeSemantic::Texture0:
out << "texcoord0"; out << "texcoord0";
break; break;
case Mesh::Texture1: case VertexAttributeSemantic::Texture1:
out << "texcoord1"; out << "texcoord1";
break; break;
case Mesh::Texture2: case VertexAttributeSemantic::Texture2:
out << "texcoord2"; out << "texcoord2";
break; break;
case Mesh::Texture3: case VertexAttributeSemantic::Texture3:
out << "texcoord3"; out << "texcoord3";
break; break;
case Mesh::PointSize: case VertexAttributeSemantic::PointSize:
out << "pointsize"; out << "pointsize";
break; break;
default: default:
@ -920,19 +1000,19 @@ AsciiModelWriter::writeVertexDescription(const Mesh::VertexDescription& desc)
switch (desc.attributes[attr].format) switch (desc.attributes[attr].format)
{ {
case Mesh::Float1: case VertexAttributeFormat::Float1:
out << "f1"; out << "f1";
break; break;
case Mesh::Float2: case VertexAttributeFormat::Float2:
out << "f2"; out << "f2";
break; break;
case Mesh::Float3: case VertexAttributeFormat::Float3:
out << "f3"; out << "f3";
break; break;
case Mesh::Float4: case VertexAttributeFormat::Float4:
out << "f4"; out << "f4";
break; break;
case Mesh::UByte4: case VertexAttributeFormat::UByte4:
out << "ub4"; out << "ub4";
break; break;
default: default:
@ -994,13 +1074,13 @@ AsciiModelWriter::writeMaterial(const Material& material)
if (!(out << "blend ").good()) { return false; } if (!(out << "blend ").good()) { return false; }
switch (material.blend) switch (material.blend)
{ {
case Material::NormalBlend: case BlendMode::NormalBlend:
out << "normal"; out << "normal";
break; break;
case Material::AdditiveBlend: case BlendMode::AdditiveBlend:
out << "add"; out << "add";
break; break;
case Material::PremultipliedAlphaBlend: case BlendMode::PremultipliedAlphaBlend:
out << "premultiplied"; out << "premultiplied";
break; break;
default: default:
@ -1011,7 +1091,7 @@ AsciiModelWriter::writeMaterial(const Material& material)
if (!out.good() || !(out << '\n').good()) { return false; } if (!out.good() || !(out << '\n').good()) { return false; }
} }
for (int i = 0; i < Material::TextureSemanticMax; i++) for (int i = 0; i < static_cast<int>(TextureSemantic::TextureSemanticMax); i++)
{ {
fs::path texSource; fs::path texSource;
if (material.maps[i] != InvalidResource) if (material.maps[i] != InvalidResource)
@ -1021,18 +1101,18 @@ AsciiModelWriter::writeMaterial(const Material& material)
if (!texSource.empty()) if (!texSource.empty())
{ {
switch (Material::TextureSemantic(i)) switch (static_cast<TextureSemantic>(i))
{ {
case Material::DiffuseMap: case TextureSemantic::DiffuseMap:
out << "texture0"; out << "texture0";
break; break;
case Material::NormalMap: case TextureSemantic::NormalMap:
out << "normalmap"; out << "normalmap";
break; break;
case Material::SpecularMap: case TextureSemantic::SpecularMap:
out << "specularmap"; out << "specularmap";
break; break;
case Material::EmissiveMap: case TextureSemantic::EmissiveMap:
out << "emissivemap"; out << "emissivemap";
break; break;
default: default:
@ -1079,7 +1159,7 @@ bool readTypeFloat1(std::istream& in, float& f)
} }
bool readTypeColor(std::istream& in, Material::Color& c) bool readTypeColor(std::istream& in, Color& c)
{ {
ModelFileType cmodType; ModelFileType cmodType;
float r, g, b; float r, g, b;
@ -1092,7 +1172,7 @@ bool readTypeColor(std::istream& in, Material::Color& c)
return false; return false;
} }
c = Material::Color(r, g, b); c = Color(r, g, b);
return true; return true;
} }
@ -1178,9 +1258,9 @@ public:
void reportError(const std::string& /*msg*/) override; void reportError(const std::string& /*msg*/) override;
Material* loadMaterial(); Material* loadMaterial();
Mesh::VertexDescription* loadVertexDescription(); VertexDescription* loadVertexDescription();
Mesh* loadMesh(); Mesh* loadMesh();
char* loadVertices(const Mesh::VertexDescription& vertexDesc, char* loadVertices(const VertexDescription& vertexDesc,
unsigned int& vertexCount); unsigned int& vertexCount);
private: private:
@ -1329,13 +1409,13 @@ BinaryModelLoader::loadMaterial()
{ {
std::int16_t blendMode; std::int16_t blendMode;
if (!celutil::readLE<std::int16_t>(in, blendMode) if (!celutil::readLE<std::int16_t>(in, blendMode)
|| blendMode < 0 || blendMode >= Material::BlendMax) || blendMode < 0 || blendMode >= static_cast<std::int16_t>(BlendMode::BlendMax))
{ {
reportError("Bad blend mode"); reportError("Bad blend mode");
delete material; delete material;
return nullptr; return nullptr;
} }
material->blend = (Material::BlendMode) blendMode; material->blend = static_cast<BlendMode>(blendMode);
} }
break; break;
@ -1343,7 +1423,7 @@ BinaryModelLoader::loadMaterial()
{ {
std::int16_t texType; std::int16_t texType;
if (!celutil::readLE<std::int16_t>(in, texType) if (!celutil::readLE<std::int16_t>(in, texType)
|| texType < 0 || texType >= Material::TextureSemanticMax) || texType < 0 || texType >= static_cast<std::int16_t>(TextureSemantic::TextureSemanticMax))
{ {
reportError("Bad texture type"); reportError("Bad texture type");
delete material; delete material;
@ -1385,7 +1465,7 @@ BinaryModelLoader::loadMaterial()
} }
Mesh::VertexDescription* VertexDescription*
BinaryModelLoader::loadVertexDescription() BinaryModelLoader::loadVertexDescription()
{ {
ModelFileToken tok; ModelFileToken tok;
@ -1398,7 +1478,7 @@ BinaryModelLoader::loadVertexDescription()
int maxAttributes = 16; int maxAttributes = 16;
int nAttributes = 0; int nAttributes = 0;
unsigned int offset = 0; unsigned int offset = 0;
auto* attributes = new Mesh::VertexAttribute[maxAttributes]; auto* attributes = new VertexAttribute[maxAttributes];
for (;;) for (;;)
{ {
@ -1414,11 +1494,11 @@ BinaryModelLoader::loadVertexDescription()
{ {
break; break;
} }
if (tok >= 0 && tok < Mesh::SemanticMax) if (tok >= 0 && tok < static_cast<std::int16_t>(VertexAttributeSemantic::SemanticMax))
{ {
std::int16_t fmt; std::int16_t vfmt;
if (!celutil::readLE<std::int16_t>(in, fmt) if (!celutil::readLE<std::int16_t>(in, vfmt)
|| fmt < 0 || fmt >= Mesh::FormatMax) || vfmt < 0 || vfmt >= static_cast<std::int16_t>(VertexAttributeFormat::FormatMax))
{ {
reportError("Invalid vertex attribute type"); reportError("Invalid vertex attribute type");
delete[] attributes; delete[] attributes;
@ -1433,12 +1513,12 @@ BinaryModelLoader::loadVertexDescription()
} }
attributes[nAttributes].semantic = attributes[nAttributes].semantic =
static_cast<Mesh::VertexAttributeSemantic>(tok); static_cast<VertexAttributeSemantic>(tok);
attributes[nAttributes].format = attributes[nAttributes].format =
static_cast<Mesh::VertexAttributeFormat>(fmt); static_cast<VertexAttributeFormat>(vfmt);
attributes[nAttributes].offset = offset; attributes[nAttributes].offset = offset;
offset += Mesh::getVertexAttributeSize(attributes[nAttributes].format); offset += VertexAttribute::getFormatSize(attributes[nAttributes].format);
nAttributes++; nAttributes++;
} }
else else
@ -1457,7 +1537,7 @@ BinaryModelLoader::loadVertexDescription()
} }
auto *vertexDesc = auto *vertexDesc =
new Mesh::VertexDescription(offset, nAttributes, attributes); new VertexDescription(offset, nAttributes, attributes);
delete[] attributes; delete[] attributes;
return vertexDesc; return vertexDesc;
} }
@ -1466,7 +1546,7 @@ BinaryModelLoader::loadVertexDescription()
Mesh* Mesh*
BinaryModelLoader::loadMesh() BinaryModelLoader::loadMesh()
{ {
Mesh::VertexDescription* vertexDesc = loadVertexDescription(); VertexDescription* vertexDesc = loadVertexDescription();
if (vertexDesc == nullptr) if (vertexDesc == nullptr)
return nullptr; return nullptr;
@ -1497,15 +1577,14 @@ BinaryModelLoader::loadMesh()
{ {
break; break;
} }
if (tok < 0 || tok >= Mesh::PrimitiveTypeMax) if (tok < 0 || tok >= static_cast<std::int16_t>(PrimitiveGroupType::PrimitiveTypeMax))
{ {
reportError("Bad primitive group type"); reportError("Bad primitive group type");
delete mesh; delete mesh;
return nullptr; return nullptr;
} }
Mesh::PrimitiveGroupType type = PrimitiveGroupType type = static_cast<PrimitiveGroupType>(tok);
static_cast<Mesh::PrimitiveGroupType>(tok);
std::uint32_t materialIndex, indexCount; std::uint32_t materialIndex, indexCount;
if (!celutil::readLE<std::uint32_t>(in, materialIndex) if (!celutil::readLE<std::uint32_t>(in, materialIndex)
|| !celutil::readLE<std::uint32_t>(in, indexCount)) || !celutil::readLE<std::uint32_t>(in, indexCount))
@ -1539,7 +1618,7 @@ BinaryModelLoader::loadMesh()
char* char*
BinaryModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc, BinaryModelLoader::loadVertices(const VertexDescription& vertexDesc,
unsigned int& vertexCount) unsigned int& vertexCount)
{ {
ModelFileToken tok; ModelFileToken tok;
@ -1565,12 +1644,12 @@ BinaryModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc,
for (unsigned int attr = 0; attr < vertexDesc.nAttributes; attr++) for (unsigned int attr = 0; attr < vertexDesc.nAttributes; attr++)
{ {
unsigned int base = offset + vertexDesc.attributes[attr].offset; unsigned int base = offset + vertexDesc.attributes[attr].offset;
Mesh::VertexAttributeFormat fmt = vertexDesc.attributes[attr].format; VertexAttributeFormat fmt = vertexDesc.attributes[attr].format;
float f[4]; float f[4];
/*int readCount = 0; Unused*/ /*int readCount = 0; Unused*/
switch (fmt) switch (fmt)
{ {
case Mesh::Float1: case VertexAttributeFormat::Float1:
if (!celutil::readLE<float>(in, f[0])) if (!celutil::readLE<float>(in, f[0]))
{ {
reportError("Failed to read Float1"); reportError("Failed to read Float1");
@ -1579,7 +1658,7 @@ BinaryModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc,
} }
std::memcpy(vertexData + base, f, sizeof(float)); std::memcpy(vertexData + base, f, sizeof(float));
break; break;
case Mesh::Float2: case VertexAttributeFormat::Float2:
if (!celutil::readLE<float>(in, f[0]) if (!celutil::readLE<float>(in, f[0])
|| !celutil::readLE<float>(in, f[1])) || !celutil::readLE<float>(in, f[1]))
{ {
@ -1589,7 +1668,7 @@ BinaryModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc,
} }
std::memcpy(vertexData + base, f, sizeof(float) * 2); std::memcpy(vertexData + base, f, sizeof(float) * 2);
break; break;
case Mesh::Float3: case VertexAttributeFormat::Float3:
if (!celutil::readLE<float>(in, f[0]) if (!celutil::readLE<float>(in, f[0])
|| !celutil::readLE<float>(in, f[1]) || !celutil::readLE<float>(in, f[1])
|| !celutil::readLE<float>(in, f[2])) || !celutil::readLE<float>(in, f[2]))
@ -1600,7 +1679,7 @@ BinaryModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc,
} }
std::memcpy(vertexData + base, f, sizeof(float) * 3); std::memcpy(vertexData + base, f, sizeof(float) * 3);
break; break;
case Mesh::Float4: case VertexAttributeFormat::Float4:
if (!celutil::readLE<float>(in, f[0]) if (!celutil::readLE<float>(in, f[0])
|| !celutil::readLE<float>(in, f[1]) || !celutil::readLE<float>(in, f[1])
|| !celutil::readLE<float>(in, f[2]) || !celutil::readLE<float>(in, f[2])
@ -1612,7 +1691,7 @@ BinaryModelLoader::loadVertices(const Mesh::VertexDescription& vertexDesc,
} }
std::memcpy(vertexData + base, f, sizeof(float) * 4); std::memcpy(vertexData + base, f, sizeof(float) * 4);
break; break;
case Mesh::UByte4: case VertexAttributeFormat::UByte4:
if (!in.get(reinterpret_cast<char*>(vertexData + base), 4).good()) if (!in.get(reinterpret_cast<char*>(vertexData + base), 4).good())
{ {
reportError("failed to read UByte4"); reportError("failed to read UByte4");
@ -1652,7 +1731,7 @@ bool writeTypeFloat1(std::ostream& out, float f)
} }
bool writeTypeColor(std::ostream& out, const Material::Color& c) bool writeTypeColor(std::ostream& out, const Color& c)
{ {
return writeType(out, CMOD_Color) return writeType(out, CMOD_Color)
&& celutil::writeLE<float>(out, c.red()) && celutil::writeLE<float>(out, c.red())
@ -1684,12 +1763,12 @@ public:
private: private:
bool writeMesh(const Mesh& /*mesh*/); bool writeMesh(const Mesh& /*mesh*/);
bool writeMaterial(const Material& /*material*/); bool writeMaterial(const Material& /*material*/);
bool writeGroup(const Mesh::PrimitiveGroup& /*group*/); bool writeGroup(const PrimitiveGroup& /*group*/);
bool writeVertexDescription(const Mesh::VertexDescription& /*desc*/); bool writeVertexDescription(const VertexDescription& /*desc*/);
bool writeVertices(const void* vertexData, bool writeVertices(const void* vertexData,
unsigned int nVertices, unsigned int nVertices,
unsigned int stride, unsigned int stride,
const Mesh::VertexDescription& desc); const VertexDescription& desc);
std::ostream& out; std::ostream& out;
}; };
@ -1715,9 +1794,9 @@ BinaryModelWriter::write(const Model& model)
bool bool
BinaryModelWriter::writeGroup(const Mesh::PrimitiveGroup& group) BinaryModelWriter::writeGroup(const PrimitiveGroup& group)
{ {
if (!celutil::writeLE<std::int16_t>(out, group.prim) if (!celutil::writeLE<std::int16_t>(out, static_cast<std::int16_t>(group.prim))
|| !celutil::writeLE<std::uint32_t>(out, group.materialIndex) || !celutil::writeLE<std::uint32_t>(out, group.materialIndex)
|| !celutil::writeLE<std::uint32_t>(out, group.nIndices)) || !celutil::writeLE<std::uint32_t>(out, group.nIndices))
{ {
@ -1759,7 +1838,7 @@ bool
BinaryModelWriter::writeVertices(const void* vertexData, BinaryModelWriter::writeVertices(const void* vertexData,
unsigned int nVertices, unsigned int nVertices,
unsigned int stride, unsigned int stride,
const Mesh::VertexDescription& desc) const VertexDescription& desc)
{ {
const auto* vertex = reinterpret_cast<const char*>(vertexData); const auto* vertex = reinterpret_cast<const char*>(vertexData);
@ -1778,29 +1857,29 @@ BinaryModelWriter::writeVertices(const void* vertexData,
bool result; bool result;
switch (desc.attributes[attr].format) switch (desc.attributes[attr].format)
{ {
case Mesh::Float1: case VertexAttributeFormat::Float1:
std::memcpy(fdata, cdata, sizeof(float)); std::memcpy(fdata, cdata, sizeof(float));
result = celutil::writeLE<float>(out, fdata[0]); result = celutil::writeLE<float>(out, fdata[0]);
break; break;
case Mesh::Float2: case VertexAttributeFormat::Float2:
std::memcpy(fdata, cdata, sizeof(float) * 2); std::memcpy(fdata, cdata, sizeof(float) * 2);
result = celutil::writeLE<float>(out, fdata[0]) result = celutil::writeLE<float>(out, fdata[0])
&& celutil::writeLE<float>(out, fdata[1]); && celutil::writeLE<float>(out, fdata[1]);
break; break;
case Mesh::Float3: case VertexAttributeFormat::Float3:
std::memcpy(fdata, cdata, sizeof(float) * 3); std::memcpy(fdata, cdata, sizeof(float) * 3);
result = celutil::writeLE<float>(out, fdata[0]) result = celutil::writeLE<float>(out, fdata[0])
&& celutil::writeLE<float>(out, fdata[1]) && celutil::writeLE<float>(out, fdata[1])
&& celutil::writeLE<float>(out, fdata[2]); && celutil::writeLE<float>(out, fdata[2]);
break; break;
case Mesh::Float4: case VertexAttributeFormat::Float4:
std::memcpy(fdata, cdata, sizeof(float) * 4); std::memcpy(fdata, cdata, sizeof(float) * 4);
result = celutil::writeLE<float>(out, fdata[0]) result = celutil::writeLE<float>(out, fdata[0])
&& celutil::writeLE<float>(out, fdata[1]) && celutil::writeLE<float>(out, fdata[1])
&& celutil::writeLE<float>(out, fdata[2]) && celutil::writeLE<float>(out, fdata[2])
&& celutil::writeLE<float>(out, fdata[3]); && celutil::writeLE<float>(out, fdata[3]);
break; break;
case Mesh::UByte4: case VertexAttributeFormat::UByte4:
result = out.write(cdata, 4).good(); result = out.write(cdata, 4).good();
break; break;
default: default:
@ -1817,14 +1896,14 @@ BinaryModelWriter::writeVertices(const void* vertexData,
bool bool
BinaryModelWriter::writeVertexDescription(const Mesh::VertexDescription& desc) BinaryModelWriter::writeVertexDescription(const VertexDescription& desc)
{ {
if (!writeToken(out, CMOD_VertexDesc)) { return false; } if (!writeToken(out, CMOD_VertexDesc)) { return false; }
for (unsigned int attr = 0; attr < desc.nAttributes; attr++) for (unsigned int attr = 0; attr < desc.nAttributes; attr++)
{ {
if (!celutil::writeLE<std::int16_t>(out, desc.attributes[attr].semantic) if (!celutil::writeLE<std::int16_t>(out, static_cast<std::int16_t>(desc.attributes[attr].semantic))
|| !celutil::writeLE<std::int16_t>(out, desc.attributes[attr].format)) || !celutil::writeLE<std::int16_t>(out, static_cast<std::int16_t>(desc.attributes[attr].format)))
{ {
return false; return false;
} }
@ -1870,12 +1949,13 @@ BinaryModelWriter::writeMaterial(const Material& material)
} }
if (material.blend != DefaultBlend if (material.blend != DefaultBlend
&& (!writeToken(out, CMOD_Blend) || !celutil::writeLE<std::int16_t>(out, material.blend))) && (!writeToken(out, CMOD_Blend)
|| !celutil::writeLE<std::int16_t>(out, static_cast<std::int16_t>(material.blend))))
{ {
return false; return false;
} }
for (int i = 0; i < Material::TextureSemanticMax; i++) for (int i = 0; i < static_cast<int>(TextureSemantic::TextureSemanticMax); i++)
{ {
if (material.maps[i] != InvalidResource) if (material.maps[i] != InvalidResource)
{ {

View File

@ -47,7 +47,7 @@ template<class T> class ResourceManager
public: public:
ResourceManager(); ResourceManager();
ResourceManager(const fs::path& _baseDir) : baseDir(_baseDir) {}; ResourceManager(const fs::path& _baseDir) : baseDir(_baseDir) {};
~ResourceManager(); ~ResourceManager() = default;
typedef typename T::ResourceType ResourceType; typedef typename T::ResourceType ResourceType;

View File

@ -9,28 +9,21 @@
// //
// Convert a 3DS file to a Celestia mesh (.cmod) file // Convert a 3DS file to a Celestia mesh (.cmod) file
#include "convert3ds.h"
#include "cmodops.h"
#include <celmodel/modelfile.h>
#include <cel3ds/3dsread.h>
#include <celmath/mathlib.h>
#include <cstring>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string>
#include <cel3ds/3dsread.h>
#include <celmath/mathlib.h>
#include "cmodops.h"
#include "convert3ds.h"
#include "pathmanager.h" #include "pathmanager.h"
using namespace cmod;
using namespace std;
using namespace celmath;
void usage() void usage()
{ {
cerr << "Usage: 3dstocmod <input 3ds file>\n"; std::cerr << "Usage: 3dstocmod <input 3ds file>\n";
} }
@ -42,60 +35,48 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
string inputFileName = argv[1]; std::string inputFileName = argv[1];
cerr << "Reading...\n"; std::cerr << "Reading...\n";
std::unique_ptr<M3DScene> scene = Read3DSFile(inputFileName); std::unique_ptr<M3DScene> scene = Read3DSFile(inputFileName);
if (scene == nullptr) if (scene == nullptr)
{ {
cerr << "Error reading 3DS file '" << inputFileName << "'\n"; std::cerr << "Error reading 3DS file '" << inputFileName << "'\n";
return 1; return 1;
} }
cerr << "Converting...\n"; std::cerr << "Converting...\n";
Model* model = Convert3DSModel(*scene, GetPathManager()->getHandle); cmod::Model* model = Convert3DSModel(*scene, GetPathManager()->getHandle);
if (!model) if (!model)
{ {
cerr << "Error converting 3DS file to Celestia model\n"; std::cerr << "Error converting 3DS file to Celestia model\n";
return 1; return 1;
} }
// Generate normals for the model // Generate normals for the model
double smoothAngle = 45.0; // degrees float smoothAngle = 45.0; // degrees
double weldTolerance = 1.0e-6; double weldTolerance = 1.0e-6;
bool weldVertices = true; bool weldVertices = true;
Model* newModel = GenerateModelNormals(*model, (float)degToRad(smoothAngle), weldVertices, weldTolerance); cmod::Model* newModel = GenerateModelNormals(*model, celmath::degToRad(smoothAngle), weldVertices, weldTolerance);
delete model; delete model;
if (!newModel) if (!newModel)
{ {
cerr << "Ran out of memory while generating surface normals.\n"; std::cerr << "Ran out of memory while generating surface normals.\n";
return 1; return 1;
} }
// Automatically uniquify vertices // Automatically uniquify vertices
for (unsigned int i = 0; newModel->getMesh(i) != nullptr; i++) for (unsigned int i = 0; newModel->getMesh(i) != nullptr; i++)
{ {
Mesh* mesh = newModel->getMesh(i); cmod::Mesh* mesh = newModel->getMesh(i);
UniquifyVertices(*mesh); UniquifyVertices(*mesh);
} }
model = newModel; model = newModel;
#if 0 SaveModelAscii(model, std::cout, GetPathManager()->getSource);
// Print information about primitive groups
for (uint32_t i = 0; model->getMesh(i); i++)
{
const Mesh* mesh = model->getMesh(i);
for (uint32_t j = 0; mesh->getGroup(j); j++)
{
const Mesh::PrimitiveGroup* group = mesh->getGroup(j);
}
}
#endif
SaveModelAscii(model, cout, GetPathManager()->getSource);
return 0; return 0;
} }

View File

@ -9,8 +9,7 @@
// //
// C++ wrapper for OpenGL framebuffer objects. // C++ wrapper for OpenGL framebuffer objects.
#ifndef _GLFRAMEBUFFER_H_ #pragma once
#define _GLFRAMEBUFFER_H_
#include "glsupport.h" #include "glsupport.h"
@ -57,5 +56,3 @@ private:
GLuint m_fboId; GLuint m_fboId;
GLenum m_status; GLenum m_status;
}; };
#endif // _GLFRAMEBUFFER_H_

View File

@ -9,10 +9,13 @@
// //
// C++ wrapper for OpenGL shaders and shader programs // C++ wrapper for OpenGL shaders and shader programs
#include "glsupport.h" #pragma once
#include <Eigen/Core>
#include <string> #include <string>
#include <Eigen/Core>
#include "glsupport.h"
class GLVertexShader; class GLVertexShader;
class GLFragmentShader; class GLFragmentShader;

View File

@ -9,38 +9,74 @@
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#include <fstream> #include <fstream>
#include <iostream>
#include <memory> #include <memory>
#include "mainwindow.h" #include <string>
#include "materialwidget.h"
#include <QActionGroup>
#include <QColor>
#include <QColorDialog>
#include <QDialog>
#include <QDialogButtonBox>
#include <QDir>
#include <QDockWidget>
#include <QDoubleValidator>
#include <QFileDialog>
#include <QFileInfo>
#include <QFileOpenEvent>
#include <QFormLayout>
#include <QKeySequence>
#include <QLineEdit>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QSetIterator>
#include <QSettings>
#include <QStatusBar>
#include <QVBoxLayout>
#include <cel3ds/3dsmodel.h>
#include <cel3ds/3dsread.h>
#include <celmodel/material.h>
#include <celmodel/mesh.h>
#include <celmodel/model.h>
#include <celmodel/modelfile.h>
#include "cmodops.h"
#include "convert3ds.h" #include "convert3ds.h"
#include "convertobj.h" #include "convertobj.h"
#include "cmodops.h"
#include "pathmanager.h" #include "pathmanager.h"
#include <cel3ds/3dsread.h>
#include <celmodel/modelfile.h>
#include <QStatusBar>
#include <QMenuBar>
#include <QSettings>
#include <QDockWidget>
#include <QEvent>
#include <QMessageBox>
#include <QCloseEvent>
#include <QFileOpenEvent>
#include <QFileInfo>
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QColorDialog>
#include <QDir>
#include <QFileDialog>
#include <QFormLayout>
using namespace cmod; #include "glshader.h"
using namespace std; #include "glsupport.h"
#include "materialwidget.h"
#include "mainwindow.h"
#include "modelviewwidget.h"
namespace
{
// Version number for saving/restoring widget layout state. Increment this // Version number for saving/restoring widget layout state. Increment this
// value whenever new tool widgets are added or removed. // value whenever new tool widgets are added or removed.
static const int CMODVIEW_STATE_VERSION = 1; constexpr int CMODVIEW_STATE_VERSION = 1;
cmod::Material*
cloneMaterial(const cmod::Material* other)
{
cmod::Material* material = new cmod::Material();
material->diffuse = other->diffuse;
material->specular = other->specular;
material->emissive = other->emissive;
material->specularPower = other->specularPower;
material->opacity = other->opacity;
material->blend = other->blend;
material->maps = other->maps;
return material;
}
} // end unnamed namespace
MainWindow::MainWindow() : MainWindow::MainWindow() :
@ -214,20 +250,7 @@ void MainWindow::initializeGL()
} }
} }
static Material*
cloneMaterial(const Material* other)
{
Material* material = new Material();
material->diffuse = other->diffuse;
material->specular = other->specular;
material->emissive = other->emissive;
material->specularPower = other->specularPower;
material->opacity = other->opacity;
material->blend = other->blend;
material->maps = other->maps;
return material;
}
bool bool
@ -254,7 +277,7 @@ MainWindow::eventFilter(QObject* obj, QEvent* e)
void void
MainWindow::setModel(const QString& fileName, Model* model) MainWindow::setModel(const QString& fileName, cmod::Model* model)
{ {
QFileInfo info(fileName); QFileInfo info(fileName);
QString modelDir = info.absoluteDir().path(); QString modelDir = info.absoluteDir().path();
@ -277,7 +300,7 @@ MainWindow::setModel(const QString& fileName, Model* model)
void void
MainWindow::showModelStatistics() MainWindow::showModelStatistics()
{ {
Model* model = m_modelView->model(); cmod::Model* model = m_modelView->model();
if (model) if (model)
{ {
// Count triangles and vertices in the mesh // Count triangles and vertices in the mesh
@ -285,18 +308,18 @@ MainWindow::showModelStatistics()
unsigned int triangleCount = 0; unsigned int triangleCount = 0;
for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex) for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex)
{ {
const Mesh* mesh = model->getMesh(meshIndex); const cmod::Mesh* mesh = model->getMesh(meshIndex);
vertexCount += mesh->getVertexCount(); vertexCount += mesh->getVertexCount();
for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex) for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex)
{ {
const Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex); const cmod::PrimitiveGroup* group = mesh->getGroup(groupIndex);
switch (group->prim) switch (group->prim)
{ {
case Mesh::TriList: case cmod::PrimitiveGroupType::TriList:
triangleCount += group->nIndices / 3; triangleCount += group->nIndices / 3;
break; break;
case Mesh::TriFan: case cmod::PrimitiveGroupType::TriFan:
case Mesh::TriStrip: case cmod::PrimitiveGroupType::TriStrip:
triangleCount += group->nIndices - 2; triangleCount += group->nIndices - 2;
break; break;
default: default:
@ -373,7 +396,7 @@ MainWindow::openModel(const QString& fileName)
{ {
if (!fileName.isEmpty()) if (!fileName.isEmpty())
{ {
string fileNameStd = string(fileName.toUtf8().data()); std::string fileNameStd = std::string(fileName.toUtf8().data());
QFileInfo info(fileName); QFileInfo info(fileName);
GetPathManager()->reset(); GetPathManager()->reset();
@ -387,7 +410,7 @@ MainWindow::openModel(const QString& fileName)
return; return;
} }
Model* model = Convert3DSModel(*scene, GetPathManager()->getHandle); cmod::Model* model = Convert3DSModel(*scene, GetPathManager()->getHandle);
if (model == nullptr) if (model == nullptr)
{ {
QMessageBox::warning(this, "Load error", tr("Internal error converting 3DS file %1").arg(fileName)); QMessageBox::warning(this, "Load error", tr("Internal error converting 3DS file %1").arg(fileName));
@ -395,11 +418,11 @@ MainWindow::openModel(const QString& fileName)
} }
// Generate normals for the model // Generate normals for the model
double smoothAngle = 45.0; // degrees float smoothAngle = 45.0; // degrees
double weldTolerance = 1.0e-6; double weldTolerance = 1.0e-6;
bool weldVertices = true; bool weldVertices = true;
Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance); cmod::Model* newModel = GenerateModelNormals(*model, celmath::degToRad(smoothAngle), weldVertices, weldTolerance);
delete model; delete model;
if (!newModel) if (!newModel)
@ -411,7 +434,7 @@ MainWindow::openModel(const QString& fileName)
// Automatically uniquify vertices // Automatically uniquify vertices
for (unsigned int i = 0; newModel->getMesh(i) != nullptr; i++) for (unsigned int i = 0; newModel->getMesh(i) != nullptr; i++)
{ {
Mesh* mesh = newModel->getMesh(i); cmod::Mesh* mesh = newModel->getMesh(i);
UniquifyVertices(*mesh); UniquifyVertices(*mesh);
} }
@ -420,8 +443,8 @@ MainWindow::openModel(const QString& fileName)
} }
else if (info.suffix().toLower() == "obj") else if (info.suffix().toLower() == "obj")
{ {
Model* model = nullptr; cmod::Model* model = nullptr;
ifstream in(fileNameStd, ios::in | ios::binary); std::ifstream in(fileNameStd, std::ios::in | std::ios::binary);
if (!in.good()) if (!in.good())
{ {
QMessageBox::warning(this, "Load error", tr("Error opening obj file %1").arg(fileName)); QMessageBox::warning(this, "Load error", tr("Error opening obj file %1").arg(fileName));
@ -440,7 +463,7 @@ MainWindow::openModel(const QString& fileName)
// Automatically uniquify vertices // Automatically uniquify vertices
for (unsigned int i = 0; model->getMesh(i) != nullptr; i++) for (unsigned int i = 0; model->getMesh(i) != nullptr; i++)
{ {
Mesh* mesh = model->getMesh(i); cmod::Mesh* mesh = model->getMesh(i);
UniquifyVertices(*mesh); UniquifyVertices(*mesh);
} }
@ -448,15 +471,15 @@ MainWindow::openModel(const QString& fileName)
} }
else if (info.suffix().toLower() == "cmod") else if (info.suffix().toLower() == "cmod")
{ {
Model* model = nullptr; cmod::Model* model = nullptr;
ifstream in(fileNameStd, ios::in | ios::binary); std::ifstream in(fileNameStd, std::ios::in | std::ios::binary);
if (!in.good()) if (!in.good())
{ {
QMessageBox::warning(this, "Load error", tr("Error opening CMOD file %1").arg(fileName)); QMessageBox::warning(this, "Load error", tr("Error opening CMOD file %1").arg(fileName));
return; return;
} }
model = LoadModel(in, GetPathManager()->getHandle); model = cmod::LoadModel(in, GetPathManager()->getHandle);
if (model == nullptr) if (model == nullptr)
{ {
QMessageBox::warning(this, "Load error", tr("Error reading CMOD file %1").arg(fileName)); QMessageBox::warning(this, "Load error", tr("Error reading CMOD file %1").arg(fileName));
@ -498,9 +521,9 @@ MainWindow::saveModelAs()
void void
MainWindow::saveModel(const QString& saveFileName) MainWindow::saveModel(const QString& saveFileName)
{ {
string fileNameStd = string(saveFileName.toUtf8().data()); std::string fileNameStd = std::string(saveFileName.toUtf8().data());
ofstream out(fileNameStd, ios::out | ios::binary); std::ofstream out(fileNameStd, std::ios::out | std::ios::binary);
bool ok = false; bool ok = false;
if (out.good()) if (out.good())
{ {
@ -557,7 +580,7 @@ MainWindow::setRenderPath(QAction* action)
void void
MainWindow::generateNormals() MainWindow::generateNormals()
{ {
Model* model = m_modelView->model(); cmod::Model* model = m_modelView->model();
if (!model) if (!model)
{ {
return; return;
@ -597,11 +620,14 @@ MainWindow::generateNormals()
if (dialog.exec() == QDialog::Accepted) if (dialog.exec() == QDialog::Accepted)
{ {
double smoothAngle = smoothAngleEdit->text().toDouble(); float smoothAngle = smoothAngleEdit->text().toFloat();
double weldTolerance = toleranceEdit->text().toDouble(); double weldTolerance = toleranceEdit->text().toDouble();
bool weldVertices = true; bool weldVertices = true;
Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance); cmod::Model* newModel = GenerateModelNormals(*model,
celmath::degToRad(smoothAngle),
weldVertices,
weldTolerance);
if (!newModel) if (!newModel)
{ {
QMessageBox::warning(this, tr("Internal Error"), tr("Out of memory error during normal generation")); QMessageBox::warning(this, tr("Internal Error"), tr("Out of memory error during normal generation"));
@ -620,7 +646,7 @@ MainWindow::generateNormals()
void void
MainWindow::generateTangents() MainWindow::generateTangents()
{ {
Model* model = m_modelView->model(); cmod::Model* model = m_modelView->model();
if (!model) if (!model)
{ {
return; return;
@ -652,7 +678,7 @@ MainWindow::generateTangents()
double weldTolerance = toleranceEdit->text().toDouble(); double weldTolerance = toleranceEdit->text().toDouble();
bool weldVertices = true; bool weldVertices = true;
Model* newModel = new Model(); cmod::Model* newModel = new cmod::Model();
// Copy materials // Copy materials
for (unsigned int i = 0; model->getMaterial(i) != nullptr; i++) for (unsigned int i = 0; model->getMaterial(i) != nullptr; i++)
@ -662,13 +688,13 @@ MainWindow::generateTangents()
for (unsigned int i = 0; model->getMesh(i) != nullptr; i++) for (unsigned int i = 0; model->getMesh(i) != nullptr; i++)
{ {
Mesh* mesh = model->getMesh(i); cmod::Mesh* mesh = model->getMesh(i);
Mesh* newMesh = nullptr; cmod::Mesh* newMesh = nullptr;
newMesh = GenerateTangents(*mesh, weldVertices); newMesh = GenerateTangents(*mesh, weldVertices);
if (newMesh == nullptr) if (newMesh == nullptr)
{ {
cerr << "Error generating normals!\n"; std::cerr << "Error generating normals!\n";
// TODO: Clone the mesh and add it to the model // TODO: Clone the mesh and add it to the model
} }
else else
@ -687,7 +713,7 @@ MainWindow::generateTangents()
void void
MainWindow::uniquifyVertices() MainWindow::uniquifyVertices()
{ {
Model* model = m_modelView->model(); cmod::Model* model = m_modelView->model();
if (!model) if (!model)
{ {
return; return;
@ -695,7 +721,7 @@ MainWindow::uniquifyVertices()
for (unsigned int i = 0; model->getMesh(i) != nullptr; i++) for (unsigned int i = 0; model->getMesh(i) != nullptr; i++)
{ {
Mesh* mesh = model->getMesh(i); cmod::Mesh* mesh = model->getMesh(i);
UniquifyVertices(*mesh); UniquifyVertices(*mesh);
} }
@ -707,13 +733,13 @@ MainWindow::uniquifyVertices()
void void
MainWindow::mergeMeshes() MainWindow::mergeMeshes()
{ {
Model* model = m_modelView->model(); cmod::Model* model = m_modelView->model();
if (!model) if (!model)
{ {
return; return;
} }
Model* newModel = MergeModelMeshes(*model); cmod::Model* newModel = MergeModelMeshes(*model);
setModel(modelFileName(), newModel); setModel(modelFileName(), newModel);
} }
@ -728,9 +754,9 @@ MainWindow::updateSelectionInfo()
else else
{ {
m_materialWidget->setEnabled(true); m_materialWidget->setEnabled(true);
QSetIterator<Mesh::PrimitiveGroup*> iter(m_modelView->selection()); QSetIterator<cmod::PrimitiveGroup*> iter(m_modelView->selection());
Mesh::PrimitiveGroup* selectedGroup = iter.next(); cmod::PrimitiveGroup* selectedGroup = iter.next();
const Material* material = m_modelView->model()->getMaterial(selectedGroup->materialIndex); const cmod::Material* material = m_modelView->model()->getMaterial(selectedGroup->materialIndex);
if (material) if (material)
{ {
m_materialWidget->setMaterial(*material); m_materialWidget->setMaterial(*material);
@ -740,12 +766,12 @@ MainWindow::updateSelectionInfo()
void void
MainWindow::changeCurrentMaterial(const Material& material) MainWindow::changeCurrentMaterial(const cmod::Material& material)
{ {
if (!m_modelView->selection().isEmpty()) if (!m_modelView->selection().isEmpty())
{ {
QSetIterator<Mesh::PrimitiveGroup*> iter(m_modelView->selection()); QSetIterator<cmod::PrimitiveGroup*> iter(m_modelView->selection());
Mesh::PrimitiveGroup* selectedGroup = iter.next(); cmod::PrimitiveGroup* selectedGroup = iter.next();
m_modelView->setMaterial(selectedGroup->materialIndex, material); m_modelView->setMaterial(selectedGroup->materialIndex, material);
} }
} }

View File

@ -8,17 +8,26 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CMODVIEW_MAINWINDOW_H_ #pragma once
#define _CMODVIEW_MAINWINDOW_H_
#include "modelviewwidget.h"
#include "materialwidget.h"
#include <QMainWindow>
#include <QString>
#include <QLabel>
#include <QAction> #include <QAction>
#include <QCloseEvent>
#include <QEvent>
#include <QLabel>
#include <QMainWindow>
#include <QObject>
#include <QString>
class MaterialWidget;
class ModelViewWidget;
namespace cmod
{
class Material;
class Model;
}
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
@ -73,6 +82,3 @@ private:
QAction* m_saveAsAction; QAction* m_saveAsAction;
QAction* m_gl2Action; QAction* m_gl2Action;
}; };
#endif // _CMODVIEW_MAINWINDOW_H_

View File

@ -7,31 +7,36 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#include "glsupport.h"
#include "materialwidget.h"
#include "pathmanager.h"
#include <celmodel/material.h>
#include <QGridLayout>
#include <QPushButton>
#include <QColorDialog> #include <QColorDialog>
#include <QDir> #include <QDir>
#include <QGridLayout>
#include <QPushButton>
#include <QSet> #include <QSet>
#include <QStringList>
using namespace cmod; #include "pathmanager.h"
#include "materialwidget.h"
static QColor toQtColor(const Material::Color& color) namespace
{ {
return QColor((int) (color.red() * 255.99f), (int) (color.green() * 255.99f), (int) (color.blue() * 255.99f));
QColor toQtColor(const cmod::Color& color)
{
return QColor(static_cast<int>(color.red() * 255.99f),
static_cast<int>(color.green() * 255.99f),
static_cast<int>(color.blue() * 255.99f));
} }
static Material::Color fromQtColor(const QColor& color)
cmod::Color fromQtColor(const QColor& color)
{ {
return Material::Color(color.redF(), color.greenF(), color.blueF()); return cmod::Color(color.redF(), color.greenF(), color.blueF());
} }
// TODO: implement a copy constructor and assignment operator for materials // TODO: implement a copy constructor and assignment operator for materials
static void copyMaterial(Material& dest, const Material& src) void copyMaterial(cmod::Material& dest, const cmod::Material& src)
{ {
dest.diffuse = src.diffuse; dest.diffuse = src.diffuse;
dest.specular = src.specular; dest.specular = src.specular;
@ -43,7 +48,7 @@ static void copyMaterial(Material& dest, const Material& src)
} }
static void setWidgetColor(QLabel* widget, const Material::Color& color) static void setWidgetColor(QLabel* widget, const cmod::Color& color)
{ {
widget->setPalette(QPalette(toQtColor(color))); widget->setPalette(QPalette(toQtColor(color)));
widget->setAutoFillBackground(true); widget->setAutoFillBackground(true);
@ -51,6 +56,54 @@ static void setWidgetColor(QLabel* widget, const Material::Color& color)
} }
inline QString toQString(const wchar_t *s)
{
return QString::fromWCharArray(s);
}
inline QString toQString(const char *s)
{
return QString::fromLocal8Bit(s);
}
void selectComboBoxItem(QComboBox* combo, const QString &text)
{
int itemIndex = combo->findText(text);
if (itemIndex < 0)
{
combo->addItem(text, text);
itemIndex = combo->count() - 1;
}
combo->setCurrentIndex(itemIndex);
}
void selectComboBoxItem(QComboBox* combo, const fs::path &path)
{
selectComboBoxItem(combo, toQString(path.c_str()));
}
// Return a list of all texture filenames in the specified folder
QSet<QString> listTextures(QDir& dir)
{
QStringList filters;
filters << "*.png" << "*.jpg" << "*.dds" << "*.dxt5nm";
QStringList textureFileNames = dir.entryList(filters, QDir::Files);
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
return QSet<QString>(textureFileNames.begin(), textureFileNames.end());
#else
return QSet<QString>::fromList(textureFileNames);
#endif
}
} // end unnamed namespace
MaterialWidget::MaterialWidget(QWidget* parent) : MaterialWidget::MaterialWidget(QWidget* parent) :
QWidget(parent) QWidget(parent)
@ -131,40 +184,14 @@ MaterialWidget::MaterialWidget(QWidget* parent) :
connect(m_normalMap, SIGNAL(activated(int)), this, SLOT(changeMaterialParameters())); connect(m_normalMap, SIGNAL(activated(int)), this, SLOT(changeMaterialParameters()));
connect(m_emissiveMap, SIGNAL(activated(int)), this, SLOT(changeMaterialParameters())); connect(m_emissiveMap, SIGNAL(activated(int)), this, SLOT(changeMaterialParameters()));
setMaterial(Material()); setMaterial(cmod::Material());
this->setLayout(layout); this->setLayout(layout);
} }
inline QString toQString(const wchar_t *s)
{
return QString::fromWCharArray(s);
}
inline QString toQString(const char *s)
{
return QString::fromLocal8Bit(s);
}
static void selectComboBoxItem(QComboBox* combo, const QString &text)
{
int itemIndex = combo->findText(text);
if (itemIndex < 0)
{
combo->addItem(text, text);
itemIndex = combo->count() - 1;
}
combo->setCurrentIndex(itemIndex);
}
static void selectComboBoxItem(QComboBox* combo, const fs::path &path)
{
selectComboBoxItem(combo, toQString(path.c_str()));
}
void void
MaterialWidget::setMaterial(const Material& material) MaterialWidget::setMaterial(const cmod::Material& material)
{ {
copyMaterial(m_material, material); copyMaterial(m_material, material);
@ -174,20 +201,20 @@ MaterialWidget::setMaterial(const Material& material)
m_opacity->setText(QString::number(m_material.opacity)); m_opacity->setText(QString::number(m_material.opacity));
m_specularPower->setText(QString::number(m_material.specularPower)); m_specularPower->setText(QString::number(m_material.specularPower));
if (m_material.maps[Material::DiffuseMap] != InvalidResource) if (m_material.getMap(cmod::TextureSemantic::DiffuseMap) != InvalidResource)
selectComboBoxItem(m_baseTexture, GetPathManager()->getSource(m_material.maps[Material::DiffuseMap])); selectComboBoxItem(m_baseTexture, GetPathManager()->getSource(m_material.getMap(cmod::TextureSemantic::DiffuseMap)));
else else
m_baseTexture->setCurrentIndex(0); m_baseTexture->setCurrentIndex(0);
if (m_material.maps[Material::SpecularMap] != InvalidResource) if (m_material.getMap(cmod::TextureSemantic::SpecularMap) != InvalidResource)
selectComboBoxItem(m_specularMap, GetPathManager()->getSource(m_material.maps[Material::SpecularMap])); selectComboBoxItem(m_specularMap, GetPathManager()->getSource(m_material.getMap(cmod::TextureSemantic::SpecularMap)));
else else
m_specularMap->setCurrentIndex(0); m_specularMap->setCurrentIndex(0);
if (m_material.maps[Material::EmissiveMap] != InvalidResource) if (m_material.getMap(cmod::TextureSemantic::EmissiveMap) != InvalidResource)
selectComboBoxItem(m_emissiveMap, GetPathManager()->getSource(m_material.maps[Material::EmissiveMap])); selectComboBoxItem(m_emissiveMap, GetPathManager()->getSource(m_material.getMap(cmod::TextureSemantic::EmissiveMap)));
else else
m_emissiveMap->setCurrentIndex(0); m_emissiveMap->setCurrentIndex(0);
if (m_material.maps[Material::NormalMap] != InvalidResource) if (m_material.getMap(cmod::TextureSemantic::NormalMap) != InvalidResource)
selectComboBoxItem(m_normalMap, GetPathManager()->getSource(m_material.maps[Material::NormalMap])); selectComboBoxItem(m_normalMap, GetPathManager()->getSource(m_material.getMap(cmod::TextureSemantic::NormalMap)));
else else
m_normalMap->setCurrentIndex(0); m_normalMap->setCurrentIndex(0);
@ -279,48 +306,34 @@ MaterialWidget::changeMaterialParameters()
m_material.opacity = m_opacity->text().toFloat(); m_material.opacity = m_opacity->text().toFloat();
m_material.specularPower = m_specularPower->text().toFloat(); m_material.specularPower = m_specularPower->text().toFloat();
m_material.maps[Material::DiffuseMap] = InvalidResource; m_material.setMap(cmod::TextureSemantic::DiffuseMap, InvalidResource);
if (!m_baseTexture->itemData(m_baseTexture->currentIndex()).isNull()) if (!m_baseTexture->itemData(m_baseTexture->currentIndex()).isNull())
{ {
m_material.maps[Material::DiffuseMap] = GetPathManager()->getHandle(m_baseTexture->currentText().toStdString()); m_material.setMap(cmod::TextureSemantic::DiffuseMap, GetPathManager()->getHandle(m_baseTexture->currentText().toStdString()));
} }
m_material.maps[Material::SpecularMap] = InvalidResource; m_material.setMap(cmod::TextureSemantic::SpecularMap, InvalidResource);
if (!m_specularMap->itemData(m_specularMap->currentIndex()).isNull()) if (!m_specularMap->itemData(m_specularMap->currentIndex()).isNull())
{ {
m_material.maps[Material::SpecularMap] = GetPathManager()->getHandle(m_specularMap->currentText().toStdString()); m_material.setMap(cmod::TextureSemantic::SpecularMap, GetPathManager()->getHandle(m_specularMap->currentText().toStdString()));
} }
m_material.maps[Material::NormalMap] = InvalidResource; m_material.setMap(cmod::TextureSemantic::NormalMap, InvalidResource);
if (!m_normalMap->itemData(m_normalMap->currentIndex()).isNull()) if (!m_normalMap->itemData(m_normalMap->currentIndex()).isNull())
{ {
m_material.maps[Material::NormalMap] = GetPathManager()->getHandle(m_normalMap->currentText().toStdString()); m_material.setMap(cmod::TextureSemantic::NormalMap, GetPathManager()->getHandle(m_normalMap->currentText().toStdString()));
} }
m_material.maps[Material::EmissiveMap] = InvalidResource; m_material.setMap(cmod::TextureSemantic::EmissiveMap, InvalidResource);
if (!m_emissiveMap->itemData(m_emissiveMap->currentIndex()).isNull()) if (!m_emissiveMap->itemData(m_emissiveMap->currentIndex()).isNull())
{ {
m_material.maps[Material::EmissiveMap] = GetPathManager()->getHandle(m_emissiveMap->currentText().toStdString()); m_material.setMap(cmod::TextureSemantic::EmissiveMap, GetPathManager()->getHandle(m_emissiveMap->currentText().toStdString()));
} }
emit materialEdited(m_material); emit materialEdited(m_material);
} }
// Return a list of all texture filenames in the specified folder
static QSet<QString> listTextures(QDir& dir)
{
QStringList filters;
filters << "*.png" << "*.jpg" << "*.dds" << "*.dxt5nm";
QStringList textureFileNames = dir.entryList(filters, QDir::Files);
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
return QSet<QString>(textureFileNames.begin(), textureFileNames.end());
#else
return QSet<QString>::fromList(textureFileNames);
#endif
}
void void
MaterialWidget::setTextureSearchPath(const QString& path) MaterialWidget::setTextureSearchPath(const QString& path)
{ {

View File

@ -7,14 +7,17 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CMODVIEW_MATERIAL_WIDGET_H_ #pragma once
#define _CMODVIEW_MATERIAL_WIDGET_H_
#include <celmodel/material.h> #include <QColor>
#include <QWidget> #include <QComboBox>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QComboBox> #include <QObject>
#include <QString>
#include <QWidget>
#include <celmodel/material.h>
class MaterialWidget : public QWidget class MaterialWidget : public QWidget
@ -66,5 +69,3 @@ private:
cmod::Material m_material; cmod::Material m_material;
}; };
#endif // _CMODVIEW_MATERIAL_WIDGET_H_

View File

@ -8,30 +8,37 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#include "glsupport.h"
#include "modelviewwidget.h"
#include "glframebuffer.h"
#include "pathmanager.h"
#include <QFileInfo>
#include <QGLWidget>
#include <QMouseEvent>
#include <QTextStream>
#include <Eigen/LU>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <cstddef>
#include <iostream> #include <iostream>
using namespace celestia; #include <QFileInfo>
using namespace cmod; #include <QIODevice>
using namespace Eigen; #include <QMap>
#include <QPixmap>
#include <QTextStream>
#include <celmath/mathlib.h>
#include <celmodel/model.h>
#include "pathmanager.h"
#include "glframebuffer.h"
#include "glshader.h"
#include "glsupport.h"
#include "modelviewwidget.h"
#define DEBUG_SHADOWS 0 #define DEBUG_SHADOWS 0
static const float VIEWPORT_FOV = 45.0; namespace
//static const double PI = 3.1415926535897932; {
static const int ShadowBufferSize = 1024; constexpr float VIEWPORT_FOV = 45.0;
static const int ShadowSampleKernelWidth = 2;
constexpr int ShadowBufferSize = 1024;
constexpr int ShadowSampleKernelWidth = 2;
enum { enum {
@ -45,11 +52,59 @@ inline QString toQString(const wchar_t *s)
return QString::fromWCharArray(s); return QString::fromWCharArray(s);
} }
inline QString toQString(const char *s) inline QString toQString(const char *s)
{ {
return QString::fromLocal8Bit(s); return QString::fromLocal8Bit(s);
} }
// Calculate the matrix used to render the model from the
// perspective of the light.
Eigen::Matrix4f
directionalLightMatrix(const Eigen::Vector3f& lightDirection)
{
Eigen::Vector3f viewDir = lightDirection;
Eigen::Vector3f upDir = viewDir.unitOrthogonal();
Eigen::Vector3f rightDir = upDir.cross(viewDir);
Eigen::Matrix4f m = Eigen::Matrix4f::Identity();
m.row(0).head(3) = rightDir;
m.row(1).head(3) = upDir;
m.row(2).head(3) = viewDir;
return m;
}
Eigen::Matrix4f
parallelProjectionMatrix(float left, float right, float bottom, float top, float zNear, float zFar)
{
// Duplicates OpenGL's glOrtho() function
Eigen::Matrix4f m = Eigen::Matrix4f::Identity();
m.diagonal() = Eigen::Vector4f(2.0f / (right - left),
2.0f / (top - bottom),
-2.0f / (zFar - zNear),
1.0f);
m.col(3) = Eigen::Vector4f(-(right + left) / (right - left),
-(top + bottom) / (top - bottom),
-(zFar + zNear) / (zFar - zNear),
1.0f);
return m;
}
Eigen::Matrix4f
shadowProjectionMatrix(float objectRadius)
{
return parallelProjectionMatrix(-objectRadius, objectRadius,
-objectRadius, objectRadius,
-objectRadius, objectRadius);
}
} // end unnamed namespace
class MaterialLibrary class MaterialLibrary
{ {
public: public:
@ -141,7 +196,7 @@ private:
ShaderKey ShaderKey
ShaderKey::Create(const Material* material, const LightingEnvironment* lighting, const Mesh::VertexDescription* vertexDesc) ShaderKey::Create(const cmod::Material* material, const LightingEnvironment* lighting, const cmod::VertexDescription* vertexDesc)
{ {
// Compute the shader key for a particular material and lighting setup // Compute the shader key for a particular material and lighting setup
unsigned int info = 0; unsigned int info = 0;
@ -150,8 +205,8 @@ ShaderKey::Create(const Material* material, const LightingEnvironment* lighting,
bool hasTexCoords = false; bool hasTexCoords = false;
if (vertexDesc) if (vertexDesc)
{ {
hasTangents = vertexDesc->getAttribute(Mesh::Tangent).format == Mesh::Float3; hasTangents = vertexDesc->getAttribute(cmod::VertexAttributeSemantic::Tangent).format == cmod::VertexAttributeFormat::Float3;
hasTexCoords = vertexDesc->getAttribute(Mesh::Texture0).format == Mesh::Float2; hasTexCoords = vertexDesc->getAttribute(cmod::VertexAttributeSemantic::Texture0).format == cmod::VertexAttributeFormat::Float2;
} }
// Bits 0-3 are the number of light sources // Bits 0-3 are the number of light sources
@ -172,30 +227,30 @@ ShaderKey::Create(const Material* material, const LightingEnvironment* lighting,
// Bits 8-15 are texture map info // Bits 8-15 are texture map info
if (hasTexCoords) if (hasTexCoords)
{ {
if (material->maps[Material::DiffuseMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::DiffuseMap) != InvalidResource)
{ {
info |= DiffuseMapMask; info |= DiffuseMapMask;
} }
if (material->maps[Material::SpecularMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::SpecularMap) != InvalidResource)
{ {
info |= SpecularMapMask; info |= SpecularMapMask;
} }
if (material->maps[Material::NormalMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::NormalMap) != InvalidResource)
{ {
info |= NormalMapMask; info |= NormalMapMask;
} }
if (material->maps[Material::EmissiveMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::EmissiveMap) != InvalidResource)
{ {
info |= EmissiveMapMask; info |= EmissiveMapMask;
} }
// Bit 16 is set if the normal map is compressed // Bit 16 is set if the normal map is compressed
if (material->maps[Material::NormalMap] != InvalidResource && hasTangents) if (material->getMap(cmod::TextureSemantic::NormalMap) != InvalidResource && hasTangents)
{ {
if (GetPathManager()->getSource(material->maps[Material::NormalMap]).extension() == ".dxt5nm") if (GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::NormalMap)).extension() == ".dxt5nm")
{ {
info |= CompressedNormalMapMask; info |= CompressedNormalMapMask;
} }
@ -206,60 +261,16 @@ ShaderKey::Create(const Material* material, const LightingEnvironment* lighting,
} }
// Calculate the matrix used to render the model from the
// perspective of the light.
static
Matrix4f directionalLightMatrix(const Vector3f& lightDirection)
{
Vector3f viewDir = lightDirection;
Vector3f upDir = viewDir.unitOrthogonal();
Vector3f rightDir = upDir.cross(viewDir);
Matrix4f m = Matrix4f::Identity();
m.row(0).head(3) = rightDir;
m.row(1).head(3) = upDir;
m.row(2).head(3) = viewDir;
return m;
}
static
Matrix4f parallelProjectionMatrix(float left, float right, float bottom, float top, float zNear, float zFar)
{
// Duplicates OpenGL's glOrtho() function
Matrix4f m = Matrix4f::Identity();
m.diagonal() = Vector4f(2.0f / (right - left),
2.0f / (top - bottom),
-2.0f / (zFar - zNear),
1.0f);
m.col(3) = Vector4f(-(right + left) / (right - left),
-(top + bottom) / (top - bottom),
-(zFar + zNear) / (zFar - zNear),
1.0f);
return m;
}
static
Matrix4f shadowProjectionMatrix(float objectRadius)
{
return parallelProjectionMatrix(-objectRadius, objectRadius,
-objectRadius, objectRadius,
-objectRadius, objectRadius);
}
ModelViewWidget::ModelViewWidget(QWidget *parent) : ModelViewWidget::ModelViewWidget(QWidget *parent) :
QGLWidget(parent), QGLWidget(parent),
m_model(nullptr), m_model(nullptr),
m_modelBoundingRadius(1.0), m_modelBoundingRadius(1.0),
m_cameraPosition(Vector3d::Zero()), m_cameraPosition(Eigen::Vector3d::Zero()),
m_cameraOrientation(Quaterniond::Identity()), m_cameraOrientation(Eigen::Quaterniond::Identity()),
m_renderStyle(NormalStyle), m_renderStyle(NormalStyle),
m_renderPath(FixedFunctionPath), m_renderPath(FixedFunctionPath),
m_materialLibrary(nullptr), m_materialLibrary(nullptr),
m_lightOrientation(Quaterniond::Identity()), m_lightOrientation(Eigen::Quaterniond::Identity()),
m_lightingEnabled(true), m_lightingEnabled(true),
m_ambientLightEnabled(true), m_ambientLightEnabled(true),
m_shadowsEnabled(false) m_shadowsEnabled(false)
@ -294,26 +305,26 @@ ModelViewWidget::setModel(cmod::Model* model, const QString& modelDirPath)
{ {
for (unsigned int i = 0; i < m_model->getMaterialCount(); ++i) for (unsigned int i = 0; i < m_model->getMaterialCount(); ++i)
{ {
const Material* material = m_model->getMaterial(i); const cmod::Material* material = m_model->getMaterial(i);
if (material->maps[Material::DiffuseMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::DiffuseMap) != InvalidResource)
{ {
m_materialLibrary->getTexture( m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::DiffuseMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::DiffuseMap)).c_str()));
} }
if (material->maps[Material::NormalMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::NormalMap) != InvalidResource)
{ {
m_materialLibrary->getTexture( m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::NormalMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::NormalMap)).c_str()));
} }
if (material->maps[Material::SpecularMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::SpecularMap) != InvalidResource)
{ {
m_materialLibrary->getTexture( m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::SpecularMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::SpecularMap)).c_str()));
} }
if (material->maps[Material::EmissiveMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::EmissiveMap) != InvalidResource)
{ {
m_materialLibrary->getTexture( m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::EmissiveMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::EmissiveMap)).c_str()));
} }
} }
} }
@ -327,7 +338,7 @@ ModelViewWidget::setModel(cmod::Model* model, const QString& modelDirPath)
void void
ModelViewWidget::resetCamera() ModelViewWidget::resetCamera()
{ {
AlignedBox<float, 3> bbox; Eigen::AlignedBox<float, 3> bbox;
if (m_model != nullptr) if (m_model != nullptr)
{ {
for (unsigned int i = 0; i < m_model->getMeshCount(); ++i) for (unsigned int i = 0; i < m_model->getMeshCount(); ++i)
@ -337,8 +348,8 @@ ModelViewWidget::resetCamera()
} }
m_modelBoundingRadius = std::max(bbox.max().norm(), bbox.min().norm()); m_modelBoundingRadius = std::max(bbox.max().norm(), bbox.min().norm());
m_cameraPosition = m_modelBoundingRadius * Vector3d::UnitZ() * 2.0; m_cameraPosition = m_modelBoundingRadius * Eigen::Vector3d::UnitZ() * 2.0;
m_cameraOrientation = Quaterniond::Identity(); m_cameraOrientation = Eigen::Quaterniond::Identity();
} }
@ -378,9 +389,9 @@ ModelViewWidget::mouseReleaseEvent(QMouseEvent* event)
int moveDistance = (event->pos() - m_mouseDownPosition).manhattanLength(); int moveDistance = (event->pos() - m_mouseDownPosition).manhattanLength();
if (moveDistance < 3) if (moveDistance < 3)
{ {
float x = (float) event->pos().x() / (float) size().width() * 2.0f - 1.0f; float x = static_cast<float>(event->pos().x()) / static_cast<float>(size().width()) * 2.0f - 1.0f;
float y = (float) event->pos().y() / (float) size().height() * -2.0f + 1.0f; float y = static_cast<float>(event->pos().y()) / static_cast<float>(size().height()) * -2.0f + 1.0f;
select(Vector2f(x, y)); select(Eigen::Vector2f(x, y));
} }
} }
@ -412,20 +423,20 @@ ModelViewWidget::mouseMoveEvent(QMouseEvent *event)
int dx = event->x() - m_lastMousePosition.x(); int dx = event->x() - m_lastMousePosition.x();
int dy = event->y() - m_lastMousePosition.y(); int dy = event->y() - m_lastMousePosition.y();
double xrotation = (double) dy / 100.0; double xrotation = static_cast<double>(dy) / 100.0;
double yrotation = (double) dx / 100.0; double yrotation = static_cast<double>(dx) / 100.0;
Quaterniond q = AngleAxis<double>(-xrotation, Vector3d::UnitX()) * Eigen::Quaterniond q = Eigen::AngleAxis<double>(-xrotation, Eigen::Vector3d::UnitX()) *
AngleAxis<double>(-yrotation, Vector3d::UnitY()); Eigen::AngleAxis<double>(-yrotation, Eigen::Vector3d::UnitY());
if (rotateLights) if (rotateLights)
{ {
Quaterniond r = m_lightOrientation * q * m_lightOrientation.conjugate(); Eigen::Quaterniond r = m_lightOrientation * q * m_lightOrientation.conjugate();
r.normalize(); r.normalize();
m_lightOrientation = r * m_lightOrientation; m_lightOrientation = r * m_lightOrientation;
} }
else if (rotateCamera) else if (rotateCamera)
{ {
Quaterniond r = m_cameraOrientation * q * m_cameraOrientation.conjugate(); Eigen::Quaterniond r = m_cameraOrientation * q * m_cameraOrientation.conjugate();
r.normalize(); // guard against accumulating rounding errors r.normalize(); // guard against accumulating rounding errors
m_cameraPosition = r * m_cameraPosition; m_cameraPosition = r * m_cameraPosition;
@ -461,22 +472,22 @@ ModelViewWidget::wheelEvent(QWheelEvent* event)
void void
ModelViewWidget::select(const Vector2f& viewportPoint) ModelViewWidget::select(const Eigen::Vector2f& viewportPoint)
{ {
if (!m_model) if (!m_model)
{ {
return; return;
} }
float aspectRatio = (float) size().width() / (float) size().height(); float aspectRatio = static_cast<float>(size().width()) / static_cast<float>(size().height());
auto fovRad = float(VIEWPORT_FOV * PI / 180.0f); auto fovRad = celmath::degToRad(VIEWPORT_FOV);
float h = (float) tan(fovRad / 2.0f); float h = std::tan(fovRad / 2.0f);
Vector3d direction(h * aspectRatio * viewportPoint.x(), h * viewportPoint.y(), -1.0f); Eigen::Vector3d direction(h * aspectRatio * viewportPoint.x(), h * viewportPoint.y(), -1.0f);
direction.normalize(); direction.normalize();
Vector3d origin = Vector3d::Zero(); Eigen::Vector3d origin = Eigen::Vector3d::Zero();
Affine3d camera(cameraTransform().inverse()); Eigen::Affine3d camera(cameraTransform().inverse());
Mesh::PickResult pickResult; cmod::Mesh::PickResult pickResult;
bool hit = m_model->pick(camera * origin, camera.linear() * direction, &pickResult); bool hit = m_model->pick(camera * origin, camera.linear() * direction, &pickResult);
if (hit) if (hit)
{ {
@ -494,10 +505,10 @@ ModelViewWidget::select(const Vector2f& viewportPoint)
} }
Affine3d Eigen::Affine3d
ModelViewWidget::cameraTransform() const ModelViewWidget::cameraTransform() const
{ {
Affine3d t(m_cameraOrientation.conjugate()); Eigen::Affine3d t(m_cameraOrientation.conjugate());
t.translate(-m_cameraPosition); t.translate(-m_cameraPosition);
return t; return t;
} }
@ -513,17 +524,17 @@ ModelViewWidget::setMaterial(unsigned int index, const cmod::Material& material)
// Copy material parameters // Copy material parameters
// TODO: eliminate const cast when Model::setMaterial() is implemented // TODO: eliminate const cast when Model::setMaterial() is implemented
Material* modelMaterial = const_cast<Material*>(m_model->getMaterial(index)); cmod::Material* modelMaterial = const_cast<cmod::Material*>(m_model->getMaterial(index));
modelMaterial->diffuse = material.diffuse; modelMaterial->diffuse = material.diffuse;
modelMaterial->specular = material.specular; modelMaterial->specular = material.specular;
modelMaterial->emissive = material.emissive; modelMaterial->emissive = material.emissive;
modelMaterial->opacity = material.opacity; modelMaterial->opacity = material.opacity;
modelMaterial->specularPower = material.specularPower; modelMaterial->specularPower = material.specularPower;
modelMaterial->maps[Material::DiffuseMap] = material.maps[Material::DiffuseMap]; modelMaterial->setMap(cmod::TextureSemantic::DiffuseMap, material.getMap(cmod::TextureSemantic::DiffuseMap));
modelMaterial->maps[Material::SpecularMap] = material.maps[Material::SpecularMap]; modelMaterial->setMap(cmod::TextureSemantic::SpecularMap, material.getMap(cmod::TextureSemantic::SpecularMap));
modelMaterial->maps[Material::NormalMap] = material.maps[Material::NormalMap]; modelMaterial->setMap(cmod::TextureSemantic::NormalMap, material.getMap(cmod::TextureSemantic::NormalMap));
modelMaterial->maps[Material::EmissiveMap] = material.maps[Material::EmissiveMap]; modelMaterial->setMap(cmod::TextureSemantic::EmissiveMap, material.getMap(cmod::TextureSemantic::EmissiveMap));
update(); update();
} }
@ -539,7 +550,7 @@ ModelViewWidget::setBackgroundColor(const QColor& color)
void void
ModelViewWidget::initializeGL() ModelViewWidget::initializeGL()
{ {
gl::init(); celestia::gl::init();
emit contextCreated(); emit contextCreated();
} }
@ -550,8 +561,8 @@ ModelViewWidget::paintGL()
// Generate the shadow buffers for each light source // Generate the shadow buffers for each light source
if (m_shadowsEnabled && m_shadowBuffers.size() > 0) if (m_shadowsEnabled && m_shadowBuffers.size() > 0)
{ {
Material defaultMaterial; cmod::Material defaultMaterial;
defaultMaterial.diffuse = Material::Color(1.0f, 1.0f, 1.0f); defaultMaterial.diffuse = cmod::Color(1.0f, 1.0f, 1.0f);
LightingEnvironment lightingOff; LightingEnvironment lightingOff;
bindMaterial(&defaultMaterial, &lightingOff, nullptr); bindMaterial(&defaultMaterial, &lightingOff, nullptr);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
@ -588,7 +599,7 @@ ModelViewWidget::paintGL()
{ {
ambientLightLevel = 0.2f; ambientLightLevel = 0.2f;
} }
Vector4f ambientLight = Vector4f::Constant(ambientLightLevel); Eigen::Vector4f ambientLight = Eigen::Vector4f::Constant(ambientLightLevel);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight.data()); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight.data());
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT); glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);
@ -603,10 +614,10 @@ ModelViewWidget::paintGL()
GLenum glLight = GL_LIGHT0 + lightIndex; GLenum glLight = GL_LIGHT0 + lightIndex;
lightIndex++; lightIndex++;
Vector3d direction = m_lightOrientation * lightSource.direction; Eigen::Vector3d direction = m_lightOrientation * lightSource.direction;
Vector4f lightColor = Vector4f::Zero(); Eigen::Vector4f lightColor = Eigen::Vector4f::Zero();
lightColor.head(3) = lightSource.color * lightSource.intensity; lightColor.head(3) = lightSource.color * lightSource.intensity;
Vector4f lightPosition = Vector4f::Zero(); Eigen::Vector4f lightPosition = Eigen::Vector4f::Zero();
lightPosition.head(3) = direction.cast<float>(); lightPosition.head(3) = direction.cast<float>();
glEnable(glLight); glEnable(glLight);
@ -690,7 +701,7 @@ ModelViewWidget::resizeGL(int width, int height)
} }
static GLenum GLComponentTypes[Mesh::FormatMax] = static GLenum GLComponentTypes[static_cast<std::size_t>(cmod::VertexAttributeFormat::FormatMax)] =
{ {
GL_FLOAT, // Float1 GL_FLOAT, // Float1
GL_FLOAT, // Float2 GL_FLOAT, // Float2
@ -699,7 +710,7 @@ static GLenum GLComponentTypes[Mesh::FormatMax] =
GL_UNSIGNED_BYTE, // UByte4 GL_UNSIGNED_BYTE, // UByte4
}; };
static int GLComponentCounts[Mesh::FormatMax] = static int GLComponentCounts[static_cast<std::size_t>(cmod::VertexAttributeFormat::FormatMax)] =
{ {
1, // Float1 1, // Float1
2, // Float2 2, // Float2
@ -710,16 +721,16 @@ static int GLComponentCounts[Mesh::FormatMax] =
static void static void
setVertexArrays(const Mesh::VertexDescription& desc, const void* vertexData) setVertexArrays(const cmod::VertexDescription& desc, const void* vertexData)
{ {
const Mesh::VertexAttribute& position = desc.getAttribute(Mesh::Position); const cmod::VertexAttribute& position = desc.getAttribute(cmod::VertexAttributeSemantic::Position);
const Mesh::VertexAttribute& normal = desc.getAttribute(Mesh::Normal); const cmod::VertexAttribute& normal = desc.getAttribute(cmod::VertexAttributeSemantic::Normal);
const Mesh::VertexAttribute& color0 = desc.getAttribute(Mesh::Color0); const cmod::VertexAttribute& color0 = desc.getAttribute(cmod::VertexAttributeSemantic::Color0);
const Mesh::VertexAttribute& texCoord0 = desc.getAttribute(Mesh::Texture0); const cmod::VertexAttribute& texCoord0 = desc.getAttribute(cmod::VertexAttributeSemantic::Texture0);
const Mesh::VertexAttribute& tangent = desc.getAttribute(Mesh::Tangent); const cmod::VertexAttribute& tangent = desc.getAttribute(cmod::VertexAttributeSemantic::Tangent);
// Can't render anything unless we have positions // Can't render anything unless we have positions
if (position.format != Mesh::Float3) if (position.format != cmod::VertexAttributeFormat::Float3)
return; return;
// Set up the vertex arrays // Set up the vertex arrays
@ -730,9 +741,9 @@ setVertexArrays(const Mesh::VertexDescription& desc, const void* vertexData)
// Set up the normal array // Set up the normal array
switch (normal.format) switch (normal.format)
{ {
case Mesh::Float3: case cmod::VertexAttributeFormat::Float3:
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GLComponentTypes[(int) normal.format], glNormalPointer(GLComponentTypes[static_cast<std::size_t>(normal.format)],
desc.stride, desc.stride,
reinterpret_cast<const char*>(vertexData) + normal.offset); reinterpret_cast<const char*>(vertexData) + normal.offset);
break; break;
@ -744,12 +755,12 @@ setVertexArrays(const Mesh::VertexDescription& desc, const void* vertexData)
// Set up the color array // Set up the color array
switch (color0.format) switch (color0.format)
{ {
case Mesh::Float3: case cmod::VertexAttributeFormat::Float3:
case Mesh::Float4: case cmod::VertexAttributeFormat::Float4:
case Mesh::UByte4: case cmod::VertexAttributeFormat::UByte4:
glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(GLComponentCounts[color0.format], glColorPointer(GLComponentCounts[static_cast<std::size_t>(color0.format)],
GLComponentTypes[color0.format], GLComponentTypes[static_cast<std::size_t>(color0.format)],
desc.stride, desc.stride,
reinterpret_cast<const char*>(vertexData) + color0.offset); reinterpret_cast<const char*>(vertexData) + color0.offset);
break; break;
@ -761,13 +772,13 @@ setVertexArrays(const Mesh::VertexDescription& desc, const void* vertexData)
// Set up the texture coordinate array // Set up the texture coordinate array
switch (texCoord0.format) switch (texCoord0.format)
{ {
case Mesh::Float1: case cmod::VertexAttributeFormat::Float1:
case Mesh::Float2: case cmod::VertexAttributeFormat::Float2:
case Mesh::Float3: case cmod::VertexAttributeFormat::Float3:
case Mesh::Float4: case cmod::VertexAttributeFormat::Float4:
glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(GLComponentCounts[(int) texCoord0.format], glTexCoordPointer(GLComponentCounts[static_cast<std::size_t>(texCoord0.format)],
GLComponentTypes[(int) texCoord0.format], GLComponentTypes[static_cast<std::size_t>(texCoord0.format)],
desc.stride, desc.stride,
reinterpret_cast<const char*>(vertexData) + texCoord0.offset); reinterpret_cast<const char*>(vertexData) + texCoord0.offset);
break; break;
@ -778,11 +789,11 @@ setVertexArrays(const Mesh::VertexDescription& desc, const void* vertexData)
switch (tangent.format) switch (tangent.format)
{ {
case Mesh::Float3: case cmod::VertexAttributeFormat::Float3:
glEnableVertexAttribArrayARB(TangentAttributeIndex); glEnableVertexAttribArrayARB(TangentAttributeIndex);
glVertexAttribPointerARB(TangentAttributeIndex, glVertexAttribPointerARB(TangentAttributeIndex,
GLComponentCounts[(int) tangent.format], GLComponentCounts[static_cast<std::size_t>(tangent.format)],
GLComponentTypes[(int) tangent.format], GLComponentTypes[static_cast<std::size_t>(tangent.format)],
GL_FALSE, GL_FALSE,
desc.stride, desc.stride,
reinterpret_cast<const char*>(vertexData) + tangent.offset); reinterpret_cast<const char*>(vertexData) + tangent.offset);
@ -797,12 +808,12 @@ setVertexArrays(const Mesh::VertexDescription& desc, const void* vertexData)
// Set just the vertex pointer // Set just the vertex pointer
void void
setVertexPointer(const Mesh::VertexDescription& desc, const void* vertexData) setVertexPointer(const cmod::VertexDescription& desc, const void* vertexData)
{ {
const Mesh::VertexAttribute& position = desc.getAttribute(Mesh::Position); const cmod::VertexAttribute& position = desc.getAttribute(cmod::VertexAttributeSemantic::Position);
// Can't render anything unless we have positions // Can't render anything unless we have positions
if (position.format != Mesh::Float3) if (position.format != cmod::VertexAttributeFormat::Float3)
return; return;
// Set up the vertex arrays // Set up the vertex arrays
@ -818,21 +829,21 @@ setVertexPointer(const Mesh::VertexDescription& desc, const void* vertexData)
static GLenum static GLenum
getGLMode(Mesh::PrimitiveGroupType primitive) getGLMode(cmod::PrimitiveGroupType primitive)
{ {
switch (primitive) switch (primitive)
{ {
case Mesh::TriList: case cmod::PrimitiveGroupType::TriList:
return GL_TRIANGLES; return GL_TRIANGLES;
case Mesh::TriStrip: case cmod::PrimitiveGroupType::TriStrip:
return GL_TRIANGLE_STRIP; return GL_TRIANGLE_STRIP;
case Mesh::TriFan: case cmod::PrimitiveGroupType::TriFan:
return GL_TRIANGLE_FAN; return GL_TRIANGLE_FAN;
case Mesh::LineList: case cmod::PrimitiveGroupType::LineList:
return GL_LINES; return GL_LINES;
case Mesh::LineStrip: case cmod::PrimitiveGroupType::LineStrip:
return GL_LINE_STRIP; return GL_LINE_STRIP;
case Mesh::PointList: case cmod::PrimitiveGroupType::PointList:
return GL_POINTS; return GL_POINTS;
default: default:
return GL_POINTS; return GL_POINTS;
@ -872,7 +883,7 @@ ModelViewWidget::setAmbientLight(bool enable)
void void
ModelViewWidget::setShadows(bool enable) ModelViewWidget::setShadows(bool enable)
{ {
if (!gl::EXT_framebuffer_object) if (!celestia::gl::EXT_framebuffer_object)
{ {
return; return;
} }
@ -897,9 +908,9 @@ ModelViewWidget::setShadows(bool enable)
void void
ModelViewWidget::bindMaterial(const Material* material, ModelViewWidget::bindMaterial(const cmod::Material* material,
const LightingEnvironment* lighting, const LightingEnvironment* lighting,
const Mesh::VertexDescription* vertexDesc) const cmod::VertexDescription* vertexDesc)
{ {
GLShaderProgram* shader = nullptr; GLShaderProgram* shader = nullptr;
@ -930,15 +941,15 @@ ModelViewWidget::bindMaterial(const Material* material,
shader->setUniformValue("modelView", cameraTransform().matrix().cast<float>()); shader->setUniformValue("modelView", cameraTransform().matrix().cast<float>());
shader->setUniformValue("diffuseColor", Vector3f(material->diffuse.red(), material->diffuse.green(), material->diffuse.blue())); shader->setUniformValue("diffuseColor", Eigen::Vector3f(material->diffuse.red(), material->diffuse.green(), material->diffuse.blue()));
shader->setUniformValue("specularColor", Vector3f(material->specular.red(), material->specular.green(), material->specular.blue())); shader->setUniformValue("specularColor", Eigen::Vector3f(material->specular.red(), material->specular.green(), material->specular.blue()));
shader->setUniformValue("opacity", material->opacity); shader->setUniformValue("opacity", material->opacity);
shader->setUniformValue("specularPower", material->specularPower); shader->setUniformValue("specularPower", material->specularPower);
if (shaderKey.hasDiffuseMap()) if (shaderKey.hasDiffuseMap())
{ {
GLuint diffuseMapId = m_materialLibrary->getTexture( GLuint diffuseMapId = m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::DiffuseMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::DiffuseMap)).c_str()));
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, diffuseMapId); glBindTexture(GL_TEXTURE_2D, diffuseMapId);
shader->setSampler("diffuseMap", 0); shader->setSampler("diffuseMap", 0);
@ -947,7 +958,7 @@ ModelViewWidget::bindMaterial(const Material* material,
if (shaderKey.hasNormalMap()) if (shaderKey.hasNormalMap())
{ {
GLuint normalMapId = m_materialLibrary->getTexture( GLuint normalMapId = m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::NormalMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::NormalMap)).c_str()));
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, normalMapId); glBindTexture(GL_TEXTURE_2D, normalMapId);
@ -958,7 +969,7 @@ ModelViewWidget::bindMaterial(const Material* material,
if (shaderKey.hasSpecularMap()) if (shaderKey.hasSpecularMap())
{ {
GLuint specularMapId = m_materialLibrary->getTexture( GLuint specularMapId = m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::SpecularMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::SpecularMap)).c_str()));
glActiveTexture(GL_TEXTURE2); glActiveTexture(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, specularMapId); glBindTexture(GL_TEXTURE_2D, specularMapId);
@ -969,7 +980,7 @@ ModelViewWidget::bindMaterial(const Material* material,
if (shaderKey.hasEmissiveMap()) if (shaderKey.hasEmissiveMap())
{ {
GLuint emissiveMapId = m_materialLibrary->getTexture( GLuint emissiveMapId = m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::EmissiveMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::EmissiveMap)).c_str()));
glActiveTexture(GL_TEXTURE3); glActiveTexture(GL_TEXTURE3);
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, emissiveMapId); glBindTexture(GL_TEXTURE_2D, emissiveMapId);
@ -978,15 +989,15 @@ ModelViewWidget::bindMaterial(const Material* material,
} }
unsigned int lightIndex = 0; unsigned int lightIndex = 0;
Matrix3d lightMatrix = m_lightOrientation.toRotationMatrix(); Eigen::Matrix3d lightMatrix = m_lightOrientation.toRotationMatrix();
Vector3f lightDirections[8]; Eigen::Vector3f lightDirections[8];
Vector3f lightColors[8]; Eigen::Vector3f lightColors[8];
foreach (LightSource lightSource, m_lightSources) foreach (LightSource lightSource, m_lightSources)
{ {
Vector3d direction = lightMatrix * lightSource.direction; Eigen::Vector3d direction = lightMatrix * lightSource.direction;
Vector3f color = lightSource.color * lightSource.intensity; Eigen::Vector3f color = lightSource.color * lightSource.intensity;
lightDirections[lightIndex] = direction.cast<float>(); lightDirections[lightIndex] = direction.cast<float>();
lightColors[lightIndex] = color; lightColors[lightIndex] = color;
@ -1005,10 +1016,10 @@ ModelViewWidget::bindMaterial(const Material* material,
{ {
ambientLightLevel = 0.2f; ambientLightLevel = 0.2f;
} }
shader->setUniformValue("ambientLightColor", Vector3f::Constant(ambientLightLevel)); shader->setUniformValue("ambientLightColor", Eigen::Vector3f::Constant(ambientLightLevel));
// Get the eye position in model space // Get the eye position in model space
Vector4f eyePosition = cameraTransform().inverse().cast<float>() * Vector4f::UnitW(); Eigen::Vector4f eyePosition = cameraTransform().inverse().cast<float>() * Eigen::Vector4f::UnitW();
shader->setUniformValue("eyePosition", eyePosition.head(3)); shader->setUniformValue("eyePosition", eyePosition.head(3));
// Set all shadow related values // Set all shadow related values
@ -1030,15 +1041,15 @@ ModelViewWidget::bindMaterial(const Material* material,
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
Matrix4f shadowMatrixes[MaxShadows]; Eigen::Matrix4f shadowMatrixes[MaxShadows];
Matrix4f bias = Matrix4f::Zero(); Eigen::Matrix4f bias = Eigen::Matrix4f::Zero();
bias.diagonal() = Vector4f(0.5f, 0.5f, 0.5f, 1.0f); bias.diagonal() = Eigen::Vector4f(0.5f, 0.5f, 0.5f, 1.0f);
bias.col(3) = Vector4f(0.5f, 0.5f, 0.5f, 1.0f); bias.col(3) = Eigen::Vector4f(0.5f, 0.5f, 0.5f, 1.0f);
for (unsigned int i = 0; i < shaderKey.shadowCount(); ++i) for (unsigned int i = 0; i < shaderKey.shadowCount(); ++i)
{ {
Matrix4f modelView = directionalLightMatrix(lightDirections[i]); Eigen::Matrix4f modelView = directionalLightMatrix(lightDirections[i]);
Matrix4f projection = shadowProjectionMatrix(m_modelBoundingRadius); Eigen::Matrix4f projection = shadowProjectionMatrix(m_modelBoundingRadius);
shadowMatrixes[i] = bias * projection * modelView; shadowMatrixes[i] = bias * projection * modelView;
// TESTING ONLY: // TESTING ONLY:
@ -1056,9 +1067,9 @@ ModelViewWidget::bindMaterial(const Material* material,
glUseProgram(0); glUseProgram(0);
} }
Vector4f diffuse(material->diffuse.red(), material->diffuse.green(), material->diffuse.blue(), material->opacity); Eigen::Vector4f diffuse(material->diffuse.red(), material->diffuse.green(), material->diffuse.blue(), material->opacity);
Vector4f specular(material->specular.red(), material->specular.green(), material->specular.blue(), 1.0f); Eigen::Vector4f specular(material->specular.red(), material->specular.green(), material->specular.blue(), 1.0f);
Vector4f emissive(material->emissive.red(), material->emissive.green(), material->emissive.blue(), 1.0f); Eigen::Vector4f emissive(material->emissive.red(), material->emissive.green(), material->emissive.blue(), 1.0f);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse.data()); glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse.data());
glMaterialfv(GL_FRONT, GL_AMBIENT, diffuse.data()); glMaterialfv(GL_FRONT, GL_AMBIENT, diffuse.data());
glColor4fv(diffuse.data()); glColor4fv(diffuse.data());
@ -1068,10 +1079,10 @@ ModelViewWidget::bindMaterial(const Material* material,
// Set up the diffuse (base) texture // Set up the diffuse (base) texture
GLuint baseTexId = 0; GLuint baseTexId = 0;
if (material->maps[Material::DiffuseMap] != InvalidResource) if (material->getMap(cmod::TextureSemantic::DiffuseMap) != InvalidResource)
{ {
baseTexId = m_materialLibrary->getTexture( baseTexId = m_materialLibrary->getTexture(
toQString(GetPathManager()->getSource(material->maps[Material::DiffuseMap]).c_str())); toQString(GetPathManager()->getSource(material->getMap(cmod::TextureSemantic::DiffuseMap)).c_str()));
} }
if (baseTexId != 0) if (baseTexId != 0)
@ -1100,10 +1111,10 @@ ModelViewWidget::bindMaterial(const Material* material,
void void
ModelViewWidget::renderModel(Model* model) ModelViewWidget::renderModel(cmod::Model* model)
{ {
Material defaultMaterial; cmod::Material defaultMaterial;
defaultMaterial.diffuse = Material::Color(1.0f, 1.0f, 1.0f); defaultMaterial.diffuse = cmod::Color(1.0f, 1.0f, 1.0f);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
if (m_renderStyle == WireFrameStyle) if (m_renderStyle == WireFrameStyle)
@ -1138,10 +1149,10 @@ ModelViewWidget::renderModel(Model* model)
// Render all meshes // Render all meshes
for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex) for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex)
{ {
const Mesh* mesh = model->getMesh(meshIndex); const cmod::Mesh* mesh = model->getMesh(meshIndex);
setVertexArrays(mesh->getVertexDescription(), mesh->getVertexData()); setVertexArrays(mesh->getVertexDescription(), mesh->getVertexData());
if (mesh->getVertexDescription().getAttribute(Mesh::Normal).format == Mesh::Float3) if (mesh->getVertexDescription().getAttribute(cmod::VertexAttributeSemantic::Normal).format == cmod::VertexAttributeFormat::Float3)
{ {
setLighting(true); setLighting(true);
} }
@ -1152,8 +1163,8 @@ ModelViewWidget::renderModel(Model* model)
for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex) for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex)
{ {
const Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex); const cmod::PrimitiveGroup* group = mesh->getGroup(groupIndex);
const Material* material = &defaultMaterial; const cmod::Material* material = &defaultMaterial;
if (group->materialIndex < model->getMaterialCount()) if (group->materialIndex < model->getMaterialCount())
{ {
material = model->getMaterial(group->materialIndex); material = model->getMaterial(group->materialIndex);
@ -1178,7 +1189,7 @@ ModelViewWidget::renderModel(Model* model)
void void
ModelViewWidget::renderSelection(Model* model) ModelViewWidget::renderSelection(cmod::Model* model)
{ {
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_LINE); glPolygonMode(GL_FRONT, GL_LINE);
@ -1191,8 +1202,8 @@ ModelViewWidget::renderSelection(Model* model)
if (renderPath() == OpenGL2Path) if (renderPath() == OpenGL2Path)
{ {
Material selectionMaterial; cmod::Material selectionMaterial;
selectionMaterial.diffuse = Material::Color(0.0f, 1.0f, 0.0f); selectionMaterial.diffuse = cmod::Color(0.0f, 1.0f, 0.0f);
selectionMaterial.opacity = 0.5f; selectionMaterial.opacity = 0.5f;
LightingEnvironment lightsOff; LightingEnvironment lightsOff;
@ -1201,12 +1212,12 @@ ModelViewWidget::renderSelection(Model* model)
for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex) for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex)
{ {
Mesh* mesh = model->getMesh(meshIndex); cmod::Mesh* mesh = model->getMesh(meshIndex);
setVertexPointer(mesh->getVertexDescription(), mesh->getVertexData()); setVertexPointer(mesh->getVertexDescription(), mesh->getVertexData());
for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex) for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex)
{ {
Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex); cmod::PrimitiveGroup* group = mesh->getGroup(groupIndex);
if (m_selection.contains(group)) if (m_selection.contains(group))
{ {
GLenum primitiveMode = getGLMode(group->prim); GLenum primitiveMode = getGLMode(group->prim);
@ -1223,18 +1234,18 @@ ModelViewWidget::renderSelection(Model* model)
void void
ModelViewWidget::renderDepthOnly(Model* model) ModelViewWidget::renderDepthOnly(cmod::Model* model)
{ {
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex) for (unsigned int meshIndex = 0; meshIndex < model->getMeshCount(); ++meshIndex)
{ {
Mesh* mesh = model->getMesh(meshIndex); cmod::Mesh* mesh = model->getMesh(meshIndex);
setVertexPointer(mesh->getVertexDescription(), mesh->getVertexData()); setVertexPointer(mesh->getVertexDescription(), mesh->getVertexData());
for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex) for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex)
{ {
Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex); cmod::PrimitiveGroup* group = mesh->getGroup(groupIndex);
GLenum primitiveMode = getGLMode(group->prim); GLenum primitiveMode = getGLMode(group->prim);
glDrawElements(primitiveMode, group->nIndices, GL_UNSIGNED_INT, group->indices); glDrawElements(primitiveMode, group->nIndices, GL_UNSIGNED_INT, group->indices);
} }
@ -1248,14 +1259,14 @@ ModelViewWidget::setupDefaultLightSources()
m_lightSources.clear(); m_lightSources.clear();
LightSource light1; LightSource light1;
light1.color = Vector3f(1.0f, 1.0f, 1.0f); light1.color = Eigen::Vector3f(1.0f, 1.0f, 1.0f);
light1.intensity = 1.0f; light1.intensity = 1.0f;
light1.direction = Vector3d(1.0, 1.0, 5.0).normalized(); light1.direction = Eigen::Vector3d(1.0, 1.0, 5.0).normalized();
LightSource light2; LightSource light2;
light2.color = Vector3f(1.0f, 1.0f, 1.0f); light2.color = Eigen::Vector3f(1.0f, 1.0f, 1.0f);
light2.intensity = 1.0f; light2.intensity = 1.0f;
light2.direction = Vector3d(3.0, -3.0, -1.0).normalized(); light2.direction = Eigen::Vector3d(3.0, -3.0, -1.0).normalized();
m_lightSources << light1;// << light2; m_lightSources << light1;// << light2;
} }
@ -1559,7 +1570,7 @@ ModelViewWidget::renderShadow(unsigned int lightIndex)
} }
GLFrameBufferObject* shadowBuffer = m_shadowBuffers[lightIndex]; GLFrameBufferObject* shadowBuffer = m_shadowBuffers[lightIndex];
Vector3f lightDirection = (m_lightOrientation * m_lightSources[lightIndex].direction).cast<float>(); Eigen::Vector3f lightDirection = (m_lightOrientation * m_lightSources[lightIndex].direction).cast<float>();
shadowBuffer->bind(); shadowBuffer->bind();
glViewport(0, 0, shadowBuffer->width(), shadowBuffer->height()); glViewport(0, 0, shadowBuffer->width(), shadowBuffer->height());

View File

@ -8,20 +8,35 @@
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
#ifndef _CMODVIEW_MODEL_VIEW_WIDGET_H_ #pragma once
#define _CMODVIEW_MODEL_VIEW_WIDGET_H_
#include "glshader.h" #include <QColor>
#include <QGLWidget> #include <QGLWidget>
#include <QSet>
#include <QHash> #include <QHash>
#include <celmodel/model.h> #include <QList>
#include <QMouseEvent>
#include <QObject>
#include <QPoint>
#include <QSet>
#include <QString>
#include <QWheelEvent>
#include <QWidget>
#include <Eigen/Core> #include <Eigen/Core>
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include <celmodel/material.h>
#include <celmodel/mesh.h>
namespace cmod
{
class Model;
}
class MaterialLibrary; class MaterialLibrary;
class GLFrameBufferObject; class GLFrameBufferObject;
class GLShaderProgram;
class LightingEnvironment class LightingEnvironment
{ {
@ -51,7 +66,7 @@ public:
static ShaderKey Create(const cmod::Material* material, static ShaderKey Create(const cmod::Material* material,
const LightingEnvironment* lighting, const LightingEnvironment* lighting,
const cmod::Mesh::VertexDescription* vertexDesc); const cmod::VertexDescription* vertexDesc);
unsigned int hash() const unsigned int hash() const
{ {
@ -157,7 +172,7 @@ public:
void wheelEvent(QWheelEvent* event); void wheelEvent(QWheelEvent* event);
void select(const Eigen::Vector2f& point); void select(const Eigen::Vector2f& point);
QSet<cmod::Mesh::PrimitiveGroup*> selection() QSet<cmod::PrimitiveGroup*> selection()
{ {
return m_selection; return m_selection;
} }
@ -217,7 +232,7 @@ private:
void renderShadow(unsigned int lightIndex); void renderShadow(unsigned int lightIndex);
void bindMaterial(const cmod::Material* material, void bindMaterial(const cmod::Material* material,
const LightingEnvironment* lighting, const LightingEnvironment* lighting,
const cmod::Mesh::VertexDescription* vertexDesc); const cmod::VertexDescription* vertexDesc);
void setupDefaultLightSources(); void setupDefaultLightSources();
GLShaderProgram* createShader(const ShaderKey& shaderKey); GLShaderProgram* createShader(const ShaderKey& shaderKey);
@ -234,7 +249,7 @@ private:
MaterialLibrary* m_materialLibrary; MaterialLibrary* m_materialLibrary;
QSet<cmod::Mesh::PrimitiveGroup*> m_selection; QSet<cmod::PrimitiveGroup*> m_selection;
QHash<ShaderKey, GLShaderProgram*> m_shaderCache; QHash<ShaderKey, GLShaderProgram*> m_shaderCache;
QColor m_backgroundColor; QColor m_backgroundColor;
@ -247,5 +262,3 @@ private:
bool m_ambientLightEnabled; bool m_ambientLightEnabled;
bool m_shadowsEnabled; bool m_shadowsEnabled;
}; };
#endif // _CMODVIEW_MODEL_VIEW_WIDGET_H_

File diff suppressed because it is too large Load Diff

View File

@ -9,23 +9,32 @@
// //
// Perform various adjustments to a Celestia mesh. // Perform various adjustments to a Celestia mesh.
#ifndef _CMODOPS_H_ #pragma once
#define _CMODOPS_H_
#include <celmodel/model.h> #include <algorithm>
#include <Eigen/Core> #include <cassert>
#include <cstdint>
#include <vector> #include <vector>
#include <Eigen/Core>
#include <celmodel/mesh.h>
namespace cmod
{
class Model;
}
struct Vertex struct Vertex
{ {
Vertex() : Vertex() :
index(0), attributes(nullptr) {}; index(0), attributes(nullptr) {};
Vertex(uint32_t _index, const void* _attributes) : Vertex(std::uint32_t _index, const void* _attributes) :
index(_index), attributes(_attributes) {}; index(_index), attributes(_attributes) {};
uint32_t index; std::uint32_t index;
const void* attributes; const void* attributes;
}; };
@ -33,8 +42,8 @@ struct Vertex
struct Face struct Face
{ {
Eigen::Vector3f normal; Eigen::Vector3f normal;
uint32_t i[3]; // vertex attribute indices std::uint32_t i[3]; // vertex attribute indices
uint32_t vi[3]; // vertex point indices -- same as above unless welding std::uint32_t vi[3]; // vertex point indices -- same as above unless welding
}; };
@ -53,7 +62,7 @@ extern bool ConvertToStrips(cmod::Mesh& mesh);
template<typename T, typename U> void template<typename T, typename U> void
JoinVertices(std::vector<Face>& faces, JoinVertices(std::vector<Face>& faces,
const void* vertexData, const void* vertexData,
const cmod::Mesh::VertexDescription& desc, const cmod::VertexDescription& desc,
const T& orderingPredicate, const T& orderingPredicate,
const U& equivalencePredicate) const U& equivalencePredicate)
{ {
@ -64,19 +73,18 @@ JoinVertices(std::vector<Face>& faces,
// Must have a position // Must have a position
assert(desc.getAttribute(cmod::Mesh::Position).format == cmod::Mesh::Float3); assert(desc.getAttribute(cmod::Mesh::Position).format == cmod::Mesh::Float3);
uint32_t posOffset = desc.getAttribute(cmod::Mesh::Position).offset; std::uint32_t posOffset = desc.getAttribute(cmod::VertexAttributeSemantic::Position).offset;
const char* vertexPoints = reinterpret_cast<const char*>(vertexData) + const char* vertexPoints = reinterpret_cast<const char*>(vertexData) + posOffset;
posOffset; std::uint32_t nVertices = faces.size() * 3;
uint32_t nVertices = faces.size() * 3;
// Initialize the array of vertices // Initialize the array of vertices
std::vector<Vertex> vertices(nVertices); std::vector<Vertex> vertices(nVertices);
uint32_t f; std::uint32_t f;
for (f = 0; f < faces.size(); f++) for (f = 0; f < faces.size(); f++)
{ {
for (uint32_t j = 0; j < 3; j++) for (std::uint32_t j = 0; j < 3; j++)
{ {
uint32_t index = faces[f].i[j]; std::uint32_t index = faces[f].i[j];
vertices[f * 3 + j] = Vertex(index, vertices[f * 3 + j] = Vertex(index,
vertexPoints + desc.stride * index); vertexPoints + desc.stride * index);
@ -84,13 +92,13 @@ JoinVertices(std::vector<Face>& faces,
} }
// Sort the vertices so that identical ones will be ordered consecutively // Sort the vertices so that identical ones will be ordered consecutively
sort(vertices.begin(), vertices.end(), orderingPredicate); std::sort(vertices.begin(), vertices.end(), orderingPredicate);
// Build the vertex merge map // Build the vertex merge map
std::vector<uint32_t> mergeMap(nVertices); std::vector<std::uint32_t> mergeMap(nVertices);
uint32_t lastUnique = 0; std::uint32_t lastUnique = 0;
uint32_t uniqueCount = 0; std::uint32_t uniqueCount = 0;
for (uint32_t i = 0; i < nVertices; i++) for (std::uint32_t i = 0; i < nVertices; i++)
{ {
if (i == 0 || !equivalencePredicate(vertices[i - 1], vertices[i])) if (i == 0 || !equivalencePredicate(vertices[i - 1], vertices[i]))
{ {
@ -104,11 +112,9 @@ JoinVertices(std::vector<Face>& faces,
// Remap the vertex indices // Remap the vertex indices
for (f = 0; f < faces.size(); f++) for (f = 0; f < faces.size(); f++)
{ {
for (uint32_t k= 0; k < 3; k++) for (std::uint32_t k= 0; k < 3; k++)
{ {
faces[f].vi[k] = mergeMap[faces[f].i[k]]; faces[f].vi[k] = mergeMap[faces[f].i[k]];
} }
} }
} }
#endif // _CMODOPS_H_

View File

@ -9,28 +9,29 @@
// //
// Functions for converting a 3DS scene into a Celestia model (cmod) // Functions for converting a 3DS scene into a Celestia model (cmod)
#include <cmath>
#include <cstdint>
#include <Eigen/Core> #include <Eigen/Core>
#include <celmodel/material.h>
#include <celmodel/mesh.h>
#include "convert3ds.h" #include "convert3ds.h"
#include "pathmanager.h"
namespace
using namespace cmod; {
using namespace Eigen; cmod::Material*
using namespace std;
static Material*
convert3dsMaterial(const M3DMaterial* material3ds, cmod::HandleGetter& handleGetter) convert3dsMaterial(const M3DMaterial* material3ds, cmod::HandleGetter& handleGetter)
{ {
Material* newMaterial = new Material(); cmod::Material* newMaterial = new cmod::Material();
M3DColor diffuse = material3ds->getDiffuseColor(); M3DColor diffuse = material3ds->getDiffuseColor();
newMaterial->diffuse = Material::Color(diffuse.red, diffuse.green, diffuse.blue); newMaterial->diffuse = cmod::Color(diffuse.red, diffuse.green, diffuse.blue);
newMaterial->opacity = material3ds->getOpacity(); newMaterial->opacity = material3ds->getOpacity();
M3DColor specular = material3ds->getSpecularColor(); M3DColor specular = material3ds->getSpecularColor();
newMaterial->specular = Material::Color(specular.red, specular.green, specular.blue); newMaterial->specular = cmod::Color(specular.red, specular.green, specular.blue);
float shininess = material3ds->getShininess(); float shininess = material3ds->getShininess();
@ -38,25 +39,29 @@ convert3dsMaterial(const M3DMaterial* material3ds, cmod::HandleGetter& handleGet
// range that OpenGL uses for the specular exponent. The // range that OpenGL uses for the specular exponent. The
// current equation is just a guess at the mapping that // current equation is just a guess at the mapping that
// 3DS actually uses. // 3DS actually uses.
newMaterial->specularPower = (float) pow(2.0, 1.0 + 0.1 * shininess); newMaterial->specularPower = std::pow(2.0f, 1.0f + 0.1f * shininess);
if (newMaterial->specularPower > 128.0f) if (newMaterial->specularPower > 128.0f)
newMaterial->specularPower = 128.0f; newMaterial->specularPower = 128.0f;
if (!material3ds->getTextureMap().empty()) if (!material3ds->getTextureMap().empty())
{ {
newMaterial->maps[Material::DiffuseMap] = handleGetter(material3ds->getTextureMap()); newMaterial->setMap(cmod::TextureSemantic::DiffuseMap, handleGetter(material3ds->getTextureMap()));
} }
return newMaterial; return newMaterial;
} }
} // end unnamed namespace
void void
Convert3DSMesh(Model& model, Convert3DSMesh(cmod::Model& model,
const M3DTriangleMesh& mesh3ds, const M3DTriangleMesh& mesh3ds,
const M3DScene& scene, const M3DScene& scene,
const string& meshName) const std::string& meshName)
{ {
int nVertices = mesh3ds.getVertexCount(); int nVertices = mesh3ds.getVertexCount();
int nTexCoords = mesh3ds.getTexCoordCount(); int nTexCoords = mesh3ds.getTexCoordCount();
@ -73,38 +78,42 @@ Convert3DSMesh(Model& model,
for (int i = 0; i < mesh3ds.getVertexCount(); ++i) for (int i = 0; i < mesh3ds.getVertexCount(); ++i)
{ {
int k = i * vertexSize; int k = i * vertexSize;
Vector3f pos = mesh3ds.getVertex(i); Eigen::Vector3f pos = mesh3ds.getVertex(i);
vertices[k + 0] = pos.x(); vertices[k + 0] = pos.x();
vertices[k + 1] = pos.y(); vertices[k + 1] = pos.y();
vertices[k + 2] = pos.z(); vertices[k + 2] = pos.z();
if (hasTexCoords) if (hasTexCoords)
{ {
Vector2f texCoord = mesh3ds.getTexCoord(i); Eigen::Vector2f texCoord = mesh3ds.getTexCoord(i);
vertices[k + 3] = texCoord.x(); vertices[k + 3] = texCoord.x();
vertices[k + 4] = texCoord.y(); vertices[k + 4] = texCoord.y();
} }
} }
Mesh::VertexAttribute attributes[8]; cmod::VertexAttribute attributes[8];
uint32_t nAttributes = 0; std::uint32_t nAttributes = 0;
uint32_t offset = 0; std::uint32_t offset = 0;
// Position attribute is always present // Position attribute is always present
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Position, Mesh::Float3, 0); attributes[nAttributes] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Position,
cmod::VertexAttributeFormat::Float3,
0);
nAttributes++; nAttributes++;
offset += 12; offset += 12;
if (hasTexCoords) if (hasTexCoords)
{ {
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Texture0, Mesh::Float2, offset); attributes[nAttributes] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Texture0,
cmod::VertexAttributeFormat::Float2,
offset);
nAttributes++; nAttributes++;
offset += 8; offset += 8;
} }
// Create the Celestia mesh // Create the Celestia mesh
Mesh* mesh = new Mesh(); cmod::Mesh* mesh = new cmod::Mesh();
mesh->setVertexDescription(Mesh::VertexDescription(offset, nAttributes, attributes)); mesh->setVertexDescription(cmod::VertexDescription(offset, nAttributes, attributes));
mesh->setVertices(nVertices, vertices); mesh->setVertices(nVertices, vertices);
mesh->setName(meshName); mesh->setName(meshName);
@ -114,34 +123,34 @@ Convert3DSMesh(Model& model,
// No material groups in the 3DS file. This is allowed. We'll create a single // No material groups in the 3DS file. This is allowed. We'll create a single
// primitive group with the default material. // primitive group with the default material.
unsigned int faceCount = mesh3ds.getFaceCount(); unsigned int faceCount = mesh3ds.getFaceCount();
auto* indices = new uint32_t[faceCount * 3]; auto* indices = new std::uint32_t[faceCount * 3];
for (unsigned int i = 0; i < faceCount; i++) for (unsigned int i = 0; i < faceCount; i++)
{ {
uint16_t v0 = 0, v1 = 0, v2 = 0; std::uint16_t v0 = 0, v1 = 0, v2 = 0;
mesh3ds.getFace(i, v0, v1, v2); mesh3ds.getFace(i, v0, v1, v2);
indices[i * 3 + 0] = v0; indices[i * 3 + 0] = v0;
indices[i * 3 + 1] = v1; indices[i * 3 + 1] = v1;
indices[i * 3 + 2] = v2; indices[i * 3 + 2] = v2;
} }
mesh->addGroup(Mesh::TriList, ~0, faceCount * 3, indices); mesh->addGroup(cmod::PrimitiveGroupType::TriList, ~0, faceCount * 3, indices);
} }
else else
{ {
// We have at least one material group. Create a cmod primitive group for // We have at least one material group. Create a cmod primitive group for
// each material group in th 3ds mesh. // each material group in th 3ds mesh.
for (uint32_t groupIndex = 0; groupIndex < mesh3ds.getMeshMaterialGroupCount(); ++groupIndex) for (std::uint32_t groupIndex = 0; groupIndex < mesh3ds.getMeshMaterialGroupCount(); ++groupIndex)
{ {
const M3DMeshMaterialGroup* matGroup = mesh3ds.getMeshMaterialGroup(groupIndex); const M3DMeshMaterialGroup* matGroup = mesh3ds.getMeshMaterialGroup(groupIndex);
uint32_t nMatGroupFaces = matGroup->faces.size(); std::uint32_t nMatGroupFaces = matGroup->faces.size();
auto* indices = new uint32_t[nMatGroupFaces * 3]; auto* indices = new std::uint32_t[nMatGroupFaces * 3];
for (unsigned int i = 0; i < nMatGroupFaces; i++) for (unsigned int i = 0; i < nMatGroupFaces; i++)
{ {
uint16_t v0 = 0, v1 = 0, v2 = 0; std::uint16_t v0 = 0, v1 = 0, v2 = 0;
uint16_t faceIndex = matGroup->faces[i]; std::uint16_t faceIndex = matGroup->faces[i];
mesh3ds.getFace(faceIndex, v0, v1, v2); mesh3ds.getFace(faceIndex, v0, v1, v2);
indices[i * 3 + 0] = v0; indices[i * 3 + 0] = v0;
indices[i * 3 + 1] = v1; indices[i * 3 + 1] = v1;
@ -150,7 +159,7 @@ Convert3DSMesh(Model& model,
// Get the material index // Get the material index
unsigned int materialIndex = ~0u; unsigned int materialIndex = ~0u;
string material3dsName = matGroup->materialName; std::string material3dsName = matGroup->materialName;
if (!material3dsName.empty()) if (!material3dsName.empty())
{ {
for (unsigned int i = 0; i < scene.getMaterialCount(); i++) for (unsigned int i = 0; i < scene.getMaterialCount(); i++)
@ -163,7 +172,7 @@ Convert3DSMesh(Model& model,
} }
} }
mesh->addGroup(Mesh::TriList, materialIndex, nMatGroupFaces * 3, indices); mesh->addGroup(cmod::PrimitiveGroupType::TriList, materialIndex, nMatGroupFaces * 3, indices);
} }
} }
@ -171,10 +180,10 @@ Convert3DSMesh(Model& model,
} }
Model* cmod::Model*
Convert3DSModel(const M3DScene& scene, cmod::HandleGetter handleGetter) Convert3DSModel(const M3DScene& scene, cmod::HandleGetter handleGetter)
{ {
Model* model = new Model(); cmod::Model* model = new cmod::Model();
// Convert materials // Convert materials
for (unsigned int i = 0; i < scene.getMaterialCount(); i++) for (unsigned int i = 0; i < scene.getMaterialCount(); i++)

View File

@ -9,12 +9,14 @@
// //
// Functions for converting a 3DS scene into a Celestia model (cmod) // Functions for converting a 3DS scene into a Celestia model (cmod)
#ifndef _CONVERT3DS_H_ #pragma once
#define _CONVERT3DS_H_
#include <string>
#include <cel3ds/3dsmodel.h>
#include <celmodel/model.h> #include <celmodel/model.h>
#include <celmodel/modelfile.h> #include <celmodel/modelfile.h>
#include <cel3ds/3dsmodel.h>
extern void Convert3DSMesh(cmod::Model& model, extern void Convert3DSMesh(cmod::Model& model,
M3DTriangleMesh& mesh3ds, M3DTriangleMesh& mesh3ds,
@ -22,5 +24,3 @@ extern void Convert3DSMesh(cmod::Model& model,
const std::string& meshName); const std::string& meshName);
extern cmod::Model* Convert3DSModel(const M3DScene& scene, extern cmod::Model* Convert3DSModel(const M3DScene& scene,
cmod::HandleGetter handleGetter); cmod::HandleGetter handleGetter);
#endif // _CONVERT3DS_H_

View File

@ -10,28 +10,22 @@
// Functions for converting a Wavefront .obj file into a // Functions for converting a Wavefront .obj file into a
// Celestia model (cmod) // Celestia model (cmod)
#include "convertobj.h"
#include <algorithm>
#include <sstream>
#include <cstdio> #include <cstdio>
#include <sstream>
using namespace cmod; #include <celmodel/material.h>
using namespace Eigen; #include <celmodel/mesh.h>
using namespace std; #include <celmodel/model.h>
#include "convertobj.h"
namespace
WavefrontLoader::WavefrontLoader(istream& in) :
m_in(in),
m_lineNumber(0),
m_model(nullptr)
{ {
}
std::string::size_type getToken(const std::string& s, std::string::size_type start, std::string& token)
string::size_type getToken(const string& s, string::size_type start, string& token)
{ {
string::size_type pos = start; std::string::size_type pos = start;
token.clear(); token.clear();
while (pos < s.size() && isspace(s[pos])) while (pos < s.size() && isspace(s[pos]))
@ -48,7 +42,6 @@ string::size_type getToken(const string& s, string::size_type start, string& tok
return pos; return pos;
} }
// Convert a 1-based array index to a zero based index. Negative // Convert a 1-based array index to a zero based index. Negative
// indices are relative to the top of the end of the array. Return // indices are relative to the top of the end of the array. Return
// -1 if the index is invalid. // -1 if the index is invalid.
@ -56,51 +49,65 @@ int convertIndex(int index, unsigned int maxValue)
{ {
if (index > 0) if (index > 0)
{ {
if (index <= (int) maxValue) if (index <= static_cast<int>(maxValue))
return index - 1; return index - 1;
return -1; return -1;
} }
if (index < 0) if (index < 0)
{ {
if (-index <= (int) maxValue) if (-index <= static_cast<int>(maxValue))
return (int) maxValue + index; return static_cast<int>(maxValue) + index;
return -1; return -1;
} }
return -1; return -1;
} }
} // end unnamed namespace
WavefrontLoader::WavefrontLoader(std::istream& in) :
m_in(in),
m_lineNumber(0),
m_model(nullptr)
{
}
cmod::Model* cmod::Model*
WavefrontLoader::load() WavefrontLoader::load()
{ {
string line; std::string line;
string keyword; std::string keyword;
unsigned int vertexCount = 0; unsigned int vertexCount = 0;
ObjVertex::VertexType lastVertexType = ObjVertex::Point; ObjVertex::VertexType lastVertexType = ObjVertex::Point;
int currentMaterialIndex = -1; int currentMaterialIndex = -1;
m_model = new Model(); m_model = new cmod::Model();
while (getline(m_in, line)) while (getline(m_in, line))
{ {
m_lineNumber++; m_lineNumber++;
// strip comments // strip comments
string::size_type commentPos = line.find('#'); std::string::size_type commentPos = line.find('#');
if (commentPos != string::npos) if (commentPos != std::string::npos)
{ {
line = line.substr(0, commentPos); line = line.substr(0, commentPos);
} }
string::size_type pos = getToken(line, 0, keyword); std::string::size_type pos = getToken(line, 0, keyword);
if (!keyword.empty()) if (!keyword.empty())
{ {
if (keyword == "v") if (keyword == "v")
{ {
Vector3f v(Vector3f::Zero()); Eigen::Vector3f v(Eigen::Vector3f::Zero());
if (sscanf(line.c_str() + pos, "%f %f %f", &v.x(), &v.y(), &v.z()) != 3) if (std::sscanf(line.c_str() + pos, "%f %f %f", &v.x(), &v.y(), &v.z()) != 3)
{ {
reportError("Bad vertex"); reportError("Bad vertex");
return nullptr; return nullptr;
@ -109,8 +116,8 @@ WavefrontLoader::load()
} }
else if (keyword == "vn") else if (keyword == "vn")
{ {
Vector3f v(Vector3f::Zero()); Eigen::Vector3f v(Eigen::Vector3f::Zero());
if (sscanf(line.c_str() + pos, "%f %f %f", &v.x(), &v.y(), &v.z()) != 3) if (std::sscanf(line.c_str() + pos, "%f %f %f", &v.x(), &v.y(), &v.z()) != 3)
{ {
reportError("Bad normal"); reportError("Bad normal");
return nullptr; return nullptr;
@ -119,8 +126,8 @@ WavefrontLoader::load()
} }
else if (keyword == "vt") else if (keyword == "vt")
{ {
Vector2f v(Vector2f::Zero()); Eigen::Vector2f v(Eigen::Vector2f::Zero());
if (sscanf(line.c_str() + pos, "%f %f", &v.x(), &v.y()) != 2) if (std::sscanf(line.c_str() + pos, "%f %f", &v.x(), &v.y()) != 2)
{ {
reportError("Bad texture coordinate"); reportError("Bad texture coordinate");
return nullptr; return nullptr;
@ -129,8 +136,8 @@ WavefrontLoader::load()
} }
else if (keyword == "usemtl") else if (keyword == "usemtl")
{ {
Material* material = new Material(); cmod::Material* material = new cmod::Material();
material->diffuse = Material::Color(1.0f, 1.0f, 1.0f); material->diffuse = cmod::Color(1.0f, 1.0f, 1.0f);
currentMaterialIndex = m_model->addMaterial(material) - 1; currentMaterialIndex = m_model->addMaterial(material) - 1;
if (!m_materialGroups.empty()) if (!m_materialGroups.empty())
{ {
@ -149,27 +156,27 @@ WavefrontLoader::load()
} }
else if (keyword == "f") else if (keyword == "f")
{ {
vector<ObjVertex> faceVertices; std::vector<ObjVertex> faceVertices;
while (pos < line.size()) while (pos < line.size())
{ {
string vertexString; std::string vertexString;
pos = getToken(line, pos, vertexString); pos = getToken(line, pos, vertexString);
if (!vertexString.empty()) if (!vertexString.empty())
{ {
ObjVertex vertex; ObjVertex vertex;
if (sscanf(vertexString.c_str(), "%d/%d/%d", &vertex.vertexIndex, &vertex.texCoordIndex, &vertex.normalIndex) == 3) if (std::sscanf(vertexString.c_str(), "%d/%d/%d", &vertex.vertexIndex, &vertex.texCoordIndex, &vertex.normalIndex) == 3)
{ {
// Vertex, texture coordinate, and normal // Vertex, texture coordinate, and normal
} }
else if (sscanf(vertexString.c_str(), "%d//%d", &vertex.vertexIndex, &vertex.normalIndex) == 2) else if (std::sscanf(vertexString.c_str(), "%d//%d", &vertex.vertexIndex, &vertex.normalIndex) == 2)
{ {
// Vertex + normal // Vertex + normal
} }
else if (sscanf(vertexString.c_str(), "%d/%d", &vertex.vertexIndex, &vertex.texCoordIndex) == 2) else if (std::sscanf(vertexString.c_str(), "%d/%d", &vertex.vertexIndex, &vertex.texCoordIndex) == 2)
{ {
// Vertex + texture coordinate // Vertex + texture coordinate
} }
else if (sscanf(vertexString.c_str(), "%d", &vertex.vertexIndex) == 1) else if (std::sscanf(vertexString.c_str(), "%d", &vertex.vertexIndex) == 1)
{ {
// Vertex only // Vertex only
} }
@ -292,10 +299,10 @@ WavefrontLoader::load()
void void
WavefrontLoader::reportError(const string& message) WavefrontLoader::reportError(const std::string& message)
{ {
//cerr << message << endl; //cerr << message << endl;
ostringstream os; std::ostringstream os;
os << "Line " << m_lineNumber << ": " << message; os << "Line " << m_lineNumber << ": " << message;
m_errorMessage = os.str(); m_errorMessage = os.str();
@ -324,25 +331,31 @@ WavefrontLoader::addVertexData(const Eigen::Vector3f& v)
void void
WavefrontLoader::createMesh(ObjVertex::VertexType vertexType, unsigned int vertexCount) WavefrontLoader::createMesh(ObjVertex::VertexType vertexType, unsigned int vertexCount)
{ {
Mesh::VertexAttribute attributes[8]; cmod::VertexAttribute attributes[8];
unsigned int nAttributes = 0; unsigned int nAttributes = 0;
unsigned int offset = 0; unsigned int offset = 0;
// Position attribute is always present // Position attribute is always present
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Position, Mesh::Float3, 0); attributes[nAttributes] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Position,
cmod::VertexAttributeFormat::Float3,
0);
nAttributes++; nAttributes++;
offset += 12; offset += 12;
if (vertexType == ObjVertex::PointNormal || vertexType == ObjVertex::PointTexNormal) if (vertexType == ObjVertex::PointNormal || vertexType == ObjVertex::PointTexNormal)
{ {
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Normal, Mesh::Float3, offset); attributes[nAttributes] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Normal,
cmod::VertexAttributeFormat::Float3,
offset);
nAttributes++; nAttributes++;
offset += 12; offset += 12;
} }
if (vertexType == ObjVertex::PointTex || vertexType == ObjVertex::PointTexNormal) if (vertexType == ObjVertex::PointTex || vertexType == ObjVertex::PointTexNormal)
{ {
attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Texture0, Mesh::Float2, offset); attributes[nAttributes] = cmod::VertexAttribute(cmod::VertexAttributeSemantic::Texture0,
cmod::VertexAttributeFormat::Float2,
offset);
nAttributes++; nAttributes++;
offset += 8; offset += 8;
} }
@ -351,8 +364,8 @@ WavefrontLoader::createMesh(ObjVertex::VertexType vertexType, unsigned int verte
copy(m_vertexData.begin(), m_vertexData.end(), vertexDataCopy); copy(m_vertexData.begin(), m_vertexData.end(), vertexDataCopy);
// Create the Celestia mesh // Create the Celestia mesh
Mesh* mesh = new Mesh(); cmod::Mesh* mesh = new cmod::Mesh();
mesh->setVertexDescription(Mesh::VertexDescription(offset, nAttributes, attributes)); mesh->setVertexDescription(cmod::VertexDescription(offset, nAttributes, attributes));
mesh->setVertices(vertexCount, vertexDataCopy); mesh->setVertices(vertexCount, vertexDataCopy);
// Add primitive groups // Add primitive groups
@ -371,9 +384,9 @@ WavefrontLoader::createMesh(ObjVertex::VertexType vertexType, unsigned int verte
if (indexCount > 0) if (indexCount > 0)
{ {
Mesh::index32* indexDataCopy = new Mesh::index32[indexCount]; cmod::index32* indexDataCopy = new cmod::index32[indexCount];
copy(m_indexData.begin() + firstIndex, m_indexData.begin() + firstIndex + indexCount, indexDataCopy); copy(m_indexData.begin() + firstIndex, m_indexData.begin() + firstIndex + indexCount, indexDataCopy);
mesh->addGroup(Mesh::TriList, m_materialGroups[i].materialIndex, indexCount, indexDataCopy); mesh->addGroup(cmod::PrimitiveGroupType::TriList, m_materialGroups[i].materialIndex, indexCount, indexDataCopy);
} }
} }

View File

@ -10,14 +10,18 @@
// Functions for converting a Wavefront .obj file into a // Functions for converting a Wavefront .obj file into a
// Celestia model (cmod) // Celestia model (cmod)
#ifndef _CMOD_CONVERTOBJ_H_ #pragma once
#define _CMOD_CONVERTOBJ_H_
#include <celmodel/model.h> #include <iosfwd>
#include <Eigen/Core>
#include <iostream>
#include <vector>
#include <string> #include <string>
#include <vector>
#include <Eigen/Core>
namespace cmod
{
class Model;
}
class WavefrontLoader class WavefrontLoader
@ -93,12 +97,10 @@ private:
std::vector<Eigen::Vector2f> m_texCoords; std::vector<Eigen::Vector2f> m_texCoords;
std::vector<float> m_vertexData; std::vector<float> m_vertexData;
std::vector<cmod::Mesh::index32> m_indexData; std::vector<cmod::index32> m_indexData;
std::vector<MaterialGroup> m_materialGroups; std::vector<MaterialGroup> m_materialGroups;
cmod::Model* m_model; cmod::Model* m_model;
std::string m_errorMessage; std::string m_errorMessage;
}; };
#endif // _CMOD_CONVERTOBJ_H_