A 3D game engine from scratch.
1// (c) 2020 Vlad-Stefan Harbuz <vlad@vladh.net>
2
3#include "../src_external/pstr.h"
4#include "peony_parser_utils.hpp"
5#include "logs.hpp"
6#include "constants.hpp"
7#include "renderer.hpp"
8#include "intrinsics.hpp"
9
10
11char *
12peony_parser_utils::get_string(peony_parser::Prop *prop)
13{
14 if (!prop) { return nullptr; }
15 return prop->values[0].string_value;
16}
17
18
19bool *
20peony_parser_utils::get_boolean(peony_parser::Prop *prop)
21{
22 if (!prop) { return nullptr; }
23 return &prop->values[0].boolean_value;
24}
25
26
27f32 *
28peony_parser_utils::get_number(peony_parser::Prop *prop)
29{
30 if (!prop) { return nullptr; }
31 return &prop->values[0].number_value;
32}
33
34
35v2 *
36peony_parser_utils::get_vec2(peony_parser::Prop *prop)
37{
38 if (!prop) { return nullptr; }
39 return &prop->values[0].vec2_value;
40}
41
42
43v3 *
44peony_parser_utils::get_vec3(peony_parser::Prop *prop)
45{
46 if (!prop) { return nullptr; }
47 return &prop->values[0].vec3_value;
48}
49
50
51v4 *
52peony_parser_utils::get_vec4(peony_parser::Prop *prop)
53{
54 if (!prop) { return nullptr; }
55 return &prop->values[0].vec4_value;
56}
57
58
59peony_parser::Prop *
60peony_parser_utils::find_prop(peony_parser::Entry *entry, char const *name)
61{
62 range (0, entry->n_props) {
63 if (pstr_eq(name, entry->props[idx].name)) {
64 return &entry->props[idx];
65 }
66 }
67 logs::warning("Could not find prop %s", name);
68 return nullptr;
69}
70
71
72void
73peony_parser_utils::get_unique_string_values_for_prop_name(
74 peony_parser::PeonyFile *pf,
75 Array<char[MAX_COMMON_NAME_LENGTH]> *unique_values,
76 char const *prop_name
77) {
78 range_named (idx_entry, 0, pf->n_entries) {
79 peony_parser::Entry *entry = &pf->entries[idx_entry];
80 peony_parser::Prop *prop = find_prop(entry, prop_name);
81 if (!prop) {
82 continue;
83 }
84 range_named (idx_value, 0, prop->n_values) {
85 peony_parser::PropValue *value = &prop->values[idx_value];
86 bool does_material_already_exist = false;
87 each (unique_value, *unique_values) {
88 if (pstr_eq(value->string_value, *unique_value)) {
89 does_material_already_exist = true;
90 break;
91 }
92 }
93 if (!does_material_already_exist) {
94 pstr_copy(*(unique_values->push()),
95 MAX_COMMON_NAME_LENGTH, value->string_value);
96 }
97 }
98 }
99}
100
101
102void
103peony_parser_utils::create_material_from_peony_file_entry(
104 mats::Material *material,
105 peony_parser::Entry *entry,
106 memory::Pool *memory_pool
107) {
108 mats::init_material(material, entry->name);
109
110 // We're calling `find_prop()` a lot here, which goes through the full
111 // list of props every time, and so this is kind of #slow. Not a huge deal, but
112 // good to keep in mind.
113 peony_parser::Prop *prop;
114
115 if ((prop = find_prop(entry, "albedo_static"))) {
116 material->albedo_static = *get_vec4(prop);
117 }
118 if ((prop = find_prop(entry, "metallic_static"))) {
119 material->metallic_static = *get_number(prop);
120 }
121 if ((prop = find_prop(entry, "roughness_static"))) {
122 material->roughness_static = *get_number(prop);
123 }
124 if ((prop = find_prop(entry, "ao_static"))) {
125 material->ao_static = *get_number(prop);
126 }
127
128 if ((prop = find_prop(entry, "shader_asset.vert_path"))) {
129 if (!pstr_is_empty(get_string(prop))) {
130 shaders::init_shader_asset(&material->shader_asset,
131 memory_pool,
132 entry->name,
133 shaders::Type::standard,
134 get_string(find_prop(entry, "shader_asset.vert_path")),
135 get_string(find_prop(entry, "shader_asset.frag_path")),
136 get_string(find_prop(entry, "shader_asset.geom_path")));
137 }
138 }
139
140 if ((prop = find_prop(entry, "depth_shader_asset.vert_path"))) {
141 if (!pstr_is_empty(get_string(prop))) {
142 shaders::init_shader_asset(&material->depth_shader_asset,
143 memory_pool,
144 entry->name,
145 shaders::Type::depth,
146 get_string(find_prop(entry, "depth_shader_asset.vert_path")),
147 get_string(find_prop(entry, "depth_shader_asset.frag_path")),
148 get_string(find_prop(entry, "depth_shader_asset.geom_path")));
149 }
150 }
151
152 auto *builtin_textures = renderer::get_builtin_textures();
153
154 // Iterate through all props to get textures, since those could have any name
155 range (0, entry->n_props) {
156 prop = &entry->props[idx];
157
158 if (pstr_starts_with(prop->name, TEXTURE_PREFIX)) {
159 // Handle a texture
160 mats::Texture texture = {};
161 // The uniform name is the prop name without the prefix
162 // The first value is the type, and the second the path
163 // e.g. textures.foam_texture = [other, water_foam.png]
164 char const *uniform_name = &prop->name[TEXTURE_PREFIX_LENGTH];
165 mats::init_texture(&texture,
166 mats::texture_type_from_string(prop->values[0].string_value),
167 prop->values[1].string_value);
168 mats::add_texture_to_material(material, texture, uniform_name);
169 } else if (pstr_starts_with(prop->name, BUILTIN_TEXTURE_PREFIX)) {
170 // Handle a builtin texture
171 char const *builtin_uniform_name = &prop->name[BUILTIN_TEXTURE_PREFIX_LENGTH];
172 if (pstr_eq(builtin_uniform_name, "g_position_texture")) {
173 mats::add_texture_to_material(
174 material, *builtin_textures->g_position_texture, builtin_uniform_name);
175 } else if (pstr_eq(builtin_uniform_name, "g_albedo_texture")) {
176 mats::add_texture_to_material(
177 material, *builtin_textures->g_albedo_texture, builtin_uniform_name);
178 } else if (pstr_eq(builtin_uniform_name, "shadowmaps_3d")) {
179 mats::add_texture_to_material(
180 material, *builtin_textures->shadowmaps_3d_texture, builtin_uniform_name);
181 } else if (pstr_eq(builtin_uniform_name, "shadowmaps_2d")) {
182 mats::add_texture_to_material(
183 material, *builtin_textures->shadowmaps_2d_texture, builtin_uniform_name);
184 } else {
185 logs::fatal("Attempted to use unsupported built-in texture %s",
186 builtin_uniform_name);
187 }
188 }
189 }
190}
191
192
193void
194peony_parser_utils::create_model_loader_from_peony_file_entry(
195 peony_parser::Entry *entry,
196 entities::Handle entity_handle,
197 models::ModelLoader *model_loader
198) {
199 peony_parser::Prop *model_path_prop = find_prop(entry, "model_path");
200 assert(model_path_prop);
201 char const *model_path = get_string(model_path_prop);
202
203 models::init_model_loader(model_loader, model_path);
204
205 peony_parser::Prop *materials_prop = find_prop(entry, "materials");
206 model_loader->n_material_names = materials_prop->n_values;
207 range (0, materials_prop->n_values) {
208 pstr_copy(model_loader->material_names[idx],
209 MAX_COMMON_NAME_LENGTH,
210 materials_prop->values[idx].string_value);
211 }
212}
213
214
215void
216peony_parser_utils::create_entity_loader_from_peony_file_entry(
217 peony_parser::Entry *entry,
218 entities::Handle entity_handle,
219 models::EntityLoader *entity_loader
220) {
221 peony_parser::Prop *model_path_prop = find_prop(entry, "model_path");
222 assert(model_path_prop);
223 char const *model_path = get_string(model_path_prop);
224
225 // Get render pass
226 auto render_pass = drawable::Pass::none;
227 peony_parser::Prop *render_passes_prop = find_prop(entry, "render_passes");
228 if (render_passes_prop) {
229 range (0, render_passes_prop->n_values) {
230 render_pass = (drawable::Pass)(
231 (u32)render_pass |
232 (u32)drawable::render_pass_from_string(
233 render_passes_prop->values[idx].string_value));
234 }
235 } else {
236 logs::warning(
237 "Loading EntityLoader with no RenderPasses, you probably don't want this?");
238 }
239
240 // Initialise everything except the components
241 models::init_entity_loader(entity_loader, entry->name, model_path,
242 render_pass, entity_handle);
243
244 // Build physics::Component, spatial::Component, lights::Component, behavior::Component
245 range (0, entry->n_props) {
246 peony_parser::Prop *prop = &entry->props[idx];
247 if (pstr_eq(prop->name, "physics_component.obb.center")) {
248 entity_loader->physics_component.obb.center = *get_vec3(prop);
249 } else if (pstr_eq(prop->name, "physics_component.obb.x_axis")) {
250 entity_loader->physics_component.obb.x_axis = *get_vec3(prop);
251 } else if (pstr_eq(prop->name, "physics_component.obb.y_axis")) {
252 entity_loader->physics_component.obb.y_axis = *get_vec3(prop);
253 } else if (pstr_eq(prop->name, "physics_component.obb.extents")) {
254 entity_loader->physics_component.obb.extents = *get_vec3(prop);
255 } else if (pstr_eq(prop->name, "spatial_component.position")) {
256 entity_loader->spatial_component.position = *get_vec3(prop);
257 } else if (pstr_eq(prop->name, "spatial_component.rotation")) {
258 entity_loader->spatial_component.rotation = glm::angleAxis(
259 radians((*get_vec4(prop))[0]),
260 v3((*get_vec4(prop))[1],
261 (*get_vec4(prop))[2],
262 (*get_vec4(prop))[3]));
263 } else if (pstr_eq(prop->name, "spatial_component.scale")) {
264 entity_loader->spatial_component.scale = *get_vec3(prop);
265 } else if (pstr_eq(prop->name, "light_component.type")) {
266 entity_loader->light_component.type =
267 lights::light_type_from_string(get_string(prop));
268 } else if (pstr_eq(prop->name, "light_component.direction")) {
269 entity_loader->light_component.direction = *get_vec3(prop);
270 } else if (pstr_eq(prop->name, "light_component.color")) {
271 entity_loader->light_component.color = *get_vec4(prop);
272 } else if (pstr_eq(prop->name, "light_component.attenuation")) {
273 entity_loader->light_component.attenuation = *get_vec4(prop);
274 } else if (pstr_eq(prop->name, "behavior_component.behavior")) {
275 entity_loader->behavior_component.behavior =
276 behavior::behavior_from_string(get_string(prop));
277 }
278 }
279}