#include #include #include #include #include #include #include #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& 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 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& 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(); }