diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..f30379bd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 2.8) + +project(dust3d) + +file(GLOB_RECURSE sources + src/glw_osx.m + src/glw_internal.h + src/glw_style.h + src/glw.c + src/glw.h + src/dmemory.h + src/dmemory.c + src/3dstruct.h + src/array.h + src/array.c + src/dict.h + src/dict.c + src/bmesh.h + src/bmesh.c + src/matrix.h + src/matrix.c + src/subdivide.h + src/subdivide.c + src/vector3d.h + src/vector3d.c + src/convexhull.h + src/convexhull.c + src/draw.h + src/draw.c + src/editor.c +) + +IF(APPLE) + add_executable(dust3d MACOSX_BUNDLE ${sources}) + find_package(OpenGL REQUIRED) + include_directories(${OPENGL_INCLUDE_DIRS}) + target_link_libraries(dust3d ${OPENGL_LIBRARIES}) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AppKit -framework Quartz") + set_target_properties(dust3d PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/src/Info.plist) + install(TARGETS dust3d + DESTINATION ".") +ENDIF (APPLE) diff --git a/README.md b/README.md index 21180566..1820b190 100644 --- a/README.md +++ b/README.md @@ -10,25 +10,6 @@ I was inspired by Jimmy Gunawan's blogs of monster generation, here is the first of 3D Articulated Shapes](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.357.7134&rep=rep1&type=pdf)(Authors: Zhongping Ji, Ligang Liu, Yigang Wang). I started to think of monster model generation for game development from years ago, thanks for this paper, Dust3D is achievable now. From my initial thought, Dust3D should be a tool like [Makehuman](http://www.makehuman.org), with more versatile features, not only can make human, but also be able to **generate monsters automatically**. -Build -============ -*Generate Xcode Project* -``` -$ cd build -$ qmake -spec macx-xcode -``` -*Generate VC Project* -``` -$ cd build -$ qmake -tp vc -``` -*Generate Makefile* -``` -$ cd build -$ qmake -$ make -``` - TODO & Progress ============== - [x] Drawing Primitives (Dec 15, 2016 ~ Dec 17, 2016) @@ -83,7 +64,10 @@ This todo already done in the B-Mesh algorithm implementation. **Feb 8, 2017:** There are lots of changes to the UI part of Dust3D, though not commit yet because of many things have not been finalize. I removed the Qt, the counterpart of the build system is CMake, and instead of using Qt based OpenGL windows, I am using my own implemented OpenGL support library. I considered using the most popular [Dear ImGui](https://github.com/ocornut/imgui) library, but I really want remove the C++ from the codebase. It's a good start of the skeleton editor UI! - + + **Feb 11, 2017:** + Qt removed, so C++ removed, but Objective-C imported for just support the OpenGL environment on OSX. + - [ ] Render rigid animation - [ ] png exporter for isometric 2.5D game diff --git a/build/dust3d.pro b/build/dust3d.pro deleted file mode 100644 index c3509d64..00000000 --- a/build/dust3d.pro +++ /dev/null @@ -1,38 +0,0 @@ -TARGET = dust3d -TEMPLATE = app - -QT += widgets opengl -CONFIG += debug - -VPATH += ../src -INCLUDEPATH += ../src - -SOURCES += main.cpp \ - mainwindow.cpp \ - render.cpp \ - vector3d.c \ - draw.cpp \ - array.c \ - bmesh.c \ - matrix.c \ - convexhull.c \ - dict.c \ - osutil.cpp \ - subdivide.c \ - skinnedmesh.c \ - dmemory.c - -HEADERS += mainwindow.h \ - render.h \ - vector3d.h \ - draw.h \ - array.h \ - bmesh.h \ - matrix.h \ - convexhull.h \ - dict.h \ - 3dstruct.h \ - osutil.h \ - subdivide.h \ - skinnedmesh.h \ - dmemory.h \ No newline at end of file diff --git a/src/Info.plist b/src/Info.plist new file mode 100644 index 00000000..4e619f32 --- /dev/null +++ b/src/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2017 dust3d. All rights reserved. + NSPrincipalClass + NSApplication + + diff --git a/src/bmesh.c b/src/bmesh.c index f40cacf3..f055104b 100644 --- a/src/bmesh.c +++ b/src/bmesh.c @@ -11,7 +11,6 @@ #include "matrix.h" #include "convexhull.h" #include "subdivide.h" -#include "draw.h" #define BMESH_MAX_PARENT_BALL_DEPTH 1000 #define BMESH_INTVAL_DIST_DIV 10 @@ -547,10 +546,6 @@ static convexHull *createConvexHullForBall(bmesh *bm, int depth, for (i = 0; i < arrayGetLength(ballPtrArray); ++i) { bmeshBall *ballItem = *((bmeshBall **)arrayGetItem(ballPtrArray, i)); ballItem->convexHullCount++; - drawDebugPrintf("convexHullCount:%d", ballItem->convexHullCount); - if (ballItem->convexHullCount == 1) { - drawDebugPoint(&ballItem->position, ballItem->index); - } } arrayDestroy(ballPtrArray); @@ -727,7 +722,7 @@ static void bmeshGenerateInbetweenMeshFrom(bmesh *bm, int depth, bmeshAddWallsBetweenQuadsToModel(bm, &ball->position, ¤tFace, &childFace); bmeshAddQuadToModel(bm, &childFace); - drawQuad(&childFace); + //drawQuad(&childFace); } } else if (1 == ball->convexHullCount && !ball->meshGenerated) { diff --git a/src/convexhull.c b/src/convexhull.c index 7b776a82..5466b103 100644 --- a/src/convexhull.c +++ b/src/convexhull.c @@ -6,7 +6,6 @@ #include "convexhull.h" #include "array.h" #include "dict.h" -#include "draw.h" // // Implement Gift wrapping method which describled in http://dccg.upc.edu/people/vera/wp-content/uploads/2014/11/GA2014-ConvexHulls3D-Roger-Hernando.pdf diff --git a/src/draw.cpp b/src/draw.c similarity index 96% rename from src/draw.cpp rename to src/draw.c index 8ceedfee..5a3932a9 100644 --- a/src/draw.cpp +++ b/src/draw.c @@ -1,9 +1,8 @@ -#include -#include #include #include #include #include +#include #include "draw.h" static GLUquadricObj *quadricId = 0; @@ -88,8 +87,6 @@ int drawCylinder(vec3 *topOrigin, vec3 *bottomOrigin, float radius, int slices, } int drawGrid(float size, float step) { - glDisable(GL_LIGHTING); - // x z plane glBegin(GL_LINES); for (GLfloat i = -size; i <= size; i += step) { @@ -105,8 +102,6 @@ int drawGrid(float size, float step) { } } glEnd(); - - glEnable(GL_LIGHTING); return 0; } @@ -115,7 +110,8 @@ int drawPrintf(int x, int y, const char *fmt, ...) { char text[1024]; va_start(args, fmt); vsnprintf(text, sizeof(text), fmt, args); - return drawText(x, y, text); + //return drawText(x, y, text); + return 0; } static const float drawDebugColors[][4] = { diff --git a/src/draw.h b/src/draw.h index 92fe6e72..9b842091 100644 --- a/src/draw.h +++ b/src/draw.h @@ -12,6 +12,10 @@ extern "C" { #endif +#ifndef M_PI +#define M_PI 3.14159265 +#endif + int drawInit(void); int drawSphere(vec3 *origin, float radius, int slices, int stacks); void drawTriangle(triangle *poly); diff --git a/src/editor.c b/src/editor.c new file mode 100644 index 00000000..3becdc32 --- /dev/null +++ b/src/editor.c @@ -0,0 +1,229 @@ +#ifdef __APPLE__ +#include +#elif defined(_WIN32) +#include +#include +#endif +#include +#include +#include +#include "glw.h" +#include "glw_style.h" +#include "draw.h" +#include "bmesh.h" + +#define GEN_ID __LINE__ + +#define ED_MODE_EDIT 0 +#define ED_MODE_ANIMATION 1 + +#define ED_BACKGROUND_COLOR 0xe4e2e4 + +#define ED_SIDEBAR_WIDTH 400 +#define ED_SPACING 10 + +#define ED_TOPBAR_HEIGHT 100 +#define ED_TOOLBAR_HEIGHT 50 + +#define ED_GL_BACKGROUND_COLOR GLW_BACKGROUND_COLOR + +typedef struct editor { + glwWin *win; + float cameraAngleX; + float cameraAngleY; + float cameraDistance; + int newImId; + int showBoneChecked; + int width; + int height; + int mode; + int glLeft; + int glTop; + int glWidth; + int glHeight; + int mouseX; + int mouseY; + bmesh *bm; +} editor; + +#include "../data/bmesh_test_2.h" + +static void display(glwWin *win) { + editor *ed = glwGetUserData(win); + + glClear(GL_COLOR_BUFFER_BIT); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, ed->width, ed->height, 0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + { + char *list[] = {"Open..", "New..", "Save As..", 0}; + glwImButtonGroup(win, GEN_ID, 100, 30, 300, list, -1); + } + + { + char *titles[] = {"Library", "Property", 0}; + glwImTabBox(win, GEN_ID, ED_SPACING / 2, ED_TOPBAR_HEIGHT, ED_SIDEBAR_WIDTH, + ed->height - ED_TOPBAR_HEIGHT - ED_SPACING / 2, + titles, 0); + } + + { + int x = ED_SPACING / 2 + ED_SIDEBAR_WIDTH + ED_SPACING; + int y = ED_TOPBAR_HEIGHT; + int width = ed->width - ED_SIDEBAR_WIDTH - ED_SPACING / 2 - ED_SPACING - ED_SPACING / 2; + int height = ed->height - ED_TOPBAR_HEIGHT - ED_SPACING / 2; + char *titles[] = {"Edit View", "Animation View", 0}; + ed->mode = glwImTabBox(win, GEN_ID, + x, y, width, height, + titles, ed->mode); + if (ED_MODE_EDIT == ed->mode) { + int glWinY = glwImNextY(win); + int bottomY = y + height - ED_TOOLBAR_HEIGHT; + glwImBottomBar(win, GEN_ID, x, bottomY, width, ED_TOOLBAR_HEIGHT); + ed->showBoneChecked = glwImCheck(win, GEN_ID, x + ED_SPACING + ED_SPACING, + bottomY + (ED_TOOLBAR_HEIGHT - glwImLineHeight(win)) / 2 + 2, + "Show Bone", + ed->showBoneChecked); + + ed->glLeft = x + 1; + ed->glTop = glWinY; + ed->glWidth = width - 3; + ed->glHeight = bottomY - glWinY; + + if (0 == ed->bm) { + bmeshBall ball; + bmeshBone bone; + unsigned int i; + ed->bm = bmeshCreate(); + + for (i = 0; i < sizeof(bmeshTestBalls) / sizeof(bmeshTestBalls[0]); ++i) { + memset(&ball, 0, sizeof(ball)); + ball.position.x = bmeshTestBalls[i][1]; + ball.position.y = bmeshTestBalls[i][2]; + ball.position.z = bmeshTestBalls[i][3]; + ball.radius = bmeshTestBalls[i][4]; + ball.type = bmeshTestBalls[i][5]; + bmeshAddBall(ed->bm, &ball); + } + + for (i = 0; i < sizeof(bmeshTestBones) / sizeof(bmeshTestBones[0]); ++i) { + memset(&bone, 0, sizeof(bone)); + bone.firstBallIndex = bmeshTestBones[i][0]; + bone.secondBallIndex = bmeshTestBones[i][1]; + bmeshAddBone(ed->bm, &bone); + } + + bmeshGenerate(ed->bm); + } + + glEnable(GL_SCISSOR_TEST); + glScissor(ed->glLeft, ED_SPACING / 2 + ED_TOOLBAR_HEIGHT + 1, + ed->glWidth, ed->glHeight); + glPushMatrix(); + glTranslatef(x + 1, glWinY, 0); + + glColor3f(glwR(ED_GL_BACKGROUND_COLOR), + glwG(ED_GL_BACKGROUND_COLOR), + glwB(ED_GL_BACKGROUND_COLOR)); + glRecti(0, 0, ed->glWidth, ed->glHeight); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60.0f, (float)ed->glWidth / ed->glHeight, 1, 1000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glPushMatrix(); + glTranslatef(0, 0, -ed->cameraDistance); + glRotatef(ed->cameraAngleX, 1, 0, 0); + glRotatef(ed->cameraAngleY, 0, 1, 0); + + drawGrid(10, 1); + + glEnable(GL_LIGHTING); + glEnable(GL_CULL_FACE); + bmeshDraw(ed->bm); + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + + glPopMatrix(); + + glPopMatrix(); + glDisable(GL_SCISSOR_TEST); + } + } +} + +static void reshape(glwWin *win, int width, int height) { + editor *ed = glwGetUserData(win); + + glShadeModel(GL_SMOOTH); + glEnable(GL_LIGHT0); + + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + + ed->width = width; + ed->height = height; + + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_ALPHA_TEST); + //glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glClearColor(glwR(ED_BACKGROUND_COLOR), + glwG(ED_BACKGROUND_COLOR), + glwB(ED_BACKGROUND_COLOR), 1); + + glViewport(0, 0, (GLsizei)width, (GLsizei)height); +} + +static void mouse(glwWin *win, int button, int state, + int x, int y){ + editor *ed = glwGetUserData(win); + if (!glwPointTest(x, y, ed->glLeft, ed->glTop, + ed->glWidth, ed->glHeight, 0)) { + return; + } + if (GLW_DOWN == state) { + ed->mouseX = x; + ed->mouseY = y; + } +} + +static void motion(glwWin *win, int x, int y) { + editor *ed = glwGetUserData(win); + if (!glwPointTest(x, y, ed->glLeft, ed->glTop, + ed->glWidth, ed->glHeight, 0)) { + return; + } + ed->cameraAngleY += (x - ed->mouseX); + ed->cameraAngleX += (y - ed->mouseY); + ed->mouseX = x; + ed->mouseY = y; +} + +int main(int argc, char *argv[]) { + editor ed; + glwInit(); + memset(&ed, 0, sizeof(ed)); + ed.cameraAngleX = 30; + ed.cameraAngleY = -312; + ed.cameraDistance = 14.4; + ed.win = glwCreateWindow(0, 0, 0, 0); + glwSetUserData(ed.win, &ed); + glwReshapeFunc(ed.win, reshape); + glwDisplayFunc(ed.win, display); + glwMouseFunc(ed.win, mouse); + glwMotionFunc(ed.win, motion); + glwMainLoop(); + return 0; +} diff --git a/src/glw.c b/src/glw.c new file mode 100644 index 00000000..9b1ed943 --- /dev/null +++ b/src/glw.c @@ -0,0 +1,690 @@ +#include +#include +#include +#include +#include "glw_internal.h" +#include "glw_style.h" + +#ifndef M_PI +#define M_PI 3.14159265 +#endif + +void glwDrawSystemText(glwWin *win, int x, int y, char *text) { + int vleft, vwidth; + float tleft, twidth; + glwSystemFontTexture *systemFontTexture = glwGetSystemFontTexture(win); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, systemFontTexture->texId); + for (vleft = 0; *text; vleft += vwidth, text++) { + int idx = (*text) - GLW_SYSTEM_FONT_BEGIN_CHAR; + if (idx < 0 || idx > GLW_SYSTEM_FONT_CHAR_NUM) { + continue; + } + tleft = (float)systemFontTexture->lefts[idx] / + systemFontTexture->size.width; + vwidth = systemFontTexture->widths[idx]; + twidth = (float)vwidth / systemFontTexture->originSize.width; + + glBegin(GL_QUADS); + glTexCoord2f(tleft, 0); + glVertex2i(x + vleft, y); + + glTexCoord2f(tleft + twidth, 0); + glVertex2i(x + vleft + vwidth, y); + + glTexCoord2f(tleft + twidth, 1); + glVertex2i(x + vleft + vwidth, y + systemFontTexture->originSize.height); + + glTexCoord2f(tleft, 1); + glVertex2i(x + vleft, y + systemFontTexture->originSize.height); + glEnd(); + } + glDisable(GL_TEXTURE_2D); +} + +void glwMouseEvent(glwWin *win, int button, int state, int x, int y) { + glwImGui *imGUI = glwGetImGUI(win); + imGUI->mouseButton = button; + imGUI->mouseState = state; + imGUI->mouseX = x; + imGUI->mouseY = y; +} + +int glwPointTest(int x, int y, int left, int top, int width, int height, + int allowOffset) { + int right = left + width - 1 + allowOffset; + int bottom = top + height - 1 + allowOffset; + left -= allowOffset; + top -= allowOffset; + return x >= left && x <= right && y >= top && y <= bottom; +} + +void glwInitSystemFontTexture(glwWin *win) { + char asciiTable[GLW_SYSTEM_FONT_CHAR_NUM + 1]; + int i; + GLuint texture = 0; + glwFont *font; + int pixelsWide = 0; + int pixelsHigh = 0; + float scaleX, scaleY; + glwSystemFontTexture *systemFontTexture = glwGetSystemFontTexture(win); + + for (i = 0; i <= GLW_SYSTEM_FONT_CHAR_NUM; ++i) { + asciiTable[i] = GLW_SYSTEM_FONT_BEGIN_CHAR + i; + } + asciiTable[GLW_SYSTEM_FONT_CHAR_NUM] = '\0'; + + font = glwCreateFont(GLW_SYSTEM_FONT_NAME, GLW_SYSTEM_FONT_WEIGHT, + GLW_SYSTEM_FONT_SIZE, 0); + systemFontTexture->lefts[0] = 0; + systemFontTexture->size.width = 0; + systemFontTexture->size.height = 0; + for (i = 0; i < GLW_SYSTEM_FONT_CHAR_NUM; ++i) { + char ch[2] = {asciiTable[i], '\0'}; + glwSize chSize = glwMeasureText(ch, font); + if (chSize.height > systemFontTexture->size.height) { + systemFontTexture->size.height = chSize.height; + } + systemFontTexture->size.width += chSize.width; + systemFontTexture->widths[i] = chSize.width; + if (i > 0) { + systemFontTexture->lefts[i] = (float)(systemFontTexture->lefts[i - 1] + + systemFontTexture->widths[i - 1]); + } + } + systemFontTexture->originSize = systemFontTexture->size; + for (i = 0; i < GLW_SYSTEM_FONT_CHAR_NUM; ++i) { + char ch[2] = {asciiTable[i], '\0'}; + glwSize chSize = {systemFontTexture->widths[i], + systemFontTexture->originSize.height}; + unsigned char *rgba = glwRenderTextToRGBA(ch, font, chSize, + &pixelsWide, &pixelsHigh); + if (0 == i) { + scaleX = (float)pixelsWide / chSize.width; + scaleY = (float)pixelsHigh / chSize.height; + systemFontTexture->size.width *= scaleX; + systemFontTexture->size.height *= scaleY; + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexImage2D(GL_TEXTURE_2D, 0, 4, + (GLsizei)(systemFontTexture->size.width), + (GLsizei)(systemFontTexture->size.height), 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + systemFontTexture->lefts[i] *= scaleX; + glTexSubImage2D(GL_TEXTURE_2D, 0, + systemFontTexture->lefts[i], 0, + (GLsizei)pixelsWide, + (GLsizei)pixelsHigh, + GL_RGBA, GL_UNSIGNED_BYTE, rgba); + free(rgba); + } + systemFontTexture->texId = (int)texture; + glwDestroyFont(font); +} + +static glwVec2 glwRoundedCorners[GLW_SMALL_ROUNDED_CORNER_SLICES] = {{0}}; + +static void createRoundedCorners(glwVec2 *arr, int num) { + float slice = M_PI / 2 / num; + int i; + float a = 0; + for (i = 0; i < num; a += slice, ++i) { + arr[i].x = cosf(a); + arr[i].y = sinf(a); + } +} + +void glwInitPrimitives(void) { + createRoundedCorners(glwRoundedCorners, GLW_SMALL_ROUNDED_CORNER_SLICES); +} + +static void glwDrawRightTopVertexs(float left, float top, float right, + float bottom, float radius) { + int i; + for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) { + glVertex2f(right - radius + radius * glwRoundedCorners[i].x, + top + radius - radius * glwRoundedCorners[i].y); + } +} + +static void glwDrawRightBottomVertexs(float left, float top, float right, + float bottom, float radius) { + int i; + for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) { + glVertex2f(right - radius + radius * glwRoundedCorners[i].x, + bottom - radius + radius * glwRoundedCorners[i].y); + } +} + +static void glwDrawLeftBottomVertexs(float left, float top, float right, + float bottom, float radius) { + int i; + for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) { + glVertex2f(left + radius - radius * glwRoundedCorners[i].x, + bottom - radius + radius * glwRoundedCorners[i].y); + } +} + +static void glwDrawLeftTopVertexs(float left, float top, float right, + float bottom, float radius) { + int i; + for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) { + glVertex2f(left + radius - radius * glwRoundedCorners[i].x, + top + radius - radius * glwRoundedCorners[i].y); + } +} + +void glwDrawCheckSymbol(int x, int y, int width, int height, + unsigned int color) { + glColor3f(glwR(color), glwG(color), glwB(color)); + glLineWidth(2); + glBegin(GL_LINE_STRIP); + glVertex2f(x + width * 0.15, y + height * 0.3); + glVertex2f(x + width * 0.45, y + height * 0.65); + glVertex2f(x + width * 1.05, y - height * 0.05); + glEnd(); + glLineWidth(1); +} + +void glwDrawDropdownArrow(int x, int y, int width, int height, + unsigned int color) { + glColor3f(glwR(color), glwG(color), glwB(color)); + glBegin(GL_TRIANGLES); + glVertex2f(x, y); + glVertex2f(x + width, y); + glVertex2f(x + width * 0.5, y + height); + glEnd(); +} + +void glwDrawVLine(int x, int y, int width, int height, + unsigned int color) { + glColor3f(glwR(color), glwG(color), glwB(color)); + glLineWidth(width); + glBegin(GL_LINES); + glVertex2f(x, y); + glVertex2f(x, y + height - 1); + glEnd(); + glLineWidth(1); +} + +void glwDrawHLine(int x, int y, int width, int height, + unsigned int color) { + glColor3f(glwR(color), glwG(color), glwB(color)); + glLineWidth(height); + glBegin(GL_LINES); + glVertex2f(x, y); + glVertex2f(x + width - 1, y); + glEnd(); + glLineWidth(1); +} + +void glwDrawRoundedRectBorder(float x, float y, float width, float height, + float radius, unsigned int color) { + float left = x; + float top = y; + float bottom = y + height - 1; + float right = x + width - 1; + glDisable(GL_TEXTURE_2D); + glColor3f(glwR(color), glwG(color), glwB(color)); + glBegin(GL_LINE_LOOP); + glVertex2f(left, top + radius); + glwDrawLeftTopVertexs(left, top, right, bottom, radius); + glVertex2f(left + radius, top); + + glVertex2f(right - radius, top); + glwDrawRightTopVertexs(left, top, right, bottom, radius); + glVertex2f(right, top + radius); + + glVertex2f(right, bottom - radius); + glwDrawRightBottomVertexs(left, top, right, bottom, radius); + glVertex2f(right - radius, bottom); + + glVertex2f(left + radius, bottom); + glwDrawLeftBottomVertexs(left, top, right, bottom, radius); + glVertex2f(left, bottom - radius); + glEnd(); +} + +void glwDrawTopRoundedRectBorder(float x, float y, float width, float height, + float radius, unsigned int color) { + float left = x; + float top = y; + float bottom = y + height - 1; + float right = x + width - 1; + glDisable(GL_TEXTURE_2D); + glColor3f(glwR(color), glwG(color), glwB(color)); + glBegin(GL_LINE_LOOP); + glVertex2f(left, top + radius); + glwDrawLeftTopVertexs(left, top, right, bottom, radius); + glVertex2f(left + radius, top); + + glVertex2f(right - radius, top); + glwDrawRightTopVertexs(left, top, right, bottom, radius); + glVertex2f(right, top + radius); + + glVertex2f(right, bottom); + glVertex2f(left, bottom); + glEnd(); +} + +void glwDrawLeftRoundedRectGradientFill(float x, float y, + float width, float height, + float radius, unsigned int topColor, unsigned int bottomColor) { + float left = x; + float top = y; + float bottom = y + height - 1; + float right = x + width - 1; + int i; + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUAD_STRIP); + for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) { + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(left + radius - radius * glwRoundedCorners[i].x, + bottom - radius + radius * glwRoundedCorners[i].y); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(left + radius - radius * glwRoundedCorners[i].x, + top + radius - radius * glwRoundedCorners[i].y); + } + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(right, bottom); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(right, top); + glEnd(); +} + +void glwDrawTopRoundedRectGradientFill(float x, float y, + float width, float height, + float radius, unsigned int topColor, unsigned int bottomColor) { + float left = x; + float top = y; + float bottom = y + height - 1; + float right = x + width - 1; + int i; + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUAD_STRIP); + for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) { + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(left + radius - radius * glwRoundedCorners[i].x, + bottom); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(left + radius - radius * glwRoundedCorners[i].x, + top + radius - radius * glwRoundedCorners[i].y); + } + for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) { + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(right - radius + radius * glwRoundedCorners[i].x, + bottom); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(right - radius + radius * glwRoundedCorners[i].x, + top + radius - radius * glwRoundedCorners[i].y); + } + glEnd(); +} + +void glwDrawRectGradientFill(float x, float y, + float width, float height, + unsigned int topColor, unsigned int bottomColor) { + float left = x; + float top = y; + float bottom = y + height - 1; + float right = x + width - 1; + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUAD_STRIP); + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(left, bottom); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(left, top); + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(right, bottom); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(right, top); + glEnd(); +} + +void glwDrawRightRoundedRectGradientFill(float x, float y, + float width, float height, + float radius, unsigned int topColor, unsigned int bottomColor) { + float left = x; + float top = y; + float bottom = y + height - 1; + float right = x + width - 1; + int i; + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUAD_STRIP); + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(left, bottom); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(left, top); + for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) { + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(right - radius + radius * glwRoundedCorners[i].x, + bottom - radius + radius * glwRoundedCorners[i].y); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(right - radius + radius * glwRoundedCorners[i].x, + top + radius - radius * glwRoundedCorners[i].y); + } + glEnd(); +} + +void glwDrawRoundedRectGradientFill(float x, float y, float width, float height, + float radius, unsigned int topColor, unsigned int bottomColor) { + float left = x; + float top = y; + float bottom = y + height - 1; + float right = x + width - 1; + int i; + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUAD_STRIP); + for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) { + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(left + radius - radius * glwRoundedCorners[i].x, + bottom - radius + radius * glwRoundedCorners[i].y); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(left + radius - radius * glwRoundedCorners[i].x, + top + radius - radius * glwRoundedCorners[i].y); + } + for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) { + glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor)); + glVertex2f(right - radius + radius * glwRoundedCorners[i].x, + bottom - radius + radius * glwRoundedCorners[i].y); + glColor3f(glwR(topColor), glwG(topColor), glwB(topColor)); + glVertex2f(right - radius + radius * glwRoundedCorners[i].x, + top + radius - radius * glwRoundedCorners[i].y); + } + glEnd(); +} + +void glwDrawButtonBackground(float x, float y, float width, float height, + glwCtrlState state) { + switch (state) + { + case GLW_CTRL_STATE_PRESS: + glwDrawRoundedRectGradientFill(x, y, width, height, + GLW_BUTTON_CORNER_RADIUS, + GLW_FILL_GRADIENT_COLOR_1_H, GLW_FILL_GRADIENT_COLOR_2_H); + break; + case GLW_CTRL_STATE_NORMAL: + default: + glwDrawRoundedRectGradientFill(x, y, width, height, + GLW_BUTTON_CORNER_RADIUS, + GLW_FILL_GRADIENT_COLOR_1, GLW_FILL_GRADIENT_COLOR_2); + break; + } + glwDrawRoundedRectBorder(x, y, width, height, + GLW_BUTTON_CORNER_RADIUS, GLW_BORDER_COLOR_2); +} + +glwSize glwMeasureSystemText(glwWin *win, char *text) { + glwSystemFontTexture *systemFontTexture = glwGetSystemFontTexture(win); + glwSize size = {0, systemFontTexture->originSize.height}; + for (; *text; text++) { + int idx = (*text) - GLW_SYSTEM_FONT_BEGIN_CHAR; + if (idx < 0 || idx > GLW_SYSTEM_FONT_CHAR_NUM) { + continue; + } + size.width += systemFontTexture->widths[idx]; + } + return size; +} + +static void glwImGUIActiveIdCheck(glwImGui *imGUI, int id, int x, int y, + int width, int height) { + if (GLW_DOWN == imGUI->mouseState) { + if (!imGUI->activeId) { + int hit = glwPointTest(imGUI->mouseX, imGUI->mouseY, x, y, + width, height, 0); + if (hit) { + imGUI->activeId = id; + } + } + } else if (GLW_UP == imGUI->mouseState) { + if (imGUI->activeId == id) { + imGUI->activeId = 0; + } + } +} + +int glwImButton(glwWin *win, int id, int x, int y, char *text) { + glwImGui *imGUI = glwGetImGUI(win); + glwSize textSize = glwMeasureSystemText(win, text); + int width = textSize.width * (1 + GLW_HOR_AUTO_MARGIN * 2); + int height = textSize.height * (1 + GLW_VER_AUTO_MARGIN * 2); + glwImGUIActiveIdCheck(imGUI, id, x, y, width, height); + glwDrawButtonBackground(x, y, width, height, + imGUI->activeId == id ? GLW_CTRL_STATE_PRESS : GLW_CTRL_STATE_NORMAL); + glwDrawSystemText(win, x + textSize.width * GLW_HOR_AUTO_MARGIN, + y + textSize.height * GLW_VER_AUTO_MARGIN, + text); + imGUI->nextX = x + width; + imGUI->nextY = y; + return imGUI->activeId == id; +} + +int glwImDropdownBox(glwWin *win, int id, int x, int y, char *text) { + glwImGui *imGUI = glwGetImGUI(win); + glwSize textSize = glwMeasureSystemText(win, text); + int arrowHeight = textSize.height * 0.3; + int arrowWidth = arrowHeight * 2; + int width = textSize.width * (1 + GLW_HOR_AUTO_MARGIN * 2) + arrowWidth * 3; + int height = textSize.height * (1 + GLW_VER_AUTO_MARGIN * 2); + glwImGUIActiveIdCheck(imGUI, id, x, y, width, height); + glwDrawButtonBackground(x, y, width, height, + imGUI->activeId == id ? GLW_CTRL_STATE_PRESS : GLW_CTRL_STATE_NORMAL); + glwDrawSystemText(win, x + textSize.width * (GLW_HOR_AUTO_MARGIN), + y + textSize.height * (GLW_VER_AUTO_MARGIN), + text); + glwDrawDropdownArrow(x + width - textSize.width * GLW_HOR_AUTO_MARGIN - + arrowWidth, + y + (height - arrowHeight) / 2, arrowWidth, arrowHeight, 0); + imGUI->nextX = x + width; + imGUI->nextY = y; + return imGUI->activeId == id; +} + +int glwImCheck(glwWin *win, int id, int x, int y, char *text, int checked) { + glwImGui *imGUI = glwGetImGUI(win); + glwSize textSize = glwMeasureSystemText(win, text); + int height = textSize.height; + int boxWidth = height; + int width = boxWidth + textSize.height * (1 + GLW_HOR_AUTO_MARGIN * 2) + + textSize.width; + int oldActiveId = imGUI->activeId; + glwImGUIActiveIdCheck(imGUI, id, x, y, width, height); + glwDrawButtonBackground(x, y, boxWidth, height, + (imGUI->activeId == id || checked) ? + GLW_CTRL_STATE_PRESS : GLW_CTRL_STATE_NORMAL); + glwDrawSystemText(win, x + textSize.height * GLW_HOR_AUTO_MARGIN + boxWidth, + y, text); + if (checked) { + glwDrawCheckSymbol(x, y, boxWidth, height, GLW_CHECK_COLOR); + } + imGUI->nextX = x + width; + imGUI->nextY = y; + return (imGUI->activeId == id && oldActiveId != id) ? !checked : checked; +} + +static int glwCalcListMaxWidth(glwWin *win, char **list, int *height, + int *len) { + int i; + int itemWidth = 0; + int maxItemWidth = 0; + + for (i = 0; list[i]; ++i) { + glwSize textSize = glwMeasureSystemText(win, list[i]); + itemWidth = textSize.width * (1 + GLW_HOR_AUTO_MARGIN * 2); + if (itemWidth > maxItemWidth) { + maxItemWidth = itemWidth; + } + if (height && !(*height)) { + *height = textSize.height * (1 + GLW_VER_AUTO_MARGIN * 2); + } + if (len) { + ++(*len); + } + } + + return maxItemWidth; +} + +int glwImButtonGroup(glwWin *win, int id, int x, int y, int parentWidth, + char **list, int sel) { + glwImGui *imGUI = glwGetImGUI(win); + int width = 0; + int height = 0; + int left = 0; + int i; + int listLen = 0; + int offset; + int maxItemWidth = 0; + maxItemWidth = glwCalcListMaxWidth(win, list, &height, &listLen); + width = maxItemWidth * listLen; + left = listLen > 1 ? x + (parentWidth - width) / 2 : + x + maxItemWidth * GLW_HOR_AUTO_MARGIN; + glwImGUIActiveIdCheck(imGUI, id, left, y, width, height); + glwDrawRoundedRectGradientFill(left, y, width, height, + GLW_BUTTON_CORNER_RADIUS, + GLW_FILL_GRADIENT_COLOR_1, GLW_FILL_GRADIENT_COLOR_2); + for (i = 0, offset = left; list[i]; ++i) { + glwSize textSize = glwMeasureSystemText(win, list[i]); + if (imGUI->activeId == id) { + int hit = glwPointTest(imGUI->mouseX, imGUI->mouseY, offset, y, + maxItemWidth, height, 0); + if (hit) { + sel = i; + } + } + if (sel == i) { + if (1 == listLen) { + glwDrawRoundedRectGradientFill(left, y, width, height, + GLW_BUTTON_CORNER_RADIUS, + GLW_FILL_GRADIENT_COLOR_1_H, GLW_FILL_GRADIENT_COLOR_2_H); + } else { + if (0 == i) { + glwDrawLeftRoundedRectGradientFill(offset, y, + maxItemWidth, height, + GLW_BUTTON_CORNER_RADIUS, + GLW_FILL_GRADIENT_COLOR_1_H, GLW_FILL_GRADIENT_COLOR_2_H); + } else if (listLen - 1 == i) { + glwDrawRightRoundedRectGradientFill(offset, y, + maxItemWidth, height, + GLW_BUTTON_CORNER_RADIUS, + GLW_FILL_GRADIENT_COLOR_1_H, GLW_FILL_GRADIENT_COLOR_2_H); + } else { + glwDrawRectGradientFill(offset, y, + maxItemWidth, height, + GLW_FILL_GRADIENT_COLOR_1_H, GLW_FILL_GRADIENT_COLOR_2_H); + } + } + } + if (0 != i) { + glwDrawVLine(offset, y, 1, height, GLW_BORDER_COLOR_2); + } + glwDrawSystemText(win, offset + (maxItemWidth - textSize.width) / 2, + y + height * 0.1, list[i]); + offset += maxItemWidth; + } + imGUI->nextX = offset; + imGUI->nextY = y; + glwDrawRoundedRectBorder(left, y, width, height, + GLW_BUTTON_CORNER_RADIUS, GLW_BORDER_COLOR_2); + return sel; +} + +int glwImTabBox(glwWin *win, int id, int x, int y, int width, int height, + char **list, int sel) { + glwImGui *imGUI = glwGetImGUI(win); + int tabWidth = 0; + int tabHeight = 0; + int left = 0; + int i; + int listLen = 0; + int offset; + int maxItemWidth = 0; + maxItemWidth = glwCalcListMaxWidth(win, list, &tabHeight, &listLen); + tabWidth = maxItemWidth * listLen; + left = x + maxItemWidth * GLW_HOR_AUTO_MARGIN / 2; + imGUI->nextX = x; + imGUI->nextY = y + tabHeight; + glwImGUIActiveIdCheck(imGUI, id, left, y, tabWidth, tabHeight); + for (i = 0, offset = left; list[i]; ++i) { + glwSize textSize = glwMeasureSystemText(win, list[i]); + if (imGUI->activeId == id) { + int hit = glwPointTest(imGUI->mouseX, imGUI->mouseY, offset, y, + maxItemWidth, tabHeight, 0); + if (hit) { + sel = i; + } + } + if (sel == i) { + glwDrawTopRoundedRectGradientFill(offset, y, + maxItemWidth, tabHeight, + GLW_BUTTON_CORNER_RADIUS, + GLW_TAB_FILL_GRADIENT_COLOR_1, GLW_TAB_FILL_GRADIENT_COLOR_2); + glwDrawTopRoundedRectBorder(offset, y, + maxItemWidth, tabHeight, + GLW_BUTTON_CORNER_RADIUS, GLW_BORDER_COLOR_1); + } + glwDrawSystemText(win, offset + (maxItemWidth - textSize.width) / 2, + y + tabHeight * GLW_VER_AUTO_MARGIN, list[i]); + offset += maxItemWidth; + } + glwDrawRectGradientFill(x + 1, y + tabHeight, + width - 2, height - tabHeight - 2, + GLW_BACKGROUND_COLOR, GLW_BACKGROUND_COLOR); + glwDrawHLine(x, y + tabHeight - 1, width, 1, GLW_BORDER_COLOR_1); + glwDrawHLine(x, y + height - 1, width, 1, GLW_BORDER_COLOR_1); + glwDrawVLine(x, y + tabHeight, 1, height - tabHeight, + GLW_BORDER_COLOR_1); + glwDrawVLine(x + width - 1, y + tabHeight, 1, height - tabHeight, + GLW_BORDER_COLOR_1); + return sel; +} + +int glwImPanel(glwWin *win, int id, int x, int y, int width, int height) { + glwDrawRectGradientFill(x, y, width, height, + GLW_PANEL_FILL_COLOR, GLW_PANEL_FILL_COLOR); + glwDrawRoundedRectBorder(x, y, width, height, + GLW_BUTTON_CORNER_RADIUS, GLW_BORDER_COLOR_1); + return 0; +} + +int glwImToolBar(glwWin *win, int id, int x, int y, int width, int height) { + glwImGui *imGUI = glwGetImGUI(win); + glwDrawRectGradientFill(x + 1, y + 1, width - 2, height - 2, + GLW_TOOLBAR_BACKGROUND_COLOR, GLW_TOOLBAR_BACKGROUND_COLOR); + glwDrawHLine(x, y + height - 1, width, 1, GLW_BORDER_COLOR_1); + imGUI->nextX = x; + imGUI->nextY = y + height; + return 0; +} + +int glwImBottomBar(glwWin *win, int id, int x, int y, int width, int height) { + glwImGui *imGUI = glwGetImGUI(win); + glwDrawRectGradientFill(x + 1, y + 1, width - 2, height - 2, + GLW_TOOLBAR_BACKGROUND_COLOR, GLW_TOOLBAR_BACKGROUND_COLOR); + glwDrawHLine(x, y, width, 1, GLW_BORDER_COLOR_1); + imGUI->nextX = x; + imGUI->nextY = y - height; + return 0; +} + +int glwImNextX(glwWin *win) { + glwImGui *imGUI = glwGetImGUI(win); + return imGUI->nextX; +} + +int glwImNextY(glwWin *win) { + glwImGui *imGUI = glwGetImGUI(win); + return imGUI->nextY; +} + +int glwImLineHeight(glwWin *win) { + glwSystemFontTexture *systemFontTexture = glwGetSystemFontTexture(win); + return systemFontTexture->originSize.height * (1 + GLW_VER_AUTO_MARGIN * 2); +} diff --git a/src/glw.h b/src/glw.h new file mode 100644 index 00000000..e4106261 --- /dev/null +++ b/src/glw.h @@ -0,0 +1,70 @@ +#ifndef GLW_H +#define GLW_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct glwSize { + int width; + int height; +} glwSize; + +typedef struct glwVec2 { + float x; + float y; +} glwVec2; + +typedef struct glwWin glwWin; +typedef struct glwFont glwFont; + +#define GLW_LEFT_BUTTON 1 +#define GLW_MIDDLE_BUTTON 2 +#define GLW_RIGHT_BUTTON 3 + +#define GLW_DOWN 1 +#define GLW_UP 2 + +void glwInit(void); +void glwMainLoop(void); +glwWin *glwCreateWindow(int x, int y, int width, int height); +glwWin *glwCreateSubWindow(glwWin *parent, int x, int y, int width, int height); +void glwSetUserData(glwWin *win, void *userData); +void *glwGetUserData(glwWin *win); +void glwDestroyWindow(glwWin *win); +void glwSetWindowTitle(glwWin *win, char *name); +void glwPositionWindow(glwWin *win, int x, int y); +void glwReshapeWindow(glwWin *win, int width, int height); +void glwDisplayFunc(glwWin *win, void (*func)(glwWin *win)); +void glwReshapeFunc(glwWin *win, void (*func)(glwWin *win, int width, + int height)); +void glwKeyboardFunc(glwWin *win, void (*func)(glwWin *win, unsigned char key, + int x, int y)); +void glwMouseFunc(glwWin *win, void (*func)(glwWin *win, int button, int state, + int x, int y)); +void glwMotionFunc(glwWin *win, void (*func)(glwWin *win, int x, int y)); +void glwPassiveMotionFunc(glwWin *win, void (*func)(glwWin *win, int x, int y)); +#define glwR(rgb) ((float)(((rgb) >> 16) & 0xff) / 255) +#define glwG(rgb) ((float)(((rgb) >> 8) & 0xff) / 255) +#define glwB(rgb) ((float)(((rgb)) & 0xff) / 255) +int glwImButton(glwWin *win, int id, int x, int y, char *text); +int glwImCheck(glwWin *win, int id, int x, int y, char *text, int checked); +int glwImDropdownBox(glwWin *win, int id, int x, int y, char *text); +int glwImButtonGroup(glwWin *win, int id, int x, int y, int parentWidth, + char **list, int sel); +int glwImPanel(glwWin *win, int id, int x, int y, int width, int height); +int glwImTabBox(glwWin *win, int id, int x, int y, int width, int height, + char **list, int sel); +int glwImToolBar(glwWin *win, int id, int x, int y, int width, int height); +int glwImBottomBar(glwWin *win, int id, int x, int y, int width, int height); +int glwImNextX(glwWin *win); +int glwImNextY(glwWin *win); +int glwImLineHeight(glwWin *win); +int glwPointTest(int x, int y, int left, int top, int width, int height, + int allowOffset); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/glw_internal.h b/src/glw_internal.h new file mode 100644 index 00000000..746467f9 --- /dev/null +++ b/src/glw_internal.h @@ -0,0 +1,54 @@ +#ifndef GLW_INTERNAL_H +#define GLW_INTERNAL_H +#include "glw.h" +#include "glw_style.h" + +#define GLW_SYSTEM_FONT_BEGIN_CHAR ' ' +#define GLW_SYSTEM_FONT_END_CHAR '~' +#define GLW_SYSTEM_FONT_CHAR_NUM (GLW_SYSTEM_FONT_END_CHAR - GLW_SYSTEM_FONT_BEGIN_CHAR + 1) + +#define GLW_TEXT_ALIGN_LEFT 0 +#define GLW_TEXT_ALIGN_CENTER 1 +#define GLW_TEXT_ALIGN_RIGHT 2 + +typedef enum glwCtrlState { + GLW_CTRL_STATE_NORMAL, + GLW_CTRL_STATE_PRESS, +} glwCtrlState; + +typedef struct glwSystemFontTexture { + int texId; + glwSize size; + glwSize originSize; + float lefts[GLW_SYSTEM_FONT_CHAR_NUM]; + int widths[GLW_SYSTEM_FONT_CHAR_NUM]; +} glwSystemFontTexture; + +typedef struct glwImGui { + int mouseButton; + int mouseState; + int mouseX; + int mouseY; + int activeId; + int nextX; + int nextY; +} glwImGui; + +glwSize glwMeasureText(char *text, glwFont *font); +unsigned char *glwRenderTextToRGBA(char *text, glwFont *font, glwSize size, + int *pixelsWide, int *pixelsHigh); +void glwDrawText(int x, int y, char *text, glwFont *font); +void glwDrawSystemText(glwWin *win, int x, int y, char *text); +glwSize glwMeasureSystemText(glwWin *win, char *text); +void glwInitSystemFontTexture(glwWin *win); +void glwInitPrimitives(void); +glwSystemFontTexture *glwGetSystemFontTexture(glwWin *win); +void glwDestroyFont(glwFont *font); +glwFont *glwCreateFont(char *name, int weight, int size, int bold); +void glwDestroyFont(glwFont *font); +void glwDrawButtonBackground(float x, float y, float width, float height, + glwCtrlState state); +void glwMouseEvent(glwWin *win, int button, int state, int x, int y); +glwImGui *glwGetImGUI(glwWin *win); + +#endif diff --git a/src/glw_osx.m b/src/glw_osx.m new file mode 100644 index 00000000..2c8107bf --- /dev/null +++ b/src/glw_osx.m @@ -0,0 +1,345 @@ +#import +#import +#import +#include "glw_internal.h" + +@interface GLView : NSOpenGLView { + CVDisplayLinkRef displayLink; +@public + void (*onReshape)(glwWin *win, int width, int height); + void (*onDisplay)(glwWin *win); + void (*onMouse)(glwWin *win, int button, int state, int x, int y); + void (*onMotion)(glwWin *win, int x, int y); + void (*onPassiveMotion)(glwWin *win, int x, int y); + void *userData; + int pendingReshape; + int viewWidth; + int viewHeight; + float scaleX; + float scaleY; + glwSystemFontTexture systemFontTexture; + glwImGui imGUI; +} + +- (CVReturn)getFrameForTime:(const CVTimeStamp *)outputTime; +- (void)drawFrame; +@end + +static CVReturn onDisplayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp *now, const CVTimeStamp *outputTime, + CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) +{ + CVReturn result = [(GLView *)displayLinkContext getFrameForTime:outputTime]; + return result; +} + +glwSystemFontTexture *glwGetSystemFontTexture(glwWin *win) { + GLView *view = ((NSWindow *)win).contentView; + return &view->systemFontTexture; +} + +@implementation GLView +- (id)initWithFrame:(NSRect)frameRect { + NSOpenGLPixelFormatAttribute attribs[] = {NSOpenGLPFAMultisample, + NSOpenGLPFASampleBuffers, 1, + NSOpenGLPFASamples, 0, + NSOpenGLPFAAccelerated, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAColorSize, 32, + NSOpenGLPFADepthSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy, + 0}; + NSOpenGLPixelFormat *windowedPixelFormat = [[NSOpenGLPixelFormat alloc] + initWithAttributes:attribs]; + self = [super initWithFrame:frameRect pixelFormat:windowedPixelFormat]; + [windowedPixelFormat release]; + + [self setWantsBestResolutionOpenGLSurface:YES]; + + return self; +} + +- (void) prepareOpenGL { + [super prepareOpenGL]; + + GLint vblSynch = 1; + [[self openGLContext] setValues:&vblSynch + forParameter:NSOpenGLCPSwapInterval]; + + CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); + CVDisplayLinkSetOutputCallback(displayLink, onDisplayLinkCallback, self); + CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, + [[self openGLContext] CGLContextObj], + [[self pixelFormat] CGLPixelFormatObj]); + + CVDisplayLinkStart(displayLink); +} + +- (void)drawFrame { + @autoreleasepool { + NSOpenGLContext *currentContext = [self openGLContext]; + [currentContext makeCurrentContext]; + CGLLockContext([currentContext CGLContextObj]); + if (self->pendingReshape) { + if (self->onReshape) { + self->pendingReshape = 0; + if (!self->systemFontTexture.texId) { + glwInitSystemFontTexture((glwWin *)self.window); + } + self->onReshape((glwWin *)self.window, self->viewWidth, + self->viewHeight); + } + } + if (self->onDisplay) { + self->onDisplay((glwWin *)self.window); + } + CGLFlushDrawable([[self openGLContext] CGLContextObj]); + CGLUnlockContext([currentContext CGLContextObj]); + } +} + +- (CVReturn)getFrameForTime:(const CVTimeStamp *)outputTime { + [self drawFrame]; + return kCVReturnSuccess; +} + +- (void)dealloc { + CVDisplayLinkRelease(displayLink); + [super dealloc]; +} + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (void)reshape { + NSRect bounds = [self bounds]; + NSRect backingBounds = [self convertRectToBacking:[self bounds]]; + viewWidth = (int)backingBounds.size.width; + viewHeight = (int)backingBounds.size.height; + self->scaleX = (float)viewWidth / bounds.size.width; + self->scaleY = (float)viewHeight / bounds.size.height; + self->pendingReshape = 1; + [self drawFrame]; +} + +- (void)resumeDisplayRenderer { + NSOpenGLContext *currentContext = [self openGLContext]; + [currentContext makeCurrentContext]; + CGLLockContext([currentContext CGLContextObj]); + CVDisplayLinkStart(displayLink); + CGLUnlockContext([currentContext CGLContextObj]); +} + +- (void)haltDisplayRenderer { + NSOpenGLContext *currentContext = [self openGLContext]; + [currentContext makeCurrentContext]; + CGLLockContext([currentContext CGLContextObj]); + CVDisplayLinkStop(displayLink); + CGLUnlockContext([currentContext CGLContextObj]); +} + +- (void)mouseMoved:(NSEvent*)event { + NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; + int x = loc.x * self->scaleX; + int y = loc.y * self->scaleY; + if (GLW_DOWN == self->imGUI.mouseState) { + if (self->onMotion) { + self->onMotion((glwWin *)self.window, x, y); + } + } else { + if (self->onPassiveMotion) { + self->onPassiveMotion((glwWin *)self.window, x, y); + } + } +} + +- (void)mouseDown:(NSEvent*)event { + NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; + int x = loc.x * self->scaleX; + int y = loc.y * self->scaleY; + glwMouseEvent((glwWin *)self.window, GLW_LEFT_BUTTON, GLW_DOWN, x, y); + if (self->onMouse) { + self->onMouse((glwWin *)self.window, GLW_LEFT_BUTTON, GLW_DOWN, x, y); + } +} + +- (void)mouseDragged:(NSEvent *)event { + NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; + int x = loc.x * self->scaleX; + int y = loc.y * self->scaleY; + if (GLW_DOWN == self->imGUI.mouseState) { + if (GLW_DOWN == self->imGUI.mouseState) { + if (self->onMotion) { + self->onMotion((glwWin *)self.window, x, y); + } + } else { + if (self->onPassiveMotion) { + self->onPassiveMotion((glwWin *)self.window, x, y); + } + } + } else { + glwMouseEvent((glwWin *)self.window, GLW_LEFT_BUTTON, GLW_DOWN, x, y); + if (self->onMouse) { + self->onMouse((glwWin *)self.window, GLW_LEFT_BUTTON, GLW_DOWN, x, y); + } + } +} + +- (BOOL)isFlipped { + return YES; +} + +- (void)mouseUp:(NSEvent*)event { + NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; + int x = loc.x * self->scaleX; + int y = loc.y * self->scaleY; + glwMouseEvent((glwWin *)self.window, GLW_LEFT_BUTTON, GLW_UP, x, y); + if (self->onMouse) { + self->onMouse((glwWin *)self.window, GLW_LEFT_BUTTON, GLW_UP, x, y); + } +} +@end + +glwImGui *glwGetImGUI(glwWin *win) { + GLView *view = ((NSWindow *)win).contentView; + return &view->imGUI; +} + +glwFont *glwCreateFont(char *name, int weight, int size, int bold) { + NSString *fontFamily = [NSString stringWithCString:name + encoding:NSMacOSRomanStringEncoding]; + NSFont *font = [[[NSFontManager sharedFontManager] fontWithFamily:fontFamily + traits:(bold ? NSBoldFontMask : NSUnboldFontMask) weight:weight size:size] retain]; + return (glwFont *)font; +} + +glwSize glwMeasureText(char *text, glwFont *font) { + NSString *aString = [NSString stringWithCString:text + encoding:NSMacOSRomanStringEncoding]; + NSSize frameSize = [aString sizeWithAttributes: + [NSDictionary dictionaryWithObject:(NSFont *)font forKey:NSFontAttributeName]]; + glwSize size = {frameSize.width, frameSize.height}; + return size; +} + +void glwInit(void) { + [NSApplication sharedApplication]; + glwInitPrimitives(); +} + +void glwMainLoop(void) { + @autoreleasepool { + [NSApp run]; + } +} + +void glwSetUserData(glwWin *win, void *userData) { + GLView *view = ((NSWindow *)win).contentView; + view->userData = userData; +} + +void *glwGetUserData(glwWin *win) { + GLView *view = ((NSWindow *)win).contentView; + return view->userData; +} + +glwWin *glwCreateWindow(int x, int y, int width, int height) { + NSUInteger windowStyle = NSTitledWindowMask | NSClosableWindowMask | + NSResizableWindowMask | NSMiniaturizableWindowMask; + NSRect fullRect = [[NSScreen mainScreen] visibleFrame]; + + if (0 == width || 0 == height) { + width = fullRect.size.width; + height = fullRect.size.height; + } + + NSRect viewRect = NSMakeRect(0, 0, width, height); + NSRect windowRect = NSMakeRect(x, y, width, height); + + NSWindow *window = [[NSWindow alloc] initWithContentRect:windowRect + styleMask:windowStyle + backing:NSBackingStoreBuffered + defer:NO]; + + GLView *view = [[GLView alloc] initWithFrame:viewRect]; + view->onReshape = 0; + view->onDisplay = 0; + view->onMouse = 0; + view->userData = 0; + view->pendingReshape = 0; + view->viewWidth = width; + view->viewHeight = height; + view->systemFontTexture.texId = 0; + memset(&view->imGUI, 0, sizeof(view->imGUI)); + view->scaleX = 1; + view->scaleY = 1; + view->onPassiveMotion = 0; + view->onMotion = 0; + + [window setAcceptsMouseMovedEvents:YES]; + [window setContentView:view]; + [window setDelegate:view]; + + [window orderFrontRegardless]; + + return (glwWin *)window; +} + +void glwDisplayFunc(glwWin *win, void (*func)(glwWin *win)) { + GLView *view = ((NSWindow *)win).contentView; + view->onDisplay = func; +} + +void glwReshapeFunc(glwWin *win, void (*func)(glwWin *win, int width, + int height)) { + GLView *view = ((NSWindow *)win).contentView; + view->onReshape = func; +} + +void glwMouseFunc(glwWin *win, void (*func)(glwWin *win, int button, int state, + int x, int y)) { + GLView *view = ((NSWindow *)win).contentView; + view->onMouse = func; +} + +void glwMotionFunc(glwWin *win, + void (*func)(glwWin *win, int x, int y)) { + GLView *view = ((NSWindow *)win).contentView; + view->onMotion = func; +} + +void glwPassiveMotionFunc(glwWin *win, + void (*func)(glwWin *win, int x, int y)) { + GLView *view = ((NSWindow *)win).contentView; + view->onPassiveMotion = func; +} + +unsigned char *glwRenderTextToRGBA(char *text, glwFont *font, glwSize size, + int *pixelsWide, int *pixelsHigh) { + NSString *aString = [NSString stringWithCString:text encoding:NSMacOSRomanStringEncoding]; + NSSize frameSize = NSMakeSize(size.width, size.height); + NSImage *image = [[NSImage alloc] initWithSize:frameSize]; + unsigned char *rgba = 0; + int rgbaBytes = 0; + [image lockFocus]; + [aString drawAtPoint:NSMakePoint(0, 0) withAttributes: + [NSDictionary dictionaryWithObject:(NSFont *)font forKey:NSFontAttributeName]]; + [image unlockFocus]; + NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]]; + *pixelsWide = [bitmap pixelsWide]; + *pixelsHigh = [bitmap pixelsHigh]; + rgbaBytes = 4 * (*pixelsWide) * (*pixelsHigh); + rgba = malloc(rgbaBytes); + memcpy(rgba, [bitmap bitmapData], rgbaBytes); + [image release]; + return rgba; +} + +void glwDestroyFont(glwFont *font) { + NSFont *nsFont = (NSFont *)font; + [nsFont release]; +} + + diff --git a/src/glw_style.h b/src/glw_style.h new file mode 100644 index 00000000..cd71d4fc --- /dev/null +++ b/src/glw_style.h @@ -0,0 +1,37 @@ +#ifndef GLW_STYLE_H +#define GLW_STYLE_H + +#define GLW_SYSTEM_FONT_NAME "Helvetica" +#define GLW_SYSTEM_FONT_WEIGHT 5 +#define GLW_SYSTEM_FONT_SIZE 20 + +#define GLW_SMALL_ROUNDED_CORNER_SLICES 5 + +#define GLW_BORDER_COLOR_1 0xa2a2a2 +#define GLW_BORDER_COLOR_2 0x616161 + +#define GLW_FILL_GRADIENT_COLOR_1 0xffffff +#define GLW_FILL_GRADIENT_COLOR_2 0xc6c4c5 + +#define GLW_FILL_GRADIENT_COLOR_1_H 0x3795fa +#define GLW_FILL_GRADIENT_COLOR_2_H 0x333333 + +#define GLW_BUTTON_CORNER_RADIUS 5 + +#define GLW_CHECK_COLOR 0x000000 + +#define GLW_TAB_FILL_GRADIENT_COLOR_1 0xffffff +#define GLW_TAB_FILL_GRADIENT_COLOR_2 0xc6c4c5 + +#define GLW_TAB_FILL_GRADIENT_COLOR_1_H 0x3795fa +#define GLW_TAB_FILL_GRADIENT_COLOR_2_H 0x333333 + +#define GLW_PANEL_FILL_COLOR 0xe2e2e2 + +#define GLW_HOR_AUTO_MARGIN 0.4 +#define GLW_VER_AUTO_MARGIN 0.15 + +#define GLW_BACKGROUND_COLOR 0xfcfcfc +#define GLW_TOOLBAR_BACKGROUND_COLOR 0xf5f5f5 + +#endif diff --git a/src/skinnedmesh.h b/src/skinnedmesh.h index da73baf0..e3ab88ea 100644 --- a/src/skinnedmesh.h +++ b/src/skinnedmesh.h @@ -13,9 +13,14 @@ typedef struct skinnedMeshVertex { } skinnedMeshVertex; typedef struct skinnedMeshBone { - vec3 translation; + vec3 position; vec3 rotation; matrix toRoot; } skinnedMeshBone; +typedef struct skinnedMesh skinnedMesh; + +skinnedMesh *skinnedMeshCreate(void); +void skinnedMeshAddBone(); + #endif diff --git a/src/osutil.cpp b/src/util.c similarity index 86% rename from src/osutil.cpp rename to src/util.c index 744200d4..760aa7a8 100644 --- a/src/osutil.cpp +++ b/src/util.c @@ -1,5 +1,6 @@ -#include "osutil.h" #include +#include +#include "osutil.h" long long osGetMilliseconds(void) { return (long long)QDateTime::currentMSecsSinceEpoch(); diff --git a/src/osutil.h b/src/util.h similarity index 57% rename from src/osutil.h rename to src/util.h index 5450cec9..cabe0e9d 100644 --- a/src/osutil.h +++ b/src/util.h @@ -5,8 +5,16 @@ extern "C" { #endif +#include + long long osGetMilliseconds(void); +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define unsued(param) (void)param + #ifdef __cplusplus } #endif