Reorganize files and remove unused code
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
EXT = .exe
|
EXT = .exe
|
||||||
CFLAGS = -O2 -DNDEBUG -I3p/glad/include -I3p/glfw-3.4.bin.WIN64/include -L3p/glfw-3.4.bin.WIN64/lib-mingw-w64
|
CFLAGS = -O2 -DNDEBUG -I3p -I3p/glad/include -I3p/glfw-3.4.bin.WIN64/include -L3p/glfw-3.4.bin.WIN64/lib-mingw-w64
|
||||||
LDFLAGS = -lglfw3 -lopengl32 -lgdi32
|
LDFLAGS = -lglfw3 -lopengl32 -lgdi32
|
||||||
else
|
else
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
ifeq ($(UNAME_S),Linux)
|
ifeq ($(UNAME_S),Linux)
|
||||||
EXT =
|
EXT =
|
||||||
CFLAGS = -O2 -DNDEBUG -I3p/glad/include
|
CFLAGS = -O2 -DNDEBUG -I3p/glad/include -I3p
|
||||||
LDFLAGS = -lglfw -lm
|
LDFLAGS = -lglfw -lm
|
||||||
endif
|
endif
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
@@ -20,7 +20,7 @@ endif
|
|||||||
all: ray_trace$(EXT)
|
all: ray_trace$(EXT)
|
||||||
|
|
||||||
ray_trace$(EXT): Makefile $(wildcard src/*.c src/*.h)
|
ray_trace$(EXT): Makefile $(wildcard src/*.c src/*.h)
|
||||||
gcc -o $@ src/main.c src/utils.c src/camera.c src/mesh.c src/vector.c src/thread.c src/sync.c src/clock.c src/profile.c 3p/glad/src/glad.c -std=c11 $(CFLAGS) $(LDFLAGS)
|
gcc -o $@ src/main.c src/utils.c src/scene.c src/camera.c src/vector.c src/os.c src/gpu_and_windowing.c 3p/glad/src/glad.c -std=c11 $(CFLAGS) $(LDFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm ray_trace ray_trace.exe
|
rm ray_trace ray_trace.exe
|
||||||
|
|||||||
@@ -23,6 +23,3 @@ You should use a number of threads equal to the number of CPU cores. The `--init
|
|||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
# License
|
|
||||||
The ray tracer itself (main.c, camera.c, and shaders) are MIT licensed. Everything else (other than 3p code) is released in the public domain.
|
|
||||||
-115
@@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#define _POSIX_C_SOURCE 1999309L
|
|
||||||
|
|
||||||
#include "clock.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define WIN32_MEAN_AND_LEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns the current absolute time in microsecods
|
|
||||||
* TODO: Specify since when the time is calculated
|
|
||||||
*/
|
|
||||||
uint64_t get_absolute_time_us(void)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
FILETIME filetime;
|
|
||||||
GetSystemTimePreciseAsFileTime(&filetime);
|
|
||||||
uint64_t time = (uint64_t) filetime.dwLowDateTime | ((uint64_t) filetime.dwHighDateTime << 32);
|
|
||||||
time /= 10;
|
|
||||||
return time;
|
|
||||||
#else
|
|
||||||
struct timespec buffer;
|
|
||||||
if (clock_gettime(CLOCK_REALTIME, &buffer))
|
|
||||||
abort();
|
|
||||||
uint64_t time = buffer.tv_sec * 1000000 + buffer.tv_nsec / 1000;
|
|
||||||
return time;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns the current time in nanoseconds since
|
|
||||||
* an unspecified point in time.
|
|
||||||
*/
|
|
||||||
uint64_t get_relative_time_ns(void)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
{
|
|
||||||
int64_t count;
|
|
||||||
int64_t freq;
|
|
||||||
int ok;
|
|
||||||
|
|
||||||
ok = QueryPerformanceCounter((LARGE_INTEGER*) &count);
|
|
||||||
if (!ok) abort();
|
|
||||||
|
|
||||||
ok = QueryPerformanceFrequency((LARGE_INTEGER*) &freq);
|
|
||||||
if (!ok) abort();
|
|
||||||
|
|
||||||
uint64_t res = 1000000000 * (double) count / freq;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
{
|
|
||||||
struct timespec time;
|
|
||||||
|
|
||||||
if (clock_gettime(CLOCK_REALTIME, &time))
|
|
||||||
abort();
|
|
||||||
|
|
||||||
uint64_t res;
|
|
||||||
|
|
||||||
uint64_t sec = time.tv_sec;
|
|
||||||
if (sec > UINT64_MAX / 1000000000)
|
|
||||||
abort();
|
|
||||||
res = sec * 1000000000;
|
|
||||||
|
|
||||||
uint64_t nsec = time.tv_nsec;
|
|
||||||
if (res > UINT64_MAX - nsec)
|
|
||||||
abort();
|
|
||||||
res += nsec;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleep_ms(float ms)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
Sleep(ms);
|
|
||||||
#else
|
|
||||||
usleep(ms*1000);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
uint64_t get_absolute_time_us(void);
|
|
||||||
uint64_t get_relative_time_ns(void);
|
|
||||||
void sleep_ms(float ms);
|
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include <stb/stb_image.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "gpu_and_windowing.h"
|
||||||
|
|
||||||
|
static GLFWwindow *window;
|
||||||
|
static unsigned int screen_program;
|
||||||
|
static unsigned int frame_texture;
|
||||||
|
static unsigned int vao;
|
||||||
|
static unsigned int vbo;
|
||||||
|
static int screen_w;
|
||||||
|
static int screen_h;
|
||||||
|
|
||||||
|
#define MAX_EVENTS 512
|
||||||
|
int event_queue[MAX_EVENTS];
|
||||||
|
int event_queue_head = 0;
|
||||||
|
int event_queue_size = 0;
|
||||||
|
|
||||||
|
void load_cubemap(Cubemap *c, const char *files[6])
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
c->data[i] = stbi_load(files[i], &c->w, &c->h, &c->chan, 0);
|
||||||
|
if (c->data[i] == NULL) {
|
||||||
|
fprintf(stderr, "Couldn't load image '%s'\n", files[i]);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_cubemap(Cubemap *c)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
stbi_image_free(c->data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 sample_cubemap(Cubemap *c, Vector3 dir)
|
||||||
|
{
|
||||||
|
float abs_x = absf(dir.x);
|
||||||
|
float abs_y = absf(dir.y);
|
||||||
|
float abs_z = absf(dir.z);
|
||||||
|
|
||||||
|
CubeFace face;
|
||||||
|
|
||||||
|
float u;
|
||||||
|
float v;
|
||||||
|
float eps = 0;
|
||||||
|
|
||||||
|
if (abs_x > abs_y && abs_x > abs_z) {
|
||||||
|
// X dominant
|
||||||
|
if (dir.x > 0) {
|
||||||
|
// right face
|
||||||
|
face = CF_RIGHT;
|
||||||
|
u = -dir.z / (abs_x + eps);
|
||||||
|
v = -dir.y / (abs_x + eps);
|
||||||
|
} else {
|
||||||
|
// left face
|
||||||
|
face = CF_LEFT;
|
||||||
|
u = dir.z / (abs_x + eps);
|
||||||
|
v = -dir.y / (abs_x + eps);
|
||||||
|
}
|
||||||
|
} else if (abs_y > abs_x && abs_y > abs_z) {
|
||||||
|
// Y dominant
|
||||||
|
assert(abs_y > 0);
|
||||||
|
if (dir.y > 0) {
|
||||||
|
// top face
|
||||||
|
face = CF_TOP;
|
||||||
|
u = dir.x / (abs_y + eps);
|
||||||
|
v = dir.z / (abs_y + eps);
|
||||||
|
} else {
|
||||||
|
// bottom face
|
||||||
|
face = CF_BOTTOM;
|
||||||
|
u = dir.x / (abs_y + eps);
|
||||||
|
v = -dir.z / (abs_y + eps);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Z dominant
|
||||||
|
if (dir.z > 0) {
|
||||||
|
// front face
|
||||||
|
face = CF_FRONT;
|
||||||
|
u = dir.x / (abs_z + eps);
|
||||||
|
v = -dir.y / (abs_z + eps);
|
||||||
|
} else {
|
||||||
|
// back face
|
||||||
|
face = CF_BACK;
|
||||||
|
u = -dir.x / (abs_z + eps);
|
||||||
|
v = -dir.y / (abs_z + eps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u = clamp(u, -1, 1);
|
||||||
|
v = clamp(v, -1, 1);
|
||||||
|
|
||||||
|
u = 0.5f * (u + 1.0f);
|
||||||
|
v = 0.5f * (v + 1.0f);
|
||||||
|
|
||||||
|
// Pixel coordinates
|
||||||
|
int x = u * (c->w - 1);
|
||||||
|
int y = v * (c->h - 1);
|
||||||
|
|
||||||
|
uint8_t *color = &c->data[face][(y * c->w + x) * c->chan];
|
||||||
|
return (Vector3) {
|
||||||
|
(float) color[0] / 255,
|
||||||
|
(float) color[1] / 255,
|
||||||
|
(float) color[2] / 255,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
compile_shader(const char *vertex_file, const char *fragment_file)
|
||||||
|
{
|
||||||
|
int success;
|
||||||
|
char infolog[512];
|
||||||
|
|
||||||
|
char *vertex_str = load_file(vertex_file, NULL);
|
||||||
|
if (vertex_str == NULL) {
|
||||||
|
fprintf(stderr, "Couldn't load file '%s'\n", vertex_file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *fragment_str = load_file(fragment_file, NULL);
|
||||||
|
if (fragment_str == NULL) {
|
||||||
|
fprintf(stderr, "Couldn't load file '%s'\n", fragment_file);
|
||||||
|
free(vertex_str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vertex_shader, 1, (const GLchar * const *) &vertex_str, NULL);
|
||||||
|
glCompileShader(vertex_shader);
|
||||||
|
|
||||||
|
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
|
||||||
|
if(!success) {
|
||||||
|
glGetShaderInfoLog(vertex_shader, sizeof(infolog), NULL, infolog);
|
||||||
|
fprintf(stderr, "Couldn't compile vertex shader '%s' (%s)\n", vertex_file, infolog);
|
||||||
|
free(vertex_str);
|
||||||
|
free(fragment_str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(fragment_shader, 1, (const GLchar * const *) &fragment_str, NULL);
|
||||||
|
glCompileShader(fragment_shader);
|
||||||
|
|
||||||
|
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
|
||||||
|
if(!success) {
|
||||||
|
glGetShaderInfoLog(fragment_shader, sizeof(infolog), NULL, infolog);
|
||||||
|
fprintf(stderr, "Couldn't compile fragment shader '%s' (%s)\n", fragment_file, infolog);
|
||||||
|
free(vertex_str);
|
||||||
|
free(fragment_str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int shader_program = glCreateProgram();
|
||||||
|
glAttachShader(shader_program, vertex_shader);
|
||||||
|
glAttachShader(shader_program, fragment_shader);
|
||||||
|
glLinkProgram(shader_program);
|
||||||
|
|
||||||
|
glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
|
||||||
|
if(!success) {
|
||||||
|
glGetProgramInfoLog(shader_program, sizeof(infolog), NULL, infolog);
|
||||||
|
fprintf(stderr, "Couldn't link shader program (%s)\n", infolog);
|
||||||
|
free(vertex_str);
|
||||||
|
free(fragment_str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteShader(vertex_shader);
|
||||||
|
glDeleteShader(fragment_shader);
|
||||||
|
free(vertex_str);
|
||||||
|
free(fragment_str);
|
||||||
|
return shader_program;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_uniform_m4(unsigned int program, const char *name, Matrix4 value)
|
||||||
|
{
|
||||||
|
int location = glGetUniformLocation(program, name);
|
||||||
|
if (location < 0) {
|
||||||
|
printf("Can't set uniform '%s'\n", name);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
glUniformMatrix4fv(location, 1, GL_FALSE, (float*) &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_uniform_v3(unsigned int program, const char *name, Vector3 value)
|
||||||
|
{
|
||||||
|
int location = glGetUniformLocation(program, name);
|
||||||
|
if (location < 0) {
|
||||||
|
printf("Can't set uniform '%s' (program %d, location %d)\n", name, program, location);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
glUniform3f(location, value.x, value.y, value.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_uniform_i(unsigned int program, const char *name, int value)
|
||||||
|
{
|
||||||
|
int location = glGetUniformLocation(program, name);
|
||||||
|
if (location < 0) {
|
||||||
|
printf("Can't set uniform '%s'\n", name);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
glUniform1i(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_uniform_f(unsigned int program, const char *name, float value)
|
||||||
|
{
|
||||||
|
int location = glGetUniformLocation(program, name);
|
||||||
|
if (location < 0) {
|
||||||
|
printf("Can't set uniform '%s'\n", name);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
glUniform1f(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void push_event(int event)
|
||||||
|
{
|
||||||
|
if (event_queue_size == MAX_EVENTS) {
|
||||||
|
fprintf(stderr, "Event queue full. An event has been lost\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int tail = (event_queue_head + event_queue_size) % MAX_EVENTS;
|
||||||
|
event_queue[tail] = event;
|
||||||
|
event_queue_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pop_event(double *mouse_x, double *mouse_y)
|
||||||
|
{
|
||||||
|
if (glfwWindowShouldClose(window))
|
||||||
|
return EVENT_CLOSE;
|
||||||
|
|
||||||
|
if (event_queue_size == 0)
|
||||||
|
return EVENT_EMPTY;
|
||||||
|
|
||||||
|
int event = event_queue[event_queue_head];
|
||||||
|
event_queue_head = (event_queue_head + 1) % MAX_EVENTS;
|
||||||
|
event_queue_size--;
|
||||||
|
|
||||||
|
if (event == EVENT_MOVE_MOUSE)
|
||||||
|
glfwGetCursorPos(window, mouse_x, mouse_y);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void error_callback(int error, const char* description)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: %s\n", description);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||||
|
{
|
||||||
|
switch (key) {
|
||||||
|
case GLFW_KEY_SPACE:
|
||||||
|
if (0) {}
|
||||||
|
else if (action == GLFW_PRESS) push_event(EVENT_PRESS_SPACE);
|
||||||
|
else if (action == GLFW_REPEAT) push_event(EVENT_AGAIN_SPACE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLFW_KEY_ESCAPE:
|
||||||
|
if (0) {}
|
||||||
|
else if (action == GLFW_PRESS) push_event(EVENT_PRESS_ESC);
|
||||||
|
else if (action == GLFW_REPEAT) push_event(EVENT_AGAIN_ESC);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLFW_KEY_W:
|
||||||
|
if (0) {}
|
||||||
|
else if (action == GLFW_PRESS) push_event(EVENT_PRESS_W);
|
||||||
|
else if (action == GLFW_REPEAT) push_event(EVENT_AGAIN_W);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLFW_KEY_A:
|
||||||
|
if (0) {}
|
||||||
|
else if (action == GLFW_PRESS) push_event(EVENT_PRESS_W);
|
||||||
|
else if (action == GLFW_REPEAT) push_event(EVENT_AGAIN_W);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLFW_KEY_S:
|
||||||
|
if (0) {}
|
||||||
|
else if (action == GLFW_PRESS) push_event(EVENT_PRESS_W);
|
||||||
|
else if (action == GLFW_REPEAT) push_event(EVENT_AGAIN_W);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLFW_KEY_D:
|
||||||
|
if (0) {}
|
||||||
|
else if (action == GLFW_PRESS) push_event(EVENT_PRESS_W);
|
||||||
|
else if (action == GLFW_REPEAT) push_event(EVENT_AGAIN_W);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cursor_callback(GLFWwindow *window, double x, double y)
|
||||||
|
{
|
||||||
|
push_event(EVENT_MOVE_MOUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void framebuffer_size_callback(GLFWwindow* window, int w, int h)
|
||||||
|
{
|
||||||
|
glViewport(0, 0, w, h);
|
||||||
|
screen_w = w;
|
||||||
|
screen_h = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startup_window_and_opengl_context_or_exit(int window_w, int window_h, const char *title)
|
||||||
|
{
|
||||||
|
glfwSetErrorCallback(error_callback);
|
||||||
|
|
||||||
|
if (!glfwInit())
|
||||||
|
exit(-1);
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
|
||||||
|
window = glfwCreateWindow(window_w, window_h, title, NULL, NULL);
|
||||||
|
if (!window) {
|
||||||
|
glfwTerminate();
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwSetKeyCallback(window, key_callback);
|
||||||
|
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||||
|
glfwSetCursorPosCallback(window, cursor_callback);
|
||||||
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||||
|
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
|
||||||
|
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
||||||
|
printf("Failed to initialize GLAD\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwSwapInterval(1);
|
||||||
|
|
||||||
|
glfwGetWindowSize(window, &screen_w, &screen_h);
|
||||||
|
|
||||||
|
screen_program = compile_shader("assets/screen.vs", "assets/screen.fs");
|
||||||
|
if (!screen_program) {
|
||||||
|
printf("Couldn't compile program\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
set_uniform_i(screen_program, "screenTexture", 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
float vertices[] = {
|
||||||
|
// positions // texCoords
|
||||||
|
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||||
|
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||||
|
1.0f, -1.0f, 1.0f, 0.0f,
|
||||||
|
|
||||||
|
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||||
|
1.0f, -1.0f, 1.0f, 0.0f,
|
||||||
|
1.0f, 1.0f, 1.0f, 1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &vao);
|
||||||
|
glGenBuffers(1, &vbo);
|
||||||
|
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenTextures(1, &frame_texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, frame_texture);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup_window_and_opengl_context(void)
|
||||||
|
{
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_screen_w(void)
|
||||||
|
{
|
||||||
|
return screen_w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_screen_h(void)
|
||||||
|
{
|
||||||
|
return screen_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_frame_to_the_gpu(int w, int h, Vector3 *data)
|
||||||
|
{
|
||||||
|
glBindTexture(GL_TEXTURE_2D, frame_texture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_FLOAT, data);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_frame(void)
|
||||||
|
{
|
||||||
|
Vector3 clear_color = {1, 1, 1};
|
||||||
|
//glViewport(0, 0, screen_w, screen_h);
|
||||||
|
glClearColor(clear_color.x, clear_color.y, clear_color.z, 1.0f);
|
||||||
|
glClearStencil(0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
|
|
||||||
|
glUseProgram(screen_program);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, frame_texture);
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
glfwPollEvents();
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include "vector.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *data[6];
|
||||||
|
int w, h, chan;
|
||||||
|
} Cubemap;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CF_FRONT,
|
||||||
|
CF_BACK,
|
||||||
|
CF_LEFT,
|
||||||
|
CF_RIGHT,
|
||||||
|
CF_TOP,
|
||||||
|
CF_BOTTOM,
|
||||||
|
} CubeFace;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EVENT_EMPTY = 0,
|
||||||
|
EVENT_CLOSE,
|
||||||
|
EVENT_PRESS_SPACE,
|
||||||
|
EVENT_PRESS_ESC,
|
||||||
|
EVENT_PRESS_W,
|
||||||
|
EVENT_PRESS_A,
|
||||||
|
EVENT_PRESS_S,
|
||||||
|
EVENT_PRESS_D,
|
||||||
|
EVENT_AGAIN_SPACE,
|
||||||
|
EVENT_AGAIN_ESC,
|
||||||
|
EVENT_AGAIN_W,
|
||||||
|
EVENT_AGAIN_A,
|
||||||
|
EVENT_AGAIN_S,
|
||||||
|
EVENT_AGAIN_D,
|
||||||
|
EVENT_MOVE_MOUSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
int pop_event(double *mouse_x, double *mouse_y);
|
||||||
|
|
||||||
|
void startup_window_and_opengl_context_or_exit(int window_w, int window_h, const char *title);
|
||||||
|
void cleanup_window_and_opengl_context(void);
|
||||||
|
|
||||||
|
int get_screen_w(void);
|
||||||
|
int get_screen_h(void);
|
||||||
|
|
||||||
|
void move_frame_to_the_gpu(int w, int h, Vector3 *data);
|
||||||
|
void draw_frame(void);
|
||||||
|
|
||||||
|
void load_cubemap(Cubemap *c, const char *files[6]);
|
||||||
|
void free_cubemap(Cubemap *c);
|
||||||
|
Vector3 sample_cubemap(Cubemap *c, Vector3 dir);
|
||||||
+179
-1233
File diff suppressed because it is too large
Load Diff
-372
@@ -1,372 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "utils.h"
|
|
||||||
#include "mesh.h"
|
|
||||||
|
|
||||||
#define TINYOBJ_LOADER_C_IMPLEMENTATION
|
|
||||||
#include "tinyobj_loader_c.h"
|
|
||||||
|
|
||||||
void append_vertex(VertexArray *array, Vertex v)
|
|
||||||
{
|
|
||||||
if (array->size == array->capacity) {
|
|
||||||
if (array->capacity == 0) {
|
|
||||||
array->data = malloc(8 * sizeof(Vertex));
|
|
||||||
array->capacity = 8;
|
|
||||||
} else {
|
|
||||||
array->data = realloc(array->data, 2 * array->capacity * sizeof(Vertex));
|
|
||||||
array->capacity *= 2;
|
|
||||||
}
|
|
||||||
if (!array->data) {
|
|
||||||
printf("OUT OF MEMORY\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
array->data[array->size++] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Vector3 get_sphere_point(float angle_x, float angle_y, float radius)
|
|
||||||
{
|
|
||||||
Vector3 p;
|
|
||||||
p.x = radius * sin(angle_y) * cos(2 * angle_x);
|
|
||||||
p.y = radius * cos(angle_y);
|
|
||||||
p.z = radius * sin(angle_y) * sin(2 * angle_x);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Vertex a;
|
|
||||||
Vertex b;
|
|
||||||
Vertex c;
|
|
||||||
} Triangle;
|
|
||||||
|
|
||||||
static void calculate_and_set_normals(Triangle *T)
|
|
||||||
{
|
|
||||||
#define X1 (T->b.x - T->a.x)
|
|
||||||
#define Y1 (T->b.y - T->a.y)
|
|
||||||
#define Z1 (T->b.z - T->a.z)
|
|
||||||
|
|
||||||
#define X2 (T->c.x - T->a.x)
|
|
||||||
#define Y2 (T->c.y - T->a.y)
|
|
||||||
#define Z2 (T->c.z - T->a.z)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* xnormal = y1*z2 - z1*y2
|
|
||||||
* ynormal = z1*x2 - x1*z2
|
|
||||||
* znormal = x1*y2 - y1*x2
|
|
||||||
*/
|
|
||||||
|
|
||||||
Vector3 n;
|
|
||||||
n.x = Y1 * Z2 - Z1 * Y2;
|
|
||||||
n.y = Z1 * X2 - X1 * Z2;
|
|
||||||
n.z = X1 * Y2 - Y1 * X2;
|
|
||||||
|
|
||||||
T->a.nx = n.x;
|
|
||||||
T->a.ny = n.y;
|
|
||||||
T->a.nz = n.z;
|
|
||||||
|
|
||||||
T->b.nx = n.x;
|
|
||||||
T->b.ny = n.y;
|
|
||||||
T->b.nz = n.z;
|
|
||||||
|
|
||||||
T->c.nx = n.x;
|
|
||||||
T->c.ny = n.y;
|
|
||||||
T->c.nz = n.z;
|
|
||||||
|
|
||||||
#undef X1
|
|
||||||
#undef Y1
|
|
||||||
#undef Z1
|
|
||||||
#undef X2
|
|
||||||
#undef Y2
|
|
||||||
#undef Z2
|
|
||||||
}
|
|
||||||
|
|
||||||
static Triangle make_triangle(Vector3 a, Vector3 b, Vector3 c)
|
|
||||||
{
|
|
||||||
Triangle T;
|
|
||||||
T.a = (Vertex) {a.x, a.y, a.z};
|
|
||||||
T.b = (Vertex) {b.x, b.y, b.z};
|
|
||||||
T.c = (Vertex) {c.x, c.y, c.z};
|
|
||||||
calculate_and_set_normals(&T);
|
|
||||||
return T;
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexArray make_sphere_mesh_2(float radius, int num_segms, bool fake_normals)
|
|
||||||
{
|
|
||||||
VertexArray vertices = {0, 0, 0};
|
|
||||||
|
|
||||||
int x_num_segms = num_segms;
|
|
||||||
int y_num_segms = num_segms;
|
|
||||||
for (int i = 0; i < x_num_segms; i++)
|
|
||||||
for (int j = 0; j < y_num_segms; j++) {
|
|
||||||
|
|
||||||
int g = j;
|
|
||||||
if (g == y_num_segms-1)
|
|
||||||
g = 0;
|
|
||||||
|
|
||||||
Vector3 p1 = get_sphere_point((i + 0) * 3.14 / x_num_segms, (g + 0) * 3.14 / y_num_segms, radius);
|
|
||||||
Vector3 p2 = get_sphere_point((i + 1) * 3.14 / x_num_segms, (g + 0) * 3.14 / y_num_segms, radius);
|
|
||||||
Vector3 p3 = get_sphere_point((i + 1) * 3.14 / x_num_segms, (g + 1) * 3.14 / y_num_segms, radius);
|
|
||||||
Vector3 p4 = get_sphere_point((i + 0) * 3.14 / x_num_segms, (g + 1) * 3.14 / y_num_segms, radius);
|
|
||||||
|
|
||||||
Triangle t1 = make_triangle(p1, p2, p3);
|
|
||||||
|
|
||||||
if (fake_normals) {
|
|
||||||
t1.a.nx = p1.x;
|
|
||||||
t1.a.ny = p1.y;
|
|
||||||
t1.a.nz = p1.z;
|
|
||||||
|
|
||||||
t1.b.nx = p2.x;
|
|
||||||
t1.b.ny = p2.y;
|
|
||||||
t1.b.nz = p2.z;
|
|
||||||
|
|
||||||
t1.c.nx = p3.x;
|
|
||||||
t1.c.ny = p3.y;
|
|
||||||
t1.c.nz = p3.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
append_vertex(&vertices, t1.a);
|
|
||||||
append_vertex(&vertices, t1.b);
|
|
||||||
append_vertex(&vertices, t1.c);
|
|
||||||
|
|
||||||
Triangle t2 = make_triangle(p4, p1, p3);
|
|
||||||
|
|
||||||
if (fake_normals) {
|
|
||||||
t2.a.nx = p4.x;
|
|
||||||
t2.a.ny = p4.y;
|
|
||||||
t2.a.nz = p4.z;
|
|
||||||
|
|
||||||
t2.b.nx = p1.x;
|
|
||||||
t2.b.ny = p1.y;
|
|
||||||
t2.b.nz = p1.z;
|
|
||||||
|
|
||||||
t2.c.nx = p3.x;
|
|
||||||
t2.c.ny = p3.y;
|
|
||||||
t2.c.nz = p3.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
append_vertex(&vertices, t2.a);
|
|
||||||
append_vertex(&vertices, t2.b);
|
|
||||||
append_vertex(&vertices, t2.c);
|
|
||||||
}
|
|
||||||
return vertices;
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexArray make_sphere_mesh(float radius)
|
|
||||||
{
|
|
||||||
return make_sphere_mesh_2(radius, 32, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexArray make_cube_mesh(void)
|
|
||||||
{
|
|
||||||
float vertices[] = {
|
|
||||||
|
|
||||||
1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
|
|
||||||
1.0, 0.0, 0.0, 0.0, 0.0, -1.0,
|
|
||||||
0.0, 0.0, 0.0, 0.0, 0.0, -1.0,
|
|
||||||
0.0, 1.0, 0.0, 0.0, 0.0, -1.0,
|
|
||||||
1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
|
|
||||||
0.0, 0.0, 0.0, 0.0, 0.0, -1.0,
|
|
||||||
|
|
||||||
0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
|
|
||||||
1.0, 0.0, 1.0, 0.0, 0.0, 1.0,
|
|
||||||
1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
|
|
||||||
0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
|
|
||||||
1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
|
|
||||||
0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
|
|
||||||
|
|
||||||
0.0, 0.0, 0.0, 0.0, -1.0, 0.0,
|
|
||||||
1.0, 0.0, 0.0, 0.0, -1.0, 0.0,
|
|
||||||
1.0, 0.0, 1.0, 0.0, -1.0, 0.0,
|
|
||||||
0.0, 0.0, 0.0, 0.0, -1.0, 0.0,
|
|
||||||
1.0, 0.0, 1.0, 0.0, -1.0, 0.0,
|
|
||||||
0.0, 0.0, 1.0, 0.0, -1.0, 0.0,
|
|
||||||
|
|
||||||
1.0, 1.0, 1.0, 0.0, 1.0, 0.0,
|
|
||||||
1.0, 1.0, 0.0, 0.0, 1.0, 0.0,
|
|
||||||
0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
|
|
||||||
0.0, 1.0, 1.0, 0.0, 1.0, 0.0,
|
|
||||||
1.0, 1.0, 1.0, 0.0, 1.0, 0.0,
|
|
||||||
0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
|
|
||||||
|
|
||||||
0.0, 1.0, 1.0, -1.0, 0.0, 0.0,
|
|
||||||
0.0, 1.0, 0.0, -1.0, 0.0, 0.0,
|
|
||||||
0.0, 0.0, 0.0, -1.0, 0.0, 0.0,
|
|
||||||
|
|
||||||
0.0, 0.0, 1.0, -1.0, 0.0, 0.0,
|
|
||||||
0.0, 1.0, 1.0, -1.0, 0.0, 0.0,
|
|
||||||
0.0, 0.0, 0.0, -1.0, 0.0, 0.0,
|
|
||||||
|
|
||||||
1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
|
|
||||||
1.0, 1.0, 0.0, 1.0, 0.0, 0.0,
|
|
||||||
1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
|
|
||||||
1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
|
|
||||||
1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
|
|
||||||
1.0, 0.0, 1.0, 1.0, 0.0, 0.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
VertexArray result = {0, 0, 0};
|
|
||||||
for (int i = 0; i < (int) (sizeof(vertices)/sizeof(vertices[0])); i += 6) {
|
|
||||||
Vertex v;
|
|
||||||
v.x = vertices[i + 0];
|
|
||||||
v.y = vertices[i + 1];
|
|
||||||
v.z = vertices[i + 2];
|
|
||||||
v.nx = vertices[i + 3];
|
|
||||||
v.ny = vertices[i + 4];
|
|
||||||
v.nz = vertices[i + 5];
|
|
||||||
v.tx = 0;
|
|
||||||
v.ty = 0;
|
|
||||||
append_vertex(&result, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
get_file_data_callback(void *context, const char *filename,
|
|
||||||
const int is_mtl, const char *obj_filename, char **data, size_t *len)
|
|
||||||
{
|
|
||||||
(void) context;
|
|
||||||
|
|
||||||
if (filename == NULL) {
|
|
||||||
fprintf(stderr, "null filename\n");
|
|
||||||
(*data) = NULL;
|
|
||||||
(*len) = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t data_len = 0;
|
|
||||||
|
|
||||||
printf("Reading file '%s'\n", filename);
|
|
||||||
*data = load_file(filename, &data_len);
|
|
||||||
(*len) = data_len;
|
|
||||||
|
|
||||||
char **free_me = context;
|
|
||||||
if (*free_me == NULL) *free_me = *data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool load_mesh_from_file(const char *file, VertexArray *result)
|
|
||||||
{
|
|
||||||
|
|
||||||
tinyobj_attrib_t attrib;
|
|
||||||
|
|
||||||
tinyobj_shape_t* shapes = NULL;
|
|
||||||
size_t num_shapes;
|
|
||||||
|
|
||||||
tinyobj_material_t* materials = NULL;
|
|
||||||
size_t num_materials;
|
|
||||||
|
|
||||||
char *free_me = NULL;
|
|
||||||
|
|
||||||
unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
|
|
||||||
int ret = tinyobj_parse_obj(&attrib, &shapes, &num_shapes, &materials, &num_materials, file, get_file_data_callback, &free_me, flags);
|
|
||||||
if (ret != TINYOBJ_SUCCESS) {
|
|
||||||
if (free_me) free(free_me);
|
|
||||||
printf("Failed loading '%s'\n", file);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = (VertexArray) {0, 0, 0};
|
|
||||||
|
|
||||||
size_t face_offset = 0;
|
|
||||||
for (int i = 0; i < (int) attrib.num_face_num_verts; i++) {
|
|
||||||
|
|
||||||
assert(attrib.face_num_verts[i] % 3 == 0); /* assume all triangle faces. */
|
|
||||||
for (size_t f = 0; f < (size_t)attrib.face_num_verts[i] / 3; f++) {
|
|
||||||
|
|
||||||
tinyobj_vertex_index_t idx0 = attrib.faces[face_offset + f * 3 + 0];
|
|
||||||
tinyobj_vertex_index_t idx1 = attrib.faces[face_offset + f * 3 + 1];
|
|
||||||
tinyobj_vertex_index_t idx2 = attrib.faces[face_offset + f * 3 + 2];
|
|
||||||
|
|
||||||
Vertex v0;
|
|
||||||
Vertex v1;
|
|
||||||
Vertex v2;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Positions
|
|
||||||
*/
|
|
||||||
|
|
||||||
v0.x = attrib.vertices[idx0.v_idx * 3 + 0];
|
|
||||||
v0.y = attrib.vertices[idx0.v_idx * 3 + 1];
|
|
||||||
v0.z = attrib.vertices[idx0.v_idx * 3 + 2];
|
|
||||||
|
|
||||||
v1.x = attrib.vertices[idx1.v_idx * 3 + 0];
|
|
||||||
v1.y = attrib.vertices[idx1.v_idx * 3 + 1];
|
|
||||||
v1.z = attrib.vertices[idx1.v_idx * 3 + 2];
|
|
||||||
|
|
||||||
v2.x = attrib.vertices[idx2.v_idx * 3 + 0];
|
|
||||||
v2.y = attrib.vertices[idx2.v_idx * 3 + 1];
|
|
||||||
v2.z = attrib.vertices[idx2.v_idx * 3 + 2];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Normals
|
|
||||||
*/
|
|
||||||
|
|
||||||
v0.nx = attrib.normals[idx0.vn_idx * 3 + 0];
|
|
||||||
v0.ny = attrib.normals[idx0.vn_idx * 3 + 1];
|
|
||||||
v0.nz = attrib.normals[idx0.vn_idx * 3 + 2];
|
|
||||||
|
|
||||||
v1.nx = attrib.normals[idx1.vn_idx * 3 + 0];
|
|
||||||
v1.ny = attrib.normals[idx1.vn_idx * 3 + 1];
|
|
||||||
v1.nz = attrib.normals[idx1.vn_idx * 3 + 2];
|
|
||||||
|
|
||||||
v2.nx = attrib.normals[idx2.vn_idx * 3 + 0];
|
|
||||||
v2.ny = attrib.normals[idx2.vn_idx * 3 + 1];
|
|
||||||
v2.nz = attrib.normals[idx2.vn_idx * 3 + 2];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Texture coordinates
|
|
||||||
*/
|
|
||||||
|
|
||||||
v0.tx = attrib.normals[idx0.vt_idx * 2 + 0];
|
|
||||||
v0.ty = attrib.normals[idx0.vt_idx * 2 + 1];
|
|
||||||
|
|
||||||
v1.tx = attrib.normals[idx1.vt_idx * 2 + 0];
|
|
||||||
v1.ty = attrib.normals[idx1.vt_idx * 2 + 1];
|
|
||||||
|
|
||||||
v2.tx = attrib.normals[idx2.vt_idx * 2 + 0];
|
|
||||||
v2.ty = attrib.normals[idx2.vt_idx * 2 + 1];
|
|
||||||
|
|
||||||
append_vertex(result, v0);
|
|
||||||
append_vertex(result, v1);
|
|
||||||
append_vertex(result, v2);
|
|
||||||
}
|
|
||||||
|
|
||||||
face_offset += (size_t)attrib.face_num_verts[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (free_me)
|
|
||||||
free(free_me);
|
|
||||||
|
|
||||||
tinyobj_attrib_free(&attrib);
|
|
||||||
tinyobj_shapes_free(shapes, num_shapes);
|
|
||||||
tinyobj_materials_free(materials, num_materials);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float x, y, z;
|
|
||||||
float nx, ny, nz;
|
|
||||||
float tx, ty;
|
|
||||||
} Vertex;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Vertex *data;
|
|
||||||
int size;
|
|
||||||
int capacity;
|
|
||||||
} VertexArray;
|
|
||||||
|
|
||||||
void append_vertex(VertexArray *array, Vertex v);
|
|
||||||
|
|
||||||
VertexArray make_sphere_mesh(float radius);
|
|
||||||
VertexArray make_sphere_mesh_2(float radius, int num_segms, bool fake_normals);
|
|
||||||
VertexArray make_cube_mesh(void);
|
|
||||||
|
|
||||||
bool load_mesh_from_file(const char *file, VertexArray *result);
|
|
||||||
@@ -0,0 +1,419 @@
|
|||||||
|
/*
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org/>
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define _POSIX_C_SOURCE 1999309L
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_MEAN_AND_LEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#define SYNC_PRINT_ERRORS
|
||||||
|
#ifdef SYNC_PRINT_ERRORS
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current absolute time in microsecods
|
||||||
|
* TODO: Specify since when the time is calculated
|
||||||
|
*/
|
||||||
|
uint64_t get_absolute_time_us(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
FILETIME filetime;
|
||||||
|
GetSystemTimePreciseAsFileTime(&filetime);
|
||||||
|
uint64_t time = (uint64_t) filetime.dwLowDateTime | ((uint64_t) filetime.dwHighDateTime << 32);
|
||||||
|
time /= 10;
|
||||||
|
return time;
|
||||||
|
#else
|
||||||
|
struct timespec buffer;
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &buffer))
|
||||||
|
abort();
|
||||||
|
uint64_t time = buffer.tv_sec * 1000000 + buffer.tv_nsec / 1000;
|
||||||
|
return time;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current time in nanoseconds since
|
||||||
|
* an unspecified point in time.
|
||||||
|
*/
|
||||||
|
uint64_t get_relative_time_ns(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
{
|
||||||
|
int64_t count;
|
||||||
|
int64_t freq;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
ok = QueryPerformanceCounter((LARGE_INTEGER*) &count);
|
||||||
|
if (!ok) abort();
|
||||||
|
|
||||||
|
ok = QueryPerformanceFrequency((LARGE_INTEGER*) &freq);
|
||||||
|
if (!ok) abort();
|
||||||
|
|
||||||
|
uint64_t res = 1000000000 * (double) count / freq;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
struct timespec time;
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &time))
|
||||||
|
abort();
|
||||||
|
|
||||||
|
uint64_t res;
|
||||||
|
|
||||||
|
uint64_t sec = time.tv_sec;
|
||||||
|
if (sec > UINT64_MAX / 1000000000)
|
||||||
|
abort();
|
||||||
|
res = sec * 1000000000;
|
||||||
|
|
||||||
|
uint64_t nsec = time.tv_nsec;
|
||||||
|
if (res > UINT64_MAX - nsec)
|
||||||
|
abort();
|
||||||
|
res += nsec;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleep_ms(float ms)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
Sleep(ms);
|
||||||
|
#else
|
||||||
|
usleep(ms*1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_mutex_create(os_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
InitializeCriticalSection(mutex);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_mutex_init(mutex, NULL))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_mutex_delete(os_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
DeleteCriticalSection(mutex);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_mutex_destroy(mutex))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_mutex_lock(os_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
EnterCriticalSection(mutex);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_mutex_lock(mutex))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_mutex_unlock(os_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
LeaveCriticalSection(mutex);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_mutex_unlock(mutex))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_condvar_create(os_condvar_t *condvar)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
InitializeConditionVariable(condvar);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_cond_init(condvar, NULL))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) condvar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_condvar_delete(os_condvar_t *condvar)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
if (pthread_cond_destroy(condvar))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) condvar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_condvar_wait(os_condvar_t *condvar, os_mutex_t *mutex, int timeout_ms)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
DWORD timeout = INFINITE;
|
||||||
|
if (timeout_ms >= 0) timeout = timeout_ms;
|
||||||
|
if (!SleepConditionVariableCS(condvar, mutex, timeout)) {
|
||||||
|
if (GetLastError() == ERROR_TIMEOUT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
|
||||||
|
int err;
|
||||||
|
if (timeout_ms < 0)
|
||||||
|
err = pthread_cond_wait(condvar, mutex);
|
||||||
|
else {
|
||||||
|
uint64_t wakeup_ms = (uint64_t) timeout_ms + get_absolute_time_us() / 1000;
|
||||||
|
struct timespec abstime = {
|
||||||
|
.tv_sec = wakeup_ms / 1000,
|
||||||
|
.tv_nsec = (wakeup_ms % 1000) * 1000000,
|
||||||
|
};
|
||||||
|
err = pthread_cond_timedwait(condvar, mutex, &abstime);
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
if (err == ETIMEDOUT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef SYNC_PRINT_ERRORS
|
||||||
|
fprintf(stderr, "ERROR!! pthread_cond_wait/timedwait: %s\n", strerror(err));
|
||||||
|
#endif
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
#else
|
||||||
|
(void) condvar;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_condvar_signal(os_condvar_t *condvar)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
WakeConditionVariable(condvar);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (pthread_cond_signal(condvar))
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
(void) condvar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void semaphore_create(semaphore_t *sem, int count)
|
||||||
|
{
|
||||||
|
sem->count = count;
|
||||||
|
os_mutex_create(&sem->mutex);
|
||||||
|
os_condvar_create(&sem->cond);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void semaphore_delete(semaphore_t *sem)
|
||||||
|
{
|
||||||
|
os_mutex_delete(&sem->mutex);
|
||||||
|
os_condvar_delete(&sem->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool semaphore_wait(semaphore_t *sem, int count, int timeout_ms)
|
||||||
|
{
|
||||||
|
assert(count > 0);
|
||||||
|
|
||||||
|
uint64_t start_time_ms = get_relative_time_ns() / 1000000;
|
||||||
|
|
||||||
|
os_mutex_lock(&sem->mutex);
|
||||||
|
while (sem->count < count) {
|
||||||
|
|
||||||
|
uint64_t current_time_ms = get_relative_time_ns() / 1000000;
|
||||||
|
|
||||||
|
int remaining_ms = -1;
|
||||||
|
if (timeout_ms >= 0)
|
||||||
|
remaining_ms = timeout_ms - (int) (current_time_ms - start_time_ms);
|
||||||
|
|
||||||
|
if (!os_condvar_wait(&sem->cond, &sem->mutex, remaining_ms)) {
|
||||||
|
os_mutex_unlock(&sem->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sem->count -= count;
|
||||||
|
os_mutex_unlock(&sem->mutex);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void semaphore_signal(semaphore_t *sem, int count)
|
||||||
|
{
|
||||||
|
assert(count > 0);
|
||||||
|
|
||||||
|
os_mutex_lock(&sem->mutex);
|
||||||
|
sem->count += count;
|
||||||
|
if (sem->count > 0)
|
||||||
|
os_condvar_signal(&sem->cond);
|
||||||
|
os_mutex_unlock(&sem->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_semaphore_create(os_semaphore_t *sem, int count, int max)
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
SECURITY_ATTRIBUTES *attr = NULL; // Default
|
||||||
|
const char *name = NULL; // No name
|
||||||
|
void *handle = CreateSemaphoreA(attr, count, max, name);
|
||||||
|
if (handle == NULL)
|
||||||
|
return false;
|
||||||
|
sem->data = handle;
|
||||||
|
ok = 1;
|
||||||
|
#else
|
||||||
|
(void) max; // POSIX doesn't use this
|
||||||
|
ok = sem_init(&sem->data, 0, count) == 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_semaphore_delete(os_semaphore_t *sem)
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
CloseHandle(sem->data);
|
||||||
|
ok = 1;
|
||||||
|
#else
|
||||||
|
ok = sem_destroy(&sem->data) == 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_semaphore_wait(os_semaphore_t *sem)
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
ok = WaitForSingleObject(sem->data, INFINITE) == WAIT_OBJECT_0;
|
||||||
|
#else
|
||||||
|
ok = sem_wait(&sem->data) == 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool os_semaphore_signal(os_semaphore_t *sem)
|
||||||
|
{
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
ok = ReleaseSemaphore(sem->data, 1, NULL);
|
||||||
|
#else
|
||||||
|
ok = sem_post(&sem->data) == 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_thread_create(os_thread *thread, void *arg, os_threadreturn (*func)(void*))
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
os_thread thread_ = CreateThread(NULL, 0, func, arg, 0, NULL);
|
||||||
|
if (thread_ == INVALID_HANDLE_VALUE)
|
||||||
|
abort();
|
||||||
|
*thread = thread_;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
int ret = pthread_create(thread, NULL, func, arg);
|
||||||
|
if (ret) abort();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
os_threadreturn os_thread_join(os_thread thread)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
os_threadreturn result;
|
||||||
|
WaitForSingleObject(thread, INFINITE);
|
||||||
|
if (!GetExitCodeThread(thread, &result))
|
||||||
|
abort();
|
||||||
|
CloseHandle(thread);
|
||||||
|
return result;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
os_threadreturn result;
|
||||||
|
int ret = pthread_join(thread, &result);
|
||||||
|
if (ret) abort();
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
(void) thread;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t get_thread_id(void)
|
||||||
|
{
|
||||||
|
static _Atomic uint64_t next_id = 1;
|
||||||
|
static _Thread_local uint64_t id = 0;
|
||||||
|
if (id == 0) id = atomic_fetch_add(&next_id, 1);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
+24
-1
@@ -28,6 +28,8 @@ For more information, please refer to <http://unlicense.org/>
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
|
|
||||||
|
// TODO: Clean up this file
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_MEAN_AND_LEAN
|
#define WIN32_MEAN_AND_LEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -54,6 +56,10 @@ typedef struct {
|
|||||||
os_condvar_t cond;
|
os_condvar_t cond;
|
||||||
} semaphore_t;
|
} semaphore_t;
|
||||||
|
|
||||||
|
uint64_t get_absolute_time_us(void);
|
||||||
|
uint64_t get_relative_time_ns(void);
|
||||||
|
void sleep_ms(float ms);
|
||||||
|
|
||||||
void os_mutex_create(os_mutex_t *mutex);
|
void os_mutex_create(os_mutex_t *mutex);
|
||||||
void os_mutex_delete(os_mutex_t *mutex);
|
void os_mutex_delete(os_mutex_t *mutex);
|
||||||
void os_mutex_lock (os_mutex_t *mutex);
|
void os_mutex_lock (os_mutex_t *mutex);
|
||||||
@@ -74,4 +80,21 @@ bool os_semaphore_delete(os_semaphore_t *sem);
|
|||||||
bool os_semaphore_wait (os_semaphore_t *sem);
|
bool os_semaphore_wait (os_semaphore_t *sem);
|
||||||
bool os_semaphore_signal(os_semaphore_t *sem);
|
bool os_semaphore_signal(os_semaphore_t *sem);
|
||||||
|
|
||||||
profile_results_t sync_profile_results(void);
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define WIN32_MEAN_AND_LEAN
|
||||||
|
#include <windows.h>
|
||||||
|
typedef void *os_thread;
|
||||||
|
typedef unsigned long os_threadreturn;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include <pthread.h>
|
||||||
|
typedef pthread_t os_thread;
|
||||||
|
typedef void *os_threadreturn;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint64_t get_thread_id(void);
|
||||||
|
|
||||||
|
void os_thread_create(os_thread *thread, void *arg, os_threadreturn (*func)(void*));
|
||||||
|
os_threadreturn os_thread_join(os_thread thread);
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "profile.h"
|
|
||||||
|
|
||||||
void human_readable_time_interval(uint64_t ns, char *dst, size_t max)
|
|
||||||
{
|
|
||||||
if (ns > 1000000000)
|
|
||||||
snprintf(dst, max, "%.1Lf s", (long double) ns / 1000000000);
|
|
||||||
else if (ns > 1000000)
|
|
||||||
snprintf(dst, max, "%.1Lf ms", (long double) ns / 1000000);
|
|
||||||
else if (ns > 1000)
|
|
||||||
snprintf(dst, max, "%.1Lf us", (long double) ns / 1000);
|
|
||||||
else
|
|
||||||
snprintf(dst, max, "%.1Lf ns", (long double) ns);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sort_callback(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
const profile_t *p1 = a;
|
|
||||||
const profile_t *p2 = b;
|
|
||||||
uint64_t n1 = atomic_load(&p1->elapsed_cycles);
|
|
||||||
uint64_t n2 = atomic_load(&p2->elapsed_cycles);
|
|
||||||
if (n1 > n2) return +1;
|
|
||||||
if (n1 < n2) return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_profile_results(profile_results_t res_list[], int num_results, long double ns_per_cycle)
|
|
||||||
{
|
|
||||||
int entry_count = 0;
|
|
||||||
for (int i = 0; i < num_results; i++)
|
|
||||||
entry_count += res_list[i].count;
|
|
||||||
|
|
||||||
profile_t entries_[128];
|
|
||||||
profile_t *entries = entries_;
|
|
||||||
|
|
||||||
if (entry_count >= (int) (sizeof(entries_)/sizeof(profile_t))) {
|
|
||||||
entries = malloc(entry_count * sizeof(profile_t));
|
|
||||||
if (entries == NULL) abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0, k = 0; i < num_results; i++)
|
|
||||||
for (int j = 0; j < res_list[i].count; j++)
|
|
||||||
entries[k++] = res_list[i].array[j];
|
|
||||||
|
|
||||||
qsort(entries, entry_count, sizeof(profile_t), sort_callback);
|
|
||||||
|
|
||||||
if (entry_count > 0)
|
|
||||||
fprintf(stderr, "| %-30s | %-10s | %-10s | %-10s |\n",
|
|
||||||
"Name", "Calls", "Total", "Latency");
|
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < entry_count; i++) {
|
|
||||||
if (!entries[i].name) continue;
|
|
||||||
int call_count = entries[i].call_count;
|
|
||||||
uint64_t elapsed_cycles = entries[i].elapsed_cycles;
|
|
||||||
|
|
||||||
long double total_ns = ns_per_cycle * elapsed_cycles;
|
|
||||||
long double latency_ns = total_ns / call_count;
|
|
||||||
|
|
||||||
char total[128];
|
|
||||||
human_readable_time_interval(total_ns, total, sizeof(total));
|
|
||||||
|
|
||||||
char latency[128];
|
|
||||||
human_readable_time_interval(latency_ns, latency, sizeof(latency));
|
|
||||||
|
|
||||||
fprintf(stderr, "| %-30s | %-10d | %-10s | %-10s |\n",
|
|
||||||
entries[i].name, call_count, total, latency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
#ifndef PROFILE_H
|
|
||||||
#define PROFILE_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <x86intrin.h>
|
|
||||||
#include <stdatomic.h>
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char *_Atomic name;
|
|
||||||
_Atomic uint64_t elapsed_cycles;
|
|
||||||
_Atomic uint64_t call_count;
|
|
||||||
} profile_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
profile_t *array;
|
|
||||||
int count;
|
|
||||||
} profile_results_t;
|
|
||||||
|
|
||||||
#ifdef PROFILE
|
|
||||||
|
|
||||||
#define PROFILE_START \
|
|
||||||
profile_t *profile__ = &profile_table__[__COUNTER__]; \
|
|
||||||
profile__->name = __func__; \
|
|
||||||
uint64_t profile_start__ = __rdtsc();
|
|
||||||
|
|
||||||
#define PROFILE_END \
|
|
||||||
atomic_fetch_add(&profile__->elapsed_cycles, __rdtsc() - profile_start__); \
|
|
||||||
atomic_fetch_add(&profile__->call_count, 1);
|
|
||||||
|
|
||||||
#define PROFILE_GLOBAL_START \
|
|
||||||
static profile_t profile_table__[];
|
|
||||||
|
|
||||||
#define PROFILE_GLOBAL_END \
|
|
||||||
static profile_t profile_table__[__COUNTER__];
|
|
||||||
|
|
||||||
#define PROFILE_RESULTS (profile_results_t) {profile_table__, sizeof(profile_table__) / sizeof(profile_table__[0])}
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define PROFILE_START
|
|
||||||
#define PROFILE_END
|
|
||||||
#define PROFILE_GLOBAL_START
|
|
||||||
#define PROFILE_GLOBAL_END
|
|
||||||
#define PROFILE_RESULTS (profile_results_t) {NULL, 0}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void human_readable_time_interval(uint64_t ns, char *dst, size_t max);
|
|
||||||
void print_profile_results(profile_results_t res_list[], int num_results, long double ns_per_cycle);
|
|
||||||
+624
@@ -0,0 +1,624 @@
|
|||||||
|
#include <math.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "scene.h"
|
||||||
|
|
||||||
|
Vector3 origin_of(Object o)
|
||||||
|
{
|
||||||
|
if (o.type == OBJECT_SPHERE)
|
||||||
|
return o.sphere.center;
|
||||||
|
return combine(o.cube.origin, o.cube.size, 1, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool intersect_cube(Ray r, Cube c, float *tnear, float *tfar, Vector3 *normal)
|
||||||
|
{
|
||||||
|
float txmin, txmax;
|
||||||
|
float tymin, tymax;
|
||||||
|
float tzmin, tzmax;
|
||||||
|
|
||||||
|
float tn;
|
||||||
|
float tf;
|
||||||
|
|
||||||
|
Vector3 a = c.origin;
|
||||||
|
Vector3 b = combine(c.origin, c.size, 1, 1);
|
||||||
|
|
||||||
|
int hit_axis = 0; // 0=x, 1=y, 2=z
|
||||||
|
|
||||||
|
if (r.direction.x >= 0) {
|
||||||
|
txmin = (a.x - r.origin.x) / r.direction.x;
|
||||||
|
txmax = (b.x - r.origin.x) / r.direction.x;
|
||||||
|
} else {
|
||||||
|
txmax = (a.x - r.origin.x) / r.direction.x;
|
||||||
|
txmin = (b.x - r.origin.x) / r.direction.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.direction.y >= 0) {
|
||||||
|
tymin = (a.y - r.origin.y) / r.direction.y;
|
||||||
|
tymax = (b.y - r.origin.y) / r.direction.y;
|
||||||
|
} else {
|
||||||
|
tymax = (a.y - r.origin.y) / r.direction.y;
|
||||||
|
tymin = (b.y - r.origin.y) / r.direction.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txmin > tymax || tymin > txmax)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (tymin > txmin) { txmin = tymin; hit_axis = 1; }
|
||||||
|
if (tymax < txmax) txmax = tymax;
|
||||||
|
|
||||||
|
if (r.direction.z >= 0) {
|
||||||
|
tzmin = (a.z - r.origin.z) / r.direction.z;
|
||||||
|
tzmax = (b.z - r.origin.z) / r.direction.z;
|
||||||
|
} else {
|
||||||
|
tzmax = (a.z - r.origin.z) / r.direction.z;
|
||||||
|
tzmin = (b.z - r.origin.z) / r.direction.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txmin > tzmax || tzmin > txmax)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (tzmin > txmin) { txmin = tzmin; hit_axis = 2; };
|
||||||
|
if (tzmax < txmax) txmax = tzmax;
|
||||||
|
|
||||||
|
if (tnear) *tnear = txmin;
|
||||||
|
if (tfar) *tfar = txmax;
|
||||||
|
if (normal) {
|
||||||
|
switch (hit_axis) {
|
||||||
|
case 0: *normal = r.direction.x > 0 ? (Vector3) {-1, 0, 0} : (Vector3) {1, 0, 0}; break;
|
||||||
|
case 1: *normal = r.direction.y > 0 ? (Vector3) {0, -1, 0} : (Vector3) {0, 1, 0}; break;
|
||||||
|
case 2: *normal = r.direction.z > 0 ? (Vector3) {0, 0, -1} : (Vector3) {0, 0, 1}; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool intersect_sphere(Ray r, Sphere s, float *t)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Any point of the ray can be written as
|
||||||
|
*
|
||||||
|
* P(t) = O + t * D
|
||||||
|
*
|
||||||
|
* with O origin and D direction.
|
||||||
|
*
|
||||||
|
* All points P=(x,y,z) of a sphere can be described as
|
||||||
|
* those (and only those) that satisfy the equation
|
||||||
|
*
|
||||||
|
* x^2 + y^2 + z^2 = R^2
|
||||||
|
* P^2 - R^2 = 0
|
||||||
|
*
|
||||||
|
* with R radius of the sphere. The sphere here is centered
|
||||||
|
* at the origin.
|
||||||
|
*
|
||||||
|
* Intersection points of the ray with the sphere must satisfy
|
||||||
|
* both:
|
||||||
|
*
|
||||||
|
* P(t) = O + t * D
|
||||||
|
* P^2 - R^2 = 0
|
||||||
|
*
|
||||||
|
* => (O + tD)^2 - R^2 = 0
|
||||||
|
* => t^2 * D^2 + t * 2OD + O^2 - R^2 = 0
|
||||||
|
*
|
||||||
|
* we can use the quadratic formula here, and more specifically
|
||||||
|
* the discriminant to check if solutions exist and how many
|
||||||
|
*/
|
||||||
|
Vector3 oc = combine(s.center, r.origin, 1, -1);
|
||||||
|
float a = dotv(r.direction, r.direction);
|
||||||
|
float b = -2 * dotv(oc, r.direction);
|
||||||
|
float c = dotv(oc, oc) - s.radius * s.radius;
|
||||||
|
|
||||||
|
float discr = b*b - 4*a*c;
|
||||||
|
|
||||||
|
if (discr > 0) {
|
||||||
|
float s0 = (- b + sqrt(discr)) / (2 * a);
|
||||||
|
float s1 = (- b - sqrt(discr)) / (2 * a);
|
||||||
|
if (s0 > s1) {
|
||||||
|
float tmp = s0;
|
||||||
|
s0 = s1;
|
||||||
|
s1 = tmp;
|
||||||
|
}
|
||||||
|
if (s0 < 0) {
|
||||||
|
s0 = s1;
|
||||||
|
if (s0 < 0) return false;
|
||||||
|
}
|
||||||
|
if (t) *t = s0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero solutions
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool intersect_object(Ray r, Object o, float *t, Vector3 *normal)
|
||||||
|
{
|
||||||
|
switch (o.type) {
|
||||||
|
|
||||||
|
case OBJECT_CUBE:
|
||||||
|
return intersect_cube(r, o.cube, t, NULL, normal);
|
||||||
|
|
||||||
|
case OBJECT_SPHERE:
|
||||||
|
if (intersect_sphere(r, o.sphere, t)) {
|
||||||
|
if (normal) {
|
||||||
|
Vector3 hit_point = combine(r.origin, r.direction, 1, *t);
|
||||||
|
*normal = normalize(combine(hit_point, o.sphere.center, 1, -1));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HitInfo trace_ray(Ray ray, Scene *scene)
|
||||||
|
{
|
||||||
|
ray.direction = normalize(ray.direction);
|
||||||
|
|
||||||
|
float nearest_t = FLT_MAX;
|
||||||
|
int nearest_object = -1;
|
||||||
|
Vector3 nearest_normal;
|
||||||
|
for (int i = 0; i < scene->num_objects; i++) {
|
||||||
|
float t;
|
||||||
|
Vector3 n;
|
||||||
|
if (!intersect_object(ray, scene->objects[i], &t, &n))
|
||||||
|
continue;
|
||||||
|
if (t >= 0 && t < nearest_t) {
|
||||||
|
nearest_t = t;
|
||||||
|
nearest_object = i;
|
||||||
|
nearest_normal = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearest_object == -1) {
|
||||||
|
HitInfo result;
|
||||||
|
result.distance = -1;
|
||||||
|
result.normal = (Vector3) {0, 0, 0};
|
||||||
|
result.point = (Vector3) {0, 0, 0};
|
||||||
|
result.object = -1;
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
HitInfo result;
|
||||||
|
result.distance = nearest_t;
|
||||||
|
result.normal = nearest_normal;
|
||||||
|
result.point = combine(ray.origin, ray.direction, 1, nearest_t);
|
||||||
|
result.object = nearest_object;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PROP_ALBEDO,
|
||||||
|
PROP_ROUGHNESS,
|
||||||
|
PROP_REFLECTANCE,
|
||||||
|
PROP_METALLIC,
|
||||||
|
PROP_EMISSION_POWER,
|
||||||
|
PROP_EMISSION_COLOR,
|
||||||
|
PROP_RADIUS,
|
||||||
|
PROP_CENTER,
|
||||||
|
PROP_ORIGIN,
|
||||||
|
PROP_SIZE,
|
||||||
|
} Property;
|
||||||
|
|
||||||
|
static bool parse_scene_string(char *src, size_t len, Scene *scene)
|
||||||
|
{
|
||||||
|
scene->num_objects = 0;
|
||||||
|
|
||||||
|
int line = 1;
|
||||||
|
size_t i = 0;
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
while (i < len && is_space(src[i])) {
|
||||||
|
if (src[i] == '\n') line++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Object object;
|
||||||
|
|
||||||
|
if (5 < len - i
|
||||||
|
&& src[i+0] == 's'
|
||||||
|
&& src[i+1] == 'p'
|
||||||
|
&& src[i+2] == 'h'
|
||||||
|
&& src[i+3] == 'e'
|
||||||
|
&& src[i+4] == 'r'
|
||||||
|
&& src[i+5] == 'e') {
|
||||||
|
object.type = OBJECT_SPHERE;
|
||||||
|
object.sphere.center = (Vector3) {0, 0, 0};
|
||||||
|
object.sphere.radius = 1;
|
||||||
|
object.material.albedo = (Vector3) {0.44, 0.68, 0.84};
|
||||||
|
object.material.roughness = 0;
|
||||||
|
object.material.reflectance = 0.2;
|
||||||
|
object.material.metallic = 0;
|
||||||
|
object.material.emission_power = 0;
|
||||||
|
object.material.emission_color = (Vector3) {1, 1, 1};
|
||||||
|
i += 6;
|
||||||
|
} else if (3 < len - i
|
||||||
|
&& src[i+0] == 'c'
|
||||||
|
&& src[i+1] == 'u'
|
||||||
|
&& src[i+2] == 'b'
|
||||||
|
&& src[i+3] == 'e') {
|
||||||
|
object.type = OBJECT_CUBE;
|
||||||
|
object.cube.origin = (Vector3) {0, 0, 0};
|
||||||
|
object.cube.size = (Vector3) {1, 1, 1};
|
||||||
|
object.material.albedo = (Vector3) {0.44, 0.68, 0.84};
|
||||||
|
object.material.roughness = 0;
|
||||||
|
object.material.reflectance = 0.2;
|
||||||
|
object.material.metallic = 0;
|
||||||
|
object.material.emission_power = 0;
|
||||||
|
object.material.emission_color = (Vector3) {1, 1, 1};
|
||||||
|
i += 4;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error: Invalid character (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
// Skip spaces before property
|
||||||
|
while (i < len && is_space(src[i])) {
|
||||||
|
if (src[i] == '\n') line++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int valuetype; // 0 for float, 1 for color (3 floats)
|
||||||
|
|
||||||
|
Property prop;
|
||||||
|
if (6 < len - i
|
||||||
|
&& src[i+0] == 'a'
|
||||||
|
&& src[i+1] == 'l'
|
||||||
|
&& src[i+2] == 'b'
|
||||||
|
&& src[i+3] == 'e'
|
||||||
|
&& src[i+4] == 'd'
|
||||||
|
&& src[i+5] == 'o') {
|
||||||
|
valuetype = 1;
|
||||||
|
prop = PROP_ALBEDO;
|
||||||
|
i += 9;
|
||||||
|
} else if (8 < len - i
|
||||||
|
&& src[i+0] == 'r'
|
||||||
|
&& src[i+1] == 'o'
|
||||||
|
&& src[i+2] == 'u'
|
||||||
|
&& src[i+3] == 'g'
|
||||||
|
&& src[i+4] == 'h'
|
||||||
|
&& src[i+5] == 'n'
|
||||||
|
&& src[i+6] == 'e'
|
||||||
|
&& src[i+7] == 's'
|
||||||
|
&& src[i+8] == 's') {
|
||||||
|
valuetype = 0;
|
||||||
|
prop = PROP_ROUGHNESS;
|
||||||
|
i += 9;
|
||||||
|
} else if (10 < len - i
|
||||||
|
&& src[i+0] == 'r'
|
||||||
|
&& src[i+1] == 'e'
|
||||||
|
&& src[i+2] == 'f'
|
||||||
|
&& src[i+3] == 'l'
|
||||||
|
&& src[i+4] == 'e'
|
||||||
|
&& src[i+5] == 'c'
|
||||||
|
&& src[i+6] == 't'
|
||||||
|
&& src[i+7] == 'a'
|
||||||
|
&& src[i+8] == 'n'
|
||||||
|
&& src[i+9] == 'c'
|
||||||
|
&& src[i+10] == 'e') {
|
||||||
|
valuetype = 0;
|
||||||
|
prop = PROP_REFLECTANCE;
|
||||||
|
i += 11;
|
||||||
|
} else if (7 < len - i
|
||||||
|
&& src[i+0] == 'm'
|
||||||
|
&& src[i+1] == 'e'
|
||||||
|
&& src[i+2] == 't'
|
||||||
|
&& src[i+3] == 'a'
|
||||||
|
&& src[i+4] == 'l'
|
||||||
|
&& src[i+5] == 'l'
|
||||||
|
&& src[i+6] == 'i'
|
||||||
|
&& src[i+7] == 'c') {
|
||||||
|
valuetype = 0;
|
||||||
|
prop = PROP_METALLIC;
|
||||||
|
i += 11;
|
||||||
|
} else if (13 < len - i
|
||||||
|
&& src[i+0] == 'e'
|
||||||
|
&& src[i+1] == 'm'
|
||||||
|
&& src[i+2] == 'i'
|
||||||
|
&& src[i+3] == 's'
|
||||||
|
&& src[i+4] == 's'
|
||||||
|
&& src[i+5] == 'i'
|
||||||
|
&& src[i+6] == 'o'
|
||||||
|
&& src[i+7] == 'n'
|
||||||
|
&& src[i+8] == '_'
|
||||||
|
&& src[i+9] == 'p'
|
||||||
|
&& src[i+10] == 'o'
|
||||||
|
&& src[i+11] == 'w'
|
||||||
|
&& src[i+12] == 'e'
|
||||||
|
&& src[i+13] == 'r') {
|
||||||
|
valuetype = 0;
|
||||||
|
prop = PROP_EMISSION_POWER;
|
||||||
|
i += 14;
|
||||||
|
} else if (13 < len - i
|
||||||
|
&& src[i+0] == 'e'
|
||||||
|
&& src[i+1] == 'm'
|
||||||
|
&& src[i+2] == 'i'
|
||||||
|
&& src[i+3] == 's'
|
||||||
|
&& src[i+4] == 's'
|
||||||
|
&& src[i+5] == 'i'
|
||||||
|
&& src[i+6] == 'o'
|
||||||
|
&& src[i+7] == 'n'
|
||||||
|
&& src[i+8] == '_'
|
||||||
|
&& src[i+9] == 'c'
|
||||||
|
&& src[i+10] == 'o'
|
||||||
|
&& src[i+11] == 'l'
|
||||||
|
&& src[i+12] == 'o'
|
||||||
|
&& src[i+13] == 'r') {
|
||||||
|
valuetype = 1;
|
||||||
|
prop = PROP_EMISSION_COLOR;
|
||||||
|
i += 14;
|
||||||
|
} else if (5 < len - i
|
||||||
|
&& src[i+0] == 'r'
|
||||||
|
&& src[i+1] == 'a'
|
||||||
|
&& src[i+2] == 'd'
|
||||||
|
&& src[i+3] == 'i'
|
||||||
|
&& src[i+4] == 'u'
|
||||||
|
&& src[i+5] == 's') {
|
||||||
|
if (object.type != OBJECT_SPHERE) {
|
||||||
|
fprintf(stderr, "Poperty 'radius' only allowed on spheres (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
valuetype = 0;
|
||||||
|
prop = PROP_RADIUS;
|
||||||
|
i += 6;
|
||||||
|
} else if (5 < len - i
|
||||||
|
&& src[i+0] == 'c'
|
||||||
|
&& src[i+1] == 'e'
|
||||||
|
&& src[i+2] == 'n'
|
||||||
|
&& src[i+3] == 't'
|
||||||
|
&& src[i+4] == 'e'
|
||||||
|
&& src[i+5] == 'r') {
|
||||||
|
if (object.type != OBJECT_SPHERE) {
|
||||||
|
fprintf(stderr, "Poperty 'center' only allowed on spheres (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
valuetype = 1;
|
||||||
|
prop = PROP_CENTER;
|
||||||
|
i += 6;
|
||||||
|
} else if (5 < len - i
|
||||||
|
&& src[i+0] == 'o'
|
||||||
|
&& src[i+1] == 'r'
|
||||||
|
&& src[i+2] == 'i'
|
||||||
|
&& src[i+3] == 'g'
|
||||||
|
&& src[i+4] == 'i'
|
||||||
|
&& src[i+5] == 'n') {
|
||||||
|
if (object.type != OBJECT_CUBE) {
|
||||||
|
fprintf(stderr, "Poperty 'origin' only allowed on cubes (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
valuetype = 1;
|
||||||
|
prop = PROP_ORIGIN;
|
||||||
|
i += 6;
|
||||||
|
} else if (3 < len - i
|
||||||
|
&& src[i+0] == 's'
|
||||||
|
&& src[i+1] == 'i'
|
||||||
|
&& src[i+2] == 'z'
|
||||||
|
&& src[i+3] == 'e') {
|
||||||
|
if (object.type != OBJECT_CUBE) {
|
||||||
|
fprintf(stderr, "Poperty 'size' only allowed on cubes (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
valuetype = 1;
|
||||||
|
prop = PROP_SIZE;
|
||||||
|
i += 4;
|
||||||
|
} else
|
||||||
|
// Not a valid property name
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Consume spaces before the value
|
||||||
|
while (i < len && is_space(src[i])) {
|
||||||
|
if (src[i] == '\n') line++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i == len) {
|
||||||
|
fprintf(stderr, "Error: Property value is missing (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float value0;
|
||||||
|
Vector3 value1;
|
||||||
|
if (valuetype == 0) {
|
||||||
|
// Parse a single float
|
||||||
|
int sign = 1;
|
||||||
|
if (src[i] == '-') {
|
||||||
|
sign = -1;
|
||||||
|
i++;
|
||||||
|
if (i == len || !is_digit(src[i])) {
|
||||||
|
fprintf(stderr, "Error: Missing number after minus sign (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!is_digit(src[i])) {
|
||||||
|
fprintf(stderr, "Error: Missing number after property name (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value0 = 0;
|
||||||
|
do {
|
||||||
|
int d = src[i] - '0';
|
||||||
|
value0 = value0 * 10 + d;
|
||||||
|
i++;
|
||||||
|
} while (i < len && is_digit(src[i]));
|
||||||
|
if (i < len && src[i] == '.') {
|
||||||
|
i++; // Skip the dot
|
||||||
|
if (i == len || !is_digit(src[i])) {
|
||||||
|
fprintf(stderr, "Error: Missing decimal part after dot (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
float q = 1.0f / 10;
|
||||||
|
do {
|
||||||
|
int d = src[i] - '0';
|
||||||
|
value0 += q * d;
|
||||||
|
q /= 10;
|
||||||
|
i++;
|
||||||
|
} while (i < len && is_digit(src[i]));
|
||||||
|
}
|
||||||
|
value0 *= sign;
|
||||||
|
} else {
|
||||||
|
assert(valuetype == 1);
|
||||||
|
|
||||||
|
if (src[i] != '{') {
|
||||||
|
fprintf(stderr, "Error: Missing '{' after property name (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
float temp[3];
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
|
||||||
|
while (i < len && is_space(src[i])) {
|
||||||
|
if (src[i] == '\n') line++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sign = 1;
|
||||||
|
if (src[i] == '-') {
|
||||||
|
sign = -1;
|
||||||
|
i++;
|
||||||
|
if (i == len || !is_digit(src[i])) {
|
||||||
|
fprintf(stderr, "Error: Missing number after minus sign (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!is_digit(src[i])) {
|
||||||
|
fprintf(stderr, "Error: Missing number %d in vector value (line %d)\n", j, line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
temp[j] = 0;
|
||||||
|
do {
|
||||||
|
int d = src[i] - '0';
|
||||||
|
temp[j] = temp[j] * 10 + d;
|
||||||
|
i++;
|
||||||
|
} while (i < len && is_digit(src[i]));
|
||||||
|
if (i < len && src[i] == '.') {
|
||||||
|
i++; // Skip the dot
|
||||||
|
if (i == len || !is_digit(src[i])) {
|
||||||
|
fprintf(stderr, "Error: Missing decimal part after dot (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
float q = 1.0f / 10;
|
||||||
|
do {
|
||||||
|
int d = src[i] - '0';
|
||||||
|
temp[j] += q * d;
|
||||||
|
q /= 10;
|
||||||
|
i++;
|
||||||
|
} while (i < len && is_digit(src[i]));
|
||||||
|
}
|
||||||
|
temp[j] *= sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i < len && is_space(src[i])) {
|
||||||
|
if (src[i] == '\n') line++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == len || src[i] != '}') {
|
||||||
|
fprintf(stderr, "Error: Missing '}' after property value (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
value1.x = temp[0];
|
||||||
|
value1.y = temp[1];
|
||||||
|
value1.z = temp[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (prop) {
|
||||||
|
|
||||||
|
case PROP_ALBEDO:
|
||||||
|
if (value1.x < 0 || value1.x > 1 ||
|
||||||
|
value1.y < 0 || value1.y > 1 ||
|
||||||
|
value1.z < 0 || value1.z > 1) {
|
||||||
|
fprintf(stderr, "Error: albedo values must be between 0 and 1 (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
object.material.albedo = value1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_ROUGHNESS:
|
||||||
|
if (value0 < 0 || value0 > 1) {
|
||||||
|
fprintf(stderr, "Error: Roughness must be between 0 and 1 (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
object.material.roughness = value0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_REFLECTANCE:
|
||||||
|
if (value0 < 0 || value0 > 1) {
|
||||||
|
fprintf(stderr, "Error: Reflectance must be between 0 and 1 (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
object.material.reflectance = value0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_METALLIC:
|
||||||
|
if (value0 < 0 || value0 > 1) {
|
||||||
|
fprintf(stderr, "Error: Metallic must be between 0 and 1 (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
object.material.metallic = value0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_EMISSION_POWER:
|
||||||
|
object.material.emission_power = value0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_EMISSION_COLOR:
|
||||||
|
if (value1.x < 0 || value1.x > 1 ||
|
||||||
|
value1.y < 0 || value1.y > 1 ||
|
||||||
|
value1.z < 0 || value1.z > 1) {
|
||||||
|
fprintf(stderr, "Error: Emission color values must be between 0 and 1 (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
object.material.emission_color = value1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_RADIUS:
|
||||||
|
object.sphere.radius = value0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_CENTER:
|
||||||
|
object.sphere.center = value1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_ORIGIN:
|
||||||
|
object.cube.origin = value1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_SIZE:
|
||||||
|
if (value1.x < 0 || value1.y < 0 || value1.z < 0) {
|
||||||
|
fprintf(stderr, "Error: Size values must be positive (line %d)\n", line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
object.cube.size = value1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scene->num_objects == MAX_OBJECTS)
|
||||||
|
fprintf(stderr, "Warning: Ignoring object because the scene is too big (line %d)\n", line);
|
||||||
|
else
|
||||||
|
scene->objects[scene->num_objects++] = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_scene_file(char *file, Scene *scene)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char *src = load_file(file, &len);
|
||||||
|
if (src == NULL) {
|
||||||
|
fprintf(stderr, "Error: Couldn't open scene file\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = parse_scene_string(src, len, scene);
|
||||||
|
|
||||||
|
free(src);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#include "vector.h"
|
||||||
|
|
||||||
|
#define MAX_OBJECTS 1024
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Vector3 albedo;
|
||||||
|
float roughness;
|
||||||
|
float reflectance;
|
||||||
|
float metallic;
|
||||||
|
float emission_power;
|
||||||
|
Vector3 emission_color;
|
||||||
|
} Material;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Vector3 origin;
|
||||||
|
Vector3 size;
|
||||||
|
} Cube;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OBJECT_CUBE,
|
||||||
|
OBJECT_SPHERE,
|
||||||
|
} ObjectType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ObjectType type;
|
||||||
|
union {
|
||||||
|
Sphere sphere;
|
||||||
|
Cube cube;
|
||||||
|
};
|
||||||
|
Material material;
|
||||||
|
} Object;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Object objects[MAX_OBJECTS];
|
||||||
|
int num_objects;
|
||||||
|
} Scene;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float distance;
|
||||||
|
Vector3 point;
|
||||||
|
Vector3 normal;
|
||||||
|
int object;
|
||||||
|
} HitInfo;
|
||||||
|
|
||||||
|
Vector3 origin_of(Object o);
|
||||||
|
HitInfo trace_ray(Ray ray, Scene *scene);
|
||||||
|
bool parse_scene_file(char *file, Scene *scene);
|
||||||
-352
@@ -1,352 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
#include <errno.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "sync.h"
|
|
||||||
#include "clock.h"
|
|
||||||
|
|
||||||
//#define SYNC_PRINT_ERRORS
|
|
||||||
#ifdef SYNC_PRINT_ERRORS
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_GLOBAL_START;
|
|
||||||
|
|
||||||
void os_mutex_create(os_mutex_t *mutex)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
InitializeCriticalSection(mutex);
|
|
||||||
#elif defined(__linux__)
|
|
||||||
if (pthread_mutex_init(mutex, NULL))
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
(void) mutex;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void os_mutex_delete(os_mutex_t *mutex)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
DeleteCriticalSection(mutex);
|
|
||||||
#elif defined(__linux__)
|
|
||||||
if (pthread_mutex_destroy(mutex))
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
(void) mutex;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void os_mutex_lock(os_mutex_t *mutex)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
EnterCriticalSection(mutex);
|
|
||||||
#elif defined(__linux__)
|
|
||||||
if (pthread_mutex_lock(mutex))
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
(void) mutex;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void os_mutex_unlock(os_mutex_t *mutex)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
LeaveCriticalSection(mutex);
|
|
||||||
#elif defined(__linux__)
|
|
||||||
if (pthread_mutex_unlock(mutex))
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
(void) mutex;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void os_condvar_create(os_condvar_t *condvar)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
InitializeConditionVariable(condvar);
|
|
||||||
#elif defined(__linux__)
|
|
||||||
if (pthread_cond_init(condvar, NULL))
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
(void) condvar;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void os_condvar_delete(os_condvar_t *condvar)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
if (pthread_cond_destroy(condvar))
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
(void) condvar;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool os_condvar_wait(os_condvar_t *condvar, os_mutex_t *mutex, int timeout_ms)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
|
|
||||||
DWORD timeout = INFINITE;
|
|
||||||
if (timeout_ms >= 0) timeout = timeout_ms;
|
|
||||||
if (!SleepConditionVariableCS(condvar, mutex, timeout)) {
|
|
||||||
if (GetLastError() == ERROR_TIMEOUT) {
|
|
||||||
PROFILE_END;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
#elif defined(__linux__)
|
|
||||||
|
|
||||||
int err;
|
|
||||||
if (timeout_ms < 0)
|
|
||||||
err = pthread_cond_wait(condvar, mutex);
|
|
||||||
else {
|
|
||||||
uint64_t wakeup_ms = (uint64_t) timeout_ms + get_absolute_time_us() / 1000;
|
|
||||||
struct timespec abstime = {
|
|
||||||
.tv_sec = wakeup_ms / 1000,
|
|
||||||
.tv_nsec = (wakeup_ms % 1000) * 1000000,
|
|
||||||
};
|
|
||||||
err = pthread_cond_timedwait(condvar, mutex, &abstime);
|
|
||||||
}
|
|
||||||
if (err) {
|
|
||||||
if (err == ETIMEDOUT) {
|
|
||||||
PROFILE_END;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#ifdef SYNC_PRINT_ERRORS
|
|
||||||
fprintf(stderr, "ERROR!! pthread_cond_wait/timedwait: %s\n", strerror(err));
|
|
||||||
#endif
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
#else
|
|
||||||
PROFILE_END;
|
|
||||||
(void) condvar;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void os_condvar_signal(os_condvar_t *condvar)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
WakeConditionVariable(condvar);
|
|
||||||
#elif defined(__linux__)
|
|
||||||
if (pthread_cond_signal(condvar))
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
(void) condvar;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void semaphore_create(semaphore_t *sem, int count)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
sem->count = count;
|
|
||||||
os_mutex_create(&sem->mutex);
|
|
||||||
os_condvar_create(&sem->cond);
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
void semaphore_delete(semaphore_t *sem)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
os_mutex_delete(&sem->mutex);
|
|
||||||
os_condvar_delete(&sem->cond);
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool semaphore_wait(semaphore_t *sem, int count, int timeout_ms)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
assert(count > 0);
|
|
||||||
|
|
||||||
uint64_t start_time_ms = get_relative_time_ns() / 1000000;
|
|
||||||
|
|
||||||
os_mutex_lock(&sem->mutex);
|
|
||||||
while (sem->count < count) {
|
|
||||||
|
|
||||||
uint64_t current_time_ms = get_relative_time_ns() / 1000000;
|
|
||||||
|
|
||||||
int remaining_ms = -1;
|
|
||||||
if (timeout_ms >= 0)
|
|
||||||
remaining_ms = timeout_ms - (int) (current_time_ms - start_time_ms);
|
|
||||||
|
|
||||||
if (!os_condvar_wait(&sem->cond, &sem->mutex, remaining_ms)) {
|
|
||||||
os_mutex_unlock(&sem->mutex);
|
|
||||||
PROFILE_END;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sem->count -= count;
|
|
||||||
os_mutex_unlock(&sem->mutex);
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void semaphore_signal(semaphore_t *sem, int count)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
assert(count > 0);
|
|
||||||
|
|
||||||
os_mutex_lock(&sem->mutex);
|
|
||||||
sem->count += count;
|
|
||||||
if (sem->count > 0)
|
|
||||||
os_condvar_signal(&sem->cond);
|
|
||||||
os_mutex_unlock(&sem->mutex);
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool os_semaphore_create(os_semaphore_t *sem, int count, int max)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
int ok;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
SECURITY_ATTRIBUTES *attr = NULL; // Default
|
|
||||||
const char *name = NULL; // No name
|
|
||||||
void *handle = CreateSemaphoreA(attr, count, max, name);
|
|
||||||
if (handle == NULL) {
|
|
||||||
PROFILE_END;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sem->data = handle;
|
|
||||||
ok = 1;
|
|
||||||
#else
|
|
||||||
(void) max; // POSIX doesn't use this
|
|
||||||
ok = sem_init(&sem->data, 0, count) == 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool os_semaphore_delete(os_semaphore_t *sem)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
int ok;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
CloseHandle(sem->data);
|
|
||||||
ok = 1;
|
|
||||||
#else
|
|
||||||
ok = sem_destroy(&sem->data) == 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool os_semaphore_wait(os_semaphore_t *sem)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
int ok;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
ok = WaitForSingleObject(sem->data, INFINITE) == WAIT_OBJECT_0;
|
|
||||||
#else
|
|
||||||
ok = sem_wait(&sem->data) == 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool os_semaphore_signal(os_semaphore_t *sem)
|
|
||||||
{
|
|
||||||
PROFILE_START;
|
|
||||||
|
|
||||||
int ok;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
ok = ReleaseSemaphore(sem->data, 1, NULL);
|
|
||||||
#else
|
|
||||||
ok = sem_post(&sem->data) == 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PROFILE_END;
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
PROFILE_GLOBAL_END;
|
|
||||||
|
|
||||||
profile_results_t sync_profile_results(void)
|
|
||||||
{
|
|
||||||
return PROFILE_RESULTS;
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
#include <stdatomic.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#else
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "thread.h"
|
|
||||||
|
|
||||||
void os_thread_create(os_thread *thread, void *arg, os_threadreturn (*func)(void*))
|
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
os_thread thread_ = CreateThread(NULL, 0, func, arg, 0, NULL);
|
|
||||||
if (thread_ == INVALID_HANDLE_VALUE)
|
|
||||||
abort();
|
|
||||||
*thread = thread_;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
int ret = pthread_create(thread, NULL, func, arg);
|
|
||||||
if (ret) abort();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
os_threadreturn os_thread_join(os_thread thread)
|
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
os_threadreturn result;
|
|
||||||
WaitForSingleObject(thread, INFINITE);
|
|
||||||
if (!GetExitCodeThread(thread, &result))
|
|
||||||
abort();
|
|
||||||
CloseHandle(thread);
|
|
||||||
return result;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
os_threadreturn result;
|
|
||||||
int ret = pthread_join(thread, &result);
|
|
||||||
if (ret) abort();
|
|
||||||
return result;
|
|
||||||
#else
|
|
||||||
(void) thread;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t get_thread_id(void)
|
|
||||||
{
|
|
||||||
static _Atomic uint64_t next_id = 1;
|
|
||||||
static _Thread_local uint64_t id = 0;
|
|
||||||
if (id == 0) id = atomic_fetch_add(&next_id, 1);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
This is free and unencumbered software released into the public domain.
|
|
||||||
|
|
||||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
||||||
distribute this software, either in source code form or as a compiled
|
|
||||||
binary, for any purpose, commercial or non-commercial, and by any
|
|
||||||
means.
|
|
||||||
|
|
||||||
In jurisdictions that recognize copyright laws, the author or authors
|
|
||||||
of this software dedicate any and all copyright interest in the
|
|
||||||
software to the public domain. We make this dedication for the benefit
|
|
||||||
of the public at large and to the detriment of our heirs and
|
|
||||||
successors. We intend this dedication to be an overt act of
|
|
||||||
relinquishment in perpetuity of all present and future rights to this
|
|
||||||
software under copyright law.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
For more information, please refer to <http://unlicense.org/>
|
|
||||||
*/
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#define WIN32_MEAN_AND_LEAN
|
|
||||||
#include <windows.h>
|
|
||||||
typedef void *os_thread;
|
|
||||||
typedef unsigned long os_threadreturn;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
#include <pthread.h>
|
|
||||||
typedef pthread_t os_thread;
|
|
||||||
typedef void *os_threadreturn;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint64_t get_thread_id(void);
|
|
||||||
|
|
||||||
void os_thread_create(os_thread *thread, void *arg, os_threadreturn (*func)(void*));
|
|
||||||
os_threadreturn os_thread_join(os_thread thread);
|
|
||||||
File diff suppressed because it is too large
Load Diff
+18
@@ -25,6 +25,7 @@ OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
For more information, please refer to <http://unlicense.org/>
|
For more information, please refer to <http://unlicense.org/>
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
@@ -55,3 +56,20 @@ char *load_file(const char *file, size_t *size)
|
|||||||
if (size) *size = size2;
|
if (size) *size = size2;
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static _Thread_local uint64_t wyhash64_x = 0;
|
||||||
|
|
||||||
|
static uint64_t wyhash64(void) {
|
||||||
|
wyhash64_x += 0x60bee2bee120fc15;
|
||||||
|
__uint128_t tmp;
|
||||||
|
tmp = (__uint128_t) wyhash64_x * 0xa3b195354a39b70d;
|
||||||
|
uint64_t m1 = (tmp >> 64) ^ tmp;
|
||||||
|
tmp = (__uint128_t)m1 * 0x1b03738712fad5c9;
|
||||||
|
uint64_t m2 = (tmp >> 64) ^ tmp;
|
||||||
|
return m2;
|
||||||
|
}
|
||||||
|
|
||||||
|
float random_float(void)
|
||||||
|
{
|
||||||
|
return (float) wyhash64() / UINT64_MAX;
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,3 +33,5 @@ char *load_file(const char *file, size_t *size);
|
|||||||
|
|
||||||
inline bool is_space(char c) { return c == ' ' || c == '\r' || c == '\t' || c == '\n'; }
|
inline bool is_space(char c) { return c == ' ' || c == '\r' || c == '\t' || c == '\n'; }
|
||||||
inline bool is_digit(char c) { return c >= '0' && c <= '9'; }
|
inline bool is_digit(char c) { return c >= '0' && c <= '9'; }
|
||||||
|
|
||||||
|
float random_float(void);
|
||||||
+73
-270
@@ -29,20 +29,93 @@ For more information, please refer to <http://unlicense.org/>
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include "utils.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
#define EPSILON 0.00001
|
#define EPSILON 0.00001
|
||||||
|
|
||||||
|
float maxf(float x, float y)
|
||||||
|
{
|
||||||
|
return x > y ? x : y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float minf(float x, float y)
|
||||||
|
{
|
||||||
|
return x < y ? x : y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float absf(float x)
|
||||||
|
{
|
||||||
|
return x < 0 ? -x : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
float clamp(float x, float min, float max)
|
||||||
|
{
|
||||||
|
assert(min <= max);
|
||||||
|
if (x < min) return min;
|
||||||
|
if (x > max) return max;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 maxv(Vector3 a, Vector3 b)
|
||||||
|
{
|
||||||
|
return (Vector3) {
|
||||||
|
maxf(a.x, b.x),
|
||||||
|
maxf(a.y, b.y),
|
||||||
|
maxf(a.z, b.z),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 vec_from_scalar(float s)
|
||||||
|
{
|
||||||
|
return (Vector3) {s, s, s};
|
||||||
|
}
|
||||||
|
|
||||||
bool isnanv(Vector3 v)
|
bool isnanv(Vector3 v)
|
||||||
{
|
{
|
||||||
return isnan(v.x) || isnan(v.y) || isnan(v.z);
|
return isnan(v.x) || isnan(v.y) || isnan(v.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool iszerof(float f)
|
||||||
|
{
|
||||||
|
return f < 0.0001 && f > -0.0001;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iszerov(Vector3 v)
|
||||||
|
{
|
||||||
|
return iszerof(v.x) && iszerof(v.y) && iszerof(v.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float avgv(Vector3 v)
|
||||||
|
{
|
||||||
|
return (v.x + v.y + v.z) / 3;
|
||||||
|
}
|
||||||
|
|
||||||
float deg2rad(float deg)
|
float deg2rad(float deg)
|
||||||
{
|
{
|
||||||
return 3.14159265358979323846 * deg / 180;
|
return 3.14159265358979323846 * deg / 180;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector3 random_vector(void)
|
||||||
|
{
|
||||||
|
return (Vector3) {
|
||||||
|
.x = random_float() * 2 - 1,
|
||||||
|
.y = random_float() * 2 - 1,
|
||||||
|
.z = random_float() * 2 - 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 random_direction(void)
|
||||||
|
{
|
||||||
|
return normalize(random_vector());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 reflect(Vector3 dir, Vector3 normal)
|
||||||
|
{
|
||||||
|
float f = -2 * dotv(normal, dir);
|
||||||
|
return combine(dir, normal, 1, f);
|
||||||
|
}
|
||||||
|
|
||||||
float norm2_of(Vector3 v)
|
float norm2_of(Vector3 v)
|
||||||
{
|
{
|
||||||
return v.x * v.x + v.y * v.y + v.z * v.z;
|
return v.x * v.x + v.y * v.y + v.z * v.z;
|
||||||
@@ -409,276 +482,6 @@ void print_matrix3(Matrix3 m)
|
|||||||
m.data[2][2]);
|
m.data[2][2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void lina_transpose(float *A, float *B, int m, int n)
|
|
||||||
{
|
|
||||||
assert(m > 0 && n > 0);
|
|
||||||
assert(A != NULL && B != NULL);
|
|
||||||
|
|
||||||
if(m == 1 || n == 1) {
|
|
||||||
// For a matrix with height or width of 1
|
|
||||||
// row-major and column-major order coincide,
|
|
||||||
// so the stransposition doesn't change the
|
|
||||||
// the memory representation. A simple copy
|
|
||||||
// does the job.
|
|
||||||
|
|
||||||
if(A != B) // Does the copy or the branch cost more?
|
|
||||||
memcpy(B, A, sizeof(A[0]) * m * n);
|
|
||||||
|
|
||||||
} else if(m == n) {
|
|
||||||
|
|
||||||
// Iterate over the upper triangular portion of
|
|
||||||
// the matrix and switch each element with the
|
|
||||||
// corresponding one in the lower triangular portion.
|
|
||||||
// NOTE: We're assuming A,B might be the same matrix.
|
|
||||||
// If A,B are the same matrix, then the diagonal
|
|
||||||
// is copied onto itself. By removing the +1 in
|
|
||||||
// the inner loop, the copying of the diagonal
|
|
||||||
// is avoided.
|
|
||||||
|
|
||||||
for(int i = 0; i < n; i += 1)
|
|
||||||
for(int j = 0; j < i+1; j += 1) {
|
|
||||||
float temp = A[i*n + j];
|
|
||||||
B[i*n + j] = A[j*n + i];
|
|
||||||
B[j*n + i] = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Not only the matrix needs to be transposed
|
|
||||||
// assuming the destination matrix is the same
|
|
||||||
// as the source matrix, but the memory representation
|
|
||||||
// of the matrix needs to switch from row-major
|
|
||||||
// to col-major, so it's not as simple as switching
|
|
||||||
// value's positions.
|
|
||||||
// This algorithm starts from the A[0][1] value and
|
|
||||||
// moves it where it needs to go, then gets the value
|
|
||||||
// that was at that position and puts that in it's
|
|
||||||
// new position. This process is iterated until the
|
|
||||||
// starting point A[0][1] is overwritten with the
|
|
||||||
// new value. In this process the first and last
|
|
||||||
// value of the matrix never move.
|
|
||||||
|
|
||||||
B[0] = A[0];
|
|
||||||
B[m*n - 1] = A[m*n - 1];
|
|
||||||
|
|
||||||
float item = A[1];
|
|
||||||
int next = m;
|
|
||||||
|
|
||||||
while(next != 1) {
|
|
||||||
float temp = A[next];
|
|
||||||
B[next] = item;
|
|
||||||
item = temp;
|
|
||||||
next = (next % n) * m + (next / n);
|
|
||||||
}
|
|
||||||
|
|
||||||
B[1] = item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int lina_decompLUP(float *A, float *L,
|
|
||||||
float *U, int *P,
|
|
||||||
int n)
|
|
||||||
{
|
|
||||||
assert(n > 0);
|
|
||||||
assert(A != L && A != U && L != U);
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++)
|
|
||||||
P[i] = i;
|
|
||||||
|
|
||||||
int swaps = 0;
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
|
|
||||||
int v = P[i];
|
|
||||||
float max_v = A[v * n + i];
|
|
||||||
int max_i = i;
|
|
||||||
|
|
||||||
for (int j = i+1; j < n; j++) {
|
|
||||||
int u = P[j];
|
|
||||||
float abs = fabs(A[u * n + j]);
|
|
||||||
if (abs > max_v) {
|
|
||||||
max_v = abs;
|
|
||||||
max_i = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (max_i != i) {
|
|
||||||
|
|
||||||
// Swap rows
|
|
||||||
int temp = P[i];
|
|
||||||
P[i] = P[max_i];
|
|
||||||
P[max_i] = temp;
|
|
||||||
|
|
||||||
swaps++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++)
|
|
||||||
for (int j = 0; j < n; j++)
|
|
||||||
U[i * n + j] = A[P[i] * n + j];
|
|
||||||
|
|
||||||
memset(L, 0, sizeof(float) * n * n);
|
|
||||||
for (int i = 0; i < n; i++)
|
|
||||||
L[i * n + i] = 1;
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++)
|
|
||||||
for (int j = i+1; j < n; j++) {
|
|
||||||
float u = U[i * n + i];
|
|
||||||
L[j * n + i] = U[j * n + i] / u;
|
|
||||||
for (int k = 0; k < n; k++)
|
|
||||||
U[j * n + k] -= L[j * n + i] * U[i * n + k];
|
|
||||||
}
|
|
||||||
|
|
||||||
return swaps;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function: lina_det
|
|
||||||
//
|
|
||||||
// Calculates the determinant of the n by n matrix A
|
|
||||||
// and returns it throught the output parameter [det].
|
|
||||||
//
|
|
||||||
// If not enough memory is available, false is returned,
|
|
||||||
// else true is returned.
|
|
||||||
//
|
|
||||||
// Notes:
|
|
||||||
// - The output parameter [det] is optional. (you can
|
|
||||||
// ignore the result by passing NULL).
|
|
||||||
//
|
|
||||||
bool lina_det(float *A, int n, float *det)
|
|
||||||
{
|
|
||||||
// Allocate the space for the L,U matrices.
|
|
||||||
// I can't think of a version of this algorithm
|
|
||||||
// where a temporary buffer isn't necessary.
|
|
||||||
float *T = (float*) malloc(sizeof(float) * n * n * 2 + sizeof(int) * n);
|
|
||||||
if (T == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Do the decomposition
|
|
||||||
float *L = T;
|
|
||||||
float *U = L + (n * n);
|
|
||||||
int *P = (int*) (U + (n * n));
|
|
||||||
|
|
||||||
int swaps = lina_decompLUP(A, L, U, P, n);
|
|
||||||
if (swaps < 0) {
|
|
||||||
free(T);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Knowing that
|
|
||||||
//
|
|
||||||
// A = LU
|
|
||||||
//
|
|
||||||
// then
|
|
||||||
//
|
|
||||||
// det(A) = det(LU) = det(L)det(U)
|
|
||||||
//
|
|
||||||
// Since L and U are triangular, their
|
|
||||||
// determinant is the product of their
|
|
||||||
// diagonals, so the product of the
|
|
||||||
// determinants is the product of both
|
|
||||||
// the diagonals.
|
|
||||||
|
|
||||||
float prod = 1;
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
float l = L[i * n + i];
|
|
||||||
float u = U[i * n + i];
|
|
||||||
prod *= l * u;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (swaps & 1)
|
|
||||||
prod = -prod;
|
|
||||||
|
|
||||||
if (det)
|
|
||||||
*det = prod;
|
|
||||||
|
|
||||||
free(T);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the n-1 by n-1 matrix D obtained by
|
|
||||||
// removing the [del_col] column and [del_row]
|
|
||||||
// frow the n by n matrix M.
|
|
||||||
static void
|
|
||||||
copyMatrixWithoutRowAndCol(float *M, float *D, int n,
|
|
||||||
int del_col, int del_row)
|
|
||||||
{
|
|
||||||
// Copy the upper-left portion of matrix M
|
|
||||||
// that comes before the deleted column and
|
|
||||||
// row.
|
|
||||||
for (int i = 0; i < del_row; i++)
|
|
||||||
for (int j = 0; j < del_col; j++)
|
|
||||||
D[i * (n-1) + j] = M[i * n + j];
|
|
||||||
|
|
||||||
// Copy the lower left portion that comes
|
|
||||||
// after both the deleted column and row.
|
|
||||||
for (int i = del_row+1; i < n; i++)
|
|
||||||
for (int j = del_col+1; j < n; j++)
|
|
||||||
D[(i-1) * (n-1) + (j-1)] = M[i * n + j];
|
|
||||||
|
|
||||||
// Copy the bottom portion that comes after
|
|
||||||
// the deleted row but before the deleted column.
|
|
||||||
for (int i = del_row+1; i < n; i++)
|
|
||||||
for (int j = 0; j < del_col; j++)
|
|
||||||
D[(i-1) * (n-1) + j] = M[i * n + j];
|
|
||||||
|
|
||||||
// Copy the right portion that comes after
|
|
||||||
// the deleted column but before the deleted row.
|
|
||||||
for (int i = 0; i < del_row; i++)
|
|
||||||
for (int j = del_col+1; j < n; j++)
|
|
||||||
D[i * (n-1) + (j-1)] = M[i * n + j];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lina_inverse(Matrix4 M, Matrix4 *D)
|
|
||||||
{
|
|
||||||
float det;
|
|
||||||
if (!lina_det((float*) &M, 4, &det))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
printf("det=%f\n", det);
|
|
||||||
|
|
||||||
if (det == 0)
|
|
||||||
return false; // The matrix can't be inverted
|
|
||||||
|
|
||||||
Matrix3 T;
|
|
||||||
Matrix4 M_t = transpose(M);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
for (int j = 0; j < 4; j++) {
|
|
||||||
|
|
||||||
copyMatrixWithoutRowAndCol((float*) &M_t, (float*)&T, 4, j, i);
|
|
||||||
|
|
||||||
float det2;
|
|
||||||
if (!lina_det((float*) &T, 3, &det2))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
printf("-----------------\n");
|
|
||||||
|
|
||||||
print_matrix3(T);
|
|
||||||
printf("det2=%f\n", det2);
|
|
||||||
|
|
||||||
// If the determinant of M isn't zero,
|
|
||||||
// neither is this!
|
|
||||||
assert(det2 != 0);
|
|
||||||
|
|
||||||
bool i_is_odd = i & 1;
|
|
||||||
bool j_is_odd = j & 1;
|
|
||||||
int sign = (i_is_odd == j_is_odd) ? 1 : -1;
|
|
||||||
|
|
||||||
D->data[i][j] = sign * det2 / det;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Matrix4 invert(Matrix4 m)
|
|
||||||
{
|
|
||||||
Matrix4 r;
|
|
||||||
if (!lina_inverse(m, &r)) {
|
|
||||||
printf("Couldn't invert!\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static bool gluInvertMatrix(const float m[16], float invOut[16])
|
static bool gluInvertMatrix(const float m[16], float invOut[16])
|
||||||
{
|
{
|
||||||
float inv[16], det;
|
float inv[16], det;
|
||||||
|
|||||||
@@ -61,7 +61,18 @@ typedef struct {
|
|||||||
} Sphere;
|
} Sphere;
|
||||||
|
|
||||||
float deg2rad(float deg);
|
float deg2rad(float deg);
|
||||||
|
|
||||||
|
float maxf(float x, float y);
|
||||||
|
float minf(float x, float y);
|
||||||
|
float absf(float x);
|
||||||
|
float clamp(float x, float min, float max);
|
||||||
|
Vector3 maxv(Vector3 a, Vector3 b);
|
||||||
bool isnanv(Vector3 v);
|
bool isnanv(Vector3 v);
|
||||||
|
bool iszerof(float f);
|
||||||
|
bool iszerov(Vector3 v);
|
||||||
|
float avgv(Vector3 v);
|
||||||
|
|
||||||
|
Vector3 vec_from_scalar(float s);
|
||||||
|
|
||||||
Matrix4 translate_matrix(Vector3 v, float f);
|
Matrix4 translate_matrix(Vector3 v, float f);
|
||||||
Matrix4 identity_matrix(void);
|
Matrix4 identity_matrix(void);
|
||||||
@@ -92,4 +103,12 @@ Matrix4 dotm(Matrix4 a, Matrix4 b);
|
|||||||
Matrix4 transpose(Matrix4 m);
|
Matrix4 transpose(Matrix4 m);
|
||||||
bool invert(Matrix4 a, Matrix4 *inv);
|
bool invert(Matrix4 a, Matrix4 *inv);
|
||||||
|
|
||||||
|
Vector3 random_vector(void);
|
||||||
|
Vector3 random_direction(void);
|
||||||
|
Vector3 reflect(Vector3 dir, Vector3 normal);
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.1415926538
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user