Added Box2D

This commit is contained in:
Julian Nießner
2018-05-06 12:43:20 +02:00
parent cb5469bb89
commit d175d433a8
351 changed files with 123545 additions and 1 deletions

View File

@@ -0,0 +1,827 @@
/*
* Copyright (c) 2006-2013 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "DebugDraw.h"
#if defined(__APPLE_CC__)
#include <OpenGL/gl3.h>
#else
#include <glew/glew.h>
#endif
#include <glfw/glfw3.h>
#include <stdio.h>
#include <stdarg.h>
#include "RenderGL3.h"
#define BUFFER_OFFSET(x) ((const void*) (x))
DebugDraw g_debugDraw;
Camera g_camera;
//
b2Vec2 Camera::ConvertScreenToWorld(const b2Vec2& ps)
{
float32 w = float32(m_width);
float32 h = float32(m_height);
float32 u = ps.x / w;
float32 v = (h - ps.y) / h;
float32 ratio = w / h;
b2Vec2 extents(ratio * 25.0f, 25.0f);
extents *= m_zoom;
b2Vec2 lower = m_center - extents;
b2Vec2 upper = m_center + extents;
b2Vec2 pw;
pw.x = (1.0f - u) * lower.x + u * upper.x;
pw.y = (1.0f - v) * lower.y + v * upper.y;
return pw;
}
//
b2Vec2 Camera::ConvertWorldToScreen(const b2Vec2& pw)
{
float32 w = float32(m_width);
float32 h = float32(m_height);
float32 ratio = w / h;
b2Vec2 extents(ratio * 25.0f, 25.0f);
extents *= m_zoom;
b2Vec2 lower = m_center - extents;
b2Vec2 upper = m_center + extents;
float32 u = (pw.x - lower.x) / (upper.x - lower.x);
float32 v = (pw.y - lower.y) / (upper.y - lower.y);
b2Vec2 ps;
ps.x = u * w;
ps.y = (1.0f - v) * h;
return ps;
}
// Convert from world coordinates to normalized device coordinates.
// http://www.songho.ca/opengl/gl_projectionmatrix.html
void Camera::BuildProjectionMatrix(float32* m, float32 zBias)
{
float32 w = float32(m_width);
float32 h = float32(m_height);
float32 ratio = w / h;
b2Vec2 extents(ratio * 25.0f, 25.0f);
extents *= m_zoom;
b2Vec2 lower = m_center - extents;
b2Vec2 upper = m_center + extents;
m[0] = 2.0f / (upper.x - lower.x);
m[1] = 0.0f;
m[2] = 0.0f;
m[3] = 0.0f;
m[4] = 0.0f;
m[5] = 2.0f / (upper.y - lower.y);
m[6] = 0.0f;
m[7] = 0.0f;
m[8] = 0.0f;
m[9] = 0.0f;
m[10] = 1.0f;
m[11] = 0.0f;
m[12] = -(upper.x + lower.x) / (upper.x - lower.x);
m[13] = -(upper.y + lower.y) / (upper.y - lower.y);
m[14] = zBias;
m[15] = 1.0f;
}
//
static void sCheckGLError()
{
GLenum errCode = glGetError();
if (errCode != GL_NO_ERROR)
{
fprintf(stderr, "OpenGL error = %d\n", errCode);
assert(false);
}
}
// Prints shader compilation errors
static void sPrintLog(GLuint object)
{
GLint log_length = 0;
if (glIsShader(object))
glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
else if (glIsProgram(object))
glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
else
{
fprintf(stderr, "printlog: Not a shader or a program\n");
return;
}
char* log = (char*)malloc(log_length);
if (glIsShader(object))
glGetShaderInfoLog(object, log_length, NULL, log);
else if (glIsProgram(object))
glGetProgramInfoLog(object, log_length, NULL, log);
fprintf(stderr, "%s", log);
free(log);
}
//
static GLuint sCreateShaderFromString(const char* source, GLenum type)
{
GLuint res = glCreateShader(type);
const char* sources[] = { source };
glShaderSource(res, 1, sources, NULL);
glCompileShader(res);
GLint compile_ok = GL_FALSE;
glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
if (compile_ok == GL_FALSE)
{
fprintf(stderr, "Error compiling shader of type %d!\n", type);
sPrintLog(res);
glDeleteShader(res);
return 0;
}
return res;
}
//
static GLuint sCreateShaderProgram(const char* vs, const char* fs)
{
GLuint vsId = sCreateShaderFromString(vs, GL_VERTEX_SHADER);
GLuint fsId = sCreateShaderFromString(fs, GL_FRAGMENT_SHADER);
assert(vsId != 0 && fsId != 0);
GLuint programId = glCreateProgram();
glAttachShader(programId, vsId);
glAttachShader(programId, fsId);
glBindFragDataLocation(programId, 0, "color");
glLinkProgram(programId);
glDeleteShader(vsId);
glDeleteShader(fsId);
GLint status = GL_FALSE;
glGetProgramiv(programId, GL_LINK_STATUS, &status);
assert(status != GL_FALSE);
return programId;
}
//
struct GLRenderPoints
{
void Create()
{
const char* vs = \
"#version 400\n"
"uniform mat4 projectionMatrix;\n"
"layout(location = 0) in vec2 v_position;\n"
"layout(location = 1) in vec4 v_color;\n"
"layout(location = 2) in float v_size;\n"
"out vec4 f_color;\n"
"void main(void)\n"
"{\n"
" f_color = v_color;\n"
" gl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f);\n"
" gl_PointSize = v_size;\n"
"}\n";
const char* fs = \
"#version 400\n"
"in vec4 f_color;\n"
"out vec4 color;\n"
"void main(void)\n"
"{\n"
" color = f_color;\n"
"}\n";
m_programId = sCreateShaderProgram(vs, fs);
m_projectionUniform = glGetUniformLocation(m_programId, "projectionMatrix");
m_vertexAttribute = 0;
m_colorAttribute = 1;
m_sizeAttribute = 2;
// Generate
glGenVertexArrays(1, &m_vaoId);
glGenBuffers(3, m_vboIds);
glBindVertexArray(m_vaoId);
glEnableVertexAttribArray(m_vertexAttribute);
glEnableVertexAttribArray(m_colorAttribute);
glEnableVertexAttribArray(m_sizeAttribute);
// Vertex buffer
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
glVertexAttribPointer(m_vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertices), m_vertices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
glVertexAttribPointer(m_colorAttribute, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glBufferData(GL_ARRAY_BUFFER, sizeof(m_colors), m_colors, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[2]);
glVertexAttribPointer(m_sizeAttribute, 1, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glBufferData(GL_ARRAY_BUFFER, sizeof(m_sizes), m_sizes, GL_DYNAMIC_DRAW);
sCheckGLError();
// Cleanup
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
m_count = 0;
}
void Destroy()
{
if (m_vaoId)
{
glDeleteVertexArrays(1, &m_vaoId);
glDeleteBuffers(2, m_vboIds);
m_vaoId = 0;
}
if (m_programId)
{
glDeleteProgram(m_programId);
m_programId = 0;
}
}
void Vertex(const b2Vec2& v, const b2Color& c, float32 size)
{
if (m_count == e_maxVertices)
Flush();
m_vertices[m_count] = v;
m_colors[m_count] = c;
m_sizes[m_count] = size;
++m_count;
}
void Flush()
{
if (m_count == 0)
return;
glUseProgram(m_programId);
float32 proj[16] = { 0.0f };
g_camera.BuildProjectionMatrix(proj, 0.0f);
glUniformMatrix4fv(m_projectionUniform, 1, GL_FALSE, proj);
glBindVertexArray(m_vaoId);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Vec2), m_vertices);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Color), m_colors);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[2]);
glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(float32), m_sizes);
glEnable(GL_PROGRAM_POINT_SIZE);
glDrawArrays(GL_POINTS, 0, m_count);
glDisable(GL_PROGRAM_POINT_SIZE);
sCheckGLError();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
m_count = 0;
}
enum { e_maxVertices = 512 };
b2Vec2 m_vertices[e_maxVertices];
b2Color m_colors[e_maxVertices];
float32 m_sizes[e_maxVertices];
int32 m_count;
GLuint m_vaoId;
GLuint m_vboIds[3];
GLuint m_programId;
GLint m_projectionUniform;
GLint m_vertexAttribute;
GLint m_colorAttribute;
GLint m_sizeAttribute;
};
//
struct GLRenderLines
{
void Create()
{
const char* vs = \
"#version 400\n"
"uniform mat4 projectionMatrix;\n"
"layout(location = 0) in vec2 v_position;\n"
"layout(location = 1) in vec4 v_color;\n"
"out vec4 f_color;\n"
"void main(void)\n"
"{\n"
" f_color = v_color;\n"
" gl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f);\n"
"}\n";
const char* fs = \
"#version 400\n"
"in vec4 f_color;\n"
"out vec4 color;\n"
"void main(void)\n"
"{\n"
" color = f_color;\n"
"}\n";
m_programId = sCreateShaderProgram(vs, fs);
m_projectionUniform = glGetUniformLocation(m_programId, "projectionMatrix");
m_vertexAttribute = 0;
m_colorAttribute = 1;
// Generate
glGenVertexArrays(1, &m_vaoId);
glGenBuffers(2, m_vboIds);
glBindVertexArray(m_vaoId);
glEnableVertexAttribArray(m_vertexAttribute);
glEnableVertexAttribArray(m_colorAttribute);
// Vertex buffer
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
glVertexAttribPointer(m_vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertices), m_vertices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
glVertexAttribPointer(m_colorAttribute, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glBufferData(GL_ARRAY_BUFFER, sizeof(m_colors), m_colors, GL_DYNAMIC_DRAW);
sCheckGLError();
// Cleanup
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
m_count = 0;
}
void Destroy()
{
if (m_vaoId)
{
glDeleteVertexArrays(1, &m_vaoId);
glDeleteBuffers(2, m_vboIds);
m_vaoId = 0;
}
if (m_programId)
{
glDeleteProgram(m_programId);
m_programId = 0;
}
}
void Vertex(const b2Vec2& v, const b2Color& c)
{
if (m_count == e_maxVertices)
Flush();
m_vertices[m_count] = v;
m_colors[m_count] = c;
++m_count;
}
void Flush()
{
if (m_count == 0)
return;
glUseProgram(m_programId);
float32 proj[16] = { 0.0f };
g_camera.BuildProjectionMatrix(proj, 0.1f);
glUniformMatrix4fv(m_projectionUniform, 1, GL_FALSE, proj);
glBindVertexArray(m_vaoId);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Vec2), m_vertices);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Color), m_colors);
glDrawArrays(GL_LINES, 0, m_count);
sCheckGLError();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
m_count = 0;
}
enum { e_maxVertices = 2 * 512 };
b2Vec2 m_vertices[e_maxVertices];
b2Color m_colors[e_maxVertices];
int32 m_count;
GLuint m_vaoId;
GLuint m_vboIds[2];
GLuint m_programId;
GLint m_projectionUniform;
GLint m_vertexAttribute;
GLint m_colorAttribute;
};
//
struct GLRenderTriangles
{
void Create()
{
const char* vs = \
"#version 400\n"
"uniform mat4 projectionMatrix;\n"
"layout(location = 0) in vec2 v_position;\n"
"layout(location = 1) in vec4 v_color;\n"
"out vec4 f_color;\n"
"void main(void)\n"
"{\n"
" f_color = v_color;\n"
" gl_Position = projectionMatrix * vec4(v_position, 0.0f, 1.0f);\n"
"}\n";
const char* fs = \
"#version 400\n"
"in vec4 f_color;\n"
"out vec4 color;\n"
"void main(void)\n"
"{\n"
" color = f_color;\n"
"}\n";
m_programId = sCreateShaderProgram(vs, fs);
m_projectionUniform = glGetUniformLocation(m_programId, "projectionMatrix");
m_vertexAttribute = 0;
m_colorAttribute = 1;
// Generate
glGenVertexArrays(1, &m_vaoId);
glGenBuffers(2, m_vboIds);
glBindVertexArray(m_vaoId);
glEnableVertexAttribArray(m_vertexAttribute);
glEnableVertexAttribArray(m_colorAttribute);
// Vertex buffer
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
glVertexAttribPointer(m_vertexAttribute, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertices), m_vertices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
glVertexAttribPointer(m_colorAttribute, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glBufferData(GL_ARRAY_BUFFER, sizeof(m_colors), m_colors, GL_DYNAMIC_DRAW);
sCheckGLError();
// Cleanup
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
m_count = 0;
}
void Destroy()
{
if (m_vaoId)
{
glDeleteVertexArrays(1, &m_vaoId);
glDeleteBuffers(2, m_vboIds);
m_vaoId = 0;
}
if (m_programId)
{
glDeleteProgram(m_programId);
m_programId = 0;
}
}
void Vertex(const b2Vec2& v, const b2Color& c)
{
if (m_count == e_maxVertices)
Flush();
m_vertices[m_count] = v;
m_colors[m_count] = c;
++m_count;
}
void Flush()
{
if (m_count == 0)
return;
glUseProgram(m_programId);
float32 proj[16] = { 0.0f };
g_camera.BuildProjectionMatrix(proj, 0.2f);
glUniformMatrix4fv(m_projectionUniform, 1, GL_FALSE, proj);
glBindVertexArray(m_vaoId);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[0]);
glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Vec2), m_vertices);
glBindBuffer(GL_ARRAY_BUFFER, m_vboIds[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, m_count * sizeof(b2Color), m_colors);
glEnable(GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDrawArrays(GL_TRIANGLES, 0, m_count);
glDisable(GL_BLEND);
sCheckGLError();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
m_count = 0;
}
enum { e_maxVertices = 3 * 512 };
b2Vec2 m_vertices[e_maxVertices];
b2Color m_colors[e_maxVertices];
int32 m_count;
GLuint m_vaoId;
GLuint m_vboIds[2];
GLuint m_programId;
GLint m_projectionUniform;
GLint m_vertexAttribute;
GLint m_colorAttribute;
};
//
DebugDraw::DebugDraw()
{
m_points = NULL;
m_lines = NULL;
m_triangles = NULL;
}
//
DebugDraw::~DebugDraw()
{
b2Assert(m_points == NULL);
b2Assert(m_lines == NULL);
b2Assert(m_triangles == NULL);
}
//
void DebugDraw::Create()
{
m_points = new GLRenderPoints;
m_points->Create();
m_lines = new GLRenderLines;
m_lines->Create();
m_triangles = new GLRenderTriangles;
m_triangles->Create();
}
//
void DebugDraw::Destroy()
{
m_points->Destroy();
delete m_points;
m_points = NULL;
m_lines->Destroy();
delete m_lines;
m_lines = NULL;
m_triangles->Destroy();
delete m_triangles;
m_triangles = NULL;
}
//
void DebugDraw::DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
{
b2Vec2 p1 = vertices[vertexCount - 1];
for (int32 i = 0; i < vertexCount; ++i)
{
b2Vec2 p2 = vertices[i];
m_lines->Vertex(p1, color);
m_lines->Vertex(p2, color);
p1 = p2;
}
}
//
void DebugDraw::DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
{
b2Color fillColor(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f);
for (int32 i = 1; i < vertexCount - 1; ++i)
{
m_triangles->Vertex(vertices[0], fillColor);
m_triangles->Vertex(vertices[i], fillColor);
m_triangles->Vertex(vertices[i+1], fillColor);
}
b2Vec2 p1 = vertices[vertexCount - 1];
for (int32 i = 0; i < vertexCount; ++i)
{
b2Vec2 p2 = vertices[i];
m_lines->Vertex(p1, color);
m_lines->Vertex(p2, color);
p1 = p2;
}
}
//
void DebugDraw::DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color)
{
const float32 k_segments = 16.0f;
const float32 k_increment = 2.0f * b2_pi / k_segments;
float32 sinInc = sinf(k_increment);
float32 cosInc = cosf(k_increment);
b2Vec2 r1(1.0f, 0.0f);
b2Vec2 v1 = center + radius * r1;
for (int32 i = 0; i < k_segments; ++i)
{
// Perform rotation to avoid additional trigonometry.
b2Vec2 r2;
r2.x = cosInc * r1.x - sinInc * r1.y;
r2.y = sinInc * r1.x + cosInc * r1.y;
b2Vec2 v2 = center + radius * r2;
m_lines->Vertex(v1, color);
m_lines->Vertex(v2, color);
r1 = r2;
v1 = v2;
}
}
//
void DebugDraw::DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color)
{
const float32 k_segments = 16.0f;
const float32 k_increment = 2.0f * b2_pi / k_segments;
float32 sinInc = sinf(k_increment);
float32 cosInc = cosf(k_increment);
b2Vec2 v0 = center;
b2Vec2 r1(cosInc, sinInc);
b2Vec2 v1 = center + radius * r1;
b2Color fillColor(0.5f * color.r, 0.5f * color.g, 0.5f * color.b, 0.5f);
for (int32 i = 0; i < k_segments; ++i)
{
// Perform rotation to avoid additional trigonometry.
b2Vec2 r2;
r2.x = cosInc * r1.x - sinInc * r1.y;
r2.y = sinInc * r1.x + cosInc * r1.y;
b2Vec2 v2 = center + radius * r2;
m_triangles->Vertex(v0, fillColor);
m_triangles->Vertex(v1, fillColor);
m_triangles->Vertex(v2, fillColor);
r1 = r2;
v1 = v2;
}
r1.Set(1.0f, 0.0f);
v1 = center + radius * r1;
for (int32 i = 0; i < k_segments; ++i)
{
b2Vec2 r2;
r2.x = cosInc * r1.x - sinInc * r1.y;
r2.y = sinInc * r1.x + cosInc * r1.y;
b2Vec2 v2 = center + radius * r2;
m_lines->Vertex(v1, color);
m_lines->Vertex(v2, color);
r1 = r2;
v1 = v2;
}
// Draw a line fixed in the circle to animate rotation.
b2Vec2 p = center + radius * axis;
m_lines->Vertex(center, color);
m_lines->Vertex(p, color);
}
//
void DebugDraw::DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color)
{
m_lines->Vertex(p1, color);
m_lines->Vertex(p2, color);
}
//
void DebugDraw::DrawTransform(const b2Transform& xf)
{
const float32 k_axisScale = 0.4f;
b2Color red(1.0f, 0.0f, 0.0f);
b2Color green(0.0f, 1.0f, 0.0f);
b2Vec2 p1 = xf.p, p2;
m_lines->Vertex(p1, red);
p2 = p1 + k_axisScale * xf.q.GetXAxis();
m_lines->Vertex(p2, red);
m_lines->Vertex(p1, green);
p2 = p1 + k_axisScale * xf.q.GetYAxis();
m_lines->Vertex(p2, green);
}
void DebugDraw::DrawPoint(const b2Vec2& p, float32 size, const b2Color& color)
{
m_points->Vertex(p, color, size);
}
void DebugDraw::DrawString(int x, int y, const char *string, ...)
{
float32 h = float32(g_camera.m_height);
char buffer[128];
va_list arg;
va_start(arg, string);
vsprintf(buffer, string, arg);
va_end(arg);
AddGfxCmdText(float(x), h - float(y), TEXT_ALIGN_LEFT, buffer, SetRGBA(230, 153, 153, 255));
}
void DebugDraw::DrawString(const b2Vec2& pw, const char *string, ...)
{
b2Vec2 ps = g_camera.ConvertWorldToScreen(pw);
float32 h = float32(g_camera.m_height);
char buffer[128];
va_list arg;
va_start(arg, string);
vsprintf(buffer, string, arg);
va_end(arg);
AddGfxCmdText(ps.x, h - ps.y, TEXT_ALIGN_LEFT, buffer, SetRGBA(230, 153, 153, 255));
}
void DebugDraw::DrawAABB(b2AABB* aabb, const b2Color& c)
{
b2Vec2 p1 = aabb->lowerBound;
b2Vec2 p2 = b2Vec2(aabb->upperBound.x, aabb->lowerBound.y);
b2Vec2 p3 = aabb->upperBound;
b2Vec2 p4 = b2Vec2(aabb->lowerBound.x, aabb->upperBound.y);
m_lines->Vertex(p1, c);
m_lines->Vertex(p2, c);
m_lines->Vertex(p2, c);
m_lines->Vertex(p3, c);
m_lines->Vertex(p3, c);
m_lines->Vertex(p4, c);
m_lines->Vertex(p4, c);
m_lines->Vertex(p1, c);
}
//
void DebugDraw::Flush()
{
m_triangles->Flush();
m_lines->Flush();
m_points->Flush();
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2006-2013 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DEBUGDRAW_H
#define DEBUGDRAW_H
#include <Box2D/Box2D.h>
struct b2AABB;
struct GLRenderPoints;
struct GLRenderLines;
struct GLRenderTriangles;
//
struct Camera
{
Camera()
{
m_center.Set(0.0f, 20.0f);
m_extent = 25.0f;
m_zoom = 1.0f;
m_width = 1280;
m_height = 800;
}
b2Vec2 ConvertScreenToWorld(const b2Vec2& screenPoint);
b2Vec2 ConvertWorldToScreen(const b2Vec2& worldPoint);
void BuildProjectionMatrix(float32* m, float32 zBias);
b2Vec2 m_center;
float32 m_extent;
float32 m_zoom;
int32 m_width;
int32 m_height;
};
// This class implements debug drawing callbacks that are invoked
// inside b2World::Step.
class DebugDraw : public b2Draw
{
public:
DebugDraw();
~DebugDraw();
void Create();
void Destroy();
void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color);
void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color);
void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color);
void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color);
void DrawTransform(const b2Transform& xf);
void DrawPoint(const b2Vec2& p, float32 size, const b2Color& color);
void DrawString(int x, int y, const char* string, ...);
void DrawString(const b2Vec2& p, const char* string, ...);
void DrawAABB(b2AABB* aabb, const b2Color& color);
void Flush();
private:
GLRenderPoints* m_points;
GLRenderLines* m_lines;
GLRenderTriangles* m_triangles;
};
extern DebugDraw g_debugDraw;
extern Camera g_camera;
#endif

View File

@@ -0,0 +1,561 @@
/*
* Copyright (c) 2006-2013 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "imgui.h"
#include "RenderGL3.h"
#include "DebugDraw.h"
#include "Test.h"
#if defined(__APPLE__)
#include <OpenGL/gl3.h>
#else
#include <glew/glew.h>
#endif
#include <glfw/glfw3.h>
#include <stdio.h>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
//
struct UIState
{
bool showMenu;
int scroll;
int scrollarea1;
bool mouseOverMenu;
bool chooseTest;
};
//
namespace
{
GLFWwindow* mainWindow = NULL;
UIState ui;
int32 testIndex = 0;
int32 testSelection = 0;
int32 testCount = 0;
TestEntry* entry;
Test* test;
Settings settings;
bool rightMouseDown;
b2Vec2 lastp;
}
//
static void sCreateUI()
{
ui.showMenu = true;
ui.scroll = 0;
ui.scrollarea1 = 0;
ui.chooseTest = false;
ui.mouseOverMenu = false;
// Init UI
const char* fontPath = "../Data/DroidSans.ttf";
if (RenderGLInit(fontPath) == false)
{
fprintf(stderr, "Could not init GUI renderer.\n");
assert(false);
return;
}
}
//
static void sResizeWindow(GLFWwindow*, int width, int height)
{
g_camera.m_width = width;
g_camera.m_height = height;
}
//
static void sKeyCallback(GLFWwindow*, int key, int scancode, int action, int mods)
{
if (action == GLFW_PRESS)
{
switch (key)
{
case GLFW_KEY_ESCAPE:
// Quit
glfwSetWindowShouldClose(mainWindow, GL_TRUE);
break;
case GLFW_KEY_LEFT:
// Pan left
if (mods == GLFW_MOD_CONTROL)
{
b2Vec2 newOrigin(2.0f, 0.0f);
test->ShiftOrigin(newOrigin);
}
else
{
g_camera.m_center.x -= 0.5f;
}
break;
case GLFW_KEY_RIGHT:
// Pan right
if (mods == GLFW_MOD_CONTROL)
{
b2Vec2 newOrigin(-2.0f, 0.0f);
test->ShiftOrigin(newOrigin);
}
else
{
g_camera.m_center.x += 0.5f;
}
break;
case GLFW_KEY_DOWN:
// Pan down
if (mods == GLFW_MOD_CONTROL)
{
b2Vec2 newOrigin(0.0f, 2.0f);
test->ShiftOrigin(newOrigin);
}
else
{
g_camera.m_center.y -= 0.5f;
}
break;
case GLFW_KEY_UP:
// Pan up
if (mods == GLFW_MOD_CONTROL)
{
b2Vec2 newOrigin(0.0f, -2.0f);
test->ShiftOrigin(newOrigin);
}
else
{
g_camera.m_center.y += 0.5f;
}
break;
case GLFW_KEY_HOME:
// Reset view
g_camera.m_zoom = 1.0f;
g_camera.m_center.Set(0.0f, 20.0f);
break;
case GLFW_KEY_Z:
// Zoom out
g_camera.m_zoom = b2Min(1.1f * g_camera.m_zoom, 20.0f);
break;
case GLFW_KEY_X:
// Zoom in
g_camera.m_zoom = b2Max(0.9f * g_camera.m_zoom, 0.02f);
break;
case GLFW_KEY_R:
// Reset test
delete test;
test = entry->createFcn();
break;
case GLFW_KEY_SPACE:
// Launch a bomb.
if (test)
{
test->LaunchBomb();
}
break;
case GLFW_KEY_P:
// Pause
settings.pause = !settings.pause;
break;
case GLFW_KEY_LEFT_BRACKET:
// Switch to previous test
--testSelection;
if (testSelection < 0)
{
testSelection = testCount - 1;
}
break;
case GLFW_KEY_RIGHT_BRACKET:
// Switch to next test
++testSelection;
if (testSelection == testCount)
{
testSelection = 0;
}
break;
case GLFW_KEY_TAB:
ui.showMenu = !ui.showMenu;
default:
if (test)
{
test->Keyboard(key);
}
}
}
else if (action == GLFW_RELEASE)
{
test->KeyboardUp(key);
}
// else GLFW_REPEAT
}
//
static void sMouseButton(GLFWwindow*, int32 button, int32 action, int32 mods)
{
double xd, yd;
glfwGetCursorPos(mainWindow, &xd, &yd);
b2Vec2 ps((float32)xd, (float32)yd);
// Use the mouse to move things around.
if (button == GLFW_MOUSE_BUTTON_1)
{
//<##>
//ps.Set(0, 0);
b2Vec2 pw = g_camera.ConvertScreenToWorld(ps);
if (action == GLFW_PRESS)
{
if (mods == GLFW_MOD_SHIFT)
{
test->ShiftMouseDown(pw);
}
else
{
test->MouseDown(pw);
}
}
if (action == GLFW_RELEASE)
{
test->MouseUp(pw);
}
}
else if (button == GLFW_MOUSE_BUTTON_2)
{
if (action == GLFW_PRESS)
{
lastp = g_camera.ConvertScreenToWorld(ps);
rightMouseDown = true;
}
if (action == GLFW_RELEASE)
{
rightMouseDown = false;
}
}
}
//
static void sMouseMotion(GLFWwindow*, double xd, double yd)
{
b2Vec2 ps((float)xd, (float)yd);
b2Vec2 pw = g_camera.ConvertScreenToWorld(ps);
test->MouseMove(pw);
if (rightMouseDown)
{
b2Vec2 diff = pw - lastp;
g_camera.m_center.x -= diff.x;
g_camera.m_center.y -= diff.y;
lastp = g_camera.ConvertScreenToWorld(ps);
}
}
//
static void sScrollCallback(GLFWwindow*, double, double dy)
{
if (ui.mouseOverMenu)
{
ui.scroll = -int(dy);
}
else
{
if (dy > 0)
{
g_camera.m_zoom /= 1.1f;
}
else
{
g_camera.m_zoom *= 1.1f;
}
}
}
//
static void sRestart()
{
delete test;
entry = g_testEntries + testIndex;
test = entry->createFcn();
}
//
static void sSimulate()
{
glEnable(GL_DEPTH_TEST);
test->Step(&settings);
test->DrawTitle(entry->name);
glDisable(GL_DEPTH_TEST);
if (testSelection != testIndex)
{
testIndex = testSelection;
delete test;
entry = g_testEntries + testIndex;
test = entry->createFcn();
g_camera.m_zoom = 1.0f;
g_camera.m_center.Set(0.0f, 20.0f);
}
}
//
static void sInterface()
{
int menuWidth = 200;
ui.mouseOverMenu = false;
if (ui.showMenu)
{
bool over = imguiBeginScrollArea("Testbed Controls", g_camera.m_width - menuWidth - 10, 10, menuWidth, g_camera.m_height - 20, &ui.scrollarea1);
if (over) ui.mouseOverMenu = true;
imguiSeparatorLine();
imguiLabel("Test");
if (imguiButton(entry->name, true))
{
ui.chooseTest = !ui.chooseTest;
}
imguiSeparatorLine();
imguiSlider("Vel Iters", &settings.velocityIterations, 0, 50, 1, true);
imguiSlider("Pos Iters", &settings.positionIterations, 0, 50, 1, true);
imguiSlider("Hertz", &settings.hz, 5.0f, 120.0f, 5.0f, true);
if (imguiCheck("Sleep", settings.enableSleep, true))
settings.enableSleep = !settings.enableSleep;
if (imguiCheck("Warm Starting", settings.enableWarmStarting, true))
settings.enableWarmStarting = !settings.enableWarmStarting;
if (imguiCheck("Time of Impact", settings.enableContinuous, true))
settings.enableContinuous = !settings.enableContinuous;
if (imguiCheck("Sub-Stepping", settings.enableSubStepping, true))
settings.enableSubStepping = !settings.enableSubStepping;
imguiSeparatorLine();
if (imguiCheck("Shapes", settings.drawShapes, true))
settings.drawShapes = !settings.drawShapes;
if (imguiCheck("Joints", settings.drawJoints, true))
settings.drawJoints = !settings.drawJoints;
if (imguiCheck("AABBs", settings.drawAABBs, true))
settings.drawAABBs = !settings.drawAABBs;
if (imguiCheck("Contact Points", settings.drawContactPoints, true))
settings.drawContactPoints = !settings.drawContactPoints;
if (imguiCheck("Contact Normals", settings.drawContactNormals, true))
settings.drawContactNormals = !settings.drawContactNormals;
if (imguiCheck("Contact Impulses", settings.drawContactImpulse, true))
settings.drawContactImpulse = !settings.drawContactImpulse;
if (imguiCheck("Friction Impulses", settings.drawFrictionImpulse, true))
settings.drawFrictionImpulse = !settings.drawFrictionImpulse;
if (imguiCheck("Center of Masses", settings.drawCOMs, true))
settings.drawCOMs = !settings.drawCOMs;
if (imguiCheck("Statistics", settings.drawStats, true))
settings.drawStats = !settings.drawStats;
if (imguiCheck("Profile", settings.drawProfile, true))
settings.drawProfile = !settings.drawProfile;
if (imguiButton("Pause", true))
settings.pause = !settings.pause;
if (imguiButton("Single Step", true))
settings.singleStep = !settings.singleStep;
if (imguiButton("Restart", true))
sRestart();
if (imguiButton("Quit", true))
glfwSetWindowShouldClose(mainWindow, GL_TRUE);
imguiEndScrollArea();
}
int testMenuWidth = 200;
if (ui.chooseTest)
{
static int testScroll = 0;
bool over = imguiBeginScrollArea("Choose Sample", g_camera.m_width - menuWidth - testMenuWidth - 20, 10, testMenuWidth, g_camera.m_height - 20, &testScroll);
if (over) ui.mouseOverMenu = true;
for (int i = 0; i < testCount; ++i)
{
if (imguiItem(g_testEntries[i].name, true))
{
delete test;
entry = g_testEntries + i;
test = entry->createFcn();
ui.chooseTest = false;
}
}
imguiEndScrollArea();
}
imguiEndFrame();
}
//
int main(int argc, char** argv)
{
#if defined(_WIN32)
// Enable memory-leak reports
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
#endif
g_camera.m_width = 1024;
g_camera.m_height = 640;
if (glfwInit() == 0)
{
fprintf(stderr, "Failed to initialize GLFW\n");
return -1;
}
char title[64];
sprintf(title, "Box2D Testbed Version %d.%d.%d", b2_version.major, b2_version.minor, b2_version.revision);
#if defined(__APPLE__)
// Not sure why, but these settings cause glewInit below to crash.
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#endif
mainWindow = glfwCreateWindow(g_camera.m_width, g_camera.m_height, title, NULL, NULL);
if (mainWindow == NULL)
{
fprintf(stderr, "Failed to open GLFW mainWindow.\n");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(mainWindow);
printf("OpenGL %s, GLSL %s\n", glGetString(GL_VERSION), glGetString(GL_SHADING_LANGUAGE_VERSION));
glfwSetScrollCallback(mainWindow, sScrollCallback);
glfwSetWindowSizeCallback(mainWindow, sResizeWindow);
glfwSetKeyCallback(mainWindow, sKeyCallback);
glfwSetMouseButtonCallback(mainWindow, sMouseButton);
glfwSetCursorPosCallback(mainWindow, sMouseMotion);
glfwSetScrollCallback(mainWindow, sScrollCallback);
#if defined(__APPLE__) == FALSE
//glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (GLEW_OK != err)
{
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
exit(EXIT_FAILURE);
}
#endif
g_debugDraw.Create();
sCreateUI();
testCount = 0;
while (g_testEntries[testCount].createFcn != NULL)
{
++testCount;
}
testIndex = b2Clamp(testIndex, 0, testCount - 1);
testSelection = testIndex;
entry = g_testEntries + testIndex;
test = entry->createFcn();
// Control the frame rate. One draw per monitor refresh.
glfwSwapInterval(1);
double time1 = glfwGetTime();
double frameTime = 0.0;
glClearColor(0.3f, 0.3f, 0.3f, 1.f);
while (!glfwWindowShouldClose(mainWindow))
{
glfwGetWindowSize(mainWindow, &g_camera.m_width, &g_camera.m_height);
glViewport(0, 0, g_camera.m_width, g_camera.m_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
unsigned char mousebutton = 0;
int mscroll = ui.scroll;
ui.scroll = 0;
double xd, yd;
glfwGetCursorPos(mainWindow, &xd, &yd);
int mousex = int(xd);
int mousey = int(yd);
mousey = g_camera.m_height - mousey;
int leftButton = glfwGetMouseButton(mainWindow, GLFW_MOUSE_BUTTON_LEFT);
if (leftButton == GLFW_PRESS)
mousebutton |= IMGUI_MBUT_LEFT;
imguiBeginFrame(mousex, mousey, mousebutton, mscroll);
sSimulate();
sInterface();
// Measure speed
double time2 = glfwGetTime();
double alpha = 0.9f;
frameTime = alpha * frameTime + (1.0 - alpha) * (time2 - time1);
time1 = time2;
char buffer[32];
snprintf(buffer, 32, "%.1f ms", 1000.0 * frameTime);
AddGfxCmdText(5, 5, TEXT_ALIGN_LEFT, buffer, WHITE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
RenderGLFlush(g_camera.m_width, g_camera.m_height);
glfwSwapBuffers(mainWindow);
glfwPollEvents();
}
g_debugDraw.Destroy();
RenderGLDestroy();
glfwTerminate();
return 0;
}

View File

@@ -0,0 +1,832 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// Source altered and distributed from https://github.com/AdrienHerubel/imgui
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#ifdef __APPLE__
#include <OpenGL/gl3.h>
#else
#include <glew/glew.h>
#include <GL/gl.h>
#endif
#include "RenderGL3.h"
// Some math headers don't have PI defined.
static const float PI = 3.14159265f;
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
// Pull render interface.
enum GfxCmdType
{
GFXCMD_RECT,
GFXCMD_TRIANGLE,
GFXCMD_LINE,
GFXCMD_TEXT,
GFXCMD_SCISSOR,
};
struct GfxRect
{
short x, y, w, h, r;
};
struct GfxText
{
float x, y;
TextAlign align;
const char* text;
};
struct GfxLine
{
short x0, y0, x1, y1, r;
};
struct GfxCmd
{
char type;
char flags;
char pad[2];
unsigned int col;
union
{
GfxLine line;
GfxRect rect;
GfxText text;
};
};
static const unsigned TEMP_COORD_COUNT = 100;
static float g_tempCoords[TEMP_COORD_COUNT * 2];
static float g_tempNormals[TEMP_COORD_COUNT * 2];
static float g_tempVertices[TEMP_COORD_COUNT * 12 + (TEMP_COORD_COUNT - 2) * 6];
static float g_tempTextureCoords[TEMP_COORD_COUNT * 12 + (TEMP_COORD_COUNT - 2) * 6];
static float g_tempColors[TEMP_COORD_COUNT * 24 + (TEMP_COORD_COUNT - 2) * 12];
static const int CIRCLE_VERTS = 8 * 4;
static float g_circleVerts[CIRCLE_VERTS * 2];
static stbtt_bakedchar g_cdata[96]; // ASCII 32..126 is 95 glyphs
static GLuint g_ftex = 0;
static GLuint g_whitetex = 0;
static GLuint g_vao = 0;
static GLuint g_vbos[3] = { 0, 0, 0 };
static GLuint g_program = 0;
static GLuint g_programViewportLocation = 0;
static GLuint g_programTextureLocation = 0;
static const unsigned TEXT_POOL_SIZE = 8000;
static char g_textPool[TEXT_POOL_SIZE];
static unsigned g_textPoolSize = 0;
static const char* allocText(const char* text)
{
unsigned len = (unsigned)strlen(text) + 1;
if (g_textPoolSize + len >= TEXT_POOL_SIZE)
return 0;
char* dst = &g_textPool[g_textPoolSize];
memcpy(dst, text, len);
g_textPoolSize += len;
return dst;
}
static const unsigned GFXCMD_QUEUE_SIZE = 5000;
static GfxCmd g_gfxCmdQueue[GFXCMD_QUEUE_SIZE];
static unsigned g_gfxCmdQueueSize = 0;
static void ResetGfxCmdQueue()
{
g_gfxCmdQueueSize = 0;
g_textPoolSize = 0;
}
void AddGfxCmdScissor(int x, int y, int w, int h)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_SCISSOR;
cmd.flags = x < 0 ? 0 : 1; // on/off flag.
cmd.col = 0;
cmd.rect.x = (short)x;
cmd.rect.y = (short)y;
cmd.rect.w = (short)w;
cmd.rect.h = (short)h;
}
void AddGfxCmdRect(float x, float y, float w, float h, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_RECT;
cmd.flags = 0;
cmd.col = color;
cmd.rect.x = (short)(x*8.0f);
cmd.rect.y = (short)(y*8.0f);
cmd.rect.w = (short)(w*8.0f);
cmd.rect.h = (short)(h*8.0f);
cmd.rect.r = 0;
}
void AddGfxCmdLine(float x0, float y0, float x1, float y1, float r, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_LINE;
cmd.flags = 0;
cmd.col = color;
cmd.line.x0 = (short)(x0*8.0f);
cmd.line.y0 = (short)(y0*8.0f);
cmd.line.x1 = (short)(x1*8.0f);
cmd.line.y1 = (short)(y1*8.0f);
cmd.line.r = (short)(r*8.0f);
}
void AddGfxCmdRoundedRect(float x, float y, float w, float h, float r, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_RECT;
cmd.flags = 0;
cmd.col = color;
cmd.rect.x = (short)(x*8.0f);
cmd.rect.y = (short)(y*8.0f);
cmd.rect.w = (short)(w*8.0f);
cmd.rect.h = (short)(h*8.0f);
cmd.rect.r = (short)(r*8.0f);
}
void AddGfxCmdTriangle(int x, int y, int w, int h, int flags, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_TRIANGLE;
cmd.flags = (char)flags;
cmd.col = color;
cmd.rect.x = (short)(x*8.0f);
cmd.rect.y = (short)(y*8.0f);
cmd.rect.w = (short)(w*8.0f);
cmd.rect.h = (short)(h*8.0f);
}
//
void AddGfxCmdText(float x, float y, TextAlign align, const char* text, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_TEXT;
cmd.flags = 0;
cmd.col = color;
cmd.text.x = x;
cmd.text.y = y;
cmd.text.align = align;
cmd.text.text = allocText(text);
}
//
void AddGfxCmdText(int x, int y, TextAlign align, const char* text, unsigned int color)
{
AddGfxCmdText(float(x), float(y), align, text, color);
}
//
static void sDrawPolygon(const float* coords, unsigned numCoords, float r, unsigned int col)
{
if (numCoords > TEMP_COORD_COUNT) numCoords = TEMP_COORD_COUNT;
for (unsigned i = 0, j = numCoords - 1; i < numCoords; j = i++)
{
const float* v0 = &coords[j * 2];
const float* v1 = &coords[i * 2];
float dx = v1[0] - v0[0];
float dy = v1[1] - v0[1];
float d = sqrtf(dx*dx + dy*dy);
if (d > 0)
{
d = 1.0f / d;
dx *= d;
dy *= d;
}
g_tempNormals[j * 2 + 0] = dy;
g_tempNormals[j * 2 + 1] = -dx;
}
float colf[4] = { (float)(col & 0xff) / 255.f, (float)((col >> 8) & 0xff) / 255.f, (float)((col >> 16) & 0xff) / 255.f, (float)((col >> 24) & 0xff) / 255.f };
float colTransf[4] = { (float)(col & 0xff) / 255.f, (float)((col >> 8) & 0xff) / 255.f, (float)((col >> 16) & 0xff) / 255.f, 0 };
for (unsigned i = 0, j = numCoords - 1; i < numCoords; j = i++)
{
float dlx0 = g_tempNormals[j * 2 + 0];
float dly0 = g_tempNormals[j * 2 + 1];
float dlx1 = g_tempNormals[i * 2 + 0];
float dly1 = g_tempNormals[i * 2 + 1];
float dmx = (dlx0 + dlx1) * 0.5f;
float dmy = (dly0 + dly1) * 0.5f;
float dmr2 = dmx*dmx + dmy*dmy;
if (dmr2 > 0.000001f)
{
float scale = 1.0f / dmr2;
if (scale > 10.0f) scale = 10.0f;
dmx *= scale;
dmy *= scale;
}
g_tempCoords[i * 2 + 0] = coords[i * 2 + 0] + dmx*r;
g_tempCoords[i * 2 + 1] = coords[i * 2 + 1] + dmy*r;
}
int vSize = numCoords * 12 + (numCoords - 2) * 6;
int uvSize = numCoords * 2 * 6 + (numCoords - 2) * 2 * 3;
int cSize = numCoords * 4 * 6 + (numCoords - 2) * 4 * 3;
float * v = g_tempVertices;
float * uv = g_tempTextureCoords;
memset(uv, 0, uvSize * sizeof(float));
float * c = g_tempColors;
memset(c, 1, cSize * sizeof(float));
float * ptrV = v;
float * ptrC = c;
for (unsigned i = 0, j = numCoords - 1; i < numCoords; j = i++)
{
*ptrV = coords[i * 2];
*(ptrV + 1) = coords[i * 2 + 1];
ptrV += 2;
*ptrV = coords[j * 2];
*(ptrV + 1) = coords[j * 2 + 1];
ptrV += 2;
*ptrV = g_tempCoords[j * 2];
*(ptrV + 1) = g_tempCoords[j * 2 + 1];
ptrV += 2;
*ptrV = g_tempCoords[j * 2];
*(ptrV + 1) = g_tempCoords[j * 2 + 1];
ptrV += 2;
*ptrV = g_tempCoords[i * 2];
*(ptrV + 1) = g_tempCoords[i * 2 + 1];
ptrV += 2;
*ptrV = coords[i * 2];
*(ptrV + 1) = coords[i * 2 + 1];
ptrV += 2;
*ptrC = colf[0];
*(ptrC + 1) = colf[1];
*(ptrC + 2) = colf[2];
*(ptrC + 3) = colf[3];
ptrC += 4;
*ptrC = colf[0];
*(ptrC + 1) = colf[1];
*(ptrC + 2) = colf[2];
*(ptrC + 3) = colf[3];
ptrC += 4;
*ptrC = colTransf[0];
*(ptrC + 1) = colTransf[1];
*(ptrC + 2) = colTransf[2];
*(ptrC + 3) = colTransf[3];
ptrC += 4;
*ptrC = colTransf[0];
*(ptrC + 1) = colTransf[1];
*(ptrC + 2) = colTransf[2];
*(ptrC + 3) = colTransf[3];
ptrC += 4;
*ptrC = colTransf[0];
*(ptrC + 1) = colTransf[1];
*(ptrC + 2) = colTransf[2];
*(ptrC + 3) = colTransf[3];
ptrC += 4;
*ptrC = colf[0];
*(ptrC + 1) = colf[1];
*(ptrC + 2) = colf[2];
*(ptrC + 3) = colf[3];
ptrC += 4;
}
for (unsigned i = 2; i < numCoords; ++i)
{
*ptrV = coords[0];
*(ptrV + 1) = coords[1];
ptrV += 2;
*ptrV = coords[(i - 1) * 2];
*(ptrV + 1) = coords[(i - 1) * 2 + 1];
ptrV += 2;
*ptrV = coords[i * 2];
*(ptrV + 1) = coords[i * 2 + 1];
ptrV += 2;
*ptrC = colf[0];
*(ptrC + 1) = colf[1];
*(ptrC + 2) = colf[2];
*(ptrC + 3) = colf[3];
ptrC += 4;
*ptrC = colf[0];
*(ptrC + 1) = colf[1];
*(ptrC + 2) = colf[2];
*(ptrC + 3) = colf[3];
ptrC += 4;
*ptrC = colf[0];
*(ptrC + 1) = colf[1];
*(ptrC + 2) = colf[2];
*(ptrC + 3) = colf[3];
ptrC += 4;
}
glBindTexture(GL_TEXTURE_2D, g_whitetex);
glBindVertexArray(g_vao);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]);
glBufferData(GL_ARRAY_BUFFER, vSize*sizeof(float), v, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]);
glBufferData(GL_ARRAY_BUFFER, uvSize*sizeof(float), uv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]);
glBufferData(GL_ARRAY_BUFFER, cSize*sizeof(float), c, GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, (numCoords * 2 + numCoords - 2) * 3);
}
static void sDrawRect(float x, float y, float w, float h, float fth, unsigned int col)
{
float verts[4 * 2] =
{
x + 0.5f, y + 0.5f,
x + w - 0.5f, y + 0.5f,
x + w - 0.5f, y + h - 0.5f,
x + 0.5f, y + h - 0.5f,
};
sDrawPolygon(verts, 4, fth, col);
}
/*
static void drawEllipse(float x, float y, float w, float h, float fth, unsigned int col)
{
float verts[CIRCLE_VERTS*2];
const float* cverts = g_circleVerts;
float* v = verts;
for (int i = 0; i < CIRCLE_VERTS; ++i)
{
*v++ = x + cverts[i*2]*w;
*v++ = y + cverts[i*2+1]*h;
}
drawPolygon(verts, CIRCLE_VERTS, fth, col);
}
*/
static void sDrawRoundedRect(float x, float y, float w, float h, float r, float fth, unsigned int col)
{
const unsigned n = CIRCLE_VERTS / 4;
float verts[(n + 1) * 4 * 2];
const float* cverts = g_circleVerts;
float* v = verts;
for (unsigned i = 0; i <= n; ++i)
{
*v++ = x + w - r + cverts[i * 2] * r;
*v++ = y + h - r + cverts[i * 2 + 1] * r;
}
for (unsigned i = n; i <= n * 2; ++i)
{
*v++ = x + r + cverts[i * 2] * r;
*v++ = y + h - r + cverts[i * 2 + 1] * r;
}
for (unsigned i = n * 2; i <= n * 3; ++i)
{
*v++ = x + r + cverts[i * 2] * r;
*v++ = y + r + cverts[i * 2 + 1] * r;
}
for (unsigned i = n * 3; i < n * 4; ++i)
{
*v++ = x + w - r + cverts[i * 2] * r;
*v++ = y + r + cverts[i * 2 + 1] * r;
}
*v++ = x + w - r + cverts[0] * r;
*v++ = y + r + cverts[1] * r;
sDrawPolygon(verts, (n + 1) * 4, fth, col);
}
//
void sRenderLine(float x0, float y0, float x1, float y1, float r, float fth, unsigned int col)
{
float dx = x1 - x0;
float dy = y1 - y0;
float d = sqrtf(dx*dx + dy*dy);
if (d > 0.0001f)
{
d = 1.0f / d;
dx *= d;
dy *= d;
}
float nx = dy;
float ny = -dx;
float verts[4 * 2];
r -= fth;
r *= 0.5f;
if (r < 0.01f) r = 0.01f;
dx *= r;
dy *= r;
nx *= r;
ny *= r;
verts[0] = x0 - dx - nx;
verts[1] = y0 - dy - ny;
verts[2] = x0 - dx + nx;
verts[3] = y0 - dy + ny;
verts[4] = x1 + dx + nx;
verts[5] = y1 + dy + ny;
verts[6] = x1 + dx - nx;
verts[7] = y1 + dy - ny;
sDrawPolygon(verts, 4, fth, col);
}
//
bool RenderGLInit(const char* fontpath)
{
for (int i = 0; i < CIRCLE_VERTS; ++i)
{
float a = (float)i / (float)CIRCLE_VERTS * PI * 2;
g_circleVerts[i * 2 + 0] = cosf(a);
g_circleVerts[i * 2 + 1] = sinf(a);
}
// Load font.
FILE* fp = fopen(fontpath, "rb");
if (!fp) return false;
fseek(fp, 0, SEEK_END);
int size = (int)ftell(fp);
fseek(fp, 0, SEEK_SET);
unsigned char* ttfBuffer = (unsigned char*)malloc(size);
if (!ttfBuffer)
{
fclose(fp);
return false;
}
fread(ttfBuffer, 1, size, fp);
fclose(fp);
fp = 0;
unsigned char* bmap = (unsigned char*)malloc(512 * 512);
if (!bmap)
{
free(ttfBuffer);
return false;
}
stbtt_BakeFontBitmap(ttfBuffer, 0, 15.0f, bmap, 512, 512, 32, 96, g_cdata);
// can free ttf_buffer at this point
glGenTextures(1, &g_ftex);
glBindTexture(GL_TEXTURE_2D, g_ftex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 512, 512, 0, GL_RED, GL_UNSIGNED_BYTE, bmap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// can free ttf_buffer at this point
unsigned char white_alpha = 255;
glGenTextures(1, &g_whitetex);
glBindTexture(GL_TEXTURE_2D, g_whitetex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &white_alpha);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenVertexArrays(1, &g_vao);
glGenBuffers(3, g_vbos);
glBindVertexArray(g_vao);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT)* 2, (void*)0);
glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT)* 2, (void*)0);
glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT)* 4, (void*)0);
glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STATIC_DRAW);
g_program = glCreateProgram();
const char * vs =
"#version 150\n"
"uniform vec2 Viewport;\n"
"in vec2 VertexPosition;\n"
"in vec2 VertexTexCoord;\n"
"in vec4 VertexColor;\n"
"out vec2 texCoord;\n"
"out vec4 vertexColor;\n"
"void main(void)\n"
"{\n"
" vertexColor = VertexColor;\n"
" texCoord = VertexTexCoord;\n"
" gl_Position = vec4(VertexPosition * 2.0 / Viewport - 1.0, 0.0, 1.0);\n"
"}\n";
GLuint vso = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vso, 1, (const char **)&vs, NULL);
glCompileShader(vso);
glAttachShader(g_program, vso);
const char * fs =
"#version 150\n"
"in vec2 texCoord;\n"
"in vec4 vertexColor;\n"
"uniform sampler2D Texture;\n"
"out vec4 Color;\n"
"void main(void)\n"
"{\n"
" float alpha = texture(Texture, texCoord).r;\n"
" Color = vec4(vertexColor.rgb, vertexColor.a * alpha);\n"
"}\n";
GLuint fso = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fso, 1, (const char **)&fs, NULL);
glCompileShader(fso);
glAttachShader(g_program, fso);
glBindAttribLocation(g_program, 0, "VertexPosition");
glBindAttribLocation(g_program, 1, "VertexTexCoord");
glBindAttribLocation(g_program, 2, "VertexColor");
glBindFragDataLocation(g_program, 0, "Color");
glLinkProgram(g_program);
glDeleteShader(vso);
glDeleteShader(fso);
glUseProgram(g_program);
g_programViewportLocation = glGetUniformLocation(g_program, "Viewport");
g_programTextureLocation = glGetUniformLocation(g_program, "Texture");
glUseProgram(0);
free(ttfBuffer);
free(bmap);
return true;
}
//
void RenderGLDestroy()
{
if (g_ftex)
{
glDeleteTextures(1, &g_ftex);
g_ftex = 0;
}
if (g_vao)
{
glDeleteVertexArrays(1, &g_vao);
glDeleteBuffers(3, g_vbos);
g_vao = 0;
}
if (g_program)
{
glDeleteProgram(g_program);
g_program = 0;
}
}
static void sGetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index,
float *xpos, float *ypos, stbtt_aligned_quad *q)
{
stbtt_bakedchar *b = chardata + char_index;
int round_x = STBTT_ifloor(*xpos + b->xoff);
int round_y = STBTT_ifloor(*ypos - b->yoff);
q->x0 = (float)round_x;
q->y0 = (float)round_y;
q->x1 = (float)round_x + b->x1 - b->x0;
q->y1 = (float)round_y - b->y1 + b->y0;
q->s0 = b->x0 / (float)pw;
q->t0 = b->y0 / (float)pw;
q->s1 = b->x1 / (float)ph;
q->t1 = b->y1 / (float)ph;
*xpos += b->xadvance;
}
static const float g_tabStops[4] = { 150, 210, 270, 330 };
static float sGetTextLength(stbtt_bakedchar *chardata, const char* text)
{
float xpos = 0;
float len = 0;
while (*text)
{
int c = (unsigned char)*text;
if (c == '\t')
{
for (int i = 0; i < 4; ++i)
{
if (xpos < g_tabStops[i])
{
xpos = g_tabStops[i];
break;
}
}
}
else if (c >= 32 && c < 128)
{
stbtt_bakedchar *b = chardata + c - 32;
int round_x = STBTT_ifloor((xpos + b->xoff) + 0.5);
len = round_x + b->x1 - b->x0 + 0.5f;
xpos += b->xadvance;
}
++text;
}
return len;
}
//
void sRenderString(float x, float y, const char *text, TextAlign align, unsigned int col)
{
if (!g_ftex) return;
if (!text) return;
if (align == TEXT_ALIGN_CENTER)
x -= sGetTextLength(g_cdata, text) / 2;
else if (align == TEXT_ALIGN_RIGHT)
x -= sGetTextLength(g_cdata, text);
float r = (float)(col & 0xff) / 255.f;
float g = (float)((col >> 8) & 0xff) / 255.f;
float b = (float)((col >> 16) & 0xff) / 255.f;
float a = (float)((col >> 24) & 0xff) / 255.f;
// assume orthographic projection with units = screen pixels, origin at top left
glBindTexture(GL_TEXTURE_2D, g_ftex);
const float ox = x;
while (*text)
{
int c = (unsigned char)*text;
if (c == '\t')
{
for (int i = 0; i < 4; ++i)
{
if (x < g_tabStops[i] + ox)
{
x = g_tabStops[i] + ox;
break;
}
}
}
else if (c >= 32 && c < 128)
{
stbtt_aligned_quad q;
sGetBakedQuad(g_cdata, 512, 512, c - 32, &x, &y, &q);
float v[12] = {
q.x0, q.y0,
q.x1, q.y1,
q.x1, q.y0,
q.x0, q.y0,
q.x0, q.y1,
q.x1, q.y1,
};
float uv[12] = {
q.s0, q.t0,
q.s1, q.t1,
q.s1, q.t0,
q.s0, q.t0,
q.s0, q.t1,
q.s1, q.t1,
};
float color[24] = {
r, g, b, a,
r, g, b, a,
r, g, b, a,
r, g, b, a,
r, g, b, a,
r, g, b, a,
};
glBindVertexArray(g_vao);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), v, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), uv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]);
glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(float), color, GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
++text;
}
}
//
void RenderGLFlush(int width, int height)
{
const GfxCmd* q = g_gfxCmdQueue;
int nq = g_gfxCmdQueueSize;
const float s = 1.0f / 8.0f;
glViewport(0, 0, width, height);
glUseProgram(g_program);
glUniform2f(g_programViewportLocation, (float)width, (float)height);
glUniform1i(g_programTextureLocation, 0);
glDisable(GL_SCISSOR_TEST);
for (int i = 0; i < nq; ++i)
{
const GfxCmd& cmd = q[i];
if (cmd.type == GFXCMD_RECT)
{
if (cmd.rect.r == 0)
{
sDrawRect((float)cmd.rect.x*s + 0.5f, (float)cmd.rect.y*s + 0.5f,
(float)cmd.rect.w*s - 1, (float)cmd.rect.h*s - 1,
1.0f, cmd.col);
}
else
{
sDrawRoundedRect((float)cmd.rect.x*s + 0.5f, (float)cmd.rect.y*s + 0.5f,
(float)cmd.rect.w*s - 1, (float)cmd.rect.h*s - 1,
(float)cmd.rect.r*s, 1.0f, cmd.col);
}
}
else if (cmd.type == GFXCMD_LINE)
{
sRenderLine(cmd.line.x0*s, cmd.line.y0*s, cmd.line.x1*s, cmd.line.y1*s, cmd.line.r*s, 1.0f, cmd.col);
}
else if (cmd.type == GFXCMD_TRIANGLE)
{
if (cmd.flags == 1)
{
const float verts[3 * 2] =
{
(float)cmd.rect.x*s + 0.5f, (float)cmd.rect.y*s + 0.5f,
(float)cmd.rect.x*s + 0.5f + (float)cmd.rect.w*s - 1, (float)cmd.rect.y*s + 0.5f + (float)cmd.rect.h*s / 2 - 0.5f,
(float)cmd.rect.x*s + 0.5f, (float)cmd.rect.y*s + 0.5f + (float)cmd.rect.h*s - 1,
};
sDrawPolygon(verts, 3, 1.0f, cmd.col);
}
if (cmd.flags == 2)
{
const float verts[3 * 2] =
{
(float)cmd.rect.x*s + 0.5f, (float)cmd.rect.y*s + 0.5f + (float)cmd.rect.h*s - 1,
(float)cmd.rect.x*s + 0.5f + (float)cmd.rect.w*s / 2 - 0.5f, (float)cmd.rect.y*s + 0.5f,
(float)cmd.rect.x*s + 0.5f + (float)cmd.rect.w*s - 1, (float)cmd.rect.y*s + 0.5f + (float)cmd.rect.h*s - 1,
};
sDrawPolygon(verts, 3, 1.0f, cmd.col);
}
}
else if (cmd.type == GFXCMD_TEXT)
{
sRenderString(cmd.text.x, cmd.text.y, cmd.text.text, cmd.text.align, cmd.col);
}
else if (cmd.type == GFXCMD_SCISSOR)
{
if (cmd.flags)
{
glEnable(GL_SCISSOR_TEST);
glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h);
}
else
{
glDisable(GL_SCISSOR_TEST);
}
}
}
glDisable(GL_SCISSOR_TEST);
glUseProgram(0);
ResetGfxCmdQueue();
}

