You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
715 lines
28 KiB
715 lines
28 KiB
#include "entity.h"
|
|
#include "../common/array.h"
|
|
#include "../common/log.h"
|
|
#include "../common/string_utils.h"
|
|
#include "transform.h"
|
|
#include "camera.h"
|
|
#include "light.h"
|
|
#include "model.h"
|
|
#include "material.h"
|
|
#include "geometry.h"
|
|
#include "framebuffer.h"
|
|
#include "scene.h"
|
|
#include "../common/variant.h"
|
|
#include "../common/parser.h"
|
|
#include "../common/hashmap.h"
|
|
#include "../system/file_io.h"
|
|
#include "../system/physics.h"
|
|
#include "scene.h"
|
|
#include "game.h"
|
|
#include "texture.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
|
|
#define MAX_ENTITY_PROP_NAME_LEN 128
|
|
#define MAX_ENTITY_PROP_LEN 256
|
|
#define MAX_LINE_LEN 512
|
|
|
|
void entity_init(struct Entity* entity, const char* name, struct Entity* parent)
|
|
{
|
|
assert(entity);
|
|
|
|
strncpy(entity->name, name ? name : "DEFAULT_ENTITY_NAME", MAX_ENTITY_NAME_LEN);
|
|
entity->name[MAX_ENTITY_NAME_LEN - 1] = '\0';
|
|
entity->type = ET_DEFAULT;
|
|
entity->archetype_index = -1;
|
|
entity->flags = EF_ACTIVE;
|
|
entity_bounding_box_reset(entity, false);
|
|
entity->derived_bounding_box.min = (vec3){ -0.5f, -0.5f, -0.5f };
|
|
entity->derived_bounding_box.max = (vec3){ 0.5f, 0.5f, 0.5f };
|
|
transform_init(entity, parent);
|
|
}
|
|
|
|
void entity_reset(struct Entity* entity, int id)
|
|
{
|
|
assert(entity);
|
|
entity->id = id;
|
|
entity->type = ET_DEFAULT;
|
|
entity->archetype_index = -1;
|
|
entity->flags = EF_NONE;
|
|
entity_bounding_box_reset(entity, false);
|
|
entity->derived_bounding_box.min = (vec3){ -0.5f, -0.5f, -0.5f };
|
|
entity->derived_bounding_box.max = (vec3){ 0.5f, 0.5f, 0.5f };
|
|
memset(entity->name, '\0', MAX_ENTITY_NAME_LEN);
|
|
}
|
|
|
|
void entity_update_derived_bounding_box(struct Entity* entity)
|
|
{
|
|
struct Bounding_Box* derived_box = &entity->derived_bounding_box;
|
|
vec3_fill(&derived_box->min, FLT_MAX, FLT_MAX, FLT_MAX);
|
|
vec3_fill(&derived_box->max, -FLT_MAX, -FLT_MAX, -FLT_MAX);
|
|
vec3 box_vertices[8];
|
|
bv_bounding_box_vertices_get(&entity->bounding_box, box_vertices);
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
vec3 transformed_vertex = { 0.f, 0.f, 0.f };
|
|
vec3_mul_mat4(&transformed_vertex, &box_vertices[i], &entity->transform.trans_mat);
|
|
if(transformed_vertex.x < derived_box->min.x) derived_box->min.x = transformed_vertex.x;
|
|
if(transformed_vertex.y < derived_box->min.y) derived_box->min.y = transformed_vertex.y;
|
|
if(transformed_vertex.z < derived_box->min.z) derived_box->min.z = transformed_vertex.z;
|
|
|
|
if(transformed_vertex.x > derived_box->max.x) derived_box->max.x = transformed_vertex.x;
|
|
if(transformed_vertex.y > derived_box->max.y) derived_box->max.y = transformed_vertex.y;
|
|
if(transformed_vertex.z > derived_box->max.z) derived_box->max.z = transformed_vertex.z;
|
|
}
|
|
}
|
|
|
|
void entity_bounding_box_reset(struct Entity* entity, bool update_derived)
|
|
{
|
|
if(entity->type == ET_STATIC_MESH)
|
|
{
|
|
struct Static_Mesh* mesh = (struct Static_Mesh*)entity;
|
|
struct Geometry* geom = geom_get(mesh->model.geometry_index);
|
|
vec3_assign(&entity->bounding_box.min, &geom->bounding_box.min);
|
|
vec3_assign(&entity->bounding_box.max, &geom->bounding_box.max);
|
|
}
|
|
else
|
|
{
|
|
vec3_fill(&entity->bounding_box.min, -0.5f, -0.5f, -0.5f);
|
|
vec3_fill(&entity->bounding_box.max, 0.5f, 0.5f, 0.5f);
|
|
}
|
|
if(update_derived) entity_update_derived_bounding_box(entity);
|
|
|
|
}
|
|
|
|
bool entity_write(struct Entity* entity, struct Parser_Object* object, bool write_transform)
|
|
{
|
|
if(!object)
|
|
{
|
|
log_error("entity:write", "Invalid object");
|
|
return false;
|
|
}
|
|
|
|
/* First write all properties common to all entity types */
|
|
struct Hashmap* entity_data = object->data;
|
|
|
|
hashmap_str_set(entity_data, "name", entity->name);
|
|
hashmap_int_set(entity_data, "type", entity->type);
|
|
hashmap_bool_set(entity_data, "active", entity->flags & EF_ACTIVE ? true : false);
|
|
|
|
//if(entity->has_collision)
|
|
//{
|
|
// if(entity->collision.rigidbody)
|
|
// hashmap_bool_set(entity_data, "has_rigidbody", true);
|
|
// else
|
|
// hashmap_bool_set(entity_data, "has_rigidbody", false);
|
|
|
|
// int shape_type = platform->physics.cs_type_get(entity->collision.collision_shape);
|
|
// hashmap_int_set(entity_data, "collision_shape_type", shape_type);
|
|
// switch(shape_type)
|
|
// {
|
|
// case CST_BOX:
|
|
// {
|
|
// float x, y, z;
|
|
// x = y = z = 0.f;
|
|
// platform->physics.cs_box_params_get(entity->collision.collision_shape, &x, &y, &z);
|
|
// hashmap_float_set(entity_data, "collision_shape_x", x);
|
|
// hashmap_float_set(entity_data, "collision_shape_y", y);
|
|
// hashmap_float_set(entity_data, "collision_shape_z", z);
|
|
// }
|
|
// break;
|
|
// case CST_SPHERE:
|
|
// {
|
|
// float radius = 0.f;
|
|
// platform->physics.cs_sphere_radius_get(entity->collision.collision_shape);
|
|
// hashmap_float_set(entity_data, "collision_shape_radius", radius);
|
|
// }
|
|
// break;
|
|
// case CST_CAPSULE:
|
|
// {
|
|
// float length = 0.f, radius = 0.f;
|
|
// platform->physics.cs_capsule_params_get(entity->collision.collision_shape, &radius, &length);
|
|
// hashmap_float_set(entity_data, "collision_shape_length", length);
|
|
// hashmap_float_set(entity_data, "collision_shape_radius", radius);
|
|
// }
|
|
// break;
|
|
// case CST_PLANE:
|
|
// {
|
|
// float a, b, c, d;
|
|
// platform->physics.cs_plane_params_get(entity->collision.collision_shape, &a, &b, &c, &d);
|
|
// hashmap_float_set(entity_data, "collision_shape_a", a);
|
|
// hashmap_float_set(entity_data, "collision_shape_b", b);
|
|
// hashmap_float_set(entity_data, "collision_shape_c", c);
|
|
// hashmap_float_set(entity_data, "collision_shape_d", d);
|
|
// }
|
|
// break;
|
|
// default: break;
|
|
// }
|
|
|
|
//}
|
|
|
|
//struct Entity* parent = entity_get_parent(entity->id);
|
|
//hashmap_str_set(entity_data, "parent", parent ? parent->name : "NONE");
|
|
|
|
/* Transform */
|
|
hashmap_vec3_set(entity_data, "position", &entity->transform.position);
|
|
hashmap_vec3_set(entity_data, "scale", &entity->transform.scale);
|
|
hashmap_quat_set(entity_data, "rotation", &entity->transform.rotation);
|
|
|
|
if(entity->type != ET_STATIC_MESH)
|
|
{
|
|
hashmap_vec3_set(entity_data, "bouding_box_min", &entity->bounding_box.min);
|
|
hashmap_vec3_set(entity_data, "bouding_box_max", &entity->bounding_box.max);
|
|
}
|
|
|
|
switch(entity->type)
|
|
{
|
|
case ET_CAMERA:
|
|
{
|
|
struct Camera* camera = (struct Camera*)entity;
|
|
hashmap_bool_set(entity_data, "ortho", camera->ortho);
|
|
hashmap_bool_set(entity_data, "resizeable", camera->resizeable);
|
|
hashmap_float_set(entity_data, "fov", camera->fov);
|
|
hashmap_float_set(entity_data, "zoom", camera->zoom);
|
|
hashmap_float_set(entity_data, "nearz", camera->nearz);
|
|
hashmap_float_set(entity_data, "farz", camera->farz);
|
|
hashmap_vec4_set(entity_data, "clear_color", &camera->clear_color);
|
|
if(camera->fbo != -1)
|
|
{
|
|
hashmap_bool_set(entity_data, "has_fbo", true);
|
|
hashmap_int_set(entity_data, "fbo_height", framebuffer_height_get(camera->fbo));
|
|
hashmap_int_set(entity_data, "fbo_width", framebuffer_width_get(camera->fbo));
|
|
hashmap_bool_set(entity_data, "fbo_has_render_tex", camera->render_tex == -1 ? false : true);
|
|
hashmap_bool_set(entity_data, "fbo_has_depth_tex", camera->depth_tex == -1 ? false : true);
|
|
}
|
|
else
|
|
{
|
|
hashmap_bool_set(entity_data, "has_fbo", true);
|
|
}
|
|
break;
|
|
}
|
|
case ET_STATIC_MESH:
|
|
{
|
|
struct Static_Mesh* mesh = (struct Static_Mesh*)entity;
|
|
struct Geometry* geom = geom_get(mesh->model.geometry_index);
|
|
hashmap_int_set(entity_data, "material", mesh->model.material->type);
|
|
|
|
//Set material model params for this particular mesh
|
|
struct Model* model = &mesh->model;
|
|
switch(model->material->type)
|
|
{
|
|
case MAT_BLINN:
|
|
hashmap_vec4_set(entity_data, "diffuse_color", &model->material_params[MMP_DIFFUSE_COL].val_vec4);
|
|
hashmap_str_set(entity_data, "diffuse_texture", model->material_params[MMP_DIFFUSE_TEX].val_int == -1 ? "default.tga" : texture_get_name(model->material_params[MMP_DIFFUSE_TEX].val_int));
|
|
hashmap_float_set(entity_data, "diffuse", model->material_params[MMP_DIFFUSE].val_float);
|
|
hashmap_float_set(entity_data, "specular", model->material_params[MMP_SPECULAR].val_float);
|
|
hashmap_float_set(entity_data, "specular_strength", model->material_params[MMP_SPECULAR_STRENGTH].val_float);
|
|
hashmap_vec2_set(entity_data, "uv_scale", &model->material_params[MMP_UV_SCALE].val_vec2);
|
|
break;
|
|
case MAT_UNSHADED:
|
|
hashmap_vec3_set(entity_data, "diffuse_color", &model->material_params[MMP_DIFFUSE_COL].val_vec3);
|
|
hashmap_str_set(entity_data, "diffuse_texture", model->material_params[MMP_DIFFUSE_TEX].val_int == -1 ? "default.tga" : texture_get_name(model->material_params[MMP_DIFFUSE_TEX].val_int));
|
|
hashmap_vec2_set(entity_data, "uv_scale", &model->material_params[MMP_UV_SCALE].val_vec2);
|
|
break;
|
|
};
|
|
|
|
hashmap_str_set(entity_data, "geometry", geom->filename);
|
|
break;
|
|
}
|
|
case ET_LIGHT:
|
|
{
|
|
struct Light* light = (struct Light*)entity;
|
|
hashmap_int_set(entity_data, "light_type", light->type);
|
|
hashmap_float_set(entity_data, "outer_angle", light->outer_angle);
|
|
hashmap_float_set(entity_data, "inner_angle", light->inner_angle);
|
|
hashmap_float_set(entity_data, "falloff", light->falloff);
|
|
hashmap_float_set(entity_data, "radius", light->radius);
|
|
hashmap_float_set(entity_data, "intensity", light->intensity);
|
|
hashmap_float_set(entity_data, "depth_bias", light->depth_bias);
|
|
hashmap_bool_set(entity_data, "valid", light->valid);
|
|
hashmap_bool_set(entity_data, "cast_shadow", light->cast_shadow);
|
|
hashmap_bool_set(entity_data, "pcf_enabled", light->pcf_enabled);
|
|
hashmap_vec3_set(entity_data, "color", &light->color);
|
|
break;
|
|
}
|
|
case ET_SOUND_SOURCE:
|
|
{
|
|
struct Sound_Source* sound_source = (struct Sound_Source*)entity;
|
|
hashmap_str_set(entity_data, "source_filename", sound_source->source_buffer->filename);
|
|
hashmap_bool_set(entity_data, "playing", sound_source->playing);
|
|
hashmap_int_set(entity_data, "sound_type", sound_source->type);
|
|
hashmap_bool_set(entity_data, "loop", sound_source->loop);
|
|
hashmap_float_set(entity_data, "volume", sound_source->volume);
|
|
hashmap_float_set(entity_data, "sound_min_distance", sound_source->min_distance);
|
|
hashmap_float_set(entity_data, "sound_max_distance", sound_source->max_distance);
|
|
hashmap_float_set(entity_data, "rolloff_factor", sound_source->rolloff_factor);
|
|
hashmap_int_set(entity_data, "sound_attenuation_type", sound_source->attenuation_type);
|
|
break;
|
|
}
|
|
};
|
|
|
|
return true;
|
|
}
|
|
|
|
bool entity_save(struct Entity* entity, const char* filename, int directory_type)
|
|
{
|
|
char prefixed_filename[MAX_FILENAME_LEN + 16];
|
|
snprintf(prefixed_filename, MAX_FILENAME_LEN + 16, "entities/%s.symtres", filename);
|
|
FILE* entity_file = io_file_open(directory_type, prefixed_filename, "w");
|
|
if(!entity_file)
|
|
{
|
|
log_error("entity:save", "Failed to open entity file %s for writing");
|
|
return false;
|
|
}
|
|
|
|
struct Parser* parser = parser_new();
|
|
struct Parser_Object* object = parser_object_new(parser, PO_ENTITY);
|
|
if(!entity_write(entity, object, false))
|
|
{
|
|
log_error("entity:save", "Failed to save entity : %s to file : %s", entity->name, prefixed_filename);
|
|
parser_free(parser);
|
|
fclose(entity_file);
|
|
return false;
|
|
}
|
|
|
|
// See if the entity has any children, if it does,
|
|
// write the entity first then, write all its children
|
|
int num_children = array_len(entity->transform.children);
|
|
for (int i = 0; i < num_children; i++)
|
|
{
|
|
struct Parser_Object* child_object = parser_object_new(parser, PO_ENTITY);
|
|
struct Entity* child_entity = entity->transform.children[i];
|
|
if(!entity_write(child_entity, child_object, true))
|
|
{
|
|
log_error("entity:save", "Failed to write child entity : %s for parent entity : %s to file : %s", entity->name, child_entity->name, prefixed_filename);
|
|
parser_free(parser);
|
|
fclose(entity_file);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(parser_write_objects(parser, entity_file, prefixed_filename))
|
|
log_message("Entity %s saved to %s", entity->name, prefixed_filename);
|
|
|
|
//Update the entity's archetype index to the one we just saved
|
|
entity->archetype_index = scene_entity_archetype_add(game_state_get()->scene, filename);
|
|
parser_free(parser);
|
|
fclose(entity_file);
|
|
return true;
|
|
}
|
|
|
|
struct Entity* entity_read(struct Parser_Object* object, struct Entity* parent_entity)
|
|
{
|
|
assert(object);
|
|
|
|
struct Scene* scene = game_state_get()->scene;
|
|
if(object->type != PO_ENTITY)
|
|
{
|
|
log_error("entity:read", "Invalid object type");
|
|
return NULL;
|
|
}
|
|
|
|
const char* name = hashmap_str_get(object->data, "name");
|
|
int type = hashmap_int_get(object->data, "type");
|
|
|
|
if(!name)
|
|
{
|
|
log_error("entity:read", "No entity name provided");
|
|
return NULL;
|
|
}
|
|
|
|
if(type < 0 || type >= ET_MAX)
|
|
{
|
|
log_error("entity:read", "Invalid entity type");
|
|
return NULL;
|
|
}
|
|
|
|
struct Entity* new_entity = NULL;
|
|
switch(type)
|
|
{
|
|
case ET_CAMERA:
|
|
{
|
|
bool has_fbo = false;
|
|
bool fbo_has_depth_tex = false;
|
|
bool fbo_has_render_tex = false;
|
|
int fbo_width = -1;
|
|
int fbo_height = -1;
|
|
struct Camera* camera = scene_camera_create(scene, name, parent_entity, 320, 240);
|
|
if(!camera) return new_entity;
|
|
if(hashmap_value_exists(object->data, "fov")) camera->fov = hashmap_float_get(object->data, "fov");
|
|
if(hashmap_value_exists(object->data, "resizeable")) camera->resizeable = hashmap_bool_get(object->data, "resizeable");
|
|
if(hashmap_value_exists(object->data, "zoom")) camera->zoom = hashmap_float_get(object->data, "zoom");
|
|
if(hashmap_value_exists(object->data, "nearz")) camera->nearz = hashmap_float_get(object->data, "nearz");
|
|
if(hashmap_value_exists(object->data, "farz")) camera->farz = hashmap_float_get(object->data, "farz");
|
|
if(hashmap_value_exists(object->data, "ortho")) camera->ortho = hashmap_bool_get(object->data, "ortho");
|
|
if(hashmap_value_exists(object->data, "has_fbo")) has_fbo = hashmap_bool_get(object->data, "has_fbo");
|
|
if(hashmap_value_exists(object->data, "fbo_has_depth_tex")) fbo_has_depth_tex = hashmap_bool_get(object->data, "fbo_has_depth_tex");
|
|
if(hashmap_value_exists(object->data, "fbo_has_render_tex")) fbo_has_render_tex = hashmap_bool_get(object->data, "fbo_has_render_tex");
|
|
if(hashmap_value_exists(object->data, "fbo_width")) fbo_width = hashmap_int_get(object->data, "fbo_width");
|
|
if(hashmap_value_exists(object->data, "fbo_height")) fbo_height = hashmap_int_get(object->data, "fbo_height");
|
|
if(hashmap_value_exists(object->data, "clear_color"))
|
|
{
|
|
vec4 color = hashmap_vec4_get(object->data, "clear_color");
|
|
vec4_assign(&camera->clear_color, &color);
|
|
}
|
|
|
|
float aspect_ratio = (float)fbo_width / (float)fbo_height;
|
|
camera->aspect_ratio = aspect_ratio <= 0.f ? (4.f / 3.f) : aspect_ratio;
|
|
|
|
camera->fbo = -1;
|
|
camera->render_tex = -1;
|
|
camera->depth_tex = -1;
|
|
|
|
if(has_fbo)
|
|
{
|
|
camera_attach_fbo(camera, fbo_width, fbo_height, fbo_has_depth_tex, fbo_has_render_tex, camera->resizeable);
|
|
}
|
|
|
|
camera_update_proj(camera);
|
|
camera_update_view(camera);
|
|
new_entity = &camera->base;
|
|
}
|
|
break;
|
|
case ET_LIGHT:
|
|
{
|
|
struct Light* light = scene_light_create(scene, name, parent_entity, LT_POINT);
|
|
if(hashmap_value_exists(object->data, "light_type")) light->type = hashmap_int_get(object->data, "light_type");
|
|
if(hashmap_value_exists(object->data, "outer_angle")) light->outer_angle = hashmap_float_get(object->data, "outer_angle");
|
|
if(hashmap_value_exists(object->data, "inner_angle")) light->inner_angle = hashmap_float_get(object->data, "inner_angle");
|
|
if(hashmap_value_exists(object->data, "falloff")) light->falloff = hashmap_float_get(object->data, "falloff");
|
|
if(hashmap_value_exists(object->data, "intensity")) light->intensity = hashmap_float_get(object->data, "intensity");
|
|
if(hashmap_value_exists(object->data, "depth_bias")) light->depth_bias = hashmap_float_get(object->data, "depth_bias");
|
|
if(hashmap_value_exists(object->data, "color")) light->color = hashmap_vec3_get(object->data, "color");
|
|
if(hashmap_value_exists(object->data, "cast_shadow")) light->cast_shadow = hashmap_bool_get(object->data, "cast_shadow");
|
|
if(hashmap_value_exists(object->data, "pcf_enabled")) light->pcf_enabled = hashmap_bool_get(object->data, "pcf_enabled");
|
|
if(hashmap_value_exists(object->data, "radius")) light->radius = hashmap_int_get(object->data, "radius");
|
|
new_entity = &light->base;
|
|
}
|
|
break;
|
|
case ET_SOUND_SOURCE:
|
|
{
|
|
struct Sound_Source* sound_source = scene_sound_source_create(scene, name, parent_entity, "sounds/teh_beatz.wav", ST_WAV, true, true);
|
|
struct Sound_Source_Buffer* default_source_buffer = sound_source->source_buffer;
|
|
uint default_source_instance = sound_source->source_instance;
|
|
|
|
if(hashmap_value_exists(object->data, "playing")) sound_source->playing = hashmap_bool_get(object->data, "playing");
|
|
if(hashmap_value_exists(object->data, "loop")) sound_source->loop = hashmap_bool_get(object->data, "loop");
|
|
if(hashmap_value_exists(object->data, "sound_min_distance")) sound_source->min_distance = hashmap_float_get(object->data, "sound_min_distance");
|
|
if(hashmap_value_exists(object->data, "sound_max_distance")) sound_source->max_distance = hashmap_float_get(object->data, "sound_max_distance");
|
|
if(hashmap_value_exists(object->data, "volume")) sound_source->volume = hashmap_float_get(object->data, "volume");
|
|
if(hashmap_value_exists(object->data, "rolloff_factor")) sound_source->rolloff_factor = hashmap_float_get(object->data, "rolloff_factor");
|
|
if(hashmap_value_exists(object->data, "sound_type")) sound_source->type = hashmap_int_get(object->data, "sound_type");
|
|
if(hashmap_value_exists(object->data, "sound_attenuation_type")) sound_source->attenuation_type = hashmap_int_get(object->data, "sound_attenuation_type");
|
|
if(hashmap_value_exists(object->data, "source_filename"))
|
|
{
|
|
struct Sound* sound = game_state_get()->sound;
|
|
|
|
sound_source->source_buffer = sound_source_create(sound, hashmap_str_get(object->data, "source_filename"), sound_source->type);
|
|
if(sound_source->source_buffer)
|
|
{
|
|
sound_source->source_instance = sound_source_instance_create(sound, sound_source->source_buffer, true);
|
|
|
|
vec3 abs_pos = {0.f, 0.f, 0.f};
|
|
transform_get_absolute_position(sound_source, &abs_pos);
|
|
sound_source_instance_update_position(sound, sound_source->source_instance, abs_pos);
|
|
|
|
sound_source_instance_loop_set(sound, sound_source->source_instance, sound_source->loop);
|
|
sound_source_instance_min_max_distance_set(sound, sound_source->source_instance, sound_source->min_distance, sound_source->max_distance);
|
|
sound_source_instance_attenuation_set(sound, sound_source->source_instance, sound_source->attenuation_type, sound_source->rolloff_factor);
|
|
sound_source_instance_volume_set(sound, sound_source->source_instance, sound_source->volume);
|
|
|
|
sound_update_3d(sound);
|
|
if(sound_source->playing)
|
|
sound_source_instance_play(sound, sound_source->source_instance);
|
|
|
|
//Stop the default sound source from playing now that we have loaded the actual buffer
|
|
sound_source_instance_destroy(sound, default_source_instance);
|
|
}
|
|
else
|
|
{
|
|
log_error("Failed to create sound source from '%s'", hashmap_str_get(object->data, "source_filename"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log_error("entity:read", "No filename provided for sound source for entity '%s'", name);
|
|
}
|
|
new_entity = &sound_source->base;
|
|
}
|
|
break;
|
|
case ET_PLAYER:
|
|
{
|
|
|
|
}
|
|
break;
|
|
case ET_STATIC_MESH:
|
|
{
|
|
const char* geometry_name = NULL;
|
|
int material_type = MAT_UNSHADED;
|
|
if(hashmap_value_exists(object->data, "geometry")) geometry_name = hashmap_str_get(object->data, "geometry");
|
|
if(hashmap_value_exists(object->data, "material")) material_type = hashmap_int_get(object->data, "material");
|
|
struct Static_Mesh* mesh = scene_static_mesh_create(scene, name, parent_entity, geometry_name, material_type);
|
|
new_entity = &mesh->base;
|
|
|
|
//Set material model params for this particular mesh
|
|
struct Model* model = &mesh->model;
|
|
switch(model->material->type)
|
|
{
|
|
case MAT_BLINN:
|
|
if(hashmap_value_exists(object->data, "diffuse_texture"))
|
|
{
|
|
const char* texture_name = hashmap_str_get(object->data, "diffuse_texture");
|
|
model->material_params[MMP_DIFFUSE_TEX].val_int = texture_create_from_file(texture_name, TU_DIFFUSE);
|
|
}
|
|
if(hashmap_value_exists(object->data, "diffuse_color")) model->material_params[MMP_DIFFUSE_COL].val_vec4 = hashmap_vec4_get(object->data, "diffuse_color");
|
|
if(hashmap_value_exists(object->data, "diffuse")) model->material_params[MMP_DIFFUSE].val_float = hashmap_float_get(object->data, "diffuse");
|
|
if(hashmap_value_exists(object->data, "specular")) model->material_params[MMP_SPECULAR].val_float = hashmap_float_get(object->data, "specular");
|
|
if(hashmap_value_exists(object->data, "specular_strength")) model->material_params[MMP_SPECULAR_STRENGTH].val_float = hashmap_float_get(object->data, "specular_strength");
|
|
if(hashmap_value_exists(object->data, "uv_scale")) model->material_params[MMP_UV_SCALE].val_vec2 = hashmap_vec2_get(object->data, "uv_scale");
|
|
break;
|
|
case MAT_UNSHADED:
|
|
if(hashmap_value_exists(object->data, "diffuse_color")) model->material_params[MMP_DIFFUSE_COL].val_vec4 = hashmap_vec4_get(object->data, "diffuse_color");
|
|
if(hashmap_value_exists(object->data, "diffuse_texture"))
|
|
{
|
|
const char* texture_name = hashmap_str_get(object->data, "diffuse_texture");
|
|
model->material_params[MMP_DIFFUSE_TEX].val_int = texture_create_from_file(texture_name, TU_DIFFUSE);
|
|
}
|
|
if(hashmap_value_exists(object->data, "uv_scale")) model->material_params[MMP_UV_SCALE].val_vec2 = hashmap_vec2_get(object->data, "uv_scale");
|
|
break;
|
|
};
|
|
}
|
|
break;
|
|
case ET_ROOT:
|
|
{
|
|
//scene_root_set(entity);
|
|
}
|
|
break;
|
|
default:
|
|
log_warning("Unhandled Entity type '%d' detected", type);
|
|
break;
|
|
}
|
|
|
|
vec3 position = { 0.f, 0.f, 0.f };
|
|
quat rotation = { 0.f, 0.f, 0.f, 1.f };
|
|
vec3 scale = { 1.f, 1.f, 1.f };
|
|
|
|
if(hashmap_value_exists(object->data, "position")) position = hashmap_vec3_get(object->data, "position");
|
|
if(hashmap_value_exists(object->data, "rotation")) rotation = hashmap_quat_get(object->data, "rotation");
|
|
if(hashmap_value_exists(object->data, "scale")) scale = hashmap_vec3_get(object->data, "scale");
|
|
|
|
transform_set_position(new_entity, &position);
|
|
transform_scale(new_entity, &scale);
|
|
quat_mul(&new_entity->transform.rotation, &new_entity->transform.rotation, &rotation);
|
|
|
|
if(new_entity->type != ET_STATIC_MESH)
|
|
{
|
|
if(hashmap_value_exists(object->data, "bounding_box_min")) new_entity->bounding_box.min = hashmap_vec3_get(object->data, "bounding_box_min");
|
|
if(hashmap_value_exists(object->data, "bounding_box_max")) new_entity->bounding_box.max = hashmap_vec3_get(object->data, "bounding_box_max");
|
|
}
|
|
|
|
transform_update_transmat(new_entity);
|
|
|
|
return new_entity;
|
|
}
|
|
|
|
struct Entity* entity_load(const char* filename, int directory_type)
|
|
{
|
|
char prefixed_filename[MAX_FILENAME_LEN + 16];
|
|
snprintf(prefixed_filename, MAX_FILENAME_LEN + 16, "entities/%s.symtres", filename);
|
|
FILE* entity_file = io_file_open(directory_type, prefixed_filename, "rb");
|
|
if(!entity_file)
|
|
{
|
|
log_error("entity:load", "Failed to open entity file %s for reading", prefixed_filename);
|
|
return false;
|
|
}
|
|
|
|
struct Parser* parsed_file = parser_load_objects(entity_file, prefixed_filename);
|
|
struct Entity* new_entity = false;
|
|
|
|
if(!parsed_file)
|
|
{
|
|
log_error("entity:load", "Failed to parse file '%s' for entity definition", prefixed_filename);
|
|
fclose(entity_file);
|
|
return false;
|
|
}
|
|
|
|
if(array_len(parsed_file->objects) == 0)
|
|
{
|
|
log_error("entity:load", "No objects found in file %s", prefixed_filename);
|
|
parser_free(parsed_file);
|
|
fclose(entity_file);
|
|
return false;
|
|
}
|
|
|
|
int num_entites_loaded = 0;
|
|
struct Entity* parent_entity = NULL;
|
|
for(int i = 0; i < array_len(parsed_file->objects); i++)
|
|
{
|
|
struct Parser_Object* object = &parsed_file->objects[i];
|
|
if(object->type != PO_ENTITY) continue;
|
|
|
|
new_entity = entity_read(object, parent_entity);
|
|
if(new_entity)
|
|
{
|
|
if(i == 0)
|
|
{
|
|
new_entity->archetype_index = scene_entity_archetype_add(game_state_get()->scene, filename);
|
|
parent_entity = new_entity;
|
|
}
|
|
// We don't want this entity to be saved when we're saving the scene because
|
|
// this entity and its parent will be loaded from this same file next time the scene is loaded
|
|
if(i != 0)
|
|
new_entity->flags |= EF_TRANSIENT;
|
|
num_entites_loaded++;
|
|
log_message("Entity %s loaded from %s", new_entity->name, prefixed_filename);
|
|
}
|
|
else
|
|
{
|
|
log_error("entity:load", "Failed to load entity from %s", prefixed_filename);
|
|
}
|
|
}
|
|
|
|
parser_free(parsed_file);
|
|
fclose(entity_file);
|
|
return parent_entity;
|
|
}
|
|
|
|
const char* entity_type_name_get(struct Entity* entity)
|
|
{
|
|
const char* typename = "NONE";
|
|
switch(entity->type)
|
|
{
|
|
case ET_NONE: typename = "None"; break;
|
|
case ET_DEFAULT: typename = "Default"; break;
|
|
case ET_CAMERA: typename = "Camera"; break;
|
|
case ET_LIGHT: typename = "Light"; break;
|
|
case ET_PLAYER: typename = "Player"; break;
|
|
case ET_ROOT: typename = "Root"; break;
|
|
case ET_SOUND_SOURCE: typename = "Sound Source"; break;
|
|
case ET_STATIC_MESH: typename = "Static Mesh"; break;
|
|
default: typename = "Unknown"; break;
|
|
};
|
|
return typename;
|
|
}
|
|
|
|
void entity_rigidbody_on_move(Rigidbody body)
|
|
{
|
|
struct Entity* entity = physics_body_data_get(body);
|
|
vec3 pos = {0.f};
|
|
quat rot = {0.f};
|
|
|
|
physics_body_position_get(body, &pos.x, &pos.y, &pos.z);
|
|
physics_body_rotation_get(body, &rot.x, &rot.y, &rot.z, &rot.w);
|
|
|
|
quat_assign(&entity->transform.rotation, &rot);
|
|
transform_set_position(entity, &pos);
|
|
entity->transform.sync_physics = false;
|
|
}
|
|
|
|
void entity_rigidbody_on_collision(Rigidbody body_A, Rigidbody body_B)
|
|
{
|
|
struct Entity* ent_A = NULL;
|
|
struct Entity* ent_B = NULL;
|
|
|
|
if(body_A)
|
|
{
|
|
ent_A = physics_body_data_get(body_A);
|
|
}
|
|
|
|
if(body_B)
|
|
{
|
|
ent_B = physics_body_data_get(body_B);
|
|
}
|
|
|
|
//if(ent_A && ent_A->collision.on_collision)
|
|
//{
|
|
// ent_A->collision.on_collision(ent_A, ent_B ? ent_B : NULL, body_A, body_B ? body_B : NULL);
|
|
//}
|
|
|
|
//if(ent_B && ent_B->collision.on_collision)
|
|
//{
|
|
// ent_B->collision.on_collision(ent_B, ent_A ? ent_A : NULL, body_B, body_A ? body_A : NULL);
|
|
//}
|
|
|
|
if(ent_A && ent_B)
|
|
{
|
|
//log_message("Entity %s collided with Entity %s", ent_A->name, ent_B->name);
|
|
}
|
|
}
|
|
|
|
void entity_rigidbody_set(struct Entity * entity, struct Collision* collision, Rigidbody body)
|
|
{
|
|
assert(entity && body);
|
|
|
|
//Remove previous rigidbody if there is any
|
|
if(collision->rigidbody || collision->collision_shape)
|
|
{
|
|
if(collision->rigidbody)
|
|
{
|
|
physics_body_remove(collision->rigidbody);
|
|
}
|
|
else if(collision->collision_shape)
|
|
{
|
|
physics_cs_remove(collision->collision_shape);
|
|
}
|
|
collision->rigidbody = NULL;
|
|
collision->collision_shape = NULL;
|
|
}
|
|
|
|
collision->rigidbody = body;
|
|
collision->collision_shape = physics_body_cs_get(body);
|
|
|
|
vec3 abs_pos = {0.f, 0.f, 0.f};
|
|
quat abs_rot = {0.f, 0.f, 0.f, 1.f};
|
|
transform_get_absolute_position(entity, &abs_pos);
|
|
transform_get_absolute_rot(entity, &abs_rot);
|
|
|
|
physics_body_rotation_set(body, abs_rot.x, abs_rot.y, abs_rot.z, abs_rot.w);
|
|
physics_body_position_set(body, abs_pos.x, abs_pos.y, abs_pos.z);
|
|
physics_body_data_set(body, entity);
|
|
}
|
|
|
|
void entity_collision_shape_set(struct Entity* entity, struct Collision* collision, Collision_Shape shape)
|
|
{
|
|
assert(entity && shape);
|
|
|
|
if(collision->rigidbody || collision->collision_shape)
|
|
{
|
|
if(collision->rigidbody)
|
|
{
|
|
physics_body_remove(collision->rigidbody);
|
|
}
|
|
else if(collision->collision_shape)
|
|
{
|
|
physics_cs_remove(collision->collision_shape);
|
|
}
|
|
collision->rigidbody = NULL;
|
|
collision->collision_shape = NULL;
|
|
}
|
|
|
|
collision->collision_shape = shape;
|
|
physics_cs_data_set(shape, entity);
|
|
}
|
|
|
|
void entity_rename(struct Entity* entity, const char* new_name)
|
|
{
|
|
assert(entity);
|
|
memset(entity->name, '\0', MAX_ENTITY_NAME_LEN);
|
|
snprintf(entity->name, MAX_ENTITY_NAME_LEN, new_name);
|
|
}
|
|
|