Browse Source

validate scene files against json schema

master
cinnaboot 8 years ago
parent
commit
84199a3fb0
  1. 76
      data/scene_schema.json
  2. 10
      data/test_scene.json
  3. 103
      src/hexgame.cpp
  4. 117
      src/scene_loader.cpp
  5. 8
      src/scene_loader.h

76
data/scene_schema.json

@ -0,0 +1,76 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"vector3": {
"type": "object",
"properties": {
"x": { "type": "number" },
"y": { "type": "number" },
"z": { "type": "number" }
},
"required": ["x", "y", "z"]
},
"rgb": {
"type": "object",
"properties": {
"r": { "type": "number" },
"g": { "type": "number" },
"b": { "type": "number" }
},
"required": ["r", "g", "b"]
},
"vector4": {
"type": "object",
"properties": {
"x": { "type": "number" },
"y": { "type": "number" },
"z": { "type": "number" },
"w": { "type": "number" }
},
"required": ["x", "y", "z", "w"]
}
},
"type": "object",
"properties": {
"camera": {
"type": "object",
"properties": {
"position" : { "$ref": "#/definitions/vector3" },
"rotation" : { "$ref": "#/definitions/vector4" }
}
},
"hex_grid" : {
"type": "object",
"properties": {
"layout_type" : { "type": "string" },
"hex_size" : { "type": "number" },
"hex_radius" : { "type": "number" }
}
},
"entities": {
"type": "array",
"items": {
"name": { "type": "string" },
"model_file": { "type": "string" },
"position": { "$ref": "#/definitions/vector3" },
"rotation": { "$ref": "#/definitions/vector4" },
"scale": { "$ref": "#/definitions/vector3" }
}
},
"lights": {
"type": "array",
"items": {
"intensity": { "type": "number" },
"color": { "type": "#/definitions/rgb" },
"position": { "$ref": "#/definitions/vector3" }
}
}
}
}

10
data/test_scene.json

@ -25,16 +25,20 @@
"name" : "catepillar 1", "name" : "catepillar 1",
"model_file" : "catepillar.dae", "model_file" : "catepillar.dae",
"position" : { "position" : {
"x" : 0, "x" : 640,
"y" : 0, "y" : 500,
"z" : 0 "z" : 0
}, },
"rotation" : { "rotation" : {
"x" : 0, "x" : 0,
"y" : 0, "y" : 0,
"z" : 0, "z" : 0,
"w" : 0 "w" : 0
},
"scale" : {
"x" : 10,
"y" : 10,
"z" : 10
} }
} }
], ],

103
src/hexgame.cpp