View File

@@ -0,0 +1,53 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// Source altered and distributed from https://github.com/AdrienHerubel/imgui
#pragma once
#include <Box2D/Common/b2Math.h>
#define SILVER (220 | (220 << 8) | (220 << 16) | (255 << 24))
#define WHITE (255 | (255 << 8) | (255 << 16) | (255 << 24))
#define RED (255 | (0 << 8) | (0 << 16) | (255 << 24))
#define GREEN (0 | (255 << 8) | (0 << 16) | (255 << 24))
#define BLUE (0 | (0 << 8) | (255 << 16) | (255 << 24))
enum TextAlign
{
TEXT_ALIGN_LEFT,
TEXT_ALIGN_CENTER,
TEXT_ALIGN_RIGHT,
};
inline unsigned int SetRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
return (r) | (g << 8) | (b << 16) | (a << 24);
}
bool RenderGLInit(const char* fontpath);
void RenderGLDestroy();
void RenderGLFlush(int width, int height);
void AddGfxCmdScissor(int x, int y, int w, int h);
void AddGfxCmdRect(float x, float y, float w, float h, unsigned int color);
void AddGfxCmdRoundedRect(float x, float y, float w, float h, float r, unsigned int color);
void AddGfxCmdLine(float x0, float y0, float x1, float y1, float r, unsigned int color);
void AddGfxCmdTriangle(int x, int y, int w, int h, int flags, unsigned int color);
void AddGfxCmdText(float x, float y, TextAlign align, const char* text, unsigned int color);
void AddGfxCmdText(int x, int y, TextAlign align, const char* text, unsigned int color);

