2016-11-19 09:28:01 +00:00
|
|
|
#include "graphics/graphics.h"
|
2019-04-05 11:58:29 +00:00
|
|
|
#include "graphics/buffer.h"
|
|
|
|
#include "graphics/canvas.h"
|
|
|
|
#include "graphics/material.h"
|
|
|
|
#include "graphics/mesh.h"
|
2019-06-27 20:44:09 +00:00
|
|
|
#include "graphics/shader.h"
|
2019-04-05 11:58:29 +00:00
|
|
|
#include "graphics/texture.h"
|
2018-01-22 16:28:33 +00:00
|
|
|
#include "data/rasterizer.h"
|
2017-04-13 02:48:17 +00:00
|
|
|
#include "event/event.h"
|
2018-12-25 05:08:06 +00:00
|
|
|
#include "math/math.h"
|
2019-05-20 09:47:33 +00:00
|
|
|
#include "core/maf.h"
|
2019-06-02 07:20:10 +00:00
|
|
|
#include "core/ref.h"
|
2019-06-21 04:43:40 +00:00
|
|
|
#include "core/util.h"
|
2016-07-16 05:39:17 +00:00
|
|
|
#include <stdlib.h>
|
2018-07-04 20:51:35 +00:00
|
|
|
#include <string.h>
|
2016-09-27 07:24:28 +00:00
|
|
|
#include <math.h>
|
2016-09-14 00:02:23 +00:00
|
|
|
|
2019-06-27 20:38:38 +00:00
|
|
|
#define MAX_TRANSFORMS 64
|
|
|
|
#define MAX_BATCHES 16
|
|
|
|
#define MAX_DRAWS 256
|
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
typedef enum {
|
|
|
|
STREAM_VERTEX,
|
|
|
|
STREAM_DRAW_ID,
|
|
|
|
STREAM_INDEX,
|
|
|
|
STREAM_TRANSFORM,
|
|
|
|
STREAM_COLOR,
|
|
|
|
MAX_STREAMS
|
|
|
|
} StreamType;
|
|
|
|
|
2019-06-27 20:38:38 +00:00
|
|
|
typedef enum {
|
|
|
|
BATCH_POINTS,
|
|
|
|
BATCH_LINES,
|
|
|
|
BATCH_TRIANGLES,
|
|
|
|
BATCH_PLANE,
|
|
|
|
BATCH_BOX,
|
|
|
|
BATCH_ARC,
|
|
|
|
BATCH_SPHERE,
|
|
|
|
BATCH_CYLINDER,
|
|
|
|
BATCH_SKYBOX,
|
|
|
|
BATCH_TEXT,
|
|
|
|
BATCH_FILL,
|
|
|
|
BATCH_MESH
|
|
|
|
} BatchType;
|
|
|
|
|
|
|
|
typedef union {
|
|
|
|
struct { DrawStyle style; } triangles;
|
|
|
|
struct { DrawStyle style; } plane;
|
|
|
|
struct { DrawStyle style; } box;
|
|
|
|
struct { DrawStyle style; ArcMode mode; float r1; float r2; int segments; } arc;
|
|
|
|
struct { float r1; float r2; bool capped; int segments; } cylinder;
|
|
|
|
struct { int segments; } sphere;
|
|
|
|
struct { float u; float v; float w; float h; } fill;
|
|
|
|
struct { struct Mesh* object; DrawMode mode; uint32_t rangeStart; uint32_t rangeCount; uint32_t instances; float* pose; } mesh;
|
|
|
|
} BatchParams;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
BatchType type;
|
|
|
|
BatchParams params;
|
|
|
|
DrawMode drawMode;
|
|
|
|
DefaultShader shader;
|
|
|
|
Pipeline* pipeline;
|
|
|
|
struct Material* material;
|
|
|
|
struct Texture* diffuseTexture;
|
|
|
|
struct Texture* environmentMap;
|
|
|
|
mat4 transform;
|
|
|
|
uint32_t vertexCount;
|
|
|
|
uint32_t indexCount;
|
|
|
|
float** vertices;
|
|
|
|
uint16_t** indices;
|
|
|
|
uint16_t* baseVertex;
|
|
|
|
bool instanced;
|
|
|
|
} BatchRequest;
|
|
|
|
|
2019-06-27 08:47:08 +00:00
|
|
|
typedef struct {
|
|
|
|
BatchType type;
|
|
|
|
BatchParams params;
|
|
|
|
DrawMode drawMode;
|
|
|
|
struct Canvas* canvas;
|
|
|
|
struct Shader* shader;
|
|
|
|
Pipeline pipeline;
|
|
|
|
struct Material* material;
|
|
|
|
mat4 transforms;
|
|
|
|
Color* colors;
|
2019-06-27 20:01:04 +00:00
|
|
|
struct { uint32_t start; uint32_t count; } cursors[MAX_STREAMS];
|
2019-06-27 08:47:08 +00:00
|
|
|
uint32_t count;
|
|
|
|
bool instanced;
|
|
|
|
} Batch;
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
bool initialized;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
Camera camera;
|
|
|
|
struct Shader* defaultShaders[MAX_DEFAULT_SHADERS];
|
|
|
|
struct Material* defaultMaterial;
|
|
|
|
struct Font* defaultFont;
|
|
|
|
TextureFilter defaultFilter;
|
|
|
|
float transforms[MAX_TRANSFORMS][16];
|
|
|
|
int transform;
|
|
|
|
Color backgroundColor;
|
|
|
|
struct Canvas* canvas;
|
|
|
|
Color color;
|
|
|
|
struct Font* font;
|
|
|
|
Pipeline pipeline;
|
|
|
|
float pointSize;
|
|
|
|
struct Shader* shader;
|
|
|
|
struct Mesh* mesh;
|
|
|
|
struct Mesh* instancedMesh;
|
|
|
|
struct Buffer* identityBuffer;
|
2019-06-27 20:01:04 +00:00
|
|
|
struct Buffer* buffers[MAX_STREAMS];
|
|
|
|
uint32_t cursors[MAX_STREAMS];
|
2019-06-27 08:47:08 +00:00
|
|
|
Batch batches[MAX_BATCHES];
|
|
|
|
uint8_t batchCount;
|
|
|
|
} state;
|
2016-07-07 07:04:24 +00:00
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
static const uint32_t bufferCount[] = {
|
2019-01-14 20:29:25 +00:00
|
|
|
[STREAM_VERTEX] = (1 << 16) - 1,
|
2019-01-16 03:43:06 +00:00
|
|
|
[STREAM_DRAW_ID] = (1 << 16) - 1,
|
2019-06-27 20:01:04 +00:00
|
|
|
[STREAM_INDEX] = 1 << 16,
|
2019-05-28 02:05:47 +00:00
|
|
|
#if defined(LOVR_WEBGL) || defined(__APPLE__) // Work around bugs where big UBOs don't work
|
2019-03-29 07:49:41 +00:00
|
|
|
[STREAM_TRANSFORM] = MAX_DRAWS,
|
|
|
|
[STREAM_COLOR] = MAX_DRAWS
|
2019-03-14 20:40:19 +00:00
|
|
|
#else
|
2019-06-27 20:01:04 +00:00
|
|
|
[STREAM_TRANSFORM] = MAX_DRAWS * MAX_BATCHES,
|
|
|
|
[STREAM_COLOR] = MAX_DRAWS * MAX_BATCHES
|
2019-03-14 20:40:19 +00:00
|
|
|
#endif
|
2018-12-27 21:50:42 +00:00
|
|
|
};
|
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
static const size_t bufferStride[] = {
|
2018-12-27 21:50:42 +00:00
|
|
|
[STREAM_VERTEX] = 8 * sizeof(float),
|
|
|
|
[STREAM_DRAW_ID] = sizeof(uint8_t),
|
2019-06-27 20:01:04 +00:00
|
|
|
[STREAM_INDEX] = sizeof(uint16_t),
|
2019-03-29 07:49:41 +00:00
|
|
|
[STREAM_TRANSFORM] = 16 * sizeof(float),
|
|
|
|
[STREAM_COLOR] = 4 * sizeof(float)
|
2018-12-27 21:50:42 +00:00
|
|
|
};
|
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
static const BufferType bufferType[] = {
|
2018-12-27 21:50:42 +00:00
|
|
|
[STREAM_VERTEX] = BUFFER_VERTEX,
|
2019-06-27 20:01:04 +00:00
|
|
|
[STREAM_DRAW_ID] = BUFFER_GENERIC,
|
2018-12-27 21:50:42 +00:00
|
|
|
[STREAM_INDEX] = BUFFER_INDEX,
|
2019-03-29 07:49:41 +00:00
|
|
|
[STREAM_TRANSFORM] = BUFFER_UNIFORM,
|
|
|
|
[STREAM_COLOR] = BUFFER_UNIFORM
|
2018-12-27 21:50:42 +00:00
|
|
|
};
|
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
static void gammaCorrectColor(Color* color) {
|
|
|
|
color->r = lovrMathGammaToLinear(color->r);
|
|
|
|
color->g = lovrMathGammaToLinear(color->g);
|
|
|
|
color->b = lovrMathGammaToLinear(color->b);
|
|
|
|
}
|
2019-01-27 22:29:06 +00:00
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
static void onCloseWindow(void) {
|
|
|
|
lovrEventPush((Event) { .type = EVENT_QUIT, .data.quit = { false, 0 } });
|
|
|
|
}
|
2018-12-27 21:50:42 +00:00
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
static void onResizeWindow(int width, int height) {
|
|
|
|
state.width = width;
|
|
|
|
state.height = height;
|
2018-12-11 21:27:59 +00:00
|
|
|
}
|
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
static void* lovrGraphicsMapBuffer(StreamType type, uint32_t count) {
|
|
|
|
lovrAssert(count <= bufferCount[type], "Whoa there! Tried to get %d elements from a buffer that only has %d elements.", count, bufferCount[type]);
|
2019-01-02 23:44:51 +00:00
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
if (state.cursors[type] + count > bufferCount[type]) {
|
2019-01-02 23:44:51 +00:00
|
|
|
lovrGraphicsFlush();
|
2019-06-27 20:01:04 +00:00
|
|
|
lovrBufferDiscard(state.buffers[type]);
|
|
|
|
state.cursors[type] = 0;
|
2019-01-02 23:44:51 +00:00
|
|
|
}
|
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
return lovrBufferMap(state.buffers[type], state.cursors[type] * bufferStride[type]);
|
2019-01-02 23:44:51 +00:00
|
|
|
}
|
|
|
|
|
2016-11-23 05:16:13 +00:00
|
|
|
// Base
|
|
|
|
|
2019-05-04 00:53:33 +00:00
|
|
|
bool lovrGraphicsInit() {
|
2019-06-27 20:26:39 +00:00
|
|
|
return false; // See lovrGraphicsCreateWindow for actual initialization
|
2016-09-28 03:20:08 +00:00
|
|
|
}
|
|
|
|
|
2016-10-04 22:13:57 +00:00
|
|
|
void lovrGraphicsDestroy() {
|
2018-02-23 03:18:36 +00:00
|
|
|
if (!state.initialized) return;
|
2017-08-09 05:39:00 +00:00
|
|
|
lovrGraphicsSetShader(NULL);
|
2017-08-09 00:53:03 +00:00
|
|
|
lovrGraphicsSetFont(NULL);
|
2018-08-27 04:36:07 +00:00
|
|
|
lovrGraphicsSetCanvas(NULL);
|
2018-07-17 06:51:11 +00:00
|
|
|
for (int i = 0; i < MAX_DEFAULT_SHADERS; i++) {
|
2019-04-05 10:41:03 +00:00
|
|
|
lovrRelease(Shader, state.defaultShaders[i]);
|
2018-02-09 05:26:53 +00:00
|
|
|
}
|
2019-06-27 20:01:04 +00:00
|
|
|
for (int i = 0; i < MAX_STREAMS; i++) {
|
2019-04-05 10:41:03 +00:00
|
|
|
lovrRelease(Buffer, state.buffers[i]);
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
2019-04-05 10:41:03 +00:00
|
|
|
lovrRelease(Mesh, state.mesh);
|
|
|
|
lovrRelease(Mesh, state.instancedMesh);
|
|
|
|
lovrRelease(Buffer, state.identityBuffer);
|
|
|
|
lovrRelease(Material, state.defaultMaterial);
|
|
|
|
lovrRelease(Font, state.defaultFont);
|
2018-07-17 10:35:01 +00:00
|
|
|
lovrGpuDestroy();
|
2019-06-27 08:47:08 +00:00
|
|
|
memset(&state, 0, sizeof(state));
|
2016-07-07 07:04:24 +00:00
|
|
|
}
|
|
|
|
|
2016-08-10 06:28:17 +00:00
|
|
|
void lovrGraphicsPresent() {
|
2018-12-11 21:27:59 +00:00
|
|
|
lovrGraphicsFlush();
|
2018-11-16 10:26:56 +00:00
|
|
|
lovrPlatformSwapBuffers();
|
2018-07-17 10:35:01 +00:00
|
|
|
lovrGpuPresent();
|
2016-07-07 07:04:24 +00:00
|
|
|
}
|
|
|
|
|
2019-01-21 04:06:40 +00:00
|
|
|
void lovrGraphicsCreateWindow(WindowFlags* flags) {
|
2018-11-16 10:26:56 +00:00
|
|
|
lovrAssert(!state.initialized, "Window is already created");
|
2019-01-21 04:06:40 +00:00
|
|
|
lovrAssert(lovrPlatformCreateWindow(flags), "Could not create window");
|
2018-11-16 10:26:56 +00:00
|
|
|
lovrPlatformOnWindowClose(onCloseWindow);
|
|
|
|
lovrPlatformOnWindowResize(onResizeWindow);
|
2018-11-16 11:18:08 +00:00
|
|
|
lovrPlatformGetFramebufferSize(&state.width, &state.height);
|
2019-05-04 00:53:33 +00:00
|
|
|
lovrGpuInit(lovrGetProcAddress);
|
2019-06-27 20:01:04 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < MAX_STREAMS; i++) {
|
|
|
|
state.buffers[i] = lovrBufferCreate(bufferCount[i] * bufferStride[i], NULL, bufferType[i], USAGE_STREAM, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The identity buffer is used for autoinstanced meshes and instanced primitives and maps the
|
|
|
|
// instance ID to a vertex attribute. Its contents never change, so they are initialized here.
|
|
|
|
state.identityBuffer = lovrBufferCreate(MAX_DRAWS, NULL, BUFFER_VERTEX, USAGE_STATIC, false);
|
|
|
|
uint8_t* id = lovrBufferMap(state.identityBuffer, 0);
|
|
|
|
for (int i = 0; i < MAX_DRAWS; i++) id[i] = i;
|
|
|
|
lovrBufferFlush(state.identityBuffer, 0, MAX_DRAWS);
|
|
|
|
lovrBufferUnmap(state.identityBuffer);
|
|
|
|
|
|
|
|
Buffer* vertexBuffer = state.buffers[STREAM_VERTEX];
|
|
|
|
size_t stride = bufferStride[STREAM_VERTEX];
|
|
|
|
|
|
|
|
MeshAttribute position = { .buffer = vertexBuffer, .offset = 0, .stride = stride, .type = F32, .components = 3 };
|
|
|
|
MeshAttribute normal = { .buffer = vertexBuffer, .offset = 12, .stride = stride, .type = F32, .components = 3 };
|
|
|
|
MeshAttribute texCoord = { .buffer = vertexBuffer, .offset = 24, .stride = stride, .type = F32, .components = 2 };
|
|
|
|
MeshAttribute drawId = { .buffer = state.buffers[STREAM_DRAW_ID], .type = U8, .components = 1, .integer = true };
|
|
|
|
MeshAttribute identity = { .buffer = state.identityBuffer, .type = U8, .components = 1, .divisor = 1, .integer = true };
|
|
|
|
|
|
|
|
state.mesh = lovrMeshCreate(DRAW_TRIANGLES, NULL, 0);
|
|
|
|
lovrMeshAttachAttribute(state.mesh, "lovrPosition", &position);
|
|
|
|
lovrMeshAttachAttribute(state.mesh, "lovrNormal", &normal);
|
|
|
|
lovrMeshAttachAttribute(state.mesh, "lovrTexCoord", &texCoord);
|
|
|
|
lovrMeshAttachAttribute(state.mesh, "lovrDrawID", &drawId);
|
|
|
|
|
|
|
|
state.instancedMesh = lovrMeshCreate(DRAW_TRIANGLES, NULL, 0);
|
|
|
|
lovrMeshAttachAttribute(state.instancedMesh, "lovrPosition", &position);
|
|
|
|
lovrMeshAttachAttribute(state.instancedMesh, "lovrNormal", &normal);
|
|
|
|
lovrMeshAttachAttribute(state.instancedMesh, "lovrTexCoord", &texCoord);
|
|
|
|
lovrMeshAttachAttribute(state.instancedMesh, "lovrDrawID", &identity);
|
|
|
|
|
2017-08-10 03:02:02 +00:00
|
|
|
lovrGraphicsReset();
|
2018-02-23 03:18:36 +00:00
|
|
|
state.initialized = true;
|
2016-09-24 03:58:56 +00:00
|
|
|
}
|
|
|
|
|
2018-08-29 06:05:09 +00:00
|
|
|
int lovrGraphicsGetWidth() {
|
|
|
|
return state.width;
|
|
|
|
}
|
2018-10-20 19:22:24 +00:00
|
|
|
|
2018-08-29 06:05:09 +00:00
|
|
|
int lovrGraphicsGetHeight() {
|
|
|
|
return state.height;
|
2018-07-17 21:53:21 +00:00
|
|
|
}
|
2017-08-10 03:02:02 +00:00
|
|
|
|
2019-02-05 20:41:11 +00:00
|
|
|
float lovrGraphicsGetPixelDensity() {
|
|
|
|
int width, framebufferWidth;
|
|
|
|
lovrPlatformGetWindowSize(&width, NULL);
|
|
|
|
lovrPlatformGetFramebufferSize(&framebufferWidth, NULL);
|
2019-02-07 19:59:48 +00:00
|
|
|
if (width == 0 || framebufferWidth == 0) {
|
|
|
|
return 0.f;
|
|
|
|
} else {
|
|
|
|
return (float) framebufferWidth / (float) width;
|
|
|
|
}
|
2019-02-05 20:41:11 +00:00
|
|
|
}
|
|
|
|
|
2018-07-17 06:51:11 +00:00
|
|
|
void lovrGraphicsSetCamera(Camera* camera, bool clear) {
|
2018-12-11 21:27:59 +00:00
|
|
|
lovrGraphicsFlush();
|
|
|
|
|
2018-08-29 19:40:19 +00:00
|
|
|
if (state.camera.canvas && (!camera || camera->canvas != state.camera.canvas)) {
|
|
|
|
lovrCanvasResolve(state.camera.canvas);
|
2017-08-10 03:02:02 +00:00
|
|
|
}
|
|
|
|
|
2018-07-17 06:51:11 +00:00
|
|
|
if (!camera) {
|
2019-01-02 23:44:51 +00:00
|
|
|
memset(&state.camera, 0, sizeof(Camera));
|
|
|
|
mat4_identity(state.camera.viewMatrix[0]);
|
|
|
|
mat4_identity(state.camera.viewMatrix[1]);
|
2019-05-13 11:28:57 +00:00
|
|
|
mat4_perspective(state.camera.projection[0], .01f, 100.f, 67.f * (float) M_PI / 180.f, (float) state.width / state.height);
|
|
|
|
mat4_perspective(state.camera.projection[1], .01f, 100.f, 67.f * (float) M_PI / 180.f, (float) state.width / state.height);
|
2018-07-17 06:51:11 +00:00
|
|
|
} else {
|
|
|
|
state.camera = *camera;
|
2017-08-10 03:02:02 +00:00
|
|
|
}
|
|
|
|
|
2018-07-17 06:51:11 +00:00
|
|
|
if (clear) {
|
2019-02-14 05:14:45 +00:00
|
|
|
Color background = state.backgroundColor;
|
|
|
|
gammaCorrectColor(&background);
|
|
|
|
lovrGpuClear(state.camera.canvas, &background, &(float) { 1. }, &(int) { 0 });
|
2017-08-10 03:02:02 +00:00
|
|
|
}
|
2018-07-17 06:51:11 +00:00
|
|
|
}
|
2017-08-10 03:02:02 +00:00
|
|
|
|
2018-12-12 06:10:29 +00:00
|
|
|
Buffer* lovrGraphicsGetIdentityBuffer() {
|
|
|
|
return state.identityBuffer;
|
|
|
|
}
|
|
|
|
|
2016-11-23 05:16:13 +00:00
|
|
|
// State
|
2017-08-10 03:02:02 +00:00
|
|
|
|
2018-07-16 00:05:06 +00:00
|
|
|
void lovrGraphicsReset() {
|
|
|
|
state.transform = 0;
|
2018-07-17 06:51:11 +00:00
|
|
|
lovrGraphicsSetCamera(NULL, false);
|
2019-05-09 03:09:42 +00:00
|
|
|
lovrGraphicsSetAlphaSampling(false);
|
2018-07-17 06:51:11 +00:00
|
|
|
lovrGraphicsSetBackgroundColor((Color) { 0, 0, 0, 1 });
|
2018-07-16 00:05:06 +00:00
|
|
|
lovrGraphicsSetBlendMode(BLEND_ALPHA, BLEND_ALPHA_MULTIPLY);
|
2018-08-27 04:36:07 +00:00
|
|
|
lovrGraphicsSetCanvas(NULL);
|
2018-07-17 06:51:11 +00:00
|
|
|
lovrGraphicsSetColor((Color) { 1, 1, 1, 1 });
|
2018-07-16 00:05:06 +00:00
|
|
|
lovrGraphicsSetCullingEnabled(false);
|
|
|
|
lovrGraphicsSetDefaultFilter((TextureFilter) { .mode = FILTER_TRILINEAR });
|
|
|
|
lovrGraphicsSetDepthTest(COMPARE_LEQUAL, true);
|
|
|
|
lovrGraphicsSetFont(NULL);
|
|
|
|
lovrGraphicsSetLineWidth(1);
|
|
|
|
lovrGraphicsSetPointSize(1);
|
|
|
|
lovrGraphicsSetShader(NULL);
|
|
|
|
lovrGraphicsSetStencilTest(COMPARE_NONE, 0);
|
|
|
|
lovrGraphicsSetWinding(WINDING_COUNTERCLOCKWISE);
|
|
|
|
lovrGraphicsSetWireframe(false);
|
|
|
|
lovrGraphicsOrigin();
|
2017-08-10 03:02:02 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 06:52:33 +00:00
|
|
|
bool lovrGraphicsGetAlphaSampling() {
|
2018-12-25 08:20:59 +00:00
|
|
|
return state.pipeline.alphaSampling;
|
2018-12-12 06:52:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsSetAlphaSampling(bool sample) {
|
2018-12-25 08:20:59 +00:00
|
|
|
state.pipeline.alphaSampling = sample;
|
2018-12-12 06:52:33 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 08:25:56 +00:00
|
|
|
Color lovrGraphicsGetBackgroundColor() {
|
2018-12-25 02:53:23 +00:00
|
|
|
return state.backgroundColor;
|
2016-07-28 02:48:59 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 08:25:56 +00:00
|
|
|
void lovrGraphicsSetBackgroundColor(Color color) {
|
2018-12-25 02:53:23 +00:00
|
|
|
state.backgroundColor = color;
|
2016-07-28 02:48:59 +00:00
|
|
|
}
|
|
|
|
|
2017-03-12 11:03:36 +00:00
|
|
|
void lovrGraphicsGetBlendMode(BlendMode* mode, BlendAlphaMode* alphaMode) {
|
2018-12-25 08:20:59 +00:00
|
|
|
*mode = state.pipeline.blendMode;
|
|
|
|
*alphaMode = state.pipeline.blendAlphaMode;
|
2017-03-12 11:03:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsSetBlendMode(BlendMode mode, BlendAlphaMode alphaMode) {
|
2018-12-25 08:20:59 +00:00
|
|
|
state.pipeline.blendMode = mode;
|
|
|
|
state.pipeline.blendAlphaMode = alphaMode;
|
2017-03-12 11:03:36 +00:00
|
|
|
}
|
|
|
|
|
2018-08-22 04:08:40 +00:00
|
|
|
Canvas* lovrGraphicsGetCanvas() {
|
2018-12-25 02:56:26 +00:00
|
|
|
return state.canvas;
|
2018-08-22 04:08:40 +00:00
|
|
|
}
|
2017-03-12 11:03:36 +00:00
|
|
|
|
2018-08-22 04:08:40 +00:00
|
|
|
void lovrGraphicsSetCanvas(Canvas* canvas) {
|
2018-12-25 03:03:19 +00:00
|
|
|
if (state.canvas && canvas != state.canvas) {
|
|
|
|
lovrCanvasResolve(state.canvas);
|
2018-12-11 21:27:59 +00:00
|
|
|
}
|
2018-12-25 03:03:19 +00:00
|
|
|
|
|
|
|
lovrRetain(canvas);
|
2019-04-05 10:41:03 +00:00
|
|
|
lovrRelease(Canvas, state.canvas);
|
2018-12-25 03:03:19 +00:00
|
|
|
state.canvas = canvas;
|
2017-03-12 11:03:36 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 08:25:56 +00:00
|
|
|
Color lovrGraphicsGetColor() {
|
2018-12-25 02:57:27 +00:00
|
|
|
return state.color;
|
2016-09-29 03:11:58 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 08:25:56 +00:00
|
|
|
void lovrGraphicsSetColor(Color color) {
|
2018-12-25 02:57:27 +00:00
|
|
|
state.color = color;
|
2016-09-29 03:11:58 +00:00
|
|
|
}
|
|
|
|
|
2017-10-31 08:14:09 +00:00
|
|
|
bool lovrGraphicsIsCullingEnabled() {
|
2018-12-25 08:20:59 +00:00
|
|
|
return state.pipeline.culling;
|
2016-09-14 00:02:23 +00:00
|
|
|
}
|
|
|
|
|
2017-10-31 08:14:09 +00:00
|
|
|
void lovrGraphicsSetCullingEnabled(bool culling) {
|
2018-12-25 08:20:59 +00:00
|
|
|
state.pipeline.culling = culling;
|
2016-07-16 02:17:27 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 07:54:33 +00:00
|
|
|
TextureFilter lovrGraphicsGetDefaultFilter() {
|
|
|
|
return state.defaultFilter;
|
2017-02-03 23:16:30 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 07:54:33 +00:00
|
|
|
void lovrGraphicsSetDefaultFilter(TextureFilter filter) {
|
|
|
|
state.defaultFilter = filter;
|
2017-02-03 23:16:30 +00:00
|
|
|
}
|
|
|
|
|
2018-02-09 05:50:47 +00:00
|
|
|
void lovrGraphicsGetDepthTest(CompareMode* mode, bool* write) {
|
2018-12-25 08:20:59 +00:00
|
|
|
*mode = state.pipeline.depthTest;
|
|
|
|
*write = state.pipeline.depthWrite;
|
2017-02-17 08:48:43 +00:00
|
|
|
}
|
|
|
|
|
2018-07-05 03:11:52 +00:00
|
|
|
void lovrGraphicsSetDepthTest(CompareMode mode, bool write) {
|
2018-12-25 08:20:59 +00:00
|
|
|
state.pipeline.depthTest = mode;
|
|
|
|
state.pipeline.depthWrite = write;
|
2016-11-27 02:58:58 +00:00
|
|
|
}
|
|
|
|
|
2017-08-08 08:36:29 +00:00
|
|
|
Font* lovrGraphicsGetFont() {
|
2018-12-25 02:58:21 +00:00
|
|
|
if (!state.font) {
|
2017-08-09 06:49:17 +00:00
|
|
|
if (!state.defaultFont) {
|
2018-01-22 16:28:33 +00:00
|
|
|
Rasterizer* rasterizer = lovrRasterizerCreate(NULL, 32);
|
|
|
|
state.defaultFont = lovrFontCreate(rasterizer);
|
2019-04-05 10:41:03 +00:00
|
|
|
lovrRelease(Rasterizer, rasterizer);
|
2017-08-09 06:49:17 +00:00
|
|
|
}
|
|
|
|
|
2017-08-08 08:36:29 +00:00
|
|
|
lovrGraphicsSetFont(state.defaultFont);
|
|
|
|
}
|
|
|
|
|
2018-12-25 02:58:21 +00:00
|
|
|
return state.font;
|
2017-08-08 08:36:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsSetFont(Font* font) {
|
2018-12-25 03:03:19 +00:00
|
|
|
lovrRetain(font);
|
2019-04-05 10:41:03 +00:00
|
|
|
lovrRelease(Font, state.font);
|
2018-12-25 03:03:19 +00:00
|
|
|
state.font = font;
|
2017-08-08 08:36:29 +00:00
|
|
|
}
|
|
|
|
|
2016-10-01 20:48:31 +00:00
|
|
|
float lovrGraphicsGetLineWidth() {
|
2018-12-25 08:20:59 +00:00
|
|
|
return state.pipeline.lineWidth;
|
2016-10-01 20:48:31 +00:00
|
|
|
}
|
|
|
|
|
2018-12-25 03:19:08 +00:00
|
|
|
void lovrGraphicsSetLineWidth(uint8_t width) {
|
2018-12-25 08:20:59 +00:00
|
|
|
state.pipeline.lineWidth = width;
|
2016-10-01 20:48:31 +00:00
|
|
|
}
|
|
|
|
|
2016-11-13 01:38:49 +00:00
|
|
|
float lovrGraphicsGetPointSize() {
|
2018-12-25 02:59:00 +00:00
|
|
|
return state.pointSize;
|
2016-11-13 01:38:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsSetPointSize(float size) {
|
2018-12-25 03:03:19 +00:00
|
|
|
state.pointSize = size;
|
2016-11-13 01:38:49 +00:00
|
|
|
}
|
|
|
|
|
2017-08-08 08:32:15 +00:00
|
|
|
Shader* lovrGraphicsGetShader() {
|
2018-12-25 03:00:23 +00:00
|
|
|
return state.shader;
|
2017-08-08 08:32:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsSetShader(Shader* shader) {
|
2018-08-09 01:07:48 +00:00
|
|
|
lovrAssert(!shader || lovrShaderGetType(shader) == SHADER_GRAPHICS, "Compute shaders can not be set as the active shader");
|
2018-12-25 03:03:19 +00:00
|
|
|
lovrRetain(shader);
|
2019-04-05 10:41:03 +00:00
|
|
|
lovrRelease(Shader, state.shader);
|
2018-12-25 03:03:19 +00:00
|
|
|
state.shader = shader;
|
2017-08-08 08:32:15 +00:00
|
|
|
}
|
|
|
|
|
2017-12-19 02:37:03 +00:00
|
|
|
void lovrGraphicsGetStencilTest(CompareMode* mode, int* value) {
|
2018-12-25 08:20:59 +00:00
|
|
|
*mode = state.pipeline.stencilMode;
|
|
|
|
*value = state.pipeline.stencilValue;
|
2017-12-19 02:37:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsSetStencilTest(CompareMode mode, int value) {
|
2018-12-25 08:20:59 +00:00
|
|
|
state.pipeline.stencilMode = mode;
|
|
|
|
state.pipeline.stencilValue = value;
|
2017-12-19 02:37:03 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 07:54:33 +00:00
|
|
|
Winding lovrGraphicsGetWinding() {
|
2018-12-25 08:20:59 +00:00
|
|
|
return state.pipeline.winding;
|
2016-10-03 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
2017-08-02 07:54:33 +00:00
|
|
|
void lovrGraphicsSetWinding(Winding winding) {
|
2018-12-25 08:20:59 +00:00
|
|
|
state.pipeline.winding = winding;
|
2016-10-03 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
2017-10-31 08:14:09 +00:00
|
|
|
bool lovrGraphicsIsWireframe() {
|
2018-12-25 08:20:59 +00:00
|
|
|
return state.pipeline.wireframe;
|
2016-10-03 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
2017-10-31 08:14:09 +00:00
|
|
|
void lovrGraphicsSetWireframe(bool wireframe) {
|
2019-01-29 23:05:23 +00:00
|
|
|
#ifdef LOVR_GL
|
2018-12-25 08:20:59 +00:00
|
|
|
state.pipeline.wireframe = wireframe;
|
2017-08-02 07:54:33 +00:00
|
|
|
#endif
|
2016-10-03 19:02:49 +00:00
|
|
|
}
|
|
|
|
|
2016-11-23 05:16:13 +00:00
|
|
|
// Transforms
|
|
|
|
|
2017-08-10 08:05:04 +00:00
|
|
|
void lovrGraphicsPush() {
|
2018-07-17 06:51:11 +00:00
|
|
|
lovrAssert(++state.transform < MAX_TRANSFORMS, "Unbalanced matrix stack (more pushes than pops?)");
|
|
|
|
mat4_init(state.transforms[state.transform], state.transforms[state.transform - 1]);
|
2016-09-21 07:55:53 +00:00
|
|
|
}
|
|
|
|
|
2017-08-10 08:05:04 +00:00
|
|
|
void lovrGraphicsPop() {
|
2018-07-17 06:51:11 +00:00
|
|
|
lovrAssert(--state.transform >= 0, "Unbalanced matrix stack (more pops than pushes?)");
|
2016-09-21 07:55:53 +00:00
|
|
|
}
|
|
|
|
|
2016-09-21 22:26:05 +00:00
|
|
|
void lovrGraphicsOrigin() {
|
2018-03-05 07:06:34 +00:00
|
|
|
mat4_identity(state.transforms[state.transform]);
|
2016-09-21 22:26:05 +00:00
|
|
|
}
|
|
|
|
|
2018-11-27 23:03:05 +00:00
|
|
|
void lovrGraphicsTranslate(vec3 translation) {
|
|
|
|
mat4_translate(state.transforms[state.transform], translation[0], translation[1], translation[2]);
|
2016-09-23 04:53:17 +00:00
|
|
|
}
|
|
|
|
|
2018-11-27 23:03:05 +00:00
|
|
|
void lovrGraphicsRotate(quat rotation) {
|
|
|
|
mat4_rotateQuat(state.transforms[state.transform], rotation);
|
2016-09-23 04:53:17 +00:00
|
|
|
}
|
|
|
|
|
2018-11-27 23:03:05 +00:00
|
|
|
void lovrGraphicsScale(vec3 scale) {
|
|
|
|
mat4_scale(state.transforms[state.transform], scale[0], scale[1], scale[2]);
|
2016-09-23 04:53:17 +00:00
|
|
|
}
|
|
|
|
|
2018-03-05 07:06:34 +00:00
|
|
|
void lovrGraphicsMatrixTransform(mat4 transform) {
|
|
|
|
mat4_multiply(state.transforms[state.transform], transform);
|
2016-09-29 07:21:38 +00:00
|
|
|
}
|
|
|
|
|
2018-10-27 00:17:18 +00:00
|
|
|
void lovrGraphicsSetProjection(mat4 projection) {
|
|
|
|
mat4_set(state.camera.projection[0], projection);
|
|
|
|
mat4_set(state.camera.projection[1], projection);
|
|
|
|
}
|
|
|
|
|
2018-08-09 01:08:28 +00:00
|
|
|
// Rendering
|
2016-09-29 05:10:03 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsClear(Color* color, float* depth, int* stencil) {
|
2018-12-25 05:08:06 +00:00
|
|
|
if (color) gammaCorrectColor(color);
|
2019-01-02 23:44:51 +00:00
|
|
|
if (color || depth || stencil) lovrGraphicsFlush();
|
2018-12-25 02:56:26 +00:00
|
|
|
lovrGpuClear(state.canvas ? state.canvas : state.camera.canvas, color, depth, stencil);
|
2017-02-03 23:16:30 +00:00
|
|
|
}
|
|
|
|
|
2018-10-26 16:14:57 +00:00
|
|
|
void lovrGraphicsDiscard(bool color, bool depth, bool stencil) {
|
2019-01-02 23:44:51 +00:00
|
|
|
if (color || depth || stencil) lovrGraphicsFlush();
|
2018-12-25 02:56:26 +00:00
|
|
|
lovrGpuDiscard(state.canvas ? state.canvas : state.camera.canvas, color, depth, stencil);
|
2018-10-26 16:14:57 +00:00
|
|
|
}
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsBatch(BatchRequest* req) {
|
2018-07-16 00:05:06 +00:00
|
|
|
|
2018-12-11 05:30:55 +00:00
|
|
|
// Resolve objects
|
2018-12-25 02:56:26 +00:00
|
|
|
Canvas* canvas = state.canvas ? state.canvas : state.camera.canvas;
|
2019-05-23 01:00:02 +00:00
|
|
|
Shader* shader = state.shader ? state.shader : (state.defaultShaders[req->shader] ? state.defaultShaders[req->shader] : (state.defaultShaders[req->shader] = lovrShaderCreateDefault(req->shader, NULL, 0)));
|
2018-12-27 21:50:42 +00:00
|
|
|
Pipeline* pipeline = req->pipeline ? req->pipeline : &state.pipeline;
|
|
|
|
Material* material = req->material ? req->material : (state.defaultMaterial ? state.defaultMaterial : (state.defaultMaterial = lovrMaterialCreate()));
|
2018-08-31 13:03:35 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
if (!req->material) {
|
|
|
|
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, req->diffuseTexture);
|
|
|
|
lovrMaterialSetTexture(material, TEXTURE_ENVIRONMENT_MAP, req->environmentMap);
|
2018-07-16 00:05:06 +00:00
|
|
|
}
|
|
|
|
|
2019-05-12 21:11:16 +00:00
|
|
|
if (lovrShaderHasUniform(shader, "lovrPose")) {
|
|
|
|
if (req->type == BATCH_MESH && req->params.mesh.pose) {
|
|
|
|
lovrShaderSetMatrices(shader, "lovrPose", req->params.mesh.pose, 0, MAX_BONES * 16);
|
|
|
|
} else {
|
|
|
|
lovrShaderSetMatrices(shader, "lovrPose", (float[]) MAT4_IDENTITY, 0, 16);
|
|
|
|
}
|
2018-08-31 13:03:35 +00:00
|
|
|
}
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
// Try to find an existing batch to use
|
|
|
|
Batch* batch = NULL;
|
2019-01-03 09:21:38 +00:00
|
|
|
if (req->type != BATCH_MESH || req->params.mesh.instances == 1) {
|
2018-12-27 21:50:42 +00:00
|
|
|
for (int i = state.batchCount - 1; i >= 0; i--) {
|
|
|
|
Batch* b = &state.batches[i];
|
2019-06-27 20:26:39 +00:00
|
|
|
BatchParams* p = &b->params;
|
|
|
|
BatchParams* q = &req->params;
|
2018-12-27 21:50:42 +00:00
|
|
|
|
2019-06-27 20:26:39 +00:00
|
|
|
if (b->type != req->type) { goto next; }
|
2019-05-12 20:58:37 +00:00
|
|
|
if (b->count >= MAX_DRAWS) { goto next; }
|
2019-06-27 20:26:39 +00:00
|
|
|
if (b->canvas != canvas) { goto next; }
|
|
|
|
if (b->shader != shader) { goto next; }
|
|
|
|
if (b->material != material) { goto next; }
|
|
|
|
if (memcmp(&b->pipeline, pipeline, sizeof(Pipeline))) { goto next; }
|
|
|
|
|
|
|
|
// Sorry, this is an ugly manual memcmp between the different BatchParam unions
|
|
|
|
if (b->type == BATCH_TRIANGLES && p->triangles.style != q->triangles.style) { goto next; }
|
|
|
|
else if (b->type == BATCH_PLANE && p->plane.style != q->plane.style) { goto next; }
|
|
|
|
else if (b->type == BATCH_BOX && p->box.style != q->box.style) { goto next; }
|
|
|
|
else if (b->type == BATCH_ARC && (p->arc.style != q->arc.style || p->arc.mode != q->arc.mode || p->arc.r1 != q->arc.r1 || p->arc.r2 != q->arc.r2 || p->arc.segments != q->arc.segments)) { goto next; }
|
|
|
|
else if (b->type == BATCH_SPHERE && p->sphere.segments != q->sphere.segments) { goto next; }
|
|
|
|
else if (b->type == BATCH_CYLINDER && (p->cylinder.r1 != q->cylinder.r1 || p->cylinder.r2 != q->cylinder.r2 || p->cylinder.capped != q->cylinder.capped || p->cylinder.segments != q->cylinder.segments)) { goto next; }
|
|
|
|
else if (b->type == BATCH_MESH && (p->mesh.object != q->mesh.object || p->mesh.mode != q->mesh.mode || p->mesh.rangeStart != q->mesh.rangeStart || p->mesh.rangeCount != q->mesh.rangeCount)) { goto next; }
|
|
|
|
|
|
|
|
// If we made it here, then we found a compatible batch to use
|
|
|
|
batch = b;
|
|
|
|
break;
|
2018-12-27 21:50:42 +00:00
|
|
|
|
2019-06-27 20:26:39 +00:00
|
|
|
next:
|
2019-01-04 09:35:29 +00:00
|
|
|
// Draws can't be reordered when blending is on, depth test is off, or either of the batches
|
|
|
|
// are streaming their vertices (since buffers are append-only)
|
2019-01-02 23:44:51 +00:00
|
|
|
if (b->pipeline.blendMode != BLEND_NONE || pipeline->blendMode != BLEND_NONE) { break; }
|
|
|
|
if (b->pipeline.depthTest == COMPARE_NONE || pipeline->depthTest == COMPARE_NONE) { break; }
|
2019-05-12 20:58:37 +00:00
|
|
|
if (!req->instanced) { break; }
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-11 21:27:59 +00:00
|
|
|
|
2019-04-05 08:28:17 +00:00
|
|
|
if (req->vertexCount > 0 && (!req->instanced || !batch)) {
|
2019-03-28 05:57:32 +00:00
|
|
|
*(req->vertices) = lovrGraphicsMapBuffer(STREAM_VERTEX, req->vertexCount);
|
|
|
|
uint8_t* ids = lovrGraphicsMapBuffer(STREAM_DRAW_ID, req->vertexCount);
|
2019-03-29 07:49:41 +00:00
|
|
|
memset(ids, batch ? batch->count : 0, req->vertexCount * sizeof(uint8_t));
|
2018-12-11 21:27:59 +00:00
|
|
|
|
2019-03-28 05:57:32 +00:00
|
|
|
if (req->indexCount > 0) {
|
|
|
|
*(req->indices) = lovrGraphicsMapBuffer(STREAM_INDEX, req->indexCount);
|
|
|
|
*(req->baseVertex) = state.cursors[STREAM_VERTEX];
|
2018-12-11 21:27:59 +00:00
|
|
|
}
|
2019-04-14 20:46:45 +00:00
|
|
|
|
|
|
|
// The buffer mapping here could have triggered a flush, so if we were hoping to batch with
|
|
|
|
// something but the batch count is zero now, we just start a new batch. Maybe there's a better
|
|
|
|
// way to detect this.
|
|
|
|
if (batch && state.batchCount == 0) {
|
|
|
|
batch = NULL;
|
|
|
|
}
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
2017-11-21 05:16:16 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
// Start a new batch
|
|
|
|
if (!batch) {
|
2019-05-12 21:04:45 +00:00
|
|
|
if (state.batchCount >= MAX_BATCHES) {
|
|
|
|
lovrGraphicsFlush();
|
|
|
|
}
|
|
|
|
|
2019-03-29 07:49:41 +00:00
|
|
|
float* transforms = lovrGraphicsMapBuffer(STREAM_TRANSFORM, MAX_DRAWS);
|
|
|
|
Color* colors = lovrGraphicsMapBuffer(STREAM_COLOR, MAX_DRAWS);
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
batch = &state.batches[state.batchCount++];
|
|
|
|
*batch = (Batch) {
|
|
|
|
.type = req->type,
|
|
|
|
.params = req->params,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = req->drawMode,
|
2018-12-27 21:50:42 +00:00
|
|
|
.canvas = canvas,
|
|
|
|
.shader = shader,
|
|
|
|
.pipeline = *pipeline,
|
|
|
|
.material = material,
|
2019-03-29 07:49:41 +00:00
|
|
|
.transforms = transforms,
|
|
|
|
.colors = colors,
|
2019-03-28 05:57:32 +00:00
|
|
|
.instanced = req->instanced
|
2018-12-27 21:50:42 +00:00
|
|
|
};
|
|
|
|
|
2019-06-27 20:01:04 +00:00
|
|
|
for (int i = 0; i < MAX_STREAMS; i++) {
|
2019-03-29 07:49:41 +00:00
|
|
|
batch->cursors[i].start = state.cursors[i];
|
|
|
|
}
|
2018-08-31 13:03:35 +00:00
|
|
|
|
2019-04-11 15:35:55 +00:00
|
|
|
batch->cursors[STREAM_TRANSFORM].count = MAX_DRAWS;
|
|
|
|
batch->cursors[STREAM_COLOR].count = MAX_DRAWS;
|
2019-03-29 07:49:41 +00:00
|
|
|
state.cursors[STREAM_TRANSFORM] += MAX_DRAWS;
|
|
|
|
state.cursors[STREAM_COLOR] += MAX_DRAWS;
|
|
|
|
}
|
2019-03-28 09:21:02 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
// Transform
|
|
|
|
if (req->transform) {
|
|
|
|
float transform[16];
|
|
|
|
mat4_multiply(mat4_init(transform, state.transforms[state.transform]), req->transform);
|
2019-03-29 07:49:41 +00:00
|
|
|
memcpy(&batch->transforms[16 * batch->count], transform, 16 * sizeof(float));
|
2018-12-27 21:50:42 +00:00
|
|
|
} else {
|
2019-03-29 07:49:41 +00:00
|
|
|
memcpy(&batch->transforms[16 * batch->count], state.transforms[state.transform], 16 * sizeof(float));
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
2018-08-31 13:03:35 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
// Color
|
|
|
|
Color color = state.color;
|
|
|
|
gammaCorrectColor(&color);
|
2019-03-29 07:49:41 +00:00
|
|
|
batch->colors[batch->count] = color;
|
2018-12-27 21:50:42 +00:00
|
|
|
|
2019-03-29 07:49:41 +00:00
|
|
|
if (!req->instanced || batch->count == 0) {
|
2019-03-28 09:21:02 +00:00
|
|
|
batch->cursors[STREAM_VERTEX].count += req->vertexCount;
|
|
|
|
batch->cursors[STREAM_DRAW_ID].count += req->vertexCount;
|
2019-06-27 20:26:39 +00:00
|
|
|
batch->cursors[STREAM_INDEX].count += req->indexCount;
|
2018-12-11 05:30:55 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
state.cursors[STREAM_VERTEX] += req->vertexCount;
|
2019-03-28 09:21:02 +00:00
|
|
|
state.cursors[STREAM_DRAW_ID] += req->vertexCount;
|
2019-06-27 20:26:39 +00:00
|
|
|
state.cursors[STREAM_INDEX] += req->indexCount;
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 07:49:41 +00:00
|
|
|
batch->count++;
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
2018-12-11 21:27:59 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsFlush() {
|
|
|
|
if (state.batchCount == 0) {
|
|
|
|
return;
|
2018-08-31 13:03:35 +00:00
|
|
|
}
|
2018-12-11 05:30:55 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
// Prevent infinite flushing >_>
|
|
|
|
int batchCount = state.batchCount;
|
|
|
|
state.batchCount = 0;
|
|
|
|
|
2019-05-14 03:35:21 +00:00
|
|
|
// Flush buffers
|
|
|
|
Batch* firstBatch = &state.batches[0];
|
|
|
|
Batch* lastBatch = &state.batches[batchCount - 1];
|
2019-06-27 20:01:04 +00:00
|
|
|
for (int i = 0; i < MAX_STREAMS; i++) {
|
|
|
|
size_t offset = firstBatch->cursors[i].start * bufferStride[i];
|
|
|
|
size_t size = (lastBatch->cursors[i].start + lastBatch->cursors[i].count - firstBatch->cursors[i].start) * bufferStride[i];
|
2019-05-14 03:35:21 +00:00
|
|
|
lovrBufferFlush(state.buffers[i], offset, size);
|
|
|
|
lovrBufferUnmap(state.buffers[i]);
|
|
|
|
}
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
for (int b = 0; b < batchCount; b++) {
|
|
|
|
Batch* batch = &state.batches[b];
|
|
|
|
BatchParams* params = &batch->params;
|
2019-03-28 05:57:32 +00:00
|
|
|
Mesh* mesh = batch->type == BATCH_MESH ? params->mesh.object : (batch->instanced ? state.instancedMesh : state.mesh);
|
2019-04-11 15:35:55 +00:00
|
|
|
int instances = batch->instanced ? batch->count : 1;
|
2018-12-11 05:30:55 +00:00
|
|
|
|
2019-03-29 07:49:41 +00:00
|
|
|
// Bind UBOs
|
2019-06-27 20:01:04 +00:00
|
|
|
lovrShaderSetBlock(batch->shader, "lovrModelBlock", state.buffers[STREAM_TRANSFORM], batch->cursors[STREAM_TRANSFORM].start * bufferStride[STREAM_TRANSFORM], MAX_DRAWS * bufferStride[STREAM_TRANSFORM], ACCESS_READ);
|
|
|
|
lovrShaderSetBlock(batch->shader, "lovrColorBlock", state.buffers[STREAM_COLOR], batch->cursors[STREAM_COLOR].start * bufferStride[STREAM_COLOR], MAX_DRAWS * bufferStride[STREAM_COLOR], ACCESS_READ);
|
2018-12-27 21:50:42 +00:00
|
|
|
|
2019-01-04 09:35:29 +00:00
|
|
|
// Uniforms
|
|
|
|
lovrMaterialBind(batch->material, batch->shader);
|
|
|
|
lovrShaderSetMatrices(batch->shader, "lovrViews", state.camera.viewMatrix[0], 0, 32);
|
|
|
|
lovrShaderSetMatrices(batch->shader, "lovrProjections", state.camera.projection[0], 0, 32);
|
|
|
|
|
2019-03-28 05:57:32 +00:00
|
|
|
if (batch->drawMode == DRAW_POINTS) {
|
2019-01-04 09:35:29 +00:00
|
|
|
lovrShaderSetFloats(batch->shader, "lovrPointSize", &state.pointSize, 0, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t rangeStart, rangeCount;
|
|
|
|
if (batch->type == BATCH_MESH) {
|
2019-03-28 05:57:32 +00:00
|
|
|
rangeStart = params->mesh.rangeStart;
|
|
|
|
rangeCount = params->mesh.rangeCount;
|
|
|
|
if (params->mesh.instances > 1) {
|
|
|
|
lovrMeshSetAttributeEnabled(mesh, "lovrDrawID", false);
|
|
|
|
instances = params->mesh.instances;
|
|
|
|
} else {
|
|
|
|
lovrMeshSetAttributeEnabled(mesh, "lovrDrawID", true);
|
2019-03-29 07:49:41 +00:00
|
|
|
instances = batch->count;
|
2019-03-28 05:57:32 +00:00
|
|
|
}
|
2019-01-04 09:35:29 +00:00
|
|
|
} else {
|
2019-03-28 09:21:02 +00:00
|
|
|
bool indexed = batch->cursors[STREAM_INDEX].count > 0;
|
|
|
|
rangeStart = batch->cursors[indexed ? STREAM_INDEX : STREAM_VERTEX].start;
|
|
|
|
rangeCount = batch->cursors[indexed ? STREAM_INDEX : STREAM_VERTEX].count;
|
|
|
|
if (indexed) {
|
2019-06-27 20:01:04 +00:00
|
|
|
lovrMeshSetIndexBuffer(mesh, state.buffers[STREAM_INDEX], bufferCount[STREAM_INDEX], sizeof(uint16_t), 0);
|
2019-01-04 09:35:29 +00:00
|
|
|
} else {
|
2019-01-17 22:14:13 +00:00
|
|
|
lovrMeshSetIndexBuffer(mesh, NULL, 0, 0, 0);
|
2019-01-04 09:35:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
// Draw!
|
|
|
|
lovrGpuDraw(&(DrawCommand) {
|
|
|
|
.mesh = mesh,
|
|
|
|
.shader = batch->shader,
|
|
|
|
.canvas = batch->canvas,
|
|
|
|
.pipeline = batch->pipeline,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = batch->drawMode,
|
2018-12-27 21:50:42 +00:00
|
|
|
.instances = instances,
|
|
|
|
.rangeStart = rangeStart,
|
|
|
|
.rangeCount = rangeCount,
|
|
|
|
.width = batch->canvas ? lovrCanvasGetWidth(batch->canvas) : state.width,
|
|
|
|
.height = batch->canvas ? lovrCanvasGetHeight(batch->canvas) : state.height,
|
2019-06-25 08:06:55 +00:00
|
|
|
.stereo = batch->canvas ? lovrCanvasIsStereo(batch->canvas) : state.camera.stereo
|
2018-12-27 21:50:42 +00:00
|
|
|
});
|
2016-10-04 03:56:45 +00:00
|
|
|
}
|
2017-01-13 09:59:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsFlushCanvas(Canvas* canvas) {
|
|
|
|
for (int i = state.batchCount - 1; i >= 0; i--) {
|
|
|
|
if (state.batches[i].canvas == canvas) {
|
|
|
|
lovrGraphicsFlush();
|
|
|
|
return;
|
|
|
|
}
|
2016-10-01 19:10:38 +00:00
|
|
|
}
|
2016-09-30 06:18:51 +00:00
|
|
|
}
|
2016-10-24 22:02:23 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsFlushShader(Shader* shader) {
|
|
|
|
for (int i = state.batchCount - 1; i >= 0; i--) {
|
|
|
|
if (state.batches[i].shader == shader) {
|
|
|
|
lovrGraphicsFlush();
|
|
|
|
return;
|
|
|
|
}
|
2017-11-22 19:32:30 +00:00
|
|
|
}
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
2017-11-22 19:32:30 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsFlushMaterial(Material* material) {
|
|
|
|
for (int i = state.batchCount - 1; i >= 0; i--) {
|
|
|
|
if (state.batches[i].material == material) {
|
|
|
|
lovrGraphicsFlush();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-22 19:32:30 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsFlushMesh(Mesh* mesh) {
|
|
|
|
for (int i = state.batchCount - 1; i >= 0; i--) {
|
|
|
|
if (state.batches[i].type == BATCH_MESH && state.batches[i].params.mesh.object == mesh) {
|
|
|
|
lovrGraphicsFlush();
|
|
|
|
return;
|
|
|
|
}
|
2017-11-22 19:32:30 +00:00
|
|
|
}
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
2017-11-22 19:32:30 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsPoints(uint32_t count, float** vertices) {
|
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_POINTS,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = DRAW_POINTS,
|
2018-12-27 21:50:42 +00:00
|
|
|
.vertexCount = count,
|
|
|
|
.vertices = vertices
|
|
|
|
});
|
|
|
|
}
|
2017-11-22 19:32:30 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
void lovrGraphicsLine(uint32_t count, float** vertices) {
|
2019-01-03 09:21:38 +00:00
|
|
|
uint32_t indexCount = count + 1;
|
|
|
|
uint16_t* indices;
|
|
|
|
uint16_t baseVertex;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_LINES,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = DRAW_LINE_STRIP,
|
2018-12-27 21:50:42 +00:00
|
|
|
.vertexCount = count,
|
2019-01-03 09:21:38 +00:00
|
|
|
.vertices = vertices,
|
|
|
|
.indexCount = indexCount,
|
|
|
|
.indices = &indices,
|
|
|
|
.baseVertex = &baseVertex
|
2018-12-27 21:50:42 +00:00
|
|
|
});
|
2019-01-03 09:21:38 +00:00
|
|
|
|
|
|
|
indices[0] = 0xffff;
|
|
|
|
for (uint32_t i = 1; i < indexCount; i++) {
|
|
|
|
indices[i] = baseVertex + i - 1;
|
|
|
|
}
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsTriangle(DrawStyle style, Material* material, uint32_t count, float** vertices) {
|
2019-01-03 09:21:38 +00:00
|
|
|
uint32_t indexCount = style == STYLE_LINE ? (4 * count / 3) : 0;
|
|
|
|
uint16_t* indices;
|
|
|
|
uint16_t baseVertex;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_TRIANGLES,
|
|
|
|
.params.triangles.style = style,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = style == STYLE_LINE ? DRAW_LINE_LOOP : DRAW_TRIANGLES,
|
2018-12-27 21:50:42 +00:00
|
|
|
.material = material,
|
|
|
|
.vertexCount = count,
|
2019-01-03 09:21:38 +00:00
|
|
|
.vertices = vertices,
|
|
|
|
.indexCount = indexCount,
|
|
|
|
.indices = &indices,
|
|
|
|
.baseVertex = &baseVertex
|
2018-12-27 21:50:42 +00:00
|
|
|
});
|
2019-01-03 09:21:38 +00:00
|
|
|
|
|
|
|
if (style == STYLE_LINE) {
|
2019-01-04 01:32:30 +00:00
|
|
|
for (uint32_t i = 0; i < count; i += 3) {
|
2019-01-03 09:21:38 +00:00
|
|
|
*indices++ = 0xffff;
|
|
|
|
*indices++ = baseVertex + i + 0;
|
|
|
|
*indices++ = baseVertex + i + 1;
|
|
|
|
*indices++ = baseVertex + i + 2;
|
|
|
|
}
|
|
|
|
}
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
|
|
|
|
2019-04-05 12:34:28 +00:00
|
|
|
void lovrGraphicsPlane(DrawStyle style, Material* material, mat4 transform, float u, float v, float w, float h) {
|
2019-03-28 05:57:32 +00:00
|
|
|
float* vertices = NULL;
|
|
|
|
uint16_t* indices = NULL;
|
|
|
|
uint16_t baseVertex;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_PLANE,
|
|
|
|
.params.plane.style = style,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = style == STYLE_LINE ? DRAW_LINE_LOOP : DRAW_TRIANGLES,
|
2018-12-27 21:50:42 +00:00
|
|
|
.material = material,
|
2019-03-28 05:57:32 +00:00
|
|
|
.transform = transform,
|
|
|
|
.vertexCount = 4,
|
|
|
|
.indexCount = style == STYLE_LINE ? 5 : 6,
|
|
|
|
.vertices = &vertices,
|
|
|
|
.indices = &indices,
|
2019-04-05 12:34:28 +00:00
|
|
|
.baseVertex = &baseVertex
|
2018-12-27 21:50:42 +00:00
|
|
|
});
|
2019-03-28 05:57:32 +00:00
|
|
|
|
2019-04-05 12:34:28 +00:00
|
|
|
if (style == STYLE_LINE) {
|
|
|
|
static float vertexData[] = {
|
|
|
|
-.5f, .5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
.5f, .5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
.5f, -.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
-.5f, -.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f
|
|
|
|
};
|
2019-03-28 05:57:32 +00:00
|
|
|
|
2019-04-05 12:34:28 +00:00
|
|
|
memcpy(vertices, vertexData, sizeof(vertexData));
|
2019-03-28 05:57:32 +00:00
|
|
|
|
2019-04-05 12:34:28 +00:00
|
|
|
indices[0] = 0xffff;
|
|
|
|
indices[1] = 0 + baseVertex;
|
|
|
|
indices[2] = 1 + baseVertex;
|
|
|
|
indices[3] = 2 + baseVertex;
|
|
|
|
indices[4] = 3 + baseVertex;
|
|
|
|
} else {
|
|
|
|
float vertexData[] = {
|
2019-04-21 00:02:48 +00:00
|
|
|
-.5f, .5f, 0.f, 0.f, 0.f, -1.f, u, v + h,
|
|
|
|
-.5f, -.5f, 0.f, 0.f, 0.f, -1.f, u, v,
|
|
|
|
.5f, .5f, 0.f, 0.f, 0.f, -1.f, u + w, v + h,
|
|
|
|
.5f, -.5f, 0.f, 0.f, 0.f, -1.f, u + w, v
|
2019-04-05 12:34:28 +00:00
|
|
|
};
|
2019-03-28 05:57:32 +00:00
|
|
|
|
2019-04-05 12:34:28 +00:00
|
|
|
memcpy(vertices, vertexData, sizeof(vertexData));
|
|
|
|
|
|
|
|
static uint16_t indexData[] = { 0, 1, 2, 2, 1, 3 };
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sizeof(indexData) / sizeof(indexData[0]); i++) {
|
|
|
|
indices[i] = indexData[i] + baseVertex;
|
2019-03-28 05:57:32 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsBox(DrawStyle style, Material* material, mat4 transform) {
|
2019-03-28 05:57:32 +00:00
|
|
|
float* vertices = NULL;
|
|
|
|
uint16_t* indices = NULL;
|
|
|
|
uint16_t baseVertex;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_BOX,
|
|
|
|
.params.box.style = style,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = style == STYLE_LINE ? DRAW_LINES : DRAW_TRIANGLES,
|
2018-12-27 21:50:42 +00:00
|
|
|
.material = material,
|
2019-03-28 05:57:32 +00:00
|
|
|
.transform = transform,
|
|
|
|
.vertexCount = style == STYLE_LINE ? 8 : 24,
|
|
|
|
.indexCount = style == STYLE_LINE ? 24 : 36,
|
|
|
|
.vertices = &vertices,
|
|
|
|
.indices = &indices,
|
|
|
|
.baseVertex = &baseVertex,
|
|
|
|
.instanced = true
|
2018-12-27 21:50:42 +00:00
|
|
|
});
|
2019-03-28 05:57:32 +00:00
|
|
|
|
|
|
|
if (vertices) {
|
|
|
|
if (style == STYLE_LINE) {
|
|
|
|
static float vertexData[] = {
|
2019-04-21 00:02:48 +00:00
|
|
|
-.5f, .5f, -.5f, 0.f, 0.f, 0.f, 0.f, 0.f, // Front
|
|
|
|
.5f, .5f, -.5f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
.5f, -.5f, -.5f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
-.5f, -.5f, -.5f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
-.5f, .5f, .5f, 0.f, 0.f, 0.f, 0.f, 0.f, // Back
|
|
|
|
.5f, .5f, .5f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
.5f, -.5f, .5f, 0.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
-.5f, -.5f, .5f, 0.f, 0.f, 0.f, 0.f, 0.f
|
2019-03-28 05:57:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
memcpy(vertices, vertexData, sizeof(vertexData));
|
|
|
|
|
|
|
|
static uint16_t indexData[] = {
|
|
|
|
0, 1, 1, 2, 2, 3, 3, 0, // Front
|
|
|
|
4, 5, 5, 6, 6, 7, 7, 4, // Back
|
|
|
|
0, 4, 1, 5, 2, 6, 3, 7 // Connections
|
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sizeof(indexData) / sizeof(indexData[0]); i++) {
|
|
|
|
indices[i] = indexData[i] + baseVertex;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
static float vertexData[] = {
|
2019-04-21 00:02:48 +00:00
|
|
|
-.5f, -.5f, -.5f, 0.f, 0.f, -1.f, 0.f, 0.f, // Front
|
|
|
|
-.5f, .5f, -.5f, 0.f, 0.f, -1.f, 0.f, 1.f,
|
|
|
|
.5f, -.5f, -.5f, 0.f, 0.f, -1.f, 1.f, 0.f,
|
|
|
|
.5f, .5f, -.5f, 0.f, 0.f, -1.f, 1.f, 1.f,
|
|
|
|
.5f, .5f, -.5f, 1.f, 0.f, 0.f, 0.f, 1.f, // Right
|
|
|
|
.5f, .5f, .5f, 1.f, 0.f, 0.f, 1.f, 1.f,
|
|
|
|
.5f, -.5f, -.5f, 1.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
.5f, -.5f, .5f, 1.f, 0.f, 0.f, 1.f, 0.f,
|
|
|
|
.5f, -.5f, .5f, 0.f, 0.f, 1.f, 0.f, 0.f, // Back
|
|
|
|
.5f, .5f, .5f, 0.f, 0.f, 1.f, 0.f, 1.f,
|
|
|
|
-.5f, -.5f, .5f, 0.f, 0.f, 1.f, 1.f, 0.f,
|
|
|
|
-.5f, .5f, .5f, 0.f, 0.f, 1.f, 1.f, 1.f,
|
|
|
|
-.5f, .5f, .5f, -1.f, 0.f, 0.f, 0.f, 1.f, // Left
|
|
|
|
-.5f, .5f, -.5f, -1.f, 0.f, 0.f, 1.f, 1.f,
|
|
|
|
-.5f, -.5f, .5f, -1.f, 0.f, 0.f, 0.f, 0.f,
|
|
|
|
-.5f, -.5f, -.5f, -1.f, 0.f, 0.f, 1.f, 0.f,
|
|
|
|
-.5f, -.5f, -.5f, 0.f, -1.f, 0.f, 0.f, 0.f, // Bottom
|
|
|
|
.5f, -.5f, -.5f, 0.f, -1.f, 0.f, 1.f, 0.f,
|
|
|
|
-.5f, -.5f, .5f, 0.f, -1.f, 0.f, 0.f, 1.f,
|
|
|
|
.5f, -.5f, .5f, 0.f, -1.f, 0.f, 1.f, 1.f,
|
|
|
|
-.5f, .5f, -.5f, 0.f, 1.f, 0.f, 0.f, 1.f, // Top
|
|
|
|
-.5f, .5f, .5f, 0.f, 1.f, 0.f, 0.f, 0.f,
|
|
|
|
.5f, .5f, -.5f, 0.f, 1.f, 0.f, 1.f, 1.f,
|
|
|
|
.5f, .5f, .5f, 0.f, 1.f, 0.f, 1.f, 0.f
|
2019-03-28 05:57:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
memcpy(vertices, vertexData, sizeof(vertexData));
|
|
|
|
|
|
|
|
uint16_t indexData[] = {
|
|
|
|
0, 1, 2, 2, 1, 3,
|
|
|
|
4, 5, 6, 6, 5, 7,
|
|
|
|
8, 9, 10, 10, 9, 11,
|
|
|
|
12, 13, 14, 14, 13, 15,
|
|
|
|
16, 17, 18, 18, 17, 19,
|
|
|
|
20, 21, 22, 22, 21, 23
|
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sizeof(indexData) / sizeof(indexData[0]); i++) {
|
|
|
|
indices[i] = indexData[i] + baseVertex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-27 21:50:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lovrGraphicsArc(DrawStyle style, ArcMode mode, Material* material, mat4 transform, float r1, float r2, int segments) {
|
2019-03-28 05:57:32 +00:00
|
|
|
bool hasCenterPoint = false;
|
|
|
|
|
2019-01-25 01:00:41 +00:00
|
|
|
if (fabsf(r1 - r2) >= 2.f * (float) M_PI) {
|
|
|
|
r1 = 0.f;
|
|
|
|
r2 = 2.f * (float) M_PI;
|
2019-03-28 05:57:32 +00:00
|
|
|
} else {
|
|
|
|
hasCenterPoint = mode == ARC_MODE_PIE;
|
2017-11-22 04:53:34 +00:00
|
|
|
}
|
|
|
|
|
2019-03-28 05:57:32 +00:00
|
|
|
uint32_t vertexCount = segments + 1 + hasCenterPoint;
|
|
|
|
float* vertices = NULL;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_ARC,
|
|
|
|
.params.arc.r1 = r1,
|
|
|
|
.params.arc.r2 = r2,
|
|
|
|
.params.arc.mode = mode,
|
|
|
|
.params.arc.style = style,
|
|
|
|
.params.arc.segments = segments,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = style == STYLE_LINE ? (mode == ARC_MODE_OPEN ? DRAW_LINE_STRIP : DRAW_LINE_LOOP) : DRAW_TRIANGLE_FAN,
|
2018-07-14 00:12:30 +00:00
|
|
|
.material = material,
|
2019-03-28 05:57:32 +00:00
|
|
|
.transform = transform,
|
|
|
|
.vertexCount = vertexCount,
|
|
|
|
.vertices = &vertices,
|
|
|
|
.instanced = true
|
2018-07-14 00:12:30 +00:00
|
|
|
});
|
2019-03-28 05:57:32 +00:00
|
|
|
|
|
|
|
if (vertices) {
|
|
|
|
if (hasCenterPoint) {
|
|
|
|
memcpy(vertices, ((float[]) { 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, .5f, .5f }), 8 * sizeof(float));
|
|
|
|
vertices += 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
float theta = r1;
|
|
|
|
float angleShift = (r2 - r1) / (float) segments;
|
|
|
|
|
|
|
|
for (int i = 0; i <= segments; i++) {
|
2019-05-13 06:34:04 +00:00
|
|
|
float x = cosf(theta);
|
|
|
|
float y = sinf(theta);
|
2019-03-28 05:57:32 +00:00
|
|
|
memcpy(vertices, ((float[]) { x, y, 0.f, 0.f, 0.f, 1.f, x + .5f, 1.f - (y + .5f) }), 8 * sizeof(float));
|
|
|
|
vertices += 8;
|
|
|
|
theta += angleShift;
|
|
|
|
}
|
|
|
|
}
|
2017-11-22 04:53:34 +00:00
|
|
|
}
|
|
|
|
|
2018-12-13 02:43:04 +00:00
|
|
|
void lovrGraphicsCircle(DrawStyle style, Material* material, mat4 transform, int segments) {
|
2019-05-13 11:28:57 +00:00
|
|
|
lovrGraphicsArc(style, ARC_MODE_OPEN, material, transform, 0, 2.f * (float) M_PI, segments);
|
2017-11-22 19:32:30 +00:00
|
|
|
}
|
|
|
|
|
2019-01-02 23:18:09 +00:00
|
|
|
void lovrGraphicsCylinder(Material* material, mat4 transform, float r1, float r2, bool capped, int segments) {
|
2019-06-03 14:13:52 +00:00
|
|
|
float length = vec3_length((float[4]) { transform[8], transform[9], transform[10] });
|
2019-03-28 05:57:32 +00:00
|
|
|
r1 /= length;
|
|
|
|
r2 /= length;
|
|
|
|
|
|
|
|
uint32_t vertexCount = ((capped && r1) * (segments + 2) + (capped && r2) * (segments + 2) + 2 * (segments + 1));
|
|
|
|
uint32_t indexCount = 3 * segments * ((capped && r1) + (capped && r2) + 2);
|
|
|
|
float* vertices = NULL;
|
|
|
|
uint16_t* indices = NULL;
|
|
|
|
uint16_t baseVertex;
|
2017-06-21 03:54:22 +00:00
|
|
|
|
2019-01-02 23:18:09 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_CYLINDER,
|
2019-03-28 05:57:32 +00:00
|
|
|
.params.cylinder.r1 = r1,
|
|
|
|
.params.cylinder.r2 = r2,
|
2019-01-02 23:18:09 +00:00
|
|
|
.params.cylinder.capped = capped,
|
|
|
|
.params.cylinder.segments = segments,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = DRAW_TRIANGLES,
|
2019-03-13 16:21:14 +00:00
|
|
|
.material = material,
|
2019-03-28 05:57:32 +00:00
|
|
|
.transform = transform,
|
|
|
|
.vertexCount = vertexCount,
|
|
|
|
.indexCount = indexCount,
|
|
|
|
.vertices = &vertices,
|
|
|
|
.indices = &indices,
|
|
|
|
.baseVertex = &baseVertex,
|
|
|
|
.instanced = true
|
2018-07-14 00:12:30 +00:00
|
|
|
});
|
2019-03-28 05:57:32 +00:00
|
|
|
|
|
|
|
if (vertices) {
|
|
|
|
float* v = vertices;
|
|
|
|
|
|
|
|
// Ring
|
|
|
|
for (int i = 0; i <= segments; i++) {
|
|
|
|
float theta = i * (2 * M_PI) / segments;
|
|
|
|
float X = cosf(theta);
|
|
|
|
float Y = sinf(theta);
|
|
|
|
memcpy(vertices, (float[16]) {
|
|
|
|
r1 * X, r1 * Y, -.5f, X, Y, 0.f, 0.f, 0.f,
|
|
|
|
r2 * X, r2 * Y, .5f, X, Y, 0.f, 0.f, 0.f
|
|
|
|
}, 16 * sizeof(float));
|
|
|
|
vertices += 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Top
|
|
|
|
int top = (segments + 1) * 2 + baseVertex;
|
|
|
|
if (capped && r1 != 0) {
|
|
|
|
memcpy(vertices, (float[8]) { 0.f, 0.f, -.5f, 0.f, 0.f, -1.f, 0.f, 0.f }, 8 * sizeof(float));
|
|
|
|
vertices += 8;
|
|
|
|
for (int i = 0; i <= segments; i++) {
|
|
|
|
int j = i * 2 * 8;
|
|
|
|
memcpy(vertices, (float[8]) { v[j + 0], v[j + 1], v[j + 2], 0.f, 0.f, -1.f, 0.f, 0.f }, 8 * sizeof(float));
|
|
|
|
vertices += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bottom
|
|
|
|
int bot = (segments + 1) * 2 + (1 + segments + 1) * (capped && r1 != 0) + baseVertex;
|
|
|
|
if (capped && r2 != 0) {
|
|
|
|
memcpy(vertices, (float[8]) { 0.f, 0.f, .5f, 0.f, 0.f, 1.f, 0.f, 0.f }, 8 * sizeof(float));
|
|
|
|
vertices += 8;
|
|
|
|
for (int i = 0; i <= segments; i++) {
|
|
|
|
int j = i * 2 * 8 + 8;
|
|
|
|
memcpy(vertices, (float[8]) { v[j + 0], v[j + 1], v[j + 2], 0.f, 0.f, 1.f, 0.f, 0.f }, 8 * sizeof(float));
|
|
|
|
vertices += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Indices
|
|
|
|
for (int i = 0; i < segments; i++) {
|
|
|
|
int j = 2 * i + baseVertex;
|
|
|
|
memcpy(indices, (uint16_t[6]) { j, j + 1, j + 2, j + 1, j + 3, j + 2 }, 6 * sizeof(uint16_t));
|
|
|
|
indices += 6;
|
|
|
|
|
|
|
|
if (capped && r1 != 0.f) {
|
|
|
|
memcpy(indices, (uint16_t[3]) { top, top + i + 1, top + i + 2 }, 3 * sizeof(uint16_t));
|
|
|
|
indices += 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (capped && r2 != 0.f) {
|
|
|
|
memcpy(indices, (uint16_t[3]) { bot, bot + i + 1, bot + i + 2 }, 3 * sizeof(uint16_t));
|
|
|
|
indices += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-21 03:54:22 +00:00
|
|
|
}
|
|
|
|
|
2017-11-26 03:02:28 +00:00
|
|
|
void lovrGraphicsSphere(Material* material, mat4 transform, int segments) {
|
2019-03-28 05:57:32 +00:00
|
|
|
float* vertices = NULL;
|
|
|
|
uint16_t* indices = NULL;
|
|
|
|
uint16_t baseVertex;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_SPHERE,
|
|
|
|
.params.sphere.segments = segments,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = DRAW_TRIANGLES,
|
2018-07-14 00:12:30 +00:00
|
|
|
.material = material,
|
2019-03-28 05:57:32 +00:00
|
|
|
.transform = transform,
|
|
|
|
.vertexCount = (segments + 1) * (segments + 1),
|
|
|
|
.indexCount = segments * segments * 6,
|
|
|
|
.vertices = &vertices,
|
|
|
|
.indices = &indices,
|
|
|
|
.baseVertex = &baseVertex,
|
|
|
|
.instanced = true
|
2018-07-14 00:12:30 +00:00
|
|
|
});
|
2019-03-28 05:57:32 +00:00
|
|
|
|
|
|
|
if (vertices) {
|
|
|
|
for (int i = 0; i <= segments; i++) {
|
|
|
|
float v = i / (float) segments;
|
|
|
|
float sinV = sinf(v * (float) M_PI);
|
|
|
|
float cosV = cosf(v * (float) M_PI);
|
|
|
|
for (int k = 0; k <= segments; k++) {
|
|
|
|
float u = k / (float) segments;
|
|
|
|
float x = sinf(u * 2.f * (float) M_PI) * sinV;
|
|
|
|
float y = cosV;
|
|
|
|
float z = -cosf(u * 2.f * (float) M_PI) * sinV;
|
|
|
|
memcpy(vertices, ((float[8]) { x, y, z, x, y, z, u, 1.f - v }), 8 * sizeof(float));
|
|
|
|
vertices += 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < segments; i++) {
|
|
|
|
uint16_t offset0 = i * (segments + 1) + baseVertex;
|
|
|
|
uint16_t offset1 = (i + 1) * (segments + 1) + baseVertex;
|
|
|
|
for (int j = 0; j < segments; j++) {
|
|
|
|
uint16_t i0 = offset0 + j;
|
|
|
|
uint16_t i1 = offset1 + j;
|
|
|
|
memcpy(indices, ((uint16_t[]) { i0, i1, i0 + 1, i1, i1 + 1, i0 + 1 }), 6 * sizeof(uint16_t));
|
|
|
|
indices += 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-06-22 06:10:45 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 23:56:00 +00:00
|
|
|
void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float az) {
|
2018-07-09 07:51:09 +00:00
|
|
|
TextureType type = lovrTextureGetType(texture);
|
2018-03-05 10:02:25 +00:00
|
|
|
lovrAssert(type == TEXTURE_CUBE || type == TEXTURE_2D, "Only 2D and cube textures can be used as skyboxes");
|
2018-12-25 08:20:59 +00:00
|
|
|
|
|
|
|
Pipeline pipeline = state.pipeline;
|
|
|
|
pipeline.winding = WINDING_COUNTERCLOCKWISE;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
float transform[16] = MAT4_IDENTITY;
|
|
|
|
mat4_rotate(transform, angle, ax, ay, az);
|
|
|
|
|
2019-03-28 05:57:32 +00:00
|
|
|
float* vertices = NULL;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_SKYBOX,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = DRAW_TRIANGLE_STRIP,
|
2018-07-14 00:12:30 +00:00
|
|
|
.shader = type == TEXTURE_CUBE ? SHADER_CUBE : SHADER_PANO,
|
2018-12-25 08:20:59 +00:00
|
|
|
.pipeline = &pipeline,
|
2018-12-27 21:50:42 +00:00
|
|
|
.transform = transform,
|
|
|
|
.diffuseTexture = type == TEXTURE_2D ? texture : NULL,
|
2019-03-28 05:57:32 +00:00
|
|
|
.environmentMap = type == TEXTURE_CUBE ? texture : NULL,
|
|
|
|
.vertexCount = 4,
|
|
|
|
.vertices = &vertices,
|
|
|
|
.instanced = true
|
2018-07-14 00:12:30 +00:00
|
|
|
});
|
2019-03-28 05:57:32 +00:00
|
|
|
|
|
|
|
if (vertices) {
|
|
|
|
static float vertexData[] = {
|
|
|
|
-1, 1, 1, 0, 0, 0, 0, 0,
|
|
|
|
-1, -1, 1, 0, 0, 0, 0, 0,
|
|
|
|
1, 1, 1, 0, 0, 0, 0, 0,
|
|
|
|
1, -1, 1, 0, 0, 0, 0, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
memcpy(vertices, vertexData, sizeof(vertexData));
|
|
|
|
}
|
2016-10-24 22:02:23 +00:00
|
|
|
}
|
2017-02-03 23:16:30 +00:00
|
|
|
|
2018-12-25 08:28:56 +00:00
|
|
|
void lovrGraphicsPrint(const char* str, size_t length, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
|
2018-12-27 21:50:42 +00:00
|
|
|
float width;
|
|
|
|
uint32_t lineCount;
|
|
|
|
uint32_t glyphCount;
|
2017-08-08 08:36:29 +00:00
|
|
|
Font* font = lovrGraphicsGetFont();
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrFontMeasure(font, str, length, wrap, &width, &lineCount, &glyphCount);
|
2018-12-25 08:20:59 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
float scale = 1.f / font->pixelDensity;
|
2019-01-30 05:49:49 +00:00
|
|
|
float offsetY = ((lineCount + 1) * font->rasterizer->height * font->lineHeight) * (valign / 2.f) * (font->flip ? -1 : 1);
|
2018-12-25 08:20:59 +00:00
|
|
|
mat4_scale(transform, scale, scale, scale);
|
2018-12-27 21:50:42 +00:00
|
|
|
mat4_translate(transform, 0.f, offsetY, 0.f);
|
2018-12-25 08:20:59 +00:00
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
Pipeline pipeline = state.pipeline;
|
|
|
|
pipeline.blendMode = pipeline.blendMode == BLEND_NONE ? BLEND_ALPHA : pipeline.blendMode;
|
|
|
|
|
|
|
|
float* vertices;
|
|
|
|
uint16_t* indices;
|
|
|
|
uint16_t baseVertex;
|
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_TEXT,
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = DRAW_TRIANGLES,
|
2018-07-14 00:12:30 +00:00
|
|
|
.shader = SHADER_FONT,
|
2018-12-25 08:20:59 +00:00
|
|
|
.pipeline = &pipeline,
|
|
|
|
.transform = transform,
|
2018-12-27 21:50:42 +00:00
|
|
|
.diffuseTexture = font->texture,
|
|
|
|
.vertexCount = glyphCount * 4,
|
|
|
|
.indexCount = glyphCount * 6,
|
|
|
|
.vertices = &vertices,
|
|
|
|
.indices = &indices,
|
|
|
|
.baseVertex = &baseVertex
|
2018-07-14 00:12:30 +00:00
|
|
|
});
|
2018-12-27 21:50:42 +00:00
|
|
|
|
|
|
|
lovrFontRender(font, str, length, wrap, halign, vertices, indices, baseVertex);
|
2017-02-03 23:16:30 +00:00
|
|
|
}
|
2017-08-08 09:13:07 +00:00
|
|
|
|
2018-10-01 01:40:51 +00:00
|
|
|
void lovrGraphicsFill(Texture* texture, float u, float v, float w, float h) {
|
2018-12-25 08:20:59 +00:00
|
|
|
Pipeline pipeline = state.pipeline;
|
|
|
|
pipeline.depthTest = COMPARE_NONE;
|
|
|
|
pipeline.depthWrite = false;
|
|
|
|
|
2019-03-28 05:57:32 +00:00
|
|
|
float* vertices = NULL;
|
|
|
|
|
2018-12-27 21:50:42 +00:00
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_FILL,
|
|
|
|
.params.fill = { .u = u, .v = v, .w = w, .h = h },
|
2019-03-28 05:57:32 +00:00
|
|
|
.drawMode = DRAW_TRIANGLE_STRIP,
|
2018-07-14 00:12:30 +00:00
|
|
|
.shader = SHADER_FILL,
|
2019-03-28 05:57:32 +00:00
|
|
|
.diffuseTexture = texture,
|
2019-06-25 08:06:55 +00:00
|
|
|
.pipeline = &pipeline,
|
2019-03-28 05:57:32 +00:00
|
|
|
.vertexCount = 4,
|
|
|
|
.vertices = &vertices
|
2018-07-14 00:12:30 +00:00
|
|
|
});
|
2019-01-04 09:35:29 +00:00
|
|
|
|
2019-03-28 05:57:32 +00:00
|
|
|
if (vertices) {
|
|
|
|
memcpy(vertices, (float[32]) {
|
2019-06-25 08:06:55 +00:00
|
|
|
-1.f, 1.f, 0.f, 0.f, 0.f, 0.f, u, v + h,
|
|
|
|
-1.f, -1.f, 0.f, 0.f, 0.f, 0.f, u, v,
|
|
|
|
1.f, 1.f, 0.f, 0.f, 0.f, 0.f, u + w, v + h,
|
|
|
|
1.f, -1.f, 0.f, 0.f, 0.f, 0.f, u + w, v
|
2019-03-28 05:57:32 +00:00
|
|
|
}, 32 * sizeof(float));
|
2019-01-04 09:35:29 +00:00
|
|
|
}
|
|
|
|
}
|
2019-06-27 20:35:43 +00:00
|
|
|
|
|
|
|
void lovrGraphicsDrawMesh(Mesh* mesh, mat4 transform, uint32_t instances, float* pose) {
|
|
|
|
uint32_t vertexCount = lovrMeshGetVertexCount(mesh);
|
|
|
|
uint32_t indexCount = lovrMeshGetIndexCount(mesh);
|
|
|
|
uint32_t defaultCount = indexCount > 0 ? indexCount : vertexCount;
|
|
|
|
uint32_t rangeStart, rangeCount;
|
|
|
|
lovrMeshGetDrawRange(mesh, &rangeStart, &rangeCount);
|
|
|
|
rangeCount = rangeCount > 0 ? rangeCount : defaultCount;
|
|
|
|
DrawMode mode = lovrMeshGetDrawMode(mesh);
|
|
|
|
Material* material = lovrMeshGetMaterial(mesh);
|
|
|
|
|
|
|
|
lovrGraphicsBatch(&(BatchRequest) {
|
|
|
|
.type = BATCH_MESH,
|
|
|
|
.params.mesh = {
|
|
|
|
.object = mesh,
|
|
|
|
.mode = mode,
|
|
|
|
.rangeStart = rangeStart,
|
|
|
|
.rangeCount = rangeCount,
|
|
|
|
.instances = instances,
|
|
|
|
.pose = pose
|
|
|
|
},
|
|
|
|
.drawMode = mode,
|
|
|
|
.transform = transform,
|
|
|
|
.material = material
|
|
|
|
});
|
|
|
|
}
|