lovr/src/graphics/graphics.c

1336 lines
43 KiB
C
Raw Normal View History

2016-11-19 09:28:01 +00:00
#include "graphics/graphics.h"
2018-01-22 16:28:33 +00:00
#include "data/rasterizer.h"
#include "event/event.h"
#include "filesystem/filesystem.h"
#include "math/math.h"
2016-11-19 09:28:01 +00:00
#include "util.h"
#include "lib/math.h"
2017-08-10 03:02:02 +00:00
#include "lib/stb/stb_image.h"
2016-07-16 05:39:17 +00:00
#include <stdlib.h>
2018-07-04 20:51:35 +00:00
#include <string.h>
#define _USE_MATH_DEFINES
2016-09-27 07:24:28 +00:00
#include <math.h>
2016-09-14 00:02:23 +00:00
2016-09-27 06:48:09 +00:00
static GraphicsState state;
2016-07-07 07:04:24 +00:00
static void gammaCorrectColor(Color* color) {
if (state.gammaCorrect) {
color->r = lovrMathGammaToLinear(color->r);
color->g = lovrMathGammaToLinear(color->g);
color->b = lovrMathGammaToLinear(color->b);
}
}
2018-11-16 10:26:56 +00:00
static void onCloseWindow() {
lovrEventPush((Event) { .type = EVENT_QUIT, .data.quit = { false, 0 } });
}
2018-11-16 10:26:56 +00:00
static void onResizeWindow(int width, int height) {
state.width = width;
state.height = height;
2017-11-26 01:57:59 +00:00
}
static const size_t BUFFER_COUNTS[] = {
2019-01-14 20:29:25 +00:00
[STREAM_VERTEX] = (1 << 16) - 1,
[STREAM_INDEX] = 1 << 16,
[STREAM_DRAW_ID] = (1 << 16) - 1,
[STREAM_DRAW_DATA] = 256 * MAX_BATCHES * 2
};
static const size_t BUFFER_STRIDES[] = {
[STREAM_VERTEX] = 8 * sizeof(float),
[STREAM_INDEX] = sizeof(uint16_t),
[STREAM_DRAW_ID] = sizeof(uint8_t),
[STREAM_DRAW_DATA] = sizeof(DrawData)
};
static const BufferType BUFFER_TYPES[] = {
[STREAM_VERTEX] = BUFFER_VERTEX,
[STREAM_INDEX] = BUFFER_INDEX,
[STREAM_DRAW_ID] = BUFFER_GENERIC,
[STREAM_DRAW_DATA] = BUFFER_UNIFORM
};
static void lovrGraphicsInitBuffers() {
for (int i = 0; i < MAX_BUFFER_ROLES; i++) {
state.buffers[i] = lovrBufferCreate(BUFFER_COUNTS[i] * BUFFER_STRIDES[i], NULL, BUFFER_TYPES[i], USAGE_STREAM, false);
}
// Compute the max number of draws per batch, since the hard cap of 256 won't always fit in a UBO
size_t maxBlockSize = lovrGpuGetLimits()->blockSize;
state.maxDraws = MIN(maxBlockSize / sizeof(DrawData) / 64 * 64, 256);
// 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(256, NULL, BUFFER_VERTEX, USAGE_STATIC, false);
uint8_t* id = lovrBufferMap(state.identityBuffer, 0);
for (int i = 0; i < 256; i++) id[i] = i;
lovrBufferFlushRange(state.identityBuffer, 0, 256);
2019-01-02 23:44:51 +00:00
Buffer* vertexBuffer = state.buffers[STREAM_VERTEX];
size_t stride = BUFFER_STRIDES[STREAM_VERTEX];
2019-01-27 22:29:06 +00:00
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);
2019-01-27 22:29:06 +00:00
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);
2018-12-11 21:27:59 +00:00
}
2019-01-02 23:44:51 +00:00
static void* lovrGraphicsMapBuffer(BufferRole role, uint32_t count) {
Buffer* buffer = state.buffers[role];
size_t limit = BUFFER_COUNTS[role];
lovrAssert(count <= limit, "Whoa there! Tried to get %d elements from a buffer that only has %d elements.", count, limit);
if (state.cursors[role] + count > limit) {
lovrGraphicsFlush();
state.cursors[role] = 0;
// Locks are placed as late as possible, causing the last lock to never get placed. Whenever we
// wrap around a buffer, we gotta place that last missing lock.
state.locks[role][MAX_LOCKS - 1] = lovrGpuLock();
2019-01-04 09:35:29 +00:00
// If we roll over the vertex/index streams, we can't reuse their contents
if (role == STREAM_VERTEX || role == STREAM_INDEX) {
state.cachedGeometry.vertexCount = 0;
}
2019-01-02 23:44:51 +00:00
}
// Wait on any pending locks for the mapped region(s)
int firstLock = state.cursors[role] / (BUFFER_COUNTS[role] / MAX_LOCKS);
int lastLock = MIN(state.cursors[role] + count, BUFFER_COUNTS[role] - 1) / (BUFFER_COUNTS[role] / MAX_LOCKS);
for (int i = firstLock; i <= lastLock; i++) {
if (state.locks[role][i]) {
lovrGpuUnlock(state.locks[role][i]);
lovrGpuDestroyLock(state.locks[role][i]);
state.locks[role][i] = NULL;
}
}
return lovrBufferMap(buffer, state.cursors[role] * BUFFER_STRIDES[role]);
}
2019-01-04 09:35:29 +00:00
static bool areBatchParamsEqual(BatchType typeA, BatchType typeB, BatchParams* a, BatchParams* b) {
if (typeA != typeB) return false;
switch (typeA) {
case BATCH_TRIANGLES:
return a->triangles.style == b->triangles.style;
case BATCH_BOX:
return a->box.style == b->box.style;
case BATCH_ARC:
return
a->arc.style == b->arc.style && a->arc.mode == b->arc.mode &&
a->arc.r1 == b->arc.r1 && a->arc.r2 == b->arc.r2 && a->arc.segments == b->arc.segments;
case BATCH_CYLINDER:
return
a->cylinder.r1 == b->cylinder.r1 && a->cylinder.r2 == b->cylinder.r2 &&
a->cylinder.capped == b->cylinder.capped && a->cylinder.segments == b->cylinder.segments;
case BATCH_SPHERE:
return a->sphere.segments == b->sphere.segments;
case BATCH_MESH:
return
a->mesh.object == b->mesh.object && a->mesh.mode == b->mesh.mode &&
a->mesh.rangeStart == b->mesh.rangeStart && a->mesh.rangeCount == b->mesh.rangeCount;
default:
return true;
}
}
static void writeGeometry(Batch* batch, float* vertices, uint16_t* indices, uint16_t I, uint32_t vertexCount, int n);
2016-11-23 05:16:13 +00:00
// Base
bool lovrGraphicsInit(bool gammaCorrect) {
state.gammaCorrect = gammaCorrect;
return false;
2016-09-28 03:20:08 +00:00
}
void lovrGraphicsDestroy() {
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++) {
2018-02-26 08:59:03 +00:00
lovrRelease(state.defaultShaders[i]);
}
for (int i = 0; i < MAX_BUFFER_ROLES; i++) {
lovrRelease(state.buffers[i]);
for (int j = 0; j < MAX_LOCKS; j++) {
lovrGpuDestroyLock(state.locks[i][j]);
}
}
lovrRelease(state.mesh);
lovrRelease(state.instancedMesh);
lovrRelease(state.identityBuffer);
2018-02-26 08:59:03 +00:00
lovrRelease(state.defaultMaterial);
lovrRelease(state.defaultFont);
2018-07-17 10:35:01 +00:00
lovrGpuDestroy();
memset(&state, 0, sizeof(GraphicsState));
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
}
void lovrGraphicsCreateWindow(WindowFlags* flags) {
2018-11-16 10:26:56 +00:00
lovrAssert(!state.initialized, "Window is already created");
flags->srgb = state.gammaCorrect;
2018-11-16 10:26:56 +00:00
#ifdef EMSCRIPTEN
flags->vsync = 1;
#else
flags->vsync = 0;
#endif
lovrAssert(lovrPlatformCreateWindow(flags), "Could not create window");
2018-11-16 10:26:56 +00:00
lovrPlatformOnWindowClose(onCloseWindow);
lovrPlatformOnWindowResize(onResizeWindow);
lovrPlatformGetFramebufferSize(&state.width, &state.height);
2018-11-16 10:26:56 +00:00
lovrGpuInit(state.gammaCorrect, lovrGetProcAddress);
lovrGraphicsInitBuffers();
2017-08-10 03:02:02 +00:00
lovrGraphicsReset();
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-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);
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-02-17 07:43:20 +00:00
mat4_perspective(state.camera.projection[0], .01f, 100.f, 67. * M_PI / 180., (float) state.width / state.height);
mat4_perspective(state.camera.projection[1], .01f, 100.f, 67. * M_PI / 180., (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) {
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);
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
}
bool lovrGraphicsGetAlphaSampling() {
2018-12-25 08:20:59 +00:00
return state.pipeline.alphaSampling;
}
void lovrGraphicsSetAlphaSampling(bool sample) {
2018-12-25 08:20:59 +00:00
state.pipeline.alphaSampling = sample;
}
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
}
Canvas* lovrGraphicsGetCanvas() {
2018-12-25 02:56:26 +00:00
return state.canvas;
}
2017-03-12 11:03:36 +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);
lovrRelease(state.canvas);
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);
lovrRelease(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);
lovrRelease(state.font);
state.font = font;
2017-08-08 08:36:29 +00:00
}
bool lovrGraphicsIsGammaCorrect() {
return state.gammaCorrect;
}
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
}
void lovrGraphicsSetLineWidth(uint8_t width) {
lovrAssert(width > 0 && width <= 255, "Line width must be between 0 and 255");
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);
lovrRelease(state.shader);
state.shader = shader;
2017-08-08 08:32:15 +00:00
}
void lovrGraphicsGetStencilTest(CompareMode* mode, int* value) {
2018-12-25 08:20:59 +00:00
*mode = state.pipeline.stencilMode;
*value = state.pipeline.stencilValue;
}
void lovrGraphicsSetStencilTest(CompareMode mode, int value) {
2018-12-25 08:20:59 +00:00
state.pipeline.stencilMode = mode;
state.pipeline.stencilValue = value;
}
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
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
}
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]);
}
2018-11-27 23:03:05 +00:00
void lovrGraphicsRotate(quat rotation) {
mat4_rotateQuat(state.transforms[state.transform], rotation);
}
2018-11-27 23:03:05 +00:00
void lovrGraphicsScale(vec3 scale) {
mat4_scale(state.transforms[state.transform], scale[0], scale[1], scale[2]);
}
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
}
void lovrGraphicsSetProjection(mat4 projection) {
mat4_set(state.camera.projection[0], projection);
mat4_set(state.camera.projection[1], projection);
}
// Rendering
void lovrGraphicsClear(Color* color, float* depth, int* stencil) {
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
}
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;
Shader* shader = state.shader ? state.shader : (state.defaultShaders[req->shader] ? state.defaultShaders[req->shader] : (state.defaultShaders[req->shader] = lovrShaderCreateDefault(req->shader)));
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
if (!req->material) {
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, req->diffuseTexture);
lovrMaterialSetTexture(material, TEXTURE_ENVIRONMENT_MAP, req->environmentMap);
2018-07-16 00:05:06 +00:00
}
if (req->type == BATCH_MESH) {
float* pose = req->params.mesh.pose ? req->params.mesh.pose : (float[]) MAT4_IDENTITY;
size_t count = req->params.mesh.pose ? (MAX_BONES * 16) : 16;
lovrShaderSetMatrices(shader, "lovrPose", pose, 0, count);
2018-08-31 13:03:35 +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) {
for (int i = state.batchCount - 1; i >= 0; i--) {
Batch* b = &state.batches[i];
2019-01-04 09:35:29 +00:00
if (b->drawCount >= state.maxDraws) { continue; }
if (!areBatchParamsEqual(req->type, b->type, &req->params, &b->params)) { continue; }
if (b->canvas == canvas && b->shader == shader && !memcmp(&b->pipeline, pipeline, sizeof(Pipeline)) && b->material == material) {
batch = b;
break;
}
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; }
if (b->vertexCount > 0 && req->vertexCount > 0) { break; }
}
}
2018-12-11 21:27:59 +00:00
size_t streamRequirements[] = {
[STREAM_VERTEX] = req->vertexCount,
[STREAM_INDEX] = req->indexCount,
[STREAM_DRAW_ID] = req->vertexCount,
[STREAM_DRAW_DATA] = 0
};
if (!batch) {
streamRequirements[STREAM_DRAW_DATA] = state.maxDraws;
if (state.batchCount >= MAX_BATCHES) {
lovrGraphicsFlush();
}
}
2018-12-11 21:27:59 +00:00
for (int i = 0; i < MAX_BUFFER_ROLES; i++) {
if (streamRequirements[i] > 0 && state.cursors[i] + streamRequirements[i] > BUFFER_COUNTS[i]) {
size_t oldCursor = state.cursors[i];
lovrGraphicsFlush();
state.locks[i][MAX_LOCKS - 1] = lovrGpuLock();
state.cursors[i] = state.cursors[i] >= oldCursor ? 0 : state.cursors[i];
batch = NULL;
streamRequirements[STREAM_DRAW_DATA] = state.maxDraws;
2019-01-04 09:56:56 +00:00
state.cachedGeometry.vertexCount = 0;
i = 0;
2018-12-11 21:27:59 +00:00
} else {
streamRequirements[i] = 0;
2018-12-11 21:27:59 +00:00
}
}
2017-11-21 05:16:16 +00:00
// Start a new batch
if (!batch) {
DrawData* drawData = lovrGraphicsMapBuffer(STREAM_DRAW_DATA, state.maxDraws);
batch = &state.batches[state.batchCount++];
*batch = (Batch) {
.type = req->type,
.params = req->params,
.canvas = canvas,
.shader = shader,
.pipeline = *pipeline,
.material = material,
.vertexStart = req->vertexCount > 0 ? state.cursors[STREAM_VERTEX] : 0,
.vertexCount = 0,
.indexStart = req->indexCount > 0 ? state.cursors[STREAM_INDEX] : 0,
.indexCount = 0,
.drawStart = state.cursors[STREAM_DRAW_DATA],
.drawCount = 0,
.drawData = drawData
};
state.cursors[STREAM_DRAW_DATA] += state.maxDraws;
2018-12-11 21:27:59 +00:00
}
2018-08-31 13:03:35 +00:00
// Transform
if (req->transform) {
float transform[16];
mat4_multiply(mat4_init(transform, state.transforms[state.transform]), req->transform);
memcpy(batch->drawData[batch->drawCount].transform, transform, 16 * sizeof(float));
} else {
memcpy(batch->drawData[batch->drawCount].transform, state.transforms[state.transform], 16 * sizeof(float));
}
2018-08-31 13:03:35 +00:00
// Color
Color color = state.color;
gammaCorrectColor(&color);
batch->drawData[batch->drawCount].color = color;
// Handle streams
uint8_t* ids = NULL;
if (req->vertexCount > 0) {
*(req->vertices) = lovrGraphicsMapBuffer(STREAM_VERTEX, req->vertexCount);
ids = lovrGraphicsMapBuffer(STREAM_DRAW_ID, req->vertexCount);
memset(ids, batch->drawCount, req->vertexCount * sizeof(uint8_t));
if (req->indexCount > 0) {
*(req->indices) = lovrGraphicsMapBuffer(STREAM_INDEX, req->indexCount);
*(req->baseVertex) = state.cursors[STREAM_VERTEX];
}
2016-09-30 02:39:25 +00:00
batch->vertexCount += req->vertexCount;
batch->indexCount += req->indexCount;
2018-12-11 05:30:55 +00:00
state.cursors[STREAM_VERTEX] += req->vertexCount;
state.cursors[STREAM_DRAW_ID] += req->vertexCount;
state.cursors[STREAM_INDEX] += req->indexCount;
}
2019-01-04 09:35:29 +00:00
batch->drawCount++;
}
2018-12-11 21:27:59 +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
// Prevent infinite flushing >_>
int batchCount = state.batchCount;
state.batchCount = 0;
for (int b = 0; b < batchCount; b++) {
Batch* batch = &state.batches[b];
BatchParams* params = &batch->params;
// Resolve geometry
Mesh* mesh = NULL;
DrawMode drawMode;
bool instanced = batch->vertexCount == 0 && batch->drawCount >= 4;
2019-01-04 09:35:29 +00:00
bool flushGeometry = batch->vertexCount > 0;
int instances = instanced ? batch->drawCount : 1;
uint32_t vertexCount = 0;
uint32_t indexCount = 0;
switch (batch->type) {
case BATCH_POINTS: mesh = state.mesh; drawMode = DRAW_POINTS; break;
case BATCH_LINES: mesh = state.mesh; drawMode = DRAW_LINE_STRIP; break;
case BATCH_TRIANGLES:
mesh = state.mesh;
drawMode = params->triangles.style == STYLE_LINE ? DRAW_LINE_LOOP : DRAW_TRIANGLES;
break;
case BATCH_PLANE:
vertexCount = 4;
2019-01-04 01:32:30 +00:00
indexCount = params->plane.style == STYLE_LINE ? 5 : 6;
mesh = instanced ? state.instancedMesh : state.mesh;
drawMode = params->plane.style == STYLE_LINE ? DRAW_LINE_LOOP : DRAW_TRIANGLES;
break;
case BATCH_BOX:
vertexCount = params->box.style == STYLE_LINE ? 8 : 24;
indexCount = params->box.style == STYLE_LINE ? 24 : 36;
mesh = instanced ? state.instancedMesh : state.mesh;
drawMode = params->box.style == STYLE_LINE ? DRAW_LINES : DRAW_TRIANGLES;
break;
case BATCH_ARC: {
bool hasCenterPoint = params->arc.mode == ARC_MODE_PIE && fabsf(params->arc.r1 - params->arc.r2) < 2.f * (float) M_PI;
vertexCount = params->arc.segments + 1 + hasCenterPoint;
2019-01-04 01:32:30 +00:00
indexCount = vertexCount + 1;
mesh = instanced ? state.instancedMesh : state.mesh;
drawMode = params->arc.style == STYLE_LINE ? (params->arc.mode == ARC_MODE_OPEN ? DRAW_LINE_STRIP : DRAW_LINE_LOOP) : DRAW_TRIANGLE_FAN;
break;
}
2019-01-02 23:18:09 +00:00
case BATCH_CYLINDER: {
bool r1 = params->cylinder.r1 > 0;
bool r2 = params->cylinder.r2 > 0;
bool capped = params->cylinder.capped;
int segments = params->cylinder.segments;
vertexCount = ((capped && r1) * (segments + 2) + (capped && r2) * (segments + 2) + 2 * (segments + 1));
indexCount = 3 * segments * ((capped && r1) + (capped && r2) + 2);
mesh = instanced ? state.instancedMesh : state.mesh;
drawMode = DRAW_TRIANGLES;
break;
}
case BATCH_SPHERE: {
int segments = params->sphere.segments;
vertexCount = (segments + 1) * (segments + 1);
indexCount = segments * segments * 6;
mesh = instanced ? state.instancedMesh : state.mesh;
drawMode = DRAW_TRIANGLES;
break;
}
case BATCH_SKYBOX:
vertexCount = 4;
instanced = false;
instances = 1;
mesh = state.mesh;
drawMode = DRAW_TRIANGLE_STRIP;
break;
case BATCH_TEXT:
mesh = state.mesh;
drawMode = DRAW_TRIANGLES;
break;
case BATCH_FILL:
vertexCount = 4;
instanced = false;
instances = 1;
mesh = state.mesh;
drawMode = DRAW_TRIANGLE_STRIP;
break;
case BATCH_MESH:
mesh = params->mesh.object;
drawMode = lovrMeshGetDrawMode(mesh);
if (params->mesh.instances > 1) {
lovrMeshSetAttributeEnabled(mesh, "lovrDrawID", false);
instances = params->mesh.instances;
} else {
lovrMeshSetAttributeEnabled(mesh, "lovrDrawID", true);
instances = batch->drawCount;
}
break;
default: break;
2018-12-11 21:27:59 +00:00
}
2018-12-11 05:30:55 +00:00
// Write geometry
if (vertexCount > 0) {
int n = instanced ? 1 : batch->drawCount;
2018-12-11 21:27:59 +00:00
2019-01-04 09:35:29 +00:00
// Try to re-use the geometry from the last batch
Batch* cached = &state.cachedGeometry;
if (areBatchParamsEqual(batch->type, cached->type, &batch->params, &cached->params) && cached->vertexCount >= vertexCount * n) {
batch->vertexStart = cached->vertexStart;
batch->indexStart = cached->indexStart;
batch->vertexCount = vertexCount * n;
batch->indexCount = indexCount * n;
} else {
float* vertices = lovrGraphicsMapBuffer(STREAM_VERTEX, vertexCount * n);
uint16_t* indices = lovrGraphicsMapBuffer(STREAM_INDEX, indexCount * n);
uint16_t I = (uint16_t) state.cursors[STREAM_VERTEX];
cached->type = batch->type;
cached->params = batch->params;
batch->vertexStart = cached->vertexStart = state.cursors[STREAM_VERTEX];
batch->indexStart = cached->indexStart = state.cursors[STREAM_INDEX];
batch->vertexCount = cached->vertexCount = vertexCount * n;
batch->indexCount = cached->indexCount = indexCount * n;
flushGeometry = true;
2019-01-02 23:18:09 +00:00
uint8_t* ids = lovrGraphicsMapBuffer(STREAM_DRAW_ID, batch->vertexCount);
for (int i = 0; i < n; i++) {
memset(ids, i, vertexCount * sizeof(uint8_t));
ids += vertexCount;
}
2019-01-04 09:35:29 +00:00
state.cursors[STREAM_VERTEX] += batch->vertexCount;
state.cursors[STREAM_INDEX] += batch->indexCount;
state.cursors[STREAM_DRAW_ID] += batch->vertexCount;
2018-12-11 21:27:59 +00:00
2019-01-04 09:35:29 +00:00
writeGeometry(batch, vertices, indices, I, vertexCount, n);
}
2018-12-11 05:30:55 +00:00
}
// Flush vertex buffer
2019-01-04 09:35:29 +00:00
if (flushGeometry && batch->vertexCount > 0) {
size_t stride = BUFFER_STRIDES[STREAM_VERTEX];
lovrBufferFlushRange(state.buffers[STREAM_VERTEX], batch->vertexStart * stride, batch->vertexCount * stride);
2018-12-11 05:30:55 +00:00
if (!instanced) {
lovrBufferFlushRange(state.buffers[STREAM_DRAW_ID], batch->vertexStart, batch->vertexCount);
}
}
2018-12-11 05:30:55 +00:00
// Flush index buffer
2019-01-04 09:35:29 +00:00
if (flushGeometry && batch->indexCount > 0) {
size_t stride = BUFFER_STRIDES[STREAM_INDEX];
lovrBufferFlushRange(state.buffers[STREAM_INDEX], batch->indexStart * stride, batch->indexCount * stride);
}
2016-11-08 07:16:33 +00:00
// Flush draw data buffer
size_t drawDataOffset = batch->drawStart * BUFFER_STRIDES[STREAM_DRAW_DATA];
size_t drawDataSize = batch->drawCount * BUFFER_STRIDES[STREAM_DRAW_DATA];
lovrBufferFlushRange(state.buffers[STREAM_DRAW_DATA], drawDataOffset, drawDataSize);
lovrShaderSetBlock(batch->shader, "lovrDrawData", state.buffers[STREAM_DRAW_DATA], drawDataOffset, state.maxDraws * BUFFER_STRIDES[STREAM_DRAW_DATA], ACCESS_READ);
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);
if (drawMode == DRAW_POINTS) {
lovrShaderSetFloats(batch->shader, "lovrPointSize", &state.pointSize, 0, 1);
}
uint32_t rangeStart, rangeCount;
if (batch->type == BATCH_MESH) {
rangeStart = batch->params.mesh.rangeStart;
rangeCount = batch->params.mesh.rangeCount;
} else {
rangeStart = batch->indexCount ? batch->indexStart : batch->vertexStart;
rangeCount = batch->indexCount ? batch->indexCount : batch->vertexCount;
if (batch->indexCount > 0) {
2019-01-17 22:14:13 +00:00
lovrMeshSetIndexBuffer(mesh, state.buffers[STREAM_INDEX], BUFFER_COUNTS[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
}
}
// Draw!
lovrGpuDraw(&(DrawCommand) {
.mesh = mesh,
.shader = batch->shader,
.canvas = batch->canvas,
.pipeline = batch->pipeline,
.drawMode = drawMode,
.instances = instances,
.rangeStart = rangeStart,
.rangeCount = rangeCount,
.width = batch->canvas ? lovrCanvasGetWidth(batch->canvas) : state.width,
.height = batch->canvas ? lovrCanvasGetHeight(batch->canvas) : state.height,
.stereo = batch->type != BATCH_FILL && (batch->canvas ? lovrCanvasIsStereo(batch->canvas) : state.camera.stereo)
});
2016-09-29 07:00:02 +00:00
// Pop lock and drop it
2017-03-12 23:57:27 +00:00
if (batch->vertexCount > 0) {
size_t lockSize = BUFFER_COUNTS[STREAM_VERTEX] / MAX_LOCKS + 1;
int firstLock = batch->vertexStart / lockSize;
int lastLock = (batch->vertexStart + batch->vertexCount) / lockSize;
for (int i = firstLock; i < lastLock; i++) {
state.locks[STREAM_VERTEX][i] = lovrGpuLock();
if (!instanced) {
state.locks[STREAM_DRAW_ID][i] = lovrGpuLock();
}
2018-07-14 00:12:30 +00:00
}
}
if (batch->indexCount > 0) {
size_t lockSize = BUFFER_COUNTS[STREAM_INDEX] / MAX_LOCKS + 1;
int firstLock = batch->indexStart / lockSize;
int lastLock = (batch->indexStart + batch->indexCount) / lockSize;
for (int i = firstLock; i < lastLock; i++) {
state.locks[STREAM_INDEX][i] = lovrGpuLock();
2018-07-14 00:12:30 +00:00
}
}
2016-11-23 04:43:22 +00:00
if (batch->drawCount > 0) {
size_t lockSize = BUFFER_COUNTS[STREAM_DRAW_DATA] / MAX_LOCKS;
int firstLock = batch->drawStart / lockSize;
int lastLock = MIN(batch->drawStart + state.maxDraws, BUFFER_COUNTS[STREAM_DRAW_DATA] - 1) / lockSize;
for (int i = firstLock; i < lastLock; i++) {
state.locks[STREAM_DRAW_DATA][i] = lovrGpuLock();
2018-07-14 00:12:30 +00:00
}
}
2016-10-04 03:56:45 +00:00
}
2017-01-13 09:59:00 +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
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
}
}
2017-11-22 19:32:30 +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
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
}
}
2017-11-22 19:32:30 +00:00
void lovrGraphicsPoints(uint32_t count, float** vertices) {
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_POINTS,
.vertexCount = count,
.vertices = vertices
});
}
2017-11-22 19:32:30 +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;
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_LINES,
.vertexCount = count,
2019-01-03 09:21:38 +00:00
.vertices = vertices,
.indexCount = indexCount,
.indices = &indices,
.baseVertex = &baseVertex
});
2019-01-03 09:21:38 +00:00
indices[0] = 0xffff;
for (uint32_t i = 1; i < indexCount; i++) {
indices[i] = baseVertex + i - 1;
}
}
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;
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_TRIANGLES,
.params.triangles.style = style,
.material = material,
.vertexCount = count,
2019-01-03 09:21:38 +00:00
.vertices = vertices,
.indexCount = indexCount,
.indices = &indices,
.baseVertex = &baseVertex
});
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;
}
}
}
void lovrGraphicsPlane(DrawStyle style, Material* material, mat4 transform) {
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_PLANE,
.params.plane.style = style,
.material = material,
.transform = transform
});
}
void lovrGraphicsBox(DrawStyle style, Material* material, mat4 transform) {
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_BOX,
.params.box.style = style,
.material = material,
.transform = transform
});
}
void lovrGraphicsArc(DrawStyle style, ArcMode mode, Material* material, mat4 transform, float r1, float r2, int segments) {
if (fabsf(r1 - r2) >= 2.f * (float) M_PI) {
r1 = 0.f;
r2 = 2.f * (float) M_PI;
2017-11-22 04:53:34 +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,
2018-07-14 00:12:30 +00:00
.material = material,
.transform = transform
2018-07-14 00:12:30 +00:00
});
2017-11-22 04:53:34 +00:00
}
void lovrGraphicsCircle(DrawStyle style, Material* material, mat4 transform, int segments) {
2019-02-17 07:43:20 +00:00
lovrGraphicsArc(style, ARC_MODE_OPEN, material, transform, 0, 2. * 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) {
float length = vec3_length((float[3]) { transform[8], transform[9], transform[10] });
2017-06-21 03:54:22 +00:00
2019-01-02 23:18:09 +00:00
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_CYLINDER,
.params.cylinder.r1 = r1 / length,
.params.cylinder.r2 = r2 / length,
.params.cylinder.capped = capped,
.params.cylinder.segments = segments,
.transform = transform,
.material = material
2018-07-14 00:12:30 +00:00
});
2017-06-21 03:54:22 +00:00
}
void lovrGraphicsSphere(Material* material, mat4 transform, int segments) {
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_SPHERE,
.params.sphere.segments = segments,
2018-07-14 00:12:30 +00:00
.material = material,
.transform = transform
2018-07-14 00:12:30 +00:00
});
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) {
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;
float transform[16] = MAT4_IDENTITY;
mat4_rotate(transform, angle, ax, ay, az);
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_SKYBOX,
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,
.transform = transform,
.diffuseTexture = type == TEXTURE_2D ? texture : NULL,
.environmentMap = type == TEXTURE_CUBE ? texture : NULL
2018-07-14 00:12:30 +00:00
});
2016-10-24 22:02:23 +00:00
}
2017-02-03 23:16:30 +00:00
void lovrGraphicsPrint(const char* str, size_t length, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
float width;
uint32_t lineCount;
uint32_t glyphCount;
2017-08-08 08:36:29 +00:00
Font* font = lovrGraphicsGetFont();
lovrFontMeasure(font, str, length, wrap, &width, &lineCount, &glyphCount);
2018-12-25 08:20:59 +00:00
float scale = 1.f / font->pixelDensity;
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);
mat4_translate(transform, 0.f, offsetY, 0.f);
2018-12-25 08:20:59 +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,
2018-07-14 00:12:30 +00:00
.shader = SHADER_FONT,
2018-12-25 08:20:59 +00:00
.pipeline = &pipeline,
.transform = transform,
.diffuseTexture = font->texture,
.vertexCount = glyphCount * 4,
.indexCount = glyphCount * 6,
.vertices = &vertices,
.indices = &indices,
.baseVertex = &baseVertex
2018-07-14 00:12:30 +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;
lovrGraphicsBatch(&(BatchRequest) {
.type = BATCH_FILL,
.params.fill = { .u = u, .v = v, .w = w, .h = h },
2018-07-14 00:12:30 +00:00
.shader = SHADER_FILL,
2018-12-25 08:20:59 +00:00
.pipeline = &pipeline,
.diffuseTexture = texture
2018-07-14 00:12:30 +00:00
});
2017-12-10 04:07:32 +00:00
}
2019-01-04 09:35:29 +00:00
static void writeGeometry(Batch* batch, float* vertices, uint16_t* indices, uint16_t I, uint32_t vertexCount, int n) {
BatchParams* params = &batch->params;
switch (batch->type) {
case BATCH_PLANE:
if (params->plane.style == STYLE_LINE) {
for (int i = 0; i < n; i++) {
memcpy(vertices, (float[32]) {
-.5, .5, 0, 0, 0, 0, 0, 0,
.5, .5, 0, 0, 0, 0, 0, 0,
.5, -.5, 0, 0, 0, 0, 0, 0,
-.5, -.5, 0, 0, 0, 0, 0, 0
}, 32 * sizeof(float));
vertices += 32;
memcpy(indices, (uint16_t[5]) { 0xffff, I + 0, I + 1, I + 2, I + 3 }, 5 * sizeof(uint16_t));
I += vertexCount;
indices += 5;
}
} else {
for (int i = 0; i < n; i++) {
memcpy(vertices, (float[32]) {
-.5, .5, 0, 0, 0, -1, 0, 1,
-.5, -.5, 0, 0, 0, -1, 0, 0,
.5, .5, 0, 0, 0, -1, 1, 1,
.5, -.5, 0, 0, 0, -1, 1, 0
}, 32 * sizeof(float));
vertices += 32;
memcpy(indices, (uint16_t[6]) { I + 0, I + 1, I + 2, I + 2, I + 1, I + 3 }, 6 * sizeof(uint16_t));
I += vertexCount;
indices += 6;
}
}
break;
case BATCH_BOX:
if (params->box.style == STYLE_LINE) {
for (int i = 0; i < n; i++) {
memcpy(vertices, (float[64]) {
-.5, .5, -.5, 0, 0, 0, 0, 0, // Front
.5, .5, -.5, 0, 0, 0, 0, 0,
.5, -.5, -.5, 0, 0, 0, 0, 0,
-.5, -.5, -.5, 0, 0, 0, 0, 0,
-.5, .5, .5, 0, 0, 0, 0, 0, // Back
.5, .5, .5, 0, 0, 0, 0, 0,
.5, -.5, .5, 0, 0, 0, 0, 0,
-.5, -.5, .5, 0, 0, 0, 0, 0
}, 64 * sizeof(float));
vertices += 64;
memcpy(indices, (uint16_t[24]) {
I + 0, I + 1, I + 1, I + 2, I + 2, I + 3, I + 3, I + 0, // Front
I + 4, I + 5, I + 5, I + 6, I + 6, I + 7, I + 7, I + 4, // Back
I + 0, I + 4, I + 1, I + 5, I + 2, I + 6, I + 3, I + 7 // Connections
}, 24 * sizeof(uint16_t));
indices += 24;
I += 8;
}
} else {
for (int i = 0; i < n; i++) {
memcpy(vertices, (float[192]) {
-.5, -.5, -.5, 0, 0, -1, 0, 0, // Front
-.5, .5, -.5, 0, 0, -1, 0, 1,
.5, -.5, -.5, 0, 0, -1, 1, 0,
.5, .5, -.5, 0, 0, -1, 1, 1,
.5, .5, -.5, 1, 0, 0, 0, 1, // Right
.5, .5, .5, 1, 0, 0, 1, 1,
.5, -.5, -.5, 1, 0, 0, 0, 0,
.5, -.5, .5, 1, 0, 0, 1, 0,
.5, -.5, .5, 0, 0, 1, 0, 0, // Back
.5, .5, .5, 0, 0, 1, 0, 1,
-.5, -.5, .5, 0, 0, 1, 1, 0,
-.5, .5, .5, 0, 0, 1, 1, 1,
-.5, .5, .5, -1, 0, 0, 0, 1, // Left
-.5, .5, -.5, -1, 0, 0, 1, 1,
-.5, -.5, .5, -1, 0, 0, 0, 0,
-.5, -.5, -.5, -1, 0, 0, 1, 0,
-.5, -.5, -.5, 0, -1, 0, 0, 0, // Bottom
.5, -.5, -.5, 0, -1, 0, 1, 0,
-.5, -.5, .5, 0, -1, 0, 0, 1,
.5, -.5, .5, 0, -1, 0, 1, 1,
-.5, .5, -.5, 0, 1, 0, 0, 1, // Top
-.5, .5, .5, 0, 1, 0, 0, 0,
.5, .5, -.5, 0, 1, 0, 1, 1,
.5, .5, .5, 0, 1, 0, 1, 0
}, 192 * sizeof(float));
vertices += 192;
memcpy(indices, (uint16_t[36]) {
I + 0, I + 1, I + 2, I + 2, I + 1, I + 3,
I + 4, I + 5, I + 6, I + 6, I + 5, I + 7,
I + 8, I + 9, I + 10, I + 10, I + 9, I + 11,
I + 12, I + 13, I + 14, I + 14, I + 13, I + 15,
I + 16, I + 17, I + 18, I + 18, I + 17, I + 19,
I + 20, I + 21, I + 22, I + 22, I + 21, I + 23
}, 36 * sizeof(uint16_t));
I += vertexCount;
indices += 36;
}
}
break;
case BATCH_ARC: {
float r1 = params->arc.r1;
float r2 = params->arc.r2;
int segments = params->arc.segments;
bool hasCenterPoint = params->arc.mode == ARC_MODE_PIE && fabsf(r1 - r2) < 2.f * (float) M_PI;
2019-01-04 09:35:29 +00:00
for (int i = 0; i < n; i++) {
if (hasCenterPoint) {
memcpy(vertices, ((float[]) { 0.f, 0.f, 0.f, 0.f, 0.f, 1.f, .5f, .5f }), 8 * sizeof(float));
2019-01-04 09:35:29 +00:00
vertices += 8;
}
float theta = r1;
float angleShift = (r2 - r1) / (float) segments;
for (int i = 0; i <= segments; i++) {
float x = cosf(theta) * .5f;
float y = sinf(theta) * .5f;
memcpy(vertices, ((float[]) { x, y, 0.f, 0.f, 0.f, 1.f, x + .5f, 1.f - (y + .5f) }), 8 * sizeof(float));
2019-01-04 09:35:29 +00:00
vertices += 8;
theta += angleShift;
}
*indices++ = 0xffff;
for (uint32_t i = 0; i < vertexCount; i++) {
*indices++ = I + i;
}
I += vertexCount;
}
break;
}
case BATCH_CYLINDER: {
bool capped = params->cylinder.capped;
float r1 = params->cylinder.r1;
float r2 = params->cylinder.r2;
int segments = params->cylinder.segments;
for (int i = 0; i < n; i++) {
float* v = vertices;
// Ring
for (int j = 0; j <= segments; j++) {
float theta = j * (2 * M_PI) / segments;
float X = cosf(theta);
float Y = sinf(theta);
2019-01-04 09:35:29 +00:00
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
2019-01-04 09:35:29 +00:00
}, 16 * sizeof(float));
vertices += 16;
}
// Top
int top = (segments + 1) * 2 + I;
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));
2019-01-04 09:35:29 +00:00
vertices += 8;
for (int j = 0; j <= segments; j++) {
int k = j * 2 * 8;
memcpy(vertices, (float[8]) { v[k + 0], v[k + 1], v[k + 2], 0.f, 0.f, -1.f, 0.f, 0.f }, 8 * sizeof(float));
2019-01-04 09:35:29 +00:00
vertices += 8;
}
}
// Bottom
int bot = (segments + 1) * 2 + (1 + segments + 1) * (capped && r1 != 0) + I;
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));
2019-01-04 09:35:29 +00:00
vertices += 8;
for (int j = 0; j <= segments; j++) {
int k = j * 2 * 8 + 8;
memcpy(vertices, (float[8]) { v[k + 0], v[k + 1], v[k + 2], 0.f, 0.f, 1.f, 0.f, 0.f }, 8 * sizeof(float));
2019-01-04 09:35:29 +00:00
vertices += 8;
}
}
// Indices
for (int i = 0; i < segments; i++) {
int j = 2 * i + I;
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;
}
}
I += vertexCount;
}
break;
}
case BATCH_SPHERE: {
int segments = params->sphere.segments;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= segments; j++) {
float v = j / (float) segments;
float sinV = sinf(v * (float) M_PI);
float cosV = cosf(v * (float) M_PI);
2019-01-04 09:35:29 +00:00
for (int k = 0; k <= segments; k++) {
float u = k / (float) segments;
float x = sinf(u * 2.f * (float) M_PI) * sinV;
2019-01-04 09:35:29 +00:00
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));
2019-01-04 09:35:29 +00:00
vertices += 8;
}
}
for (int j = 0; j < segments; j++) {
uint16_t offset0 = j * (segments + 1) + I;
uint16_t offset1 = (j + 1) * (segments + 1) + I;
for (int k = 0; k < segments; k++) {
uint16_t i0 = offset0 + k;
uint16_t i1 = offset1 + k;
memcpy(indices, ((uint16_t[]) { i0, i1, i0 + 1, i1, i1 + 1, i0 + 1 }), 6 * sizeof(uint16_t));
indices += 6;
}
}
I += vertexCount;
}
break;
}
case BATCH_SKYBOX:
for (int i = 0; i < n; i++) {
memcpy(vertices, (float[32]) {
-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
}, 32 * sizeof(float));
vertices += 32;
}
break;
case BATCH_FILL:
for (int i = 0; i < n; i++) {
float u = params->fill.u;
float v = params->fill.v;
float w = params->fill.w;
float h = params->fill.h;
memcpy(vertices, (float[32]) {
-1, 1, 0, 0, 0, 0, u, v + h,
-1, -1, 0, 0, 0, 0, u, v,
1, 1, 0, 0, 0, 0, u + w, v + h,
1, -1, 0, 0, 0, 0, u + w, v
}, 32 * sizeof(float));
vertices += 32;
}
break;
default: break;
}
}