View File

@@ -0,0 +1,456 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "Test.h"
#include <stdio.h>
void DestructionListener::SayGoodbye(b2Joint* joint)
{
if (test->m_mouseJoint == joint)
{
test->m_mouseJoint = NULL;
}
else
{
test->JointDestroyed(joint);
}
}
Test::Test()
{
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
m_world = new b2World(gravity);
m_bomb = NULL;
m_textLine = 30;
m_mouseJoint = NULL;
m_pointCount = 0;
m_destructionListener.test = this;
m_world->SetDestructionListener(&m_destructionListener);
m_world->SetContactListener(this);
m_world->SetDebugDraw(&g_debugDraw);
m_bombSpawning = false;
m_stepCount = 0;
b2BodyDef bodyDef;
m_groundBody = m_world->CreateBody(&bodyDef);
memset(&m_maxProfile, 0, sizeof(b2Profile));
memset(&m_totalProfile, 0, sizeof(b2Profile));
}
Test::~Test()
{
// By deleting the world, we delete the bomb, mouse joint, etc.
delete m_world;
m_world = NULL;
}
void Test::PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
const b2Manifold* manifold = contact->GetManifold();
if (manifold->pointCount == 0)
{
return;
}
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
b2PointState state1[b2_maxManifoldPoints], state2[b2_maxManifoldPoints];
b2GetPointStates(state1, state2, oldManifold, manifold);
b2WorldManifold worldManifold;
contact->GetWorldManifold(&worldManifold);
for (int32 i = 0; i < manifold->pointCount && m_pointCount < k_maxContactPoints; ++i)
{
ContactPoint* cp = m_points + m_pointCount;
cp->fixtureA = fixtureA;
cp->fixtureB = fixtureB;
cp->position = worldManifold.points[i];
cp->normal = worldManifold.normal;
cp->state = state2[i];
cp->normalImpulse = manifold->points[i].normalImpulse;
cp->tangentImpulse = manifold->points[i].tangentImpulse;
cp->separation = worldManifold.separations[i];
++m_pointCount;
}
}
void Test::DrawTitle(const char *string)
{
g_debugDraw.DrawString(5, DRAW_STRING_NEW_LINE, string);
m_textLine = 3 * DRAW_STRING_NEW_LINE;
}
class QueryCallback : public b2QueryCallback
{
public:
QueryCallback(const b2Vec2& point)
{
m_point = point;
m_fixture = NULL;
}
bool ReportFixture(b2Fixture* fixture)
{
b2Body* body = fixture->GetBody();
if (body->GetType() == b2_dynamicBody)
{
bool inside = fixture->TestPoint(m_point);
if (inside)
{
m_fixture = fixture;
// We are done, terminate the query.
return false;
}
}
// Continue the query.
return true;
}
b2Vec2 m_point;
b2Fixture* m_fixture;
};
void Test::MouseDown(const b2Vec2& p)
{
m_mouseWorld = p;
if (m_mouseJoint != NULL)
{
return;
}
// Make a small box.
b2AABB aabb;
b2Vec2 d;
d.Set(0.001f, 0.001f);
aabb.lowerBound = p - d;
aabb.upperBound = p + d;
// Query the world for overlapping shapes.
QueryCallback callback(p);
m_world->QueryAABB(&callback, aabb);
if (callback.m_fixture)
{
b2Body* body = callback.m_fixture->GetBody();
b2MouseJointDef md;
md.bodyA = m_groundBody;
md.bodyB = body;
md.target = p;
md.maxForce = 1000.0f * body->GetMass();
m_mouseJoint = (b2MouseJoint*)m_world->CreateJoint(&md);
body->SetAwake(true);
}
}
void Test::SpawnBomb(const b2Vec2& worldPt)
{
m_bombSpawnPoint = worldPt;
m_bombSpawning = true;
}
void Test::CompleteBombSpawn(const b2Vec2& p)
{
if (m_bombSpawning == false)
{
return;
}
const float multiplier = 30.0f;
b2Vec2 vel = m_bombSpawnPoint - p;
vel *= multiplier;
LaunchBomb(m_bombSpawnPoint,vel);
m_bombSpawning = false;
}
void Test::ShiftMouseDown(const b2Vec2& p)
{
m_mouseWorld = p;
if (m_mouseJoint != NULL)
{
return;
}
SpawnBomb(p);
}
void Test::MouseUp(const b2Vec2& p)
{
if (m_mouseJoint)
{
m_world->DestroyJoint(m_mouseJoint);
m_mouseJoint = NULL;
}
if (m_bombSpawning)
{
CompleteBombSpawn(p);
}
}
void Test::MouseMove(const b2Vec2& p)
{
m_mouseWorld = p;
if (m_mouseJoint)
{
m_mouseJoint->SetTarget(p);
}
}
void Test::LaunchBomb()
{
b2Vec2 p(RandomFloat(-15.0f, 15.0f), 30.0f);
b2Vec2 v = -5.0f * p;
LaunchBomb(p, v);
}
void Test::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity)
{
if (m_bomb)
{
m_world->DestroyBody(m_bomb);
m_bomb = NULL;
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = position;
bd.bullet = true;
m_bomb = m_world->CreateBody(&bd);
m_bomb->SetLinearVelocity(velocity);
b2CircleShape circle;
circle.m_radius = 0.3f;
b2FixtureDef fd;
fd.shape = &circle;
fd.density = 20.0f;
fd.restitution = 0.0f;
b2Vec2 minV = position - b2Vec2(0.3f,0.3f);
b2Vec2 maxV = position + b2Vec2(0.3f,0.3f);
b2AABB aabb;
aabb.lowerBound = minV;
aabb.upperBound = maxV;
m_bomb->CreateFixture(&fd);
}
void Test::Step(Settings* settings)
{
float32 timeStep = settings->hz > 0.0f ? 1.0f / settings->hz : float32(0.0f);
if (settings->pause)
{
if (settings->singleStep)
{
settings->singleStep = 0;
}
else
{
timeStep = 0.0f;
}
g_debugDraw.DrawString(5, m_textLine, "****PAUSED****");
m_textLine += DRAW_STRING_NEW_LINE;
}
uint32 flags = 0;
flags += settings->drawShapes * b2Draw::e_shapeBit;
flags += settings->drawJoints * b2Draw::e_jointBit;
flags += settings->drawAABBs * b2Draw::e_aabbBit;
flags += settings->drawCOMs * b2Draw::e_centerOfMassBit;
g_debugDraw.SetFlags(flags);
m_world->SetAllowSleeping(settings->enableSleep);
m_world->SetWarmStarting(settings->enableWarmStarting);
m_world->SetContinuousPhysics(settings->enableContinuous);
m_world->SetSubStepping(settings->enableSubStepping);
m_pointCount = 0;
m_world->Step(timeStep, settings->velocityIterations, settings->positionIterations);
m_world->DrawDebugData();
g_debugDraw.Flush();
if (timeStep > 0.0f)
{
++m_stepCount;
}
if (settings->drawStats)
{
int32 bodyCount = m_world->GetBodyCount();
int32 contactCount = m_world->GetContactCount();
int32 jointCount = m_world->GetJointCount();
g_debugDraw.DrawString(5, m_textLine, "bodies/contacts/joints = %d/%d/%d", bodyCount, contactCount, jointCount);
m_textLine += DRAW_STRING_NEW_LINE;
int32 proxyCount = m_world->GetProxyCount();
int32 height = m_world->GetTreeHeight();
int32 balance = m_world->GetTreeBalance();
float32 quality = m_world->GetTreeQuality();
g_debugDraw.DrawString(5, m_textLine, "proxies/height/balance/quality = %d/%d/%d/%g", proxyCount, height, balance, quality);
m_textLine += DRAW_STRING_NEW_LINE;
}
// Track maximum profile times
{
const b2Profile& p = m_world->GetProfile();
m_maxProfile.step = b2Max(m_maxProfile.step, p.step);
m_maxProfile.collide = b2Max(m_maxProfile.collide, p.collide);
m_maxProfile.solve = b2Max(m_maxProfile.solve, p.solve);
m_maxProfile.solveInit = b2Max(m_maxProfile.solveInit, p.solveInit);
m_maxProfile.solveVelocity = b2Max(m_maxProfile.solveVelocity, p.solveVelocity);
m_maxProfile.solvePosition = b2Max(m_maxProfile.solvePosition, p.solvePosition);
m_maxProfile.solveTOI = b2Max(m_maxProfile.solveTOI, p.solveTOI);
m_maxProfile.broadphase = b2Max(m_maxProfile.broadphase, p.broadphase);
m_totalProfile.step += p.step;
m_totalProfile.collide += p.collide;
m_totalProfile.solve += p.solve;
m_totalProfile.solveInit += p.solveInit;
m_totalProfile.solveVelocity += p.solveVelocity;
m_totalProfile.solvePosition += p.solvePosition;
m_totalProfile.solveTOI += p.solveTOI;
m_totalProfile.broadphase += p.broadphase;
}
if (settings->drawProfile)
{
const b2Profile& p = m_world->GetProfile();
b2Profile aveProfile;
memset(&aveProfile, 0, sizeof(b2Profile));
if (m_stepCount > 0)
{
float32 scale = 1.0f / m_stepCount;
aveProfile.step = scale * m_totalProfile.step;
aveProfile.collide = scale * m_totalProfile.collide;
aveProfile.solve = scale * m_totalProfile.solve;
aveProfile.solveInit = scale * m_totalProfile.solveInit;
aveProfile.solveVelocity = scale * m_totalProfile.solveVelocity;
aveProfile.solvePosition = scale * m_totalProfile.solvePosition;
aveProfile.solveTOI = scale * m_totalProfile.solveTOI;
aveProfile.broadphase = scale * m_totalProfile.broadphase;
}
g_debugDraw.DrawString(5, m_textLine, "step [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.step, aveProfile.step, m_maxProfile.step);
m_textLine += DRAW_STRING_NEW_LINE;
g_debugDraw.DrawString(5, m_textLine, "collide [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.collide, aveProfile.collide, m_maxProfile.collide);
m_textLine += DRAW_STRING_NEW_LINE;
g_debugDraw.DrawString(5, m_textLine, "solve [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solve, aveProfile.solve, m_maxProfile.solve);
m_textLine += DRAW_STRING_NEW_LINE;
g_debugDraw.DrawString(5, m_textLine, "solve init [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveInit, aveProfile.solveInit, m_maxProfile.solveInit);
m_textLine += DRAW_STRING_NEW_LINE;
g_debugDraw.DrawString(5, m_textLine, "solve velocity [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveVelocity, aveProfile.solveVelocity, m_maxProfile.solveVelocity);
m_textLine += DRAW_STRING_NEW_LINE;
g_debugDraw.DrawString(5, m_textLine, "solve position [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solvePosition, aveProfile.solvePosition, m_maxProfile.solvePosition);
m_textLine += DRAW_STRING_NEW_LINE;
g_debugDraw.DrawString(5, m_textLine, "solveTOI [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveTOI, aveProfile.solveTOI, m_maxProfile.solveTOI);
m_textLine += DRAW_STRING_NEW_LINE;
g_debugDraw.DrawString(5, m_textLine, "broad-phase [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.broadphase, aveProfile.broadphase, m_maxProfile.broadphase);
m_textLine += DRAW_STRING_NEW_LINE;
}
if (m_mouseJoint)
{
b2Vec2 p1 = m_mouseJoint->GetAnchorB();
b2Vec2 p2 = m_mouseJoint->GetTarget();
b2Color c;
c.Set(0.0f, 1.0f, 0.0f);
g_debugDraw.DrawPoint(p1, 4.0f, c);
g_debugDraw.DrawPoint(p2, 4.0f, c);
c.Set(0.8f, 0.8f, 0.8f);
g_debugDraw.DrawSegment(p1, p2, c);
}
if (m_bombSpawning)
{
b2Color c;
c.Set(0.0f, 0.0f, 1.0f);
g_debugDraw.DrawPoint(m_bombSpawnPoint, 4.0f, c);
c.Set(0.8f, 0.8f, 0.8f);
g_debugDraw.DrawSegment(m_mouseWorld, m_bombSpawnPoint, c);
}
if (settings->drawContactPoints)
{
const float32 k_impulseScale = 0.1f;
const float32 k_axisScale = 0.3f;
for (int32 i = 0; i < m_pointCount; ++i)
{
ContactPoint* point = m_points + i;
if (point->state == b2_addState)
{
// Add
g_debugDraw.DrawPoint(point->position, 10.0f, b2Color(0.3f, 0.95f, 0.3f));
}
else if (point->state == b2_persistState)
{
// Persist
g_debugDraw.DrawPoint(point->position, 5.0f, b2Color(0.3f, 0.3f, 0.95f));
}
if (settings->drawContactNormals == 1)
{
b2Vec2 p1 = point->position;
b2Vec2 p2 = p1 + k_axisScale * point->normal;
g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.9f));
}
else if (settings->drawContactImpulse == 1)
{
b2Vec2 p1 = point->position;
b2Vec2 p2 = p1 + k_impulseScale * point->normalImpulse * point->normal;
g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
}
if (settings->drawFrictionImpulse == 1)
{
b2Vec2 tangent = b2Cross(point->normal, 1.0f);
b2Vec2 p1 = point->position;
b2Vec2 p2 = p1 + k_impulseScale * point->tangentImpulse * tangent;
g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f));
}
}
}
}
void Test::ShiftOrigin(const b2Vec2& newOrigin)
{
m_world->ShiftOrigin(newOrigin);
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TEST_H
#define TEST_H
#include <Box2D/Box2D.h>
#include "DebugDraw.h"
#if defined(__APPLE__)
#include <OpenGL/gl3.h>
#else
#include <glew/glew.h>
#endif
#include <glfw/glfw3.h>
#include <stdlib.h>
class Test;
struct Settings;
typedef Test* TestCreateFcn();
#define RAND_LIMIT 32767
#define DRAW_STRING_NEW_LINE 16
/// Random number in range [-1,1]
inline float32 RandomFloat()
{
float32 r = (float32)(std::rand() & (RAND_LIMIT));
r /= RAND_LIMIT;
r = 2.0f * r - 1.0f;
return r;
}
/// Random floating point number in range [lo, hi]
inline float32 RandomFloat(float32 lo, float32 hi)
{
float32 r = (float32)(std::rand() & (RAND_LIMIT));
r /= RAND_LIMIT;
r = (hi - lo) * r + lo;
return r;
}
/// Test settings. Some can be controlled in the GUI.
struct Settings
{
Settings()
{
hz = 60.0f;
velocityIterations = 8;
positionIterations = 3;
drawShapes = true;
drawJoints = true;
drawAABBs = false;
drawContactPoints = false;
drawContactNormals = false;
drawContactImpulse = false;
drawFrictionImpulse = false;
drawCOMs = false;
drawStats = false;
drawProfile = false;
enableWarmStarting = true;
enableContinuous = true;
enableSubStepping = false;
enableSleep = true;
pause = false;
singleStep = false;
}
float32 hz;
int32 velocityIterations;
int32 positionIterations;
bool drawShapes;
bool drawJoints;
bool drawAABBs;
bool drawContactPoints;
bool drawContactNormals;
bool drawContactImpulse;
bool drawFrictionImpulse;
bool drawCOMs;
bool drawStats;
bool drawProfile;
bool enableWarmStarting;
bool enableContinuous;
bool enableSubStepping;
bool enableSleep;
bool pause;
bool singleStep;
};
struct TestEntry
{
const char *name;
TestCreateFcn *createFcn;
};
extern TestEntry g_testEntries[];
// This is called when a joint in the world is implicitly destroyed
// because an attached body is destroyed. This gives us a chance to
// nullify the mouse joint.
class DestructionListener : public b2DestructionListener
{
public:
void SayGoodbye(b2Fixture* fixture) { B2_NOT_USED(fixture); }
void SayGoodbye(b2Joint* joint);
Test* test;
};
const int32 k_maxContactPoints = 2048;
struct ContactPoint
{
b2Fixture* fixtureA;
b2Fixture* fixtureB;
b2Vec2 normal;
b2Vec2 position;
b2PointState state;
float32 normalImpulse;
float32 tangentImpulse;
float32 separation;
};
class Test : public b2ContactListener
{
public:
Test();
virtual ~Test();
void DrawTitle(const char *string);
virtual void Step(Settings* settings);
virtual void Keyboard(int key) { B2_NOT_USED(key); }
virtual void KeyboardUp(int key) { B2_NOT_USED(key); }
void ShiftMouseDown(const b2Vec2& p);
virtual void MouseDown(const b2Vec2& p);
virtual void MouseUp(const b2Vec2& p);
void MouseMove(const b2Vec2& p);
void LaunchBomb();
void LaunchBomb(const b2Vec2& position, const b2Vec2& velocity);
void SpawnBomb(const b2Vec2& worldPt);
void CompleteBombSpawn(const b2Vec2& p);
// Let derived tests know that a joint was destroyed.
virtual void JointDestroyed(b2Joint* joint) { B2_NOT_USED(joint); }
// Callbacks for derived classes.
virtual void BeginContact(b2Contact* contact) { B2_NOT_USED(contact); }
virtual void EndContact(b2Contact* contact) { B2_NOT_USED(contact); }
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{
B2_NOT_USED(contact);
B2_NOT_USED(impulse);
}
void ShiftOrigin(const b2Vec2& newOrigin);
protected:
friend class DestructionListener;
friend class BoundaryListener;
friend class ContactListener;
b2Body* m_groundBody;
b2AABB m_worldAABB;
ContactPoint m_points[k_maxContactPoints];
int32 m_pointCount;
DestructionListener m_destructionListener;
int32 m_textLine;
b2World* m_world;
b2Body* m_bomb;
b2MouseJoint* m_mouseJoint;
b2Vec2 m_bombSpawnPoint;
bool m_bombSpawning;
b2Vec2 m_mouseWorld;
int32 m_stepCount;
b2Profile m_maxProfile;
b2Profile m_totalProfile;
};
#endif

View File

@@ -0,0 +1,616 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// Source altered and distributed from https://github.com/AdrienHerubel/imgui
#include <stdio.h>
#include <string.h>
//#define _USE_MATH_DEFINES
#include <math.h>
#include "imgui.h"
#include "RenderGL3.h"
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct GuiState
{
GuiState()
{
left = false;
leftPressed = false;
leftReleased = false;
mx = -1;
my = -1;
scroll = 0;
active = 0;
hot = 0;
hotToBe = 0;
isHot = false;
isActive = false;
wentActive = false;
dragX = 0;
dragY = 0;
dragOrig = 0;
widgetX = 0;
widgetY = 0;
widgetW = 100;
insideCurrentScroll = false;
areaId = 0;
widgetId = 0;
}
bool left;
bool leftPressed, leftReleased;
int mx, my;
int scroll;
unsigned int active;
unsigned int hot;
unsigned int hotToBe;
bool isHot;
bool isActive;
bool wentActive;
int dragX, dragY;
float dragOrig;
int widgetX, widgetY, widgetW;
bool insideCurrentScroll;
unsigned int areaId;
unsigned int widgetId;
};
static GuiState s_state;
inline bool anyActive()
{
return s_state.active != 0;
}
inline bool isActive(unsigned int id)
{
return s_state.active == id;
}
inline bool isHot(unsigned int id)
{
return s_state.hot == id;
}
inline bool inRect(int x, int y, int w, int h, bool checkScroll = true)
{
return (!checkScroll || s_state.insideCurrentScroll) && s_state.mx >= x && s_state.mx <= x + w && s_state.my >= y && s_state.my <= y + h;
}
inline void clearInput()
{
s_state.leftPressed = false;
s_state.leftReleased = false;
s_state.scroll = 0;
}
inline void clearActive()
{
s_state.active = 0;
// mark all UI for this frame as processed
clearInput();
}
inline void setActive(unsigned int id)
{
s_state.active = id;
s_state.wentActive = true;
}
inline void setHot(unsigned int id)
{
s_state.hotToBe = id;
}
static bool buttonLogic(unsigned int id, bool over)
{
bool res = false;
// process down
if (!anyActive())
{
if (over)
setHot(id);
if (isHot(id) && s_state.leftPressed)
setActive(id);
}
// if button is active, then react on left up
if (isActive(id))
{
s_state.isActive = true;
if (over)
setHot(id);
if (s_state.leftReleased)
{
if (isHot(id))
res = true;
clearActive();
}
}
if (isHot(id))
s_state.isHot = true;
return res;
}
static void updateInput(int mx, int my, unsigned char mbut, int scroll)
{
bool left = (mbut & IMGUI_MBUT_LEFT) != 0;
s_state.mx = mx;
s_state.my = my;
s_state.leftPressed = !s_state.left && left;
s_state.leftReleased = s_state.left && !left;
s_state.left = left;
s_state.scroll = scroll;
}
void imguiBeginFrame(int mx, int my, unsigned char mbut, int scroll)
{
updateInput(mx, my, mbut, scroll);
s_state.hot = s_state.hotToBe;
s_state.hotToBe = 0;
s_state.wentActive = false;
s_state.isActive = false;
s_state.isHot = false;
s_state.widgetX = 0;
s_state.widgetY = 0;
s_state.widgetW = 0;
s_state.areaId = 1;
s_state.widgetId = 1;
}
void imguiEndFrame()
{
clearInput();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const int BUTTON_HEIGHT = 20;
static const int SLIDER_HEIGHT = 20;
static const int SLIDER_MARKER_WIDTH = 10;
static const int CHECK_SIZE = 8;
static const int DEFAULT_SPACING = 4;
static const int TEXT_HEIGHT = 8;
static const int SCROLL_AREA_PADDING = 6;
static const int INDENT_SIZE = 16;
static const int AREA_HEADER = 28;
static int g_scrollTop = 0;
static int g_scrollBottom = 0;
static int g_scrollRight = 0;
static int g_scrollAreaTop = 0;
static int* g_scrollVal = 0;
static int g_focusTop = 0;
static int g_focusBottom = 0;
static unsigned int g_scrollId = 0;
static bool g_insideScrollArea = false;
bool imguiBeginScrollArea(const char* name, int x, int y, int w, int h, int* scroll)
{
s_state.areaId++;
s_state.widgetId = 0;
g_scrollId = (s_state.areaId << 16) | s_state.widgetId;
s_state.widgetX = x + SCROLL_AREA_PADDING;
s_state.widgetY = y + h - AREA_HEADER + (*scroll);
s_state.widgetW = w - SCROLL_AREA_PADDING * 4;
g_scrollTop = y - AREA_HEADER + h;
g_scrollBottom = y + SCROLL_AREA_PADDING;
g_scrollRight = x + w - SCROLL_AREA_PADDING * 3;
g_scrollVal = scroll;
g_scrollAreaTop = s_state.widgetY;
g_focusTop = y - AREA_HEADER;
g_focusBottom = y - AREA_HEADER + h;
g_insideScrollArea = inRect(x, y, w, h, false);
s_state.insideCurrentScroll = g_insideScrollArea;
AddGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 6, SetRGBA(0, 0, 0, 192));
AddGfxCmdText(x + AREA_HEADER / 2, y + h - AREA_HEADER / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, name, SetRGBA(255, 255, 255, 128));
AddGfxCmdScissor(x + SCROLL_AREA_PADDING, y + SCROLL_AREA_PADDING, w - SCROLL_AREA_PADDING * 4, h - AREA_HEADER - SCROLL_AREA_PADDING);
return g_insideScrollArea;
}
void imguiEndScrollArea()
{
// Disable scissoring.
AddGfxCmdScissor(-1, -1, -1, -1);
// Draw scroll bar
int x = g_scrollRight + SCROLL_AREA_PADDING / 2;
int y = g_scrollBottom;
int w = SCROLL_AREA_PADDING * 2;
int h = g_scrollTop - g_scrollBottom;
int stop = g_scrollAreaTop;
int sbot = s_state.widgetY;
int sh = stop - sbot; // The scrollable area height.
float barHeight = (float)h / (float)sh;
if (barHeight < 1)
{
float barY = (float)(y - sbot) / (float)sh;
if (barY < 0) barY = 0;
if (barY > 1) barY = 1;
// Handle scroll bar logic.
unsigned int hid = g_scrollId;
int hx = x;
int hy = y + (int)(barY*h);
int hw = w;
int hh = (int)(barHeight*h);
const int range = h - (hh - 1);
bool over = inRect(hx, hy, hw, hh);
buttonLogic(hid, over);
if (isActive(hid))
{
float u = (float)(hy - y) / (float)range;
if (s_state.wentActive)
{
s_state.dragY = s_state.my;
s_state.dragOrig = u;
}
if (s_state.dragY != s_state.my)
{
u = s_state.dragOrig + (s_state.my - s_state.dragY) / (float)range;
if (u < 0) u = 0;
if (u > 1) u = 1;
*g_scrollVal = (int)((1 - u) * (sh - h));
}
}
// BG
AddGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, (float)w / 2 - 1, SetRGBA(0, 0, 0, 196));
// Bar
if (isActive(hid))
AddGfxCmdRoundedRect((float)hx, (float)hy, (float)hw, (float)hh, (float)w / 2 - 1, SetRGBA(255, 196, 0, 196));
else
AddGfxCmdRoundedRect((float)hx, (float)hy, (float)hw, (float)hh, (float)w / 2 - 1, isHot(hid) ? SetRGBA(255, 196, 0, 96) : SetRGBA(255, 255, 255, 64));
// Handle mouse scrolling.
if (g_insideScrollArea) // && !anyActive())
{
if (s_state.scroll)
{
*g_scrollVal += 20 * s_state.scroll;
if (*g_scrollVal < 0) *g_scrollVal = 0;
if (*g_scrollVal >(sh - h)) *g_scrollVal = (sh - h);
}
}
}
s_state.insideCurrentScroll = false;
}
bool imguiButton(const char* text, bool enabled)
{
s_state.widgetId++;
unsigned int id = (s_state.areaId << 16) | s_state.widgetId;
int x = s_state.widgetX;
int y = s_state.widgetY - BUTTON_HEIGHT;
int w = s_state.widgetW;
int h = BUTTON_HEIGHT;
s_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
bool over = enabled && inRect(x, y, w, h);
bool res = buttonLogic(id, over);
AddGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, (float)BUTTON_HEIGHT / 2 - 1, SetRGBA(128, 128, 128, isActive(id) ? 196 : 96));
if (enabled)
AddGfxCmdText(x + BUTTON_HEIGHT / 2, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, isHot(id) ? SetRGBA(255, 196, 0, 255) : SetRGBA(255, 255, 255, 200));
else
AddGfxCmdText(x + BUTTON_HEIGHT / 2, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, SetRGBA(128, 128, 128, 200));
return res;
}
bool imguiItem(const char* text, bool enabled)
{
s_state.widgetId++;
unsigned int id = (s_state.areaId << 16) | s_state.widgetId;
int x = s_state.widgetX;
int y = s_state.widgetY - BUTTON_HEIGHT;
int w = s_state.widgetW;
int h = BUTTON_HEIGHT;
s_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
bool over = enabled && inRect(x, y, w, h);
bool res = buttonLogic(id, over);
if (isHot(id))
AddGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 2.0f, SetRGBA(255, 196, 0, isActive(id) ? 196 : 96));
if (enabled)
AddGfxCmdText(x + BUTTON_HEIGHT / 2, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, SetRGBA(255, 255, 255, 200));
else
AddGfxCmdText(x + BUTTON_HEIGHT / 2, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, SetRGBA(128, 128, 128, 200));
return res;
}
bool imguiCheck(const char* text, bool checked, bool enabled)
{
s_state.widgetId++;
unsigned int id = (s_state.areaId << 16) | s_state.widgetId;
int x = s_state.widgetX;
int y = s_state.widgetY - BUTTON_HEIGHT;
int w = s_state.widgetW;
int h = BUTTON_HEIGHT;
s_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
bool over = enabled && inRect(x, y, w, h);
bool res = buttonLogic(id, over);
const int cx = x + BUTTON_HEIGHT / 2 - CHECK_SIZE / 2;
const int cy = y + BUTTON_HEIGHT / 2 - CHECK_SIZE / 2;
AddGfxCmdRoundedRect((float)cx - 3, (float)cy - 3, (float)CHECK_SIZE + 6, (float)CHECK_SIZE + 6, 4, SetRGBA(128, 128, 128, isActive(id) ? 196 : 96));
if (checked)
{
if (enabled)
AddGfxCmdRoundedRect((float)cx, (float)cy, (float)CHECK_SIZE, (float)CHECK_SIZE, (float)CHECK_SIZE / 2 - 1, SetRGBA(255, 255, 255, isActive(id) ? 255 : 200));
else
AddGfxCmdRoundedRect((float)cx, (float)cy, (float)CHECK_SIZE, (float)CHECK_SIZE, (float)CHECK_SIZE / 2 - 1, SetRGBA(128, 128, 128, 200));
}
if (enabled)
AddGfxCmdText(x + BUTTON_HEIGHT, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, isHot(id) ? SetRGBA(255, 196, 0, 255) : SetRGBA(255, 255, 255, 200));
else
AddGfxCmdText(x + BUTTON_HEIGHT, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, SetRGBA(128, 128, 128, 200));
return res;
}
bool imguiCollapse(const char* text, const char* subtext, bool checked, bool enabled)
{
s_state.widgetId++;
unsigned int id = (s_state.areaId << 16) | s_state.widgetId;
int x = s_state.widgetX;
int y = s_state.widgetY - BUTTON_HEIGHT;
int w = s_state.widgetW;
int h = BUTTON_HEIGHT;
s_state.widgetY -= BUTTON_HEIGHT; // + DEFAULT_SPACING;
const int cx = x + BUTTON_HEIGHT / 2 - CHECK_SIZE / 2;
const int cy = y + BUTTON_HEIGHT / 2 - CHECK_SIZE / 2;
bool over = enabled && inRect(x, y, w, h);
bool res = buttonLogic(id, over);
if (checked)
AddGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 2, SetRGBA(255, 255, 255, isActive(id) ? 255 : 200));
else
AddGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 1, SetRGBA(255, 255, 255, isActive(id) ? 255 : 200));
if (enabled)
AddGfxCmdText(x + BUTTON_HEIGHT, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, isHot(id) ? SetRGBA(255, 196, 0, 255) : SetRGBA(255, 255, 255, 200));
else
AddGfxCmdText(x + BUTTON_HEIGHT, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, SetRGBA(128, 128, 128, 200));
if (subtext)
AddGfxCmdText(x + w - BUTTON_HEIGHT / 2, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_RIGHT, subtext, SetRGBA(255, 255, 255, 128));
return res;
}
void imguiLabel(const char* text)
{
int x = s_state.widgetX;
int y = s_state.widgetY - BUTTON_HEIGHT;
s_state.widgetY -= BUTTON_HEIGHT;
AddGfxCmdText(x, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, SetRGBA(255, 255, 255, 255));
}
void imguiValue(const char* text)
{
const int x = s_state.widgetX;
const int y = s_state.widgetY - BUTTON_HEIGHT;
const int w = s_state.widgetW;
s_state.widgetY -= BUTTON_HEIGHT;
AddGfxCmdText(x + w - BUTTON_HEIGHT / 2, y + BUTTON_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_RIGHT, text, SetRGBA(255, 255, 255, 200));
}
bool imguiSlider(const char* text, float* val, float vmin, float vmax, float vinc, bool enabled)
{
s_state.widgetId++;
unsigned int id = (s_state.areaId << 16) | s_state.widgetId;
int x = s_state.widgetX;
int y = s_state.widgetY - BUTTON_HEIGHT;
int w = s_state.widgetW;
int h = SLIDER_HEIGHT;
s_state.widgetY -= SLIDER_HEIGHT + DEFAULT_SPACING;
AddGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 4.0f, SetRGBA(0, 0, 0, 128));
const int range = w - SLIDER_MARKER_WIDTH;
float u = (*val - vmin) / (vmax - vmin);
if (u < 0) u = 0;
if (u > 1) u = 1;
int m = (int)(u * range);
bool over = enabled && inRect(x + m, y, SLIDER_MARKER_WIDTH, SLIDER_HEIGHT);
bool res = buttonLogic(id, over);
bool valChanged = false;
if (isActive(id))
{
if (s_state.wentActive)
{
s_state.dragX = s_state.mx;
s_state.dragOrig = u;
}
if (s_state.dragX != s_state.mx)
{
u = s_state.dragOrig + (float)(s_state.mx - s_state.dragX) / (float)range;
if (u < 0) u = 0;
if (u > 1) u = 1;
*val = vmin + u*(vmax - vmin);
*val = floorf(*val / vinc + 0.5f)*vinc; // Snap to vinc
m = (int)(u * range);
valChanged = true;
}
}
if (isActive(id))
AddGfxCmdRoundedRect((float)(x + m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, SetRGBA(255, 255, 255, 255));
else
AddGfxCmdRoundedRect((float)(x + m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, isHot(id) ? SetRGBA(255, 196, 0, 128) : SetRGBA(255, 255, 255, 64));
// TODO: fix this, take a look at 'nicenum'.
int digits = (int)(ceilf(log10f(vinc)));
char fmt[16];
snprintf(fmt, 16, "%%.%df", digits >= 0 ? 0 : -digits);
char msg[128];
snprintf(msg, 128, fmt, *val);
if (enabled)
{
AddGfxCmdText(x + SLIDER_HEIGHT / 2, y + SLIDER_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, isHot(id) ? SetRGBA(255, 196, 0, 255) : SetRGBA(255, 255, 255, 200));
AddGfxCmdText(x + w - SLIDER_HEIGHT / 2, y + SLIDER_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_RIGHT, msg, isHot(id) ? SetRGBA(255, 196, 0, 255) : SetRGBA(255, 255, 255, 200));
}
else
{
AddGfxCmdText(x + SLIDER_HEIGHT / 2, y + SLIDER_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, SetRGBA(128, 128, 128, 200));
AddGfxCmdText(x + w - SLIDER_HEIGHT / 2, y + SLIDER_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_RIGHT, msg, SetRGBA(128, 128, 128, 200));
}
return res || valChanged;
}
bool imguiSlider(const char* text, int* val, int vmin, int vmax, int vinc, bool enabled)
{
s_state.widgetId++;
unsigned int id = (s_state.areaId << 16) | s_state.widgetId;
int x = s_state.widgetX;
int y = s_state.widgetY - BUTTON_HEIGHT;
int w = s_state.widgetW;
int h = SLIDER_HEIGHT;
s_state.widgetY -= SLIDER_HEIGHT + DEFAULT_SPACING;
AddGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 4.0f, SetRGBA(0, 0, 0, 128));
const int range = w - SLIDER_MARKER_WIDTH;
float u = (*val - vmin) / float(vmax - vmin);
if (u < 0) u = 0;
if (u > 1) u = 1;
int m = (int)(u * range);
bool over = enabled && inRect(x + m, y, SLIDER_MARKER_WIDTH, SLIDER_HEIGHT);
bool res = buttonLogic(id, over);
bool valChanged = false;
if (isActive(id))
{
if (s_state.wentActive)
{
s_state.dragX = s_state.mx;
s_state.dragOrig = u;
}
if (s_state.dragX != s_state.mx)
{
u = s_state.dragOrig + (float)(s_state.mx - s_state.dragX) / (float)range;
if (u < 0) u = 0;
if (u > 1) u = 1;
*val = int(vmin + u*(vmax - vmin));
*val = int(floorf(*val / float(vinc) + 0.5f))*vinc; // Snap to vinc
m = (int)(u * range);
valChanged = true;
}
}
if (isActive(id))
AddGfxCmdRoundedRect((float)(x + m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, SetRGBA(255, 255, 255, 255));
else
AddGfxCmdRoundedRect((float)(x + m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, isHot(id) ? SetRGBA(255, 196, 0, 128) : SetRGBA(255, 255, 255, 64));
char msg[128];
snprintf(msg, 128, "%d", *val);
if (enabled)
{
AddGfxCmdText(x + SLIDER_HEIGHT / 2, y + SLIDER_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, isHot(id) ? SetRGBA(255, 196, 0, 255) : SetRGBA(255, 255, 255, 200));
AddGfxCmdText(x + w - SLIDER_HEIGHT / 2, y + SLIDER_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_RIGHT, msg, isHot(id) ? SetRGBA(255, 196, 0, 255) : SetRGBA(255, 255, 255, 200));
}
else
{
AddGfxCmdText(x + SLIDER_HEIGHT / 2, y + SLIDER_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_LEFT, text, SetRGBA(128, 128, 128, 200));
AddGfxCmdText(x + w - SLIDER_HEIGHT / 2, y + SLIDER_HEIGHT / 2 - TEXT_HEIGHT / 2, TEXT_ALIGN_RIGHT, msg, SetRGBA(128, 128, 128, 200));
}
return res || valChanged;
}
void imguiIndent()
{
s_state.widgetX += INDENT_SIZE;
s_state.widgetW -= INDENT_SIZE;
}
void imguiUnindent()
{
s_state.widgetX -= INDENT_SIZE;
s_state.widgetW += INDENT_SIZE;
}
void imguiSeparator()
{
s_state.widgetY -= DEFAULT_SPACING * 3;
}
void imguiSeparatorLine()
{
int x = s_state.widgetX;
int y = s_state.widgetY - DEFAULT_SPACING * 2;
int w = s_state.widgetW;
int h = 1;
s_state.widgetY -= DEFAULT_SPACING * 4;
AddGfxCmdRect((float)x, (float)y, (float)w, (float)h, SetRGBA(255, 255, 255, 32));
}

View File

@@ -0,0 +1,51 @@
//
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// Source altered and distributed from https://github.com/AdrienHerubel/imgui
#ifndef IMGUI_H
#define IMGUI_H
enum imguiMouseButton
{
IMGUI_MBUT_LEFT = 0x01,
IMGUI_MBUT_RIGHT = 0x02,
};
void imguiBeginFrame(int mx, int my, unsigned char mbut, int scroll);
void imguiEndFrame();
bool imguiBeginScrollArea(const char* name, int x, int y, int w, int h, int* scroll);
void imguiEndScrollArea();
void imguiIndent();
void imguiUnindent();
void imguiSeparator();
void imguiSeparatorLine();
bool imguiButton(const char* text, bool enabled);
bool imguiItem(const char* text, bool enabled);
bool imguiCheck(const char* text, bool checked, bool enabled);
bool imguiCollapse(const char* text, const char* subtext, bool checked, bool enabled);
void imguiLabel(const char* text);
void imguiValue(const char* text);
bool imguiSlider(const char* text, float* val, float vmin, float vmax, float vinc, bool enabled);
bool imguiSlider(const char* text, int* val, int vmin, int vmax, int vinc, bool enabled);
#endif // IMGUI_H

File diff suppressed because it is too large Load Diff