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.
347 lines
9.0 KiB
347 lines
9.0 KiB
|
|
#include <string> |
|
|
|
#include <glm/glm.hpp> |
|
#include <rapidjson/document.h> |
|
#include <rapidjson/schema.h> |
|
#include <rapidjson/stringbuffer.h> |
|
#include <rapidjson/error/en.h> |
|
#include <rapidjson/writer.h> |
|
#include "aixlog.hpp" |
|
|
|
#include "entity.h" |
|
#include "hexlib.h" |
|
#include "mesh.h" |
|
#include "scene_loader.h" |
|
|
|
|
|
struct slSceneDoc |
|
{ |
|
rapidjson::Document* doc; |
|
}; |
|
|
|
// TODO: find a better way to avoid threading 'data_dir' through gooey |
|
// this would be a good candidate to go in some global application config structure |
|
static const char* g_data_dir; |
|
|
|
|
|
// forward declarations |
|
bool parseFile(rapidjson::Document* doc, const char* data_dir, const char* file_name); |
|
glm::vec3 parseVec3(const rapidjson::Value& node); |
|
glm::vec4 parseVec4(const rapidjson::Value& node); |
|
bool validateScene(rapidjson::Document* schema_doc, rapidjson::Document* scene_doc, |
|
const char* scene_file); |
|
void addV3f(rapidjson::Writer<rapidjson::StringBuffer>& writer, float x, float y, float z); |
|
|
|
|
|
// interface |
|
|
|
slSceneDoc* |
|
slLoadFile(const char* data_dir, const char* scene_file, const char* schema_file) |
|
{ |
|
LOG(INFO) << "Loading scene file: " << scene_file << "\n"; |
|
rapidjson::Document schema_doc; |
|
slSceneDoc* sd = UTIL_ALLOC(1, slSceneDoc); |
|
sd->doc = new rapidjson::Document(); |
|
|
|
bool retDoc = parseFile(sd->doc, data_dir, scene_file); |
|
bool retSchema = parseFile(&schema_doc, data_dir, schema_file); |
|
|
|
if (!retDoc || !retSchema || !validateScene(&schema_doc, sd->doc, scene_file)) { |
|
slFreeSceneDoc(sd); |
|
return nullptr; |
|
} |
|
|
|
g_data_dir = data_dir; |
|
return sd; |
|
} |
|
|
|
void |
|
slFreeSceneDoc(slSceneDoc* sd) |
|
{ |
|
if (sd->doc != nullptr) { |
|
delete sd->doc; |
|
sd->doc = nullptr; |
|
} |
|
|
|
utilSafeFree(sd); |
|
} |
|
|
|
bool |
|
slParseEntities( |
|
slSceneDoc* sd, |
|
Entity* entity_array, |
|
uint& entity_count, |
|
uint max_entities, |
|
const char* data_dir) |
|
{ |
|
const rapidjson::Value& entities = (*sd->doc)["entities"]; |
|
if (entities.Size() > max_entities) { |
|
LOG(ERROR) << "entity count: " << entities.Size() << ", was more than max_entities: " |
|
<< max_entities << "\n"; |
|
return false; |
|
} |
|
|
|
for (uint i = 0; i < entities.Size(); i++) { |
|
meMeshGroup mg; |
|
std::string model_path; |
|
model_path.append(data_dir).append("/").append(entities[i]["model_file"].GetString()); |
|
|
|
if (meLoadFromFile(data_dir, model_path.c_str(), mg)) { |
|
Entity& e = entity_array[i]; |
|
e.mesh_group = mg; |
|
e.scale = parseVec3(entities[i]["scale"]); |
|
e.translation = parseVec3(entities[i]["position"]); |
|
} else { |
|
LOG(ERROR) << "Error loading mesh file\n"; |
|
meFreeMeshGroup(mg); |
|
return false; |
|
} |
|
|
|
entity_count++; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void |
|
slParseCamera(slSceneDoc* sd, camera& cam) |
|
{ |
|
const rapidjson::Value& json_cam = (*sd->doc)["camera"]; |
|
cameraInitPerspective(cam, |
|
parseVec3(json_cam["position"]), |
|
parseVec3(json_cam["target"]), |
|
parseVec3(json_cam["world_up"]) |
|
); |
|
} |
|
|
|
void |
|
slParseHexGrid(slSceneDoc* sd, hexgrid& hg, util_image& palette_image) |
|
{ |
|
const rapidjson::Value& json_grid = (*sd->doc)["hex_grid"]; |
|
|
|
std::string grid_type_str = json_grid["grid_type"].GetString(); |
|
if (grid_type_str == "hexagon") { |
|
hg.gridT = HEXAGON; |
|
hg.hex_radius = json_grid["hex_radius"].GetInt(); |
|
} |
|
else if (grid_type_str == "parallelogram") |
|
hg.gridT = PARALLELOGRAM; |
|
else if (grid_type_str == "rhombus") |
|
hg.gridT = RHOMBUS; |
|
else if (grid_type_str == "hash_map") { |
|
hg.gridT = HASH_MAP; |
|
// NOTE: 256 is the size of hg.map_file[256] |
|
// TODO: should probably define max_len as a constant somewhere |
|
utilCopyCStr(hg.map_file, utilBaseName(json_grid["map_file"].GetString()), 256); |
|
} |
|
|
|
glm::vec3 pos = parseVec3(json_grid["position"]); |
|
hg.position.x = pos.x; |
|
hg.position.y = pos.y; |
|
hg.position.z = pos.z; |
|
hg.hex_size = json_grid["hex_size"].GetInt(); |
|
|
|
hg.fill_color_uv = utilGetPaletteCoords(palette_image, json_grid["fill_color"].GetInt()); |
|
hg.selected_fill_color_uv = utilGetPaletteCoords(palette_image, |
|
json_grid["selected_fill_color"].GetInt()); |
|
hg.line_color_uv = utilGetPaletteCoords(palette_image, json_grid["hex_line_color"].GetInt()); |
|
|
|
std::string orientation_str = json_grid["hexlib_orientation"].GetString(); |
|
hg.layout_mode = (orientation_str == "layout_flat") ? LAYOUT_FLAT : LAYOUT_POINTY; |
|
} |
|
|
|
bool |
|
slCreateHexRenderGroups(hexgrid& hg, render_state* rs) |
|
{ |
|
uint buf_len = MAX_HEX_COUNT * IDX_PER_HEX_FILLED; |
|
rs->filled_hex_render_group = rgInitSingle(rs->default_shader, buf_len, true); |
|
|
|
buf_len = MAX_HEX_COUNT * IDX_PER_HEX_LINES; |
|
rs->hex_line_render_group = rgInitSingle(rs->default_shader, buf_len, true, 0, GL_LINES); |
|
|
|
if ((rs->filled_hex_render_group == nullptr) || (rs->hex_line_render_group == nullptr)) { |
|
LOG(ERROR) << "Error allocating render_group, exiting\n"; |
|
return false; |
|
} |
|
|
|
// TODO: can remove these once we're using the global color palatte for all textures |
|
rs->filled_hex_render_group->render_objects[0]->tex_id = rs->palette_id; |
|
rs->hex_line_render_group->render_objects[0]->tex_id = rs->palette_id; |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
slParseLights(slSceneDoc* sd, rg_point_light* lights, uint& num_lights, uint max_lights) |
|
{ |
|
const rapidjson::Value& json_lights = (*sd->doc)["lights"]; |
|
|
|
if (json_lights.Size() > max_lights) { |
|
LOG(ERROR) << "Too many lights\n"; |
|
return false; |
|
} |
|
|
|
num_lights = json_lights.Size(); |
|
|
|
for (uint i = 0; i < num_lights; i++) { |
|
lights[i].position = parseVec3(json_lights[i]["position"]); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
slSaveGridFile(hexgrid& hg) |
|
{ |
|
uint max_len = 256; |
|
char full_path[max_len]; |
|
if (utilConcatPath(full_path, g_data_dir, hg.map_file, max_len)) { |
|
LOG(INFO) << "saving grid file: " << full_path << "\n"; |
|
|
|
rapidjson::StringBuffer sb; |
|
rapidjson::Writer<rapidjson::StringBuffer> writer(sb); |
|
|
|
writer.StartObject(); |
|
|
|
writer.Key("hex_size"); writer.Uint(hg.hex_size); |
|
writer.Key("layout_mode"); |
|
writer.String((hg.layout_mode == LAYOUT_FLAT) ? "layout_flat" : "layout_pointy"); |
|
writer.Key("world_position"); |
|
addV3f(writer, hg.position.x, hg.position.y, hg.position.z); |
|
writer.Key("world_normal"); |
|
addV3f(writer, hg.normal.x, hg.normal.y, hg.normal.z); |
|
|
|
writer.Key("hexes"); |
|
writer.StartArray(); |
|
|
|
for (auto& it : hg.hex_map) { |
|
hex_info& hxi = it.second; |
|
writer.StartObject(); |
|
writer.Key("XPos"); writer.Double(hxi.XPos); |
|
writer.Key("YPos"); writer.Double(hxi.YPos); |
|
writer.Key("q"); writer.Int(hxi.hex.q); |
|
writer.Key("r"); writer.Int(hxi.hex.r); |
|
writer.Key("s"); writer.Int(hxi.hex.s); |
|
writer.Key("vertices"); |
|
writer.StartArray(); |
|
|
|
for (uint i = 0; i < hxi.vertices.size(); i++) { |
|
// TODO: hard coded z value for hex vertices |
|
Point p = hxi.vertices[i]; |
|
addV3f(writer, p.x, p.y, 0.f); |
|
} |
|
|
|
writer.EndArray(); |
|
writer.EndObject(); |
|
} |
|
|
|
writer.EndArray(); |
|
|
|
writer.EndObject(); |
|
|
|
if (writer.IsComplete()) { |
|
if (utilWriteTextFile(full_path, sb.GetString())) { |
|
LOG(INFO) << "save successful\n"; |
|
return true; |
|
} |
|
|
|
LOG(ERROR) << "error saving file: " << full_path << "\n"; |
|
return false; |
|
} |
|
|
|
LOG(ERROR) << "malformed json\n"; |
|
return false; |
|
} |
|
|
|
LOG(ERROR) << "error creating path\n"; |
|
return false; |
|
} |
|
|
|
|
|
// internal |
|
|
|
bool |
|
parseFile(rapidjson::Document* doc, const char* data_dir, const char* file_name) |
|
{ |
|
bool ret = true; |
|
std::string file_path; |
|
file_path.append(data_dir).append("/").append(file_name); |
|
char* contents = utilDumpTextFile(file_path.c_str()); |
|
|
|
if (contents == nullptr) { |
|
LOG(ERROR) << "Error reading file: " << file_name << "\n"; |
|
ret = false; |
|
} |
|
|
|
rapidjson::ParseResult pr = doc->Parse(contents/*, rapidjson::kParseStopWhenDoneFlag*/); |
|
if (!pr || !doc->IsObject()) { |
|
LOG(ERROR) << "Error parrsing scene file: " << file_name << "\n"; |
|
LOG(ERROR) << "Error desscription: " << rapidjson::GetParseError_En(pr.Code()) |
|
<< " (" << pr.Offset() << ")\n"; |
|
ret = false; |
|
} |
|
|
|
utilSafeFree(contents); |
|
|
|
return ret; |
|
} |
|
|
|
bool |
|
validateScene(rapidjson::Document* schema_doc, rapidjson::Document* scene_doc, const char* scene_file) |
|
{ |
|
rapidjson::SchemaDocument schema(*schema_doc); |
|
rapidjson::SchemaValidator validator(schema); |
|
|
|
if (!scene_doc->Accept(validator)) { |
|
LOG(ERROR) << "Error validating scene file: " << scene_file << "\n"; |
|
rapidjson::StringBuffer sb; |
|
validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); |
|
LOG(ERROR) << "Invalid schema: " << sb.GetString() << "\n"; |
|
LOG(ERROR) << "Invalid keyword:" << validator.GetInvalidSchemaKeyword() << "\n"; |
|
sb.Clear(); |
|
validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); |
|
LOG(ERROR) << "Invalid Document: " << sb.GetString() << "\n"; |
|
return false; |
|
} |
|
|
|
LOG(INFO) << "scene file: " << scene_file << " passed schema validation\n"; |
|
|
|
return true; |
|
} |
|
|
|
glm::vec3 |
|
parseVec3(const rapidjson::Value& node) |
|
{ |
|
glm::vec3 v3; |
|
v3.x = node["x"].GetFloat(); |
|
v3.y = node["y"].GetFloat(); |
|
v3.z = node["z"].GetFloat(); |
|
|
|
return v3; |
|
} |
|
|
|
glm::vec4 |
|
parseVec4(const rapidjson::Value& node) |
|
{ |
|
glm::vec4 v4; |
|
v4.x = node["x"].GetFloat(); |
|
v4.y = node["y"].GetFloat(); |
|
v4.z = node["z"].GetFloat(); |
|
v4.w = node["w"].GetFloat(); |
|
|
|
return v4; |
|
} |
|
|
|
void |
|
addV3f(rapidjson::Writer<rapidjson::StringBuffer>& writer, float x, float y, float z) |
|
{ |
|
writer.StartObject(); |
|
writer.Key("x"); |
|
writer.Double(x); |
|
writer.Key("y"); |
|
writer.Double(y); |
|
writer.Key("z"); |
|
writer.Double(z); |
|
writer.EndObject(); |
|
}
|
|
|