@ -1,28 +1,29 @@
//----------------------------------------------------------------------------- /*******************************************************************************
// TODO: * TODO:
// - add a color buffer to hex_line and debug render groups to re-use * - add a color buffer to hex_line and debug render groups to re-use
// fragment shaders and simplify draw rgDraw() * fragment shaders and simplify draw rgDraw()
// - need to add normal buffer too * - need to add normal buffer too
// - lighting * - lighting
// - add light struct * - add light struct
// - pass all lights to render groups/shaders every frame * - pass all lights to render groups/shaders every frame
// - map generation * - map generation
// - pathfinding * - pathfinding
// - assimp animation * - assimp animation
// - replace aixlog with custom logging iostream? * - replace aixlog with custom logging iostream?
// - fix vector normalization in renderer.cpp when moving in 2 directions * - fix vector normalization in renderer.cpp when moving in 2 directions
// - may be fixed?? need to test by examining camera position between frames * - may be fixed?? need to test by examining camera position between frames
// and compare the distance * and compare the distance
// - add prefix to interface function names for eg) gooey.h, renderer.h * - add prefix to interface function names for eg) gooey.h, renderer.h
// - move hex logic to new file to leave only init glue in a main.cpp * - move hex logic to new file to leave only init glue in a main.cpp
// - move SDL input callbacks somewhere too? * - move SDL input callbacks somewhere too?
// - think about combining mesh data and render_group data to save some memory * - think about combining mesh data and render_group data to save some memory
// - actually don't need to save the vertex/normal buffers after passing * - actually don't need to save the vertex/normal buffers after passing
// to opengl * to opengl
// - replace calls to malloc/free with safe(r) function in util.h * - use a storage pool for assimp meshes allowing reuse across entities
// - add cpu perforcmace counters in render loop * - replace calls to malloc/free with safe(r) function in util.h
// - check for memory leaks w/ valgrind * - add cpu perforcmace counters in render loop
//----------------------------------------------------------------------------- * - check for memory leaks w/ valgrind
******************************************************************************/
// Some defaults for the game layout // Some defaults for the game layout
#define HEX_SIZE 10 #define HEX_SIZE 10
@ -36,8 +37,10 @@
#define CONE_ANGLE 30 #define CONE_ANGLE 30
#define VSYNC_ENABLED true #define VSYNC_ENABLED true
//testing // TODO: testing scene_loader
#define DEFAULT_SCENE_FILE "../data/test_scene.json" #define DATA_DIR "../data"
#define DEFAULT_SCENE_FILE "test_scene.json"
#define SCENE_SCHEMA_FILE "scene_schema.json"
#include <vector> #include <vector>
@ -506,6 +509,9 @@ int main(int argc, char* argv[])
g_game_state->start_hex = 0x0; g_game_state->start_hex = 0x0;
g_game_state->current_hex = 0x0; g_game_state->current_hex = 0x0;
g_game_state->draw_mode = CONE_FILL; g_game_state->draw_mode = CONE_FILL;
// TODO: better entity memory management
// TODO: hard coded limit of 1000 entities here
g_game_state->entities = UTIL_ALLOC(1000, Entity);
// init global render state // init global render state
g_render_state = new render_state(); g_render_state = new render_state();
@ -548,42 +554,7 @@ int main(int argc, char* argv[])
} }
} }
// TODO: better entity memory management if (!meInitAssimp()) {
if (meInitAssimp()) {
// TODO: hard coded limit of 1000 entities here
game_state* g = g_game_state;
g->entities = (Entity*) std::calloc(1000, sizeof(Entity));
#if 0
meMeshGroup mg1, mg2;
// TODO: store these meMeshGroups serperately from entities to re-use them,
// and to make initializing easier, eg) appling translation has to happen after
// rgInitEntity() called from renderer createScene()
if (meLoadFromFile("../data/animated.block.dae", mg1)) {
Entity& e = g->entities[0];
e.mesh_group = mg1;
e.scale = glm::vec3(100,100,100);
g->entity_count++;
}
else {
LOG(ERROR) << "Error loading file, exiting\n";
return 1;
}
if (meLoadFromFile("../data/catepillar.dae", mg2)) {
Entity& e = g->entities[1];
e.mesh_group = mg2;
e.scale = glm::vec3(10,10,10);
e.translation = glm::vec3(640, 500, 0);
g->entity_count++;
}
else {
LOG(ERROR) << "Error loading file, exiting\n";
return 1;
}
#endif
} else {
LOG(ERROR) << "Error initializing assimp\n"; LOG(ERROR) << "Error initializing assimp\n";
return 1; return 1;
} }
@ -591,10 +562,12 @@ int main(int argc, char* argv[])
// testing scene_loader // testing scene_loader
if (slLoadFile(DEFAULT_SCENE_FILE, g_game_state->entities, g_game_state->entity_count, 1000)) { if (slLoadFile(DATA_DIR, DEFAULT_SCENE_FILE, SCENE_SCHEMA_FILE,
g_game_state->entities, g_game_state->entity_count, 1000))
{
// ??? // ???
} else { } else {
LOG(ERROR) << "Error reading scene file, exiting\n"; LOG(ERROR) << "Error loading scene, exiting\n";
return 1; return 1;
} }

117
src/scene_loader.cpp

