diff --git a/CrystalJohan.cpp b/CrystalJohan.cpp
index e3fc234..a784572 100644
--- a/CrystalJohan.cpp
+++ b/CrystalJohan.cpp
@@ -10,22 +10,51 @@ void CrystalJohan::init()
glClearColor(0.7, 0.7, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ mousePosition = Vec2f(width / 2, height / 2);
}
void CrystalJohan::draw()
{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ //Draw world
+ glEnable(GL_LIGHTING);
+ glEnable(GL_DEPTH_TEST);
+
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70, width / (float)height, 0.1f, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
-
world->draw();
+
+ //Draw Cursor
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0,width, height,0,-10,10);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glDisable(GL_LIGHTING);
+ glDisable(GL_DEPTH_TEST);
+ glColor4f(1, cos(glutGet(GLUT_ELAPSED_TIME) / 1000.0f), sin(glutGet(GLUT_ELAPSED_TIME) / 1000.0f), 1);
+ glBegin(GL_TRIANGLES);
+ glVertex2f(mousePosition.x, mousePosition.y);
+ glVertex2f(mousePosition.x+15, mousePosition.y+15);
+ glVertex2f(mousePosition.x+5, mousePosition.y+20);
+
+ glEnd();
+
+
+
+ glutSwapBuffers();
+
+
}
@@ -34,20 +63,33 @@ void CrystalJohan::update()
float frameTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
float deltaTime = frameTime - lastFrameTime;
lastFrameTime = frameTime;
-
+
// if(keyboardState.special[GLUT_KEY_LEFT] && !prevKeyboardState.special[GLUT_KEY_LEFT])
if (keyboardState.keys[27])
exit(0);
- world->player.rotation.y += mouseOffset.x/10.0f;
- world->player.rotation.x += mouseOffset.y/10.0f;
+ world->player.rotation.y += mouseOffset.x / 10.0f;
+ world->player.rotation.x += mouseOffset.y / 10.0f;
if (world->player.rotation.x > 90)
world->player.rotation.x = 90;
if (world->player.rotation.x < -90)
world->player.rotation.x = -90;
+ Vec3f oldPosition = world->player.position;
+ if (keyboardState.keys['a']) world->player.setPosition(0, deltaTime, false);
+ if (keyboardState.keys['d']) world->player.setPosition(180, deltaTime, false);
+ if (keyboardState.keys['w']) world->player.setPosition(90, deltaTime, false);
+ if (keyboardState.keys['s']) world->player.setPosition(270, deltaTime, false);
+ if (keyboardState.keys['q']) world->player.setPosition(1, deltaTime, true);
+ if (keyboardState.keys['e']) world->player.setPosition(-1, deltaTime, true);
+ if (!world->isPlayerPositionValid())
+ world->player.position = oldPosition;
+
+
+ mousePosition = mousePosition + mouseOffset;
+
mouseOffset = Vec2f(0, 0);
prevKeyboardState = keyboardState;
glutPostRedisplay();
diff --git a/CrystalJohan.h b/CrystalJohan.h
index 1a5864f..0fe1945 100644
--- a/CrystalJohan.h
+++ b/CrystalJohan.h
@@ -28,6 +28,8 @@ public:
Vec2f mouseOffset;
+ Vec2f mousePosition;
+
float lastFrameTime;
};
\ No newline at end of file
diff --git a/CrystalJohan.vcxproj b/CrystalJohan.vcxproj
index 4abd2f9..2c3970d 100644
--- a/CrystalJohan.vcxproj
+++ b/CrystalJohan.vcxproj
@@ -156,6 +156,7 @@
+
@@ -167,6 +168,7 @@
+
diff --git a/CrystalJohan.vcxproj.filters b/CrystalJohan.vcxproj.filters
index 9a7325d..7339905 100644
--- a/CrystalJohan.vcxproj.filters
+++ b/CrystalJohan.vcxproj.filters
@@ -45,6 +45,9 @@
Source Files
+
+ Source Files
+
@@ -80,5 +83,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/Entity.cpp b/Entity.cpp
index d04b625..b6a3bba 100644
--- a/Entity.cpp
+++ b/Entity.cpp
@@ -8,6 +8,8 @@
Entity::Entity()
{
model = NULL;
+ scale = 1;
+ canCollide = true;
}
@@ -34,3 +36,14 @@ void Entity::draw()
}
+bool Entity::inObject(const Vec3f & point)
+{
+ if (!model)
+ return false;
+ Vec3f center = position + model->center;
+ float distance = sqrt((point.x - center.x) * (point.x - center.x) + (point.z - center.z)*(point.z - center.z));
+ if (distance < model->radius*scale)
+ return true;
+ return false;
+}
+
diff --git a/Entity.h b/Entity.h
index 68e48ec..a3dc294 100644
--- a/Entity.h
+++ b/Entity.h
@@ -16,5 +16,8 @@ public:
Vec3f position;
Vec3f rotation;
float scale;
+
+ bool canCollide;
+ bool inObject(const Vec3f &position);
};
diff --git a/LevelObject.cpp b/LevelObject.cpp
index e054f2c..c2a9639 100644
--- a/LevelObject.cpp
+++ b/LevelObject.cpp
@@ -1,12 +1,20 @@
#include "LevelObject.h"
+#include "Model.h"
-LevelObject::LevelObject()
+LevelObject::LevelObject(const std::string &fileName, const Vec3f &position, const Vec3f &rotation, const float &scale, const bool &hasCollision)
{
+ model = Model::load(fileName);
+ this->position = position;
+ this->rotation = rotation;
+ this->scale = scale;
+ this->canCollide = hasCollision;
}
LevelObject::~LevelObject()
{
+ if (model)
+ Model::unload(model);
}
diff --git a/LevelObject.h b/LevelObject.h
index c0f7770..6c26358 100644
--- a/LevelObject.h
+++ b/LevelObject.h
@@ -1,8 +1,13 @@
#pragma once
-class LevelObject
+
+#include "Entity.h"
+#include
+
+
+class LevelObject : public Entity
{
public:
- LevelObject();
+ LevelObject(const std::string &fileName, const Vec3f &position, const Vec3f &rotation, const float &scale, const bool &hasCollision);
~LevelObject();
};
diff --git a/Main.cpp b/Main.cpp
index aca6afe..14ce684 100644
--- a/Main.cpp
+++ b/Main.cpp
@@ -4,6 +4,8 @@
#include
#include "vector.h"
+void configureOpenGL(void);
+
CrystalJohan* app;
bool justMoved = false;
@@ -12,12 +14,8 @@ int main(int argc, char* argv[])
{
app = new CrystalJohan();
glutInit(&argc, argv);
- //Init window and glut display mode
- glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
- glutInitWindowSize(800, 600);
- glutCreateWindow("Crystal Point");
- glutFullScreen();
- //glutPositionWindow((glutGet(GLUT_SCREEN_WIDTH) / 2) - (glutGet(GLUT_WINDOW_WIDTH) / 2), (glutGet(GLUT_SCREEN_HEIGHT) / 2) - (glutGet(GLUT_WINDOW_HEIGHT) / 2));
+
+ configureOpenGL();
app->init();
@@ -28,9 +26,8 @@ int main(int argc, char* argv[])
//Keyboard
glutKeyboardFunc([](unsigned char c, int, int) { app->keyboardState.keys[c] = true; });
glutKeyboardUpFunc([](unsigned char c, int, int) { app->keyboardState.keys[c] = false; });
-
+
//Mouse
-// glutMouseFunc(mouse);
glutPassiveMotionFunc([](int x, int y)
{
if (justMoved)
@@ -49,7 +46,43 @@ int main(int argc, char* argv[])
});
glutMainLoop();
-
-
return 0;
+}
+
+void configureOpenGL()
+{
+ //Init window and glut display mode
+ glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
+ glutInitWindowSize(800, 600);
+ glutCreateWindow("Crystal Point");
+ glutFullScreen();
+
+ //Depth testing
+ glEnable(GL_DEPTH_TEST);
+
+ //Alpha blending
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ //Alpha testing
+ glEnable(GL_ALPHA_TEST);
+ glAlphaFunc(GL_GREATER, 0.01f);
+
+ //Lighting
+ GLfloat mat_specular[] = { 0.2, 0.2, 0.2, 0 };
+ //GLfloat mat_shininess[] = { 5.0 };
+ GLfloat light_position[] = { 0.0, 2.0, 1.0, 0 };
+ GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 0 };
+ GLfloat light_ambient[] = { 0.3, 0.3, 0.3, 0 };
+ glClearColor(0.7, 0.7, 1.0, 1.0);
+
+ //glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
+ //glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
+ //glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
+ //glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
+
+ //glEnable(GL_LIGHTING);
+ //glEnable(GL_LIGHT0);
+
+ glutSetCursor(GLUT_CURSOR_CROSSHAIR);
}
\ No newline at end of file
diff --git a/Model.cpp b/Model.cpp
index 0e32a46..a8549a6 100644
--- a/Model.cpp
+++ b/Model.cpp
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
//Prototypes
std::vector split(std::string str, std::string sep);
@@ -170,8 +171,25 @@ void Model::draw()
}
}
glEnd();
-
}
+
+ minVertex = vertices[0];
+ maxVertex = vertices[0];
+ for (auto v : vertices)
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ minVertex[i] = fmin(minVertex[i], v[i]);
+ maxVertex[i] = fmax(maxVertex[i], v[i]);
+ }
+ }
+ center = (minVertex + maxVertex) / 2.0f;
+ radius = 0;
+ for (auto v : vertices)
+ radius = fmax(radius, (center.x - v.x) * (center.x - v.x) + (center.z - v.z) * (center.z - v.z));
+ radius = sqrt(radius);
+
+
}
void Model::loadMaterialFile(std::string fileName, std::string dirName)
@@ -327,13 +345,19 @@ Model* Model::load(const std::string &fileName)
return cache[fileName].first;
}
-void Model::unload(const std::string & fileName)
+void Model::unload(Model* model)
{
- assert(cache.find(fileName) != cache.end());
- cache[fileName].second--;
- if (cache[fileName].second == 0)
+ for (auto m : cache)
{
- delete cache[fileName].first;
- cache.erase(cache.find(fileName));
+ if (m.second.first == model)
+ {
+ m.second.second--;
+ if (m.second.second == 0)
+ {
+ delete m.second.first;
+ cache.erase(cache.find(m.first));
+ }
+
+ }
}
}
diff --git a/Model.h b/Model.h
index 1e3ba91..a8a9f8c 100644
--- a/Model.h
+++ b/Model.h
@@ -71,7 +71,13 @@ public:
static std::map > cache;
static Model* load(const std::string &fileName);
- static void unload(const std::string &fileName);
+ static void unload(Model* model);
void draw();
+
+ Vec3f minVertex;
+ Vec3f maxVertex;
+
+ Vec3f center;
+ float radius;
};
diff --git a/Player.cpp b/Player.cpp
index e02106a..23f7ec3 100644
--- a/Player.cpp
+++ b/Player.cpp
@@ -1,9 +1,11 @@
+#define _USE_MATH_DEFINES
+#include
#include "Player.h"
#include
Player::Player()
{
-
+ speed = 10;
}
void Player::setCamera()
@@ -13,3 +15,14 @@ void Player::setCamera()
glTranslatef(-position.x, -position.y, -position.z);
}
+
+void Player::setPosition(float angle, float fac, bool height)
+{
+ if (height)
+ position.y += angle*fac;
+ else
+ {
+ position.x -= (float)cos((rotation.y + angle) / 180 * M_PI) * fac*speed;
+ position.z -= (float)sin((rotation.y + angle) / 180 * M_PI) * fac*speed;
+ }
+}
\ No newline at end of file
diff --git a/Player.h b/Player.h
index 66159ad..2e8aea3 100644
--- a/Player.h
+++ b/Player.h
@@ -12,10 +12,14 @@ public:
Player();
void setCamera();
+ void setPosition(float angle, float fac, bool height);
Vec3f position;
Vec2f rotation;
Model* leftWeapon;
Model* rightWeapon;
+
+
+ float speed;
};
\ No newline at end of file
diff --git a/Vector.cpp b/Vector.cpp
index 3a3bb8d..21b8169 100644
--- a/Vector.cpp
+++ b/Vector.cpp
@@ -24,6 +24,16 @@ float& Vec3f::operator [](int index)
return v[index];
}
+Vec3f Vec3f::operator+(const Vec3f & other)
+{
+ return Vec3f(x + other.x, y + other.y, z + other.z);
+}
+
+Vec3f Vec3f::operator/(float value)
+{
+ return Vec3f(x / value, y / value, z / value);
+}
+
Vec2f::Vec2f(float x, float y)
diff --git a/Vector.h b/Vector.h
index 7272adb..c8e057c 100644
--- a/Vector.h
+++ b/Vector.h
@@ -15,6 +15,8 @@ public:
Vec3f(Vec3f &other);
Vec3f(float x, float y, float z);
float& operator [](int);
+ Vec3f operator + (const Vec3f &other);
+ Vec3f operator / (float value);
};
class Vec2f
diff --git a/World.cpp b/World.cpp
index f987ce3..2c9789c 100644
--- a/World.cpp
+++ b/World.cpp
@@ -1,12 +1,35 @@
#include "World.h"
#include
#include "Entity.h"
+#include "LevelObject.h"
+#include "json.h"
+#include
World::World() : player(Player::getInstance())
{
- player.position.y = 1.7;
+ json::Value v = json::readJson(std::ifstream("worlds/world1.json"));
- //entities.push_back(new LevelObject("tree"));
+ player.position.x = v["player"]["startposition"][0];
+ player.position.y = v["player"]["startposition"][1];
+ player.position.z = v["player"]["startposition"][2];
+
+ for (auto object : v["objects"])
+ {
+ bool hasCollision = true;
+ if (!object["collide"].isNull())
+ hasCollision = object["collide"].asBool();
+
+ Vec3f rotation(0, 0, 0);
+ if(!object["rot"].isNull())
+ rotation = Vec3f(object["rot"][0], object["rot"][1], object["rot"][2]);
+
+ float scale = 1;
+ if (!object["scale"].isNull())
+ scale = object["scale"].asFloat();
+
+ Vec3f position(object["pos"][0], object["pos"][1], object["pos"][2]);
+ entities.push_back(new LevelObject(object["file"], position, rotation, scale, hasCollision));
+ }
}
@@ -18,8 +41,13 @@ void World::draw()
{
player.setCamera();
+ float lightPosition[4] = { 0, 2, 1, 0 };
+ glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
+ float lightAmbient[4] = { 0.5, 0.5, 0.5, 1 };
+ glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
glColor3f(0.5f, 0.9f, 0.5f);
+ glNormal3f(0, 1, 0);
glBegin(GL_QUADS);
glVertex3f(-50, 0, -50);
glVertex3f(-50, 0, 50);
@@ -30,7 +58,6 @@ void World::draw()
for (auto e : entities)
e->draw();
- glutSwapBuffers();
}
void World::update(float elapsedTime)
@@ -38,3 +65,13 @@ void World::update(float elapsedTime)
for (auto e : entities)
e->update(elapsedTime);
}
+
+bool World::isPlayerPositionValid()
+{
+ for (auto e : entities)
+ {
+ if (e->canCollide && e->inObject(player.position))
+ return false;
+ }
+ return true;
+}
diff --git a/World.h b/World.h
index 0eb5f56..b37377f 100644
--- a/World.h
+++ b/World.h
@@ -17,6 +17,6 @@ public:
void draw();
void update(float elapsedTime);
-
+ bool isPlayerPositionValid();
};
diff --git a/json.cpp b/json.cpp
new file mode 100644
index 0000000..c6aeccd
--- /dev/null
+++ b/json.cpp
@@ -0,0 +1,770 @@
+#include "json.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace json
+{
+
+ Value Value::null;
+ //constructors
+ Value::Value()
+ {
+ type = Type::nullValue;
+ value.objectValue = NULL;
+ }
+
+ Value::Value(Type type)
+ {
+ this->type = type;
+ if (type == Type::stringValue)
+ value.stringValue = new std::string();
+ if (type == Type::arrayValue)
+ value.arrayValue = new std::vector();
+ if (type == Type::objectValue)
+ value.objectValue = new std::map();
+ }
+
+ Value::Value(int value)
+ {
+ type = Type::intValue;
+ this->value.intValue = value;
+ }
+
+ Value::Value(__int64 value)
+ {
+ //todo: value too big ?
+ type = Type::intValue;
+ this->value.intValue = value;
+ }
+
+ Value::Value(float value)
+ {
+ type = Type::floatValue;
+ this->value.floatValue = value;
+ }
+
+ Value::Value(bool value)
+ {
+ type = Type::boolValue;
+ this->value.boolValue = value;
+ }
+
+ Value::Value(const std::string &value)
+ {
+ type = Type::stringValue;
+ this->value.stringValue = new std::string();
+ this->value.stringValue->assign(value);
+ }
+ Value::Value(const char* value)
+ {
+ type = Type::stringValue;
+ this->value.stringValue = new std::string();
+ this->value.stringValue->assign(value);
+ }
+
+ Value::Value(const Value& other) : Value(other.type)
+ {
+ if (type == Type::objectValue)
+ *this->value.objectValue = *other.value.objectValue;
+ else if (type == Type::arrayValue)
+ *this->value.arrayValue = *other.value.arrayValue;
+ else if (type == Type::stringValue)
+ this->value.stringValue->assign(*other.value.stringValue);
+ else
+ this->value = other.value;
+ }
+
+ void Value::operator=(const Value& other)
+ {
+ if (type != other.type)
+ {
+ if (type == Type::stringValue)
+ delete value.stringValue;
+ if (type == Type::arrayValue)
+ delete value.arrayValue;
+ if (type == Type::objectValue)
+ delete value.objectValue;
+ this->type = other.type;
+ if (type == Type::stringValue)
+ value.stringValue = new std::string();
+ if (type == Type::arrayValue)
+ value.arrayValue = new std::vector();
+ if (type == Type::objectValue)
+ value.objectValue = new std::map();
+ }
+
+ if (type == Type::objectValue)
+ *this->value.objectValue = *other.value.objectValue;
+ else if (type == Type::arrayValue)
+ *this->value.arrayValue = *other.value.arrayValue;
+ else if (type == Type::stringValue)
+ this->value.stringValue->assign(*other.value.stringValue);
+ else
+ this->value = other.value;
+ }
+
+ Value::~Value()
+ {
+ if (type == Type::stringValue)
+ delete value.stringValue;
+ else if (type == Type::arrayValue)
+ delete value.arrayValue;
+ else if (type == Type::objectValue)
+ delete value.objectValue;
+ }
+
+ size_t Value::size() const
+ {
+ assert(type == Type::arrayValue || type == Type::objectValue);
+ if (type == Type::arrayValue)
+ return value.arrayValue->size();
+ else if (type == Type::objectValue)
+ return value.objectValue->size();
+ throw "Unsupported";
+ }
+
+ void Value::push_back(const Value& value)
+ {
+ assert(type == Type::arrayValue || type == Type::nullValue);
+ if (type == Type::nullValue)
+ {
+ type = Type::arrayValue;
+ this->value.arrayValue = new std::vector();
+ }
+ this->value.arrayValue->push_back(value);
+ }
+
+
+ Value& Value::operator[](const std::string &key)
+ {
+ assert(type == Type::objectValue || type == Type::nullValue);
+ if (type == Type::nullValue)
+ {
+ type = Type::objectValue;
+ value.objectValue = new std::map();
+ }
+ return (*value.objectValue)[key];
+ }
+ Value& Value::operator[](const std::string &key) const
+ {
+ assert(type == Type::objectValue);
+ return (*value.objectValue)[key];
+ }
+
+ Value& Value::operator[](const char* key)
+ {
+ assert(type == Type::objectValue || type == Type::nullValue);
+ if (type == Type::nullValue)
+ {
+ type = Type::objectValue;
+ value.objectValue = new std::map();
+ }
+ return (*value.objectValue)[std::string(key)];
+ }
+
+ Value& Value::operator[](const char* key) const
+ {
+ assert(type == Type::objectValue);
+ return (*value.objectValue)[std::string(key)];
+ }
+
+ Value& Value::operator[](size_t index)
+ {
+ assert(type == Type::arrayValue);
+ return (*value.arrayValue)[index];
+ }
+ Value& Value::operator[](size_t index) const
+ {
+ assert(type == Type::arrayValue);
+ return (*value.arrayValue)[index];
+ }
+ Value& Value::operator[](int index)
+ {
+ assert(type == Type::arrayValue);
+ return (*value.arrayValue)[index];
+ }
+ Value& Value::operator[](int index) const
+ {
+ assert(type == Type::arrayValue);
+ return (*value.arrayValue)[index];
+ }
+
+ void Value::erase(size_t index)
+ {
+ throw "Cannot cast";
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Value::Iterator Value::end() const
+ {
+ if (type == Type::objectValue)
+ return Iterator(value.objectValue->end());
+ else if (type == Type::arrayValue)
+ return Iterator(value.arrayValue->end());
+ throw "oops";
+ }
+
+ Value::Iterator Value::begin() const
+ {
+ if (type == Type::objectValue)
+ return Iterator(value.objectValue->begin());
+ else if (type == Type::arrayValue)
+ return Iterator(value.arrayValue->begin());
+ throw "oops";
+ }
+
+ ///iterator stuff
+
+ Value Value::Iterator::operator*()
+ {
+ if (type == Type::objectValue)
+ return this->objectIterator->second;
+ else if (type == Type::arrayValue)
+ return *arrayIterator;
+ throw "Oops";
+ }
+
+ bool Value::Iterator::operator!=(const Iterator &other)
+ {
+ if (type == Type::objectValue)
+ return this->objectIterator != other.objectIterator;
+ else if (type == Type::arrayValue)
+ return this->arrayIterator != other.arrayIterator;
+ throw "Oops";
+ }
+
+ void Value::Iterator::operator++()
+ {
+ if (type == Type::objectValue)
+ this->objectIterator++;
+ else if (type == Type::arrayValue)
+ this->arrayIterator++;
+ }
+ void Value::Iterator::operator++(int)
+ {
+ if (type == Type::objectValue)
+ this->objectIterator++;
+ else if (type == Type::arrayValue)
+ this->arrayIterator++;
+ }
+
+ Value::Iterator::Iterator(const std::vector::iterator& arrayIterator)
+ {
+ type = Type::arrayValue;
+ this->arrayIterator = arrayIterator;
+ }
+
+ Value::Iterator::Iterator(const std::map::iterator& objectIterator)
+ {
+ type = Type::objectValue;
+ this->objectIterator = objectIterator;
+ }
+
+ std::string Value::Iterator::key()
+ {
+ assert(type == Type::objectValue);
+ return this->objectIterator->first;
+ }
+ Value& Value::Iterator::value()
+ {
+ assert(type == Type::objectValue);
+ return this->objectIterator->second;
+ }
+
+
+
+
+
+
+
+ static int lineNumber;
+
+ static void ltrim(std::istream& stream);
+ static void eatComment(std::istream& stream);
+ static Value eatString(std::istream& stream);
+ static Value eatObject(std::istream& stream);
+ static Value eatArray(std::istream& stream);
+ static Value eatNumeric(std::istream& stream, char firstChar);
+ static Value eatBool(std::istream& stream);
+ static Value eatNull(std::istream& stream);
+ static Value eatValue(std::istream& stream);
+
+ //reading
+ static void ltrim(std::istream& stream)
+ {
+ char c = stream.peek();
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ {
+ if (c == '\n')
+ lineNumber++;
+ stream.get();
+ c = stream.peek();
+ }
+ };
+
+
+ static Value eatString(std::istream& stream)
+ {
+ std::string value = "";
+ bool escaped = false;
+ while (!stream.eof())
+ {
+ char c = stream.get();
+ if (c == '\\' && !escaped)
+ escaped = !escaped;
+ else if (c == '\"' && !escaped)
+ return Value(value);
+ else
+ {
+ value += c;
+ escaped = false;
+ }
+ }
+ return Value(value);
+ };
+
+
+
+
+ static Value eatObject(std::istream& stream)
+ {
+ Value obj(Type::objectValue);
+ while (!stream.eof())
+ {
+ ltrim(stream);
+ char token = stream.get();
+ if (token == '}')
+ break; //empty object
+ if (token == '/')
+ {
+ eatComment(stream);
+ token = stream.get();
+ }
+
+ assert(token == '"');
+ Value key = eatString(stream);
+ ltrim(stream);
+ token = stream.get();
+ assert(token == ':');
+ ltrim(stream);
+ Value val = eatValue(stream);
+ obj[key.asString()] = val;
+ ltrim(stream);
+
+ token = stream.get();
+ if (token == '}')
+ break;
+ if (token != ',')
+ throw "arg";
+ assert(token == ',');
+ }
+ return obj;
+ };
+ static Value eatArray(std::istream& stream)
+ {
+ Value obj(Type::arrayValue);
+ while (!stream.eof())
+ {
+ ltrim(stream);
+ if (stream.peek() == ']')
+ {
+ stream.get();
+ break;
+ }
+ obj.push_back(eatValue(stream));
+ ltrim(stream);
+ char token = stream.get();
+ if (token == '/')
+ {
+ eatComment(stream);
+ token = stream.get();
+ }
+ if (token == ']')
+ break;
+ assert(token == ',');
+ }
+ return obj;
+ };
+ static Value eatNumeric(std::istream& stream, char firstChar)
+ {
+ std::string numeric(1, firstChar);
+ while (!stream.eof())
+ {
+ char token = stream.peek();
+ if ((token >= '0' && token <= '9') || token == '.' || token == '-' || token == 'E')
+ numeric += stream.get();
+ else
+ break;
+ }
+ if (numeric.find('.') == std::string::npos)
+ return Value(atoi(numeric.c_str()));
+ else
+ return Value((float)atof(numeric.c_str()));
+ };
+
+ static Value eatBool(std::istream& stream)
+ {
+ char token = stream.get();
+ if (token == 'a') //fAlse
+ {
+ stream.get(); //l
+ stream.get(); //s
+ stream.get(); //e
+ return false;
+ }
+ else if (token == 'r') //tRue
+ {
+ stream.get(); // u
+ stream.get(); // e
+ return true;
+ }
+ return Value(Type::nullValue);
+ };
+ static Value eatNull(std::istream& stream)
+ {
+ stream.get(); // u
+ stream.get(); // l
+ stream.get(); // l
+ return Value(Type::nullValue);
+ };
+
+ //precondition: / is already eaten
+ static void eatComment(std::istream& stream)
+ {
+ char token = stream.get();
+ assert(token == '/' || token == '*');
+ if (token == '*')
+ {
+ char last = token;
+ while ((last != '*' || token != '/') && !stream.eof())
+ {
+ last = token;
+ token = stream.get();
+ }
+ }
+ else if (token == '/')
+ while (token != '\n' && !stream.eof())
+ token = stream.get();
+ ltrim(stream);
+ }
+
+
+ static Value eatValue(std::istream& stream)
+ {
+ ltrim(stream);
+ char token = stream.get();
+ if (token == '{')
+ return eatObject(stream);
+ if (token == '[')
+ return eatArray(stream);
+ if ((token >= '0' && token <= '9') || token == '.' || token == '-')
+ return eatNumeric(stream, token);
+ if (token == '"')
+ return eatString(stream);
+ if (token == 't' || token == 'f')
+ return eatBool(stream);
+ if (token == 'n')
+ return eatNull(stream);
+ if (token == '/')
+ {
+ eatComment(stream);
+ return eatValue(stream);
+ }
+ throw "Unable to parse json";
+ };
+
+
+ Value readJson(const std::string &data)
+ {
+ std::stringstream stream;
+ stream << data;
+ lineNumber = 1;
+ return eatValue(stream);
+ }
+
+ Value readJson(std::istream &stream)
+ {
+ assert(!stream.eof() && stream.good() && !stream.bad());
+ lineNumber = 1;
+ return eatValue(stream);
+ }
+
+
+
+ std::ostream& indent(std::ostream& stream, int level)
+ {
+ for (int i = 0; i < level; i++)
+ stream << '\t';
+ return stream;
+ }
+
+ std::ostream& Value::prettyPrint(std::ostream& stream, json::Value& printConfig, int level) const
+ {
+ stream << std::fixed << std::setprecision(6);
+ switch (type)
+ {
+ case Type::intValue:
+ stream << value.intValue;
+ break;
+ case Type::floatValue:
+ assert(!isnan(value.floatValue));
+ //assert(isnormal(value.floatValue));
+ if (value.floatValue >= 0)
+ stream << " ";
+ stream << value.floatValue;
+ break;
+ case Type::boolValue:
+ stream << (value.boolValue ? "true" : "false");
+ break;
+ case Type::stringValue:
+ stream << "\"" << *value.stringValue << "\""; //TODO: escape \'s
+ break;
+ case Type::arrayValue:
+ {
+ stream << "[";
+ int wrap = 99999;
+ if (value.arrayValue->size() > 10)
+ wrap = 1;
+ if (value.arrayValue->at(0).isArray() || value.arrayValue->at(0).isObject())
+ wrap = 1;
+ else
+ wrap = 3;
+
+ std::string seperator = " ";
+
+ if (!printConfig.isNull() && printConfig.isMember("wrap"))
+ wrap = printConfig["wrap"];
+ if (!printConfig.isNull() && printConfig.isMember("seperator"))
+ seperator = printConfig["seperator"].asString();
+
+
+ int index = 0;
+
+ if ((long)size() > wrap)
+ {
+ stream << "\n";
+ indent(stream, level + 1);
+ }
+ for (auto v : *this)
+ {
+ if (index > 0)
+ {
+ stream << "," << seperator;
+ if (index % wrap == 0)
+ {
+ stream << "\n";
+ indent(stream, level + 1);
+ }
+ }
+
+ json::Value childPrintConfig = json::Value::null;
+ if (!printConfig.isNull())
+ {
+ if (printConfig.isMember("elements"))
+ childPrintConfig = printConfig["elements"];
+ else if (printConfig.isMember("recursive") && printConfig["recursive"].asBool() == true)
+ childPrintConfig = printConfig;
+ }
+
+ v.prettyPrint(stream, childPrintConfig, level + 1);
+ index++;
+ }
+ if ((long)size() > wrap)
+ {
+ stream << "\n";
+ indent(stream, level);
+ }
+ stream << "]";
+ break;
+ }
+ case Type::objectValue:
+ {
+ stream << "{\n";
+ int wrap = 99999;
+ if (value.arrayValue->size() > 10)
+ wrap = 1;
+ if (value.arrayValue->at(0).isArray() || value.arrayValue->at(0).isObject())
+ wrap = 1;
+ else
+ wrap = 3;
+
+ std::string seperator = " ";
+
+ if (!printConfig.isNull() && printConfig.isMember("wrap"))
+ wrap = printConfig["wrap"];
+ if (!printConfig.isNull() && printConfig.isMember("seperator"))
+ seperator = printConfig["seperator"].asString();
+
+
+ int index = 0;
+ indent(stream, level + 1);
+
+
+
+ //for (auto v : *value.objectValue)
+ auto printEl = [&](const std::pair v)
+ {
+ if (index > 0)
+ {
+ stream << "," << seperator;
+ if (index % wrap == 0)
+ {
+ stream << "\n";
+ indent(stream, level + 1);
+ }
+ }
+ stream << "\"" << v.first << "\" : ";
+ if (
+ (v.second.isArray() || v.second.isObject()) &&
+ (printConfig.isNull() ||
+ (
+ printConfig.isMember(v.first) &&
+ printConfig[v.first].isObject() &&
+ printConfig[v.first].isMember("wrap") &&
+ printConfig[v.first]["wrap"].asInt() < (int)v.second.size())
+ )
+ )
+ {
+ stream << "\n";
+ indent(stream, level + 1);
+ }
+
+ json::Value childPrintConfig = json::Value::null;
+ if (!printConfig.isNull())
+ {
+ if (printConfig.isMember(v.first))
+ childPrintConfig = printConfig[v.first];
+ else if (printConfig.isMember("recursive") && printConfig["recursive"].asBool() == true)
+ childPrintConfig = printConfig;
+ }
+
+ v.second.prettyPrint(stream, childPrintConfig, level + 1);;
+ index++;
+ };
+
+
+
+ std::set printed;
+ if (!printConfig.isNull())
+ {
+ if (printConfig.isMember("sort"))
+ {
+ for (std::string el : printConfig["sort"])
+ {
+ if (isMember(el))
+ {
+ printEl(std::pair(el, (*value.objectValue)[el]));
+ printed.insert(el);
+ }
+ }
+ }
+ }
+
+ for (auto v : *value.objectValue)
+ if (printed.find(v.first) == printed.end())
+ printEl(v);
+
+ stream << "\n";
+ indent(stream, level);
+ stream << "}";
+ break;
+ }
+ case Type::nullValue:
+ stream << "null";
+ break;
+ }
+ return stream;
+ }
+
+ const Value& Value::get(const char* key, const Value& default) const
+ {
+ if (isMember(key))
+ return (*this)[key];
+ return default;
+ }
+
+
+ std::string& operator <<(std::string &string, const Value& value)
+ {
+ std::stringstream stream;
+ stream << value;
+ string += stream.str();
+ return string;
+ }
+
+
+ std::ostream & operator<<(std::ostream &stream, const Value& value)
+ {
+ stream << std::fixed << std::setprecision(6);
+ switch (value.type)
+ {
+ case Type::intValue:
+ stream << value.value.intValue;
+ break;
+ case Type::floatValue:
+ assert(!isnan(value.value.floatValue));
+ //assert(isnormal(value.value.floatValue));
+ stream << value.value.floatValue;
+ break;
+ case Type::boolValue:
+ stream << (value.value.boolValue ? "true" : "false");
+ break;
+ case Type::stringValue:
+ {
+ std::string escaped = *value.value.stringValue;
+ escaped = std::regex_replace(escaped, std::regex("\\\\"), "\\\\");
+ stream << "\"" << escaped << "\""; //TODO: escape \'s
+ break;
+ }
+ case Type::arrayValue:
+ {
+ stream << "[";
+ bool first = true;
+ for (auto v : value)
+ {
+ if (!first)
+ stream << ", ";
+ stream << v;
+ first = false;
+ }
+ stream << "]";
+ break;
+ }
+ case Type::objectValue:
+ {
+ stream << "{";
+ bool first = true;
+ for (auto v : *value.value.objectValue)
+ {
+ if (!first)
+ stream << ", ";
+ stream << "\"" << v.first << "\" : " << v.second << std::endl;
+ first = false;
+ }
+ stream << "}";
+ break;
+ }
+ case Type::nullValue:
+ stream << "null";
+ break;
+ }
+ return stream;
+ }
+}
\ No newline at end of file
diff --git a/json.h b/json.h
new file mode 100644
index 0000000..e0738aa
--- /dev/null
+++ b/json.h
@@ -0,0 +1,132 @@
+#pragma once
+
+#include
+#include