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

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