Symphony Of Empires
model.cpp
Go to the documentation of this file.
1 // Eng3D - General purpouse game engine
2 // Copyright (C) 2021, Eng3D contributors
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 //
17 // ----------------------------------------------------------------------------
18 // Name:
19 // model.cpp
20 //
21 // Abstract:
22 // Defines some minor functions and defines big functions for parsing
23 // polygonal data from files.
24 // ----------------------------------------------------------------------------
25 
26 #include <algorithm>
27 #include <fstream>
28 #include <vector>
29 #include <iterator>
30 
31 #ifdef E3D_FEATURE_ASSIMP
32 # include <assimp/Importer.hpp>
33 # include <assimp/scene.h>
34 extern "C" {
35 # include <assimp/postprocess.h>
36 # include <assimp/material.h>
37 }
38 #endif
39 
40 #include "eng3d/model.hpp"
41 #include "eng3d/shader.hpp"
42 #include "eng3d/material.hpp"
43 #include "eng3d/texture.hpp"
44 #include "eng3d/shader.hpp"
45 #include "eng3d/state.hpp"
46 #include "eng3d/utils.hpp"
47 #include "eng3d/log.hpp"
48 
49 //
50 // Simple model
51 //
52 void Eng3D::SimpleModel::draw(const Eng3D::OpenGL::Program& shader, int) const {
53  // Change color if material wants it
54  shader.set_texture(0, "diffuse_map", *material->diffuse_map);
55  shader.set_uniform("diffuse_color", material->diffuse_color);
56 
57  shader.set_texture(1, "ambient_map", *material->ambient_map);
58  shader.set_uniform("ambient_color", material->ambient_color);
59 
60  shader.set_texture(2, "occlussion_map", *material->occlussion_map);
61  shader.set_texture(3, "height_map", *material->height_map);
62 
63  shader.set_texture(4, "specular_map", *material->specular_map);
64  shader.set_uniform("specular_color", material->specular_color);
65 
66  shader.set_texture(5, "normal_map", *material->normal_map);
67 
68  ((Eng3D::Mesh<glm::vec3, glm::vec2>*)this)->draw();
69 }
70 
71 //
72 // Model
73 //
74 #ifdef E3D_FEATURE_ASSIMP
75 static inline std::shared_ptr<Eng3D::Texture> get_material_texture(const aiMaterial& material, aiTextureType type) {
76  auto& s = Eng3D::State::get_instance();
77  for(size_t i = 0; i < material.GetTextureCount(type); i++) {
78  aiString str;
79  material.GetTexture(type, i, &str);
80  Eng3D::Log::debug("assimp", Eng3D::translate_format("Loading texture for material %s", str.C_Str()));
81  auto path = std::string("gfx/") + str.C_Str();
82  return s.tex_man.load(s.package_man.get_unique(path));
83  }
84  return s.tex_man.get_white();
85 }
86 
87 Eng3D::SimpleModel Eng3D::Model::process_simple_model(aiMesh& mesh, const aiScene& scene) {
89  auto& s = Eng3D::State::get_instance();
90  simple_model.buffer.resize(mesh.mNumVertices);
91  glm::vec3 max_vert{};
92  for(size_t i = 0; i < mesh.mNumVertices; i++)
93  max_vert = glm::vec3(glm::max(max_vert.x, mesh.mVertices[i].x), glm::max(max_vert.y, mesh.mVertices[i].y), glm::max(max_vert.z, mesh.mVertices[i].z));
94 
95  max_vert = glm::vec3(max_vert.x, glm::max(max_vert.y, max_vert.x), glm::max(max_vert.z, max_vert.x));
96  max_vert = glm::vec3(glm::max(max_vert.x, max_vert.y), max_vert.y, glm::max(max_vert.z, max_vert.y));
97  max_vert = glm::vec3(glm::max(max_vert.x, max_vert.z), glm::max(max_vert.y, max_vert.z), max_vert.z);
98 
99  for(size_t i = 0; i < mesh.mNumVertices; i++) {
100  auto vertice = glm::vec3(mesh.mVertices[i].x, mesh.mVertices[i].y, mesh.mVertices[i].z) / max_vert;
101  auto texcoord = glm::vec2(0.f, 0.f);
102  if(mesh.mTextureCoords[0])
103  texcoord = glm::vec2(mesh.mTextureCoords[0][i].x, mesh.mTextureCoords[0][i].y);
104  simple_model.buffer[i] = Eng3D::MeshData<glm::vec3, glm::vec2>(vertice, texcoord);
105  }
106 
107  for(size_t i = 0; i < mesh.mNumFaces; i++) {
108  auto& face = mesh.mFaces[i];
109  simple_model.indices.reserve(simple_model.indices.size() + face.mNumIndices);
110  for(size_t j = 0; j < face.mNumIndices; j++)
111  simple_model.indices.push_back(face.mIndices[j]);
112  }
113 
114  auto& material = *scene.mMaterials[mesh.mMaterialIndex];
115 
116  // Textures
117  simple_model.material = s.material_man.load(material.GetName().C_Str());
118  simple_model.material->diffuse_map = get_material_texture(material, aiTextureType_DIFFUSE);
119  simple_model.material->specular_map = get_material_texture(material, aiTextureType_SPECULAR);
120  simple_model.material->ambient_map = get_material_texture(material, aiTextureType_AMBIENT);
121  simple_model.material->height_map = get_material_texture(material, aiTextureType_HEIGHT);
122  simple_model.material->occlussion_map = get_material_texture(material, aiTextureType_AMBIENT_OCCLUSION);
123  simple_model.material->normal_map = get_material_texture(material, aiTextureType_NORMALS);
124 
125  // Colors
126  aiColor4D diffuse;
127  if(AI_SUCCESS == aiGetMaterialColor(&material, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
128  simple_model.material->diffuse_color = glm::vec4(diffuse.r, diffuse.g, diffuse.b, diffuse.a);
129  aiColor4D specular;
130  if(AI_SUCCESS == aiGetMaterialColor(&material, AI_MATKEY_COLOR_SPECULAR, &specular))
131  simple_model.material->specular_color = glm::vec4(specular.r, specular.g, specular.b, specular.a);
132  aiColor4D ambient;
133  if(AI_SUCCESS == aiGetMaterialColor(&material, AI_MATKEY_COLOR_AMBIENT, &ambient))
134  simple_model.material->ambient_color = glm::vec4(ambient.r, ambient.g, ambient.b, ambient.a);
135 
136  simple_model.upload();
137  return simple_model;
138 }
139 
140 void Eng3D::Model::process_node(aiNode& node, const aiScene& scene) {
141  // process all thehis-> node's meshes (if any)
142  for(size_t i = 0; i < node.mNumMeshes; i++) {
143  auto& mesh = *scene.mMeshes[node.mMeshes[i]];
144  this->simple_models.push_back(this->process_simple_model(mesh, scene));
145  }
146  // then do the same for each of its children
147  for(size_t i = 0; i < node.mNumChildren; i++)
148  this->process_node(*node.mChildren[i], scene);
149 }
150 #endif
151 
152 //
153 // ModelManager
154 //
155 std::shared_ptr<Eng3D::Model> Eng3D::ModelManager::load(const std::string& path) {
156  auto it = models.find(path);
157  if(it != models.cend())
158  return (*it).second;
159 
160  // Wavefront OBJ loader
161  std::shared_ptr<Eng3D::Model> model;
162  try {
163 #ifdef E3D_FEATURE_ASSIMP
165  Assimp::Importer importer;
166  const auto* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
167  if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || scene->mRootNode == nullptr)
168  CXX_THROW(std::runtime_error, importer.GetErrorString());
169 #endif
170  model = std::make_shared<Eng3D::Model>();
171 #ifdef E3D_FEATURE_ASSIMP
172  model->process_node(*scene->mRootNode, *scene);
173 #endif
174  } catch(std::runtime_error& e) {
175  // Make a dummy model
176  model = std::make_shared<Eng3D::Model>();
177  }
178  models[path] = model;
179  return model;
180 }
181 
182 std::shared_ptr<Eng3D::Model> Eng3D::ModelManager::load(std::shared_ptr<Eng3D::IO::Asset::Base> asset) {
183  return this->load(asset.get() != nullptr ? asset->get_abs_path() : "");
184 }
std::shared_ptr< Eng3D::Model > load(const std::string &path)
Definition: model.cpp:155
void set_texture(int value, const std::string &name, const Eng3D::Texture &texture) const
void set_uniform(const std::string &name, glm::mat4 uniform) const
static State & get_instance()
Definition: state.cpp:514
void debug(const std::string_view category, const std::string_view msg)
Definition: log.cpp:58
std::string translate_format(const std::string_view format, Args &&... args)
String formatter, with translation.
Definition: string.hpp:128
void load(GameState &gs, const std::string &savefile_path)
A simple object - use these to store "simple" objects that MAY repeat.
Definition: model.hpp:53
std::shared_ptr< Eng3D::Material > material
Definition: model.hpp:61
virtual void draw(const Eng3D::OpenGL::Program &shader, int instances=0) const
Definition: model.cpp:52
#define CXX_THROW(class,...)
Definition: utils.hpp:98