@ -1,9 +1,10 @@
#include <glm/glm.hpp> #include <string>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <glm/glm.hpp>
#include <rapidjson/document.h>
#include <rapidjson/schema.h>
#include <rapidjson/stringbuffer.h>
#include "aixlog.hpp" #include "aixlog.hpp"
#include "entity.h" #include "entity.h"
@ -11,42 +12,118 @@
#include "util.h" #include "util.h"
#include "scene_loader.h" #include "scene_loader.h"
// forward declarations
bool parseFile(rapidjson::Document& doc, const char* data_dir, const char* file_name);
bool validateScene(rapidjson::Document& schema_doc, rapidjson::Document& scene_doc,
const char* scene_file);
// interface
bool bool
slLoadFile(const char* file_name, Entity* entity_array, uint& entity_count, uint max_entities) slLoadFile(
const char* data_dir,
const char* scene_file,
const char* schema_file,
Entity* entity_array,
uint& entity_count,
uint max_entities)
{ {
if (entity_array == nullptr) { if (entity_array == nullptr) {
LOG(ERROR) << "entity array not initialized\n"; LOG(ERROR) << "entity array not initialized\n";
return false; return false;
} }
rapidjson::Document doc; LOG(INFO) << "Loading scene file: " << scene_file << "\n";
char* contents = utilDumpTextFile(file_name); rapidjson::Document doc, schema_doc;
bool retDoc = parseFile(doc, data_dir, scene_file);
bool retSchema = parseFile(schema_doc, data_dir, schema_file);
if (!retDoc || !retSchema || !validateScene(schema_doc, doc, scene_file))
return false;
// camera
const rapidjson::Value& cam = doc["camera"];
// hex grid
// entities
const rapidjson::Value& e = doc["entities"];
if (e.Size() > max_entities) {
LOG(ERROR) << "entity count: " << e.Size() << ", was more than max_entities: "
<< max_entities << "\n";
return false;
}
for (uint i = 0; i < e.Size(); i++) {
meMeshGroup mg;
std::string model_path;
model_path.append(data_dir).append("/").append(e[i]["model_file"].GetString());
if (meLoadFromFile(model_path.c_str(), mg)) {
Entity& e = entity_array[entity_count];
e.mesh_group = mg;
//
e.scale = glm::vec3(10,10,10);
e.translation = glm::vec3(640, 500, 0);
//
} else {
LOG(ERROR) << "Error loading mesh file\n";
return false;
}
entity_count++;
}
// lights
return true;
}
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) { if (contents == nullptr) {
LOG(ERROR) << "Error reading file: " << file_name << "\n"; LOG(ERROR) << "Error reading file: " << file_name << "\n";
return false; ret = false;
} }
if (doc.Parse(contents).HasParseError() || !doc.IsObject()) { if (doc.Parse(contents).HasParseError() || !doc.IsObject()) {
LOG(ERROR) << "Error parrsing scene file: " << file_name << "\n"; LOG(ERROR) << "Error parrsing scene file: " << file_name << "\n";
return false; ret = false;
} }
// entities utilSafeFree(contents);
if (doc.HasMember("entities")) { return ret;
const rapidjson::Value& e = doc["entities"]; }
assert (e.IsArray());
for (uint i = 0; i < e.Size(); i++) { bool
assert(e[i].HasMember("name")); validateScene(rapidjson::Document& schema_doc, rapidjson::Document& scene_doc, const char* scene_file)
assert(e[i]["name"].IsString()); {
assert(i < max_entities); rapidjson::SchemaDocument schema(schema_doc);
rapidjson::SchemaValidator validator(schema);
std::string s = e[i]["name"].GetString(); if (!scene_doc.Accept(validator)) {
LOG(INFO) << "e[" << i << "][\"name\"]: " << s << "\n"; 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; return true;
} }

8
src/scene_loader.h

@ -4,4 +4,10 @@
/* /*
* @param max_entities maximum size of array allocated * @param max_entities maximum size of array allocated
*/ */
bool slLoadFile(const char* file_name, Entity* entity_array, uint& entity_count, uint max_entities); bool slLoadFile(
const char* data_dir,
const char* scene_file,
const char* schema_file,
Entity* entity_array,
uint& entity_count,
uint max_entities);

Loading…
Cancel
Save