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. 2
      src/hexgame.h
  5. 6
      src/mesh.cpp
  6. 117
      src/scene_loader.cpp
  7. 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",
"model_file" : "catepillar.dae",
"position" : {
"x" : 0,
"y" : 0,
"x" : 640,
"y" : 500,
"z" : 0
},
"rotation" : {
"x" : 0,
"y" : 0,
"z" : 0,
"w" : 0
},
"scale" : {
"x" : 10,
"y" : 10,
"z" : 10
}
}
],

103
src/hexgame.cpp

@ -1,28 +1,29 @@
//-----------------------------------------------------------------------------
// TODO:
// - add a color buffer to hex_line and debug render groups to re-use
// fragment shaders and simplify draw rgDraw()
// - need to add normal buffer too
// - lighting
// - add light struct
// - pass all lights to render groups/shaders every frame
// - map generation
// - pathfinding
// - assimp animation
// - replace aixlog with custom logging iostream?
// - fix vector normalization in renderer.cpp when moving in 2 directions
// - may be fixed?? need to test by examining camera position between frames
// and compare the distance
// - 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 SDL input callbacks somewhere too?
// - 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
// to opengl
// - replace calls to malloc/free with safe(r) function in util.h
// - add cpu perforcmace counters in render loop
// - check for memory leaks w/ valgrind
//-----------------------------------------------------------------------------
/*******************************************************************************
* TODO:
* - add a color buffer to hex_line and debug render groups to re-use
* fragment shaders and simplify draw rgDraw()
* - need to add normal buffer too
* - lighting
* - add light struct
* - pass all lights to render groups/shaders every frame
* - map generation
* - pathfinding
* - assimp animation
* - replace aixlog with custom logging iostream?
* - fix vector normalization in renderer.cpp when moving in 2 directions
* - may be fixed?? need to test by examining camera position between frames
* and compare the distance
* - 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 SDL input callbacks somewhere too?
* - 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
* to opengl
* - use a storage pool for assimp meshes allowing reuse across entities
* - replace calls to malloc/free with safe(r) function in util.h
* - add cpu perforcmace counters in render loop
* - check for memory leaks w/ valgrind
******************************************************************************/
// Some defaults for the game layout
#define HEX_SIZE 10
@ -36,8 +37,10 @@
#define CONE_ANGLE 30
#define VSYNC_ENABLED true
//testing
#define DEFAULT_SCENE_FILE "../data/test_scene.json"
// TODO: testing scene_loader
#define DATA_DIR "../data"
#define DEFAULT_SCENE_FILE "test_scene.json"
#define SCENE_SCHEMA_FILE "scene_schema.json"
#include <vector>
@ -506,6 +509,9 @@ int main(int argc, char* argv[])
g_game_state->start_hex = 0x0;
g_game_state->current_hex = 0x0;
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
g_render_state = new render_state();
@ -548,42 +554,7 @@ int main(int argc, char* argv[])
}
}
// TODO: better entity memory management
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 {
if (!meInitAssimp()) {
LOG(ERROR) << "Error initializing assimp\n";
return 1;
}
@ -591,10 +562,12 @@ int main(int argc, char* argv[])
// 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 {
LOG(ERROR) << "Error reading scene file, exiting\n";
LOG(ERROR) << "Error loading scene, exiting\n";
return 1;
}

2
src/hexgame.h

@ -3,7 +3,7 @@
#include <vector>
#if defined (_WIN32)
#if defined (_WIN32)
#include <SDL.h>
#else
#include <SDL2/SDL.h>

6
src/mesh.cpp

@ -23,8 +23,8 @@ meInitAssimp()
{
LOG(INFO) << "Initializing Assimp\n";
/* get a handle to the predefined STDOUT log stream and attach
* it to the logging system. It remains active for all further
* calls to aiImportFile(Ex) and aiApplyPostProcessing. */
* it to the logging system. It remains active for all further
* calls to aiImportFile(Ex) and aiApplyPostProcessing. */
aiLogStream stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL);
aiAttachLogStream(&stream);
@ -43,7 +43,7 @@ meLoadFromFile(const char* filename, meMeshGroup& mesh_group)
}
mesh_group.num_meshes = scene->mNumMeshes;
mesh_group.meshes = (meMeshInfo**) std::calloc(mesh_group.num_meshes, sizeof(meMeshInfo*));
mesh_group.meshes = (meMeshInfo**) std::calloc(mesh_group.num_meshes, sizeof(meMeshInfo*));
mesh_group.use_normals = scene->mMeshes[0]->HasNormals();
for (uint i = 0; i < scene->mNumMeshes; i++) {

117
src/scene_loader.cpp

@ -1,9 +1,10 @@
#include <glm/glm.hpp>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <string>
#include <glm/glm.hpp>
#include <rapidjson/document.h>
#include <rapidjson/schema.h>
#include <rapidjson/stringbuffer.h>
#include "aixlog.hpp"
#include "entity.h"
@ -11,42 +12,118 @@
#include "util.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
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) {
LOG(ERROR) << "entity array not initialized\n";
return false;
}
rapidjson::Document doc;
char* contents = utilDumpTextFile(file_name);
LOG(INFO) << "Loading scene file: " << scene_file << "\n";
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) {
LOG(ERROR) << "Error reading file: " << file_name << "\n";
return false;
ret = false;
}
if (doc.Parse(contents).HasParseError() || !doc.IsObject()) {
LOG(ERROR) << "Error parrsing scene file: " << file_name << "\n";
return false;
ret = false;
}
// entities
utilSafeFree(contents);
if (doc.HasMember("entities")) {
const rapidjson::Value& e = doc["entities"];
assert (e.IsArray());
return ret;
}
for (uint i = 0; i < e.Size(); i++) {
assert(e[i].HasMember("name"));
assert(e[i]["name"].IsString());
assert(i < max_entities);
bool
validateScene(rapidjson::Document& schema_doc, rapidjson::Document& scene_doc, const char* scene_file)
{
rapidjson::SchemaDocument schema(schema_doc);
rapidjson::SchemaValidator validator(schema);
std::string s = e[i]["name"].GetString();
LOG(INFO) << "e[" << i << "][\"name\"]: " << s << "\n";
}
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;
}

8
src/scene_loader.h

@ -4,4 +4,10 @@
/*
* @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