Reorganize files and remove unused code

This commit is contained in:
2024-10-10 14:57:27 +02:00
parent ab6bddbafd
commit 479100ec4a
26 changed files with 1901 additions and 4474 deletions
+3 -3
View File
@@ -1,13 +1,13 @@
ifeq ($(OS),Windows_NT)
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
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
EXT =
CFLAGS = -O2 -DNDEBUG -I3p/glad/include
CFLAGS = -O2 -DNDEBUG -I3p/glad/include -I3p
LDFLAGS = -lglfw -lm
endif
ifeq ($(UNAME_S),Darwin)
@@ -20,7 +20,7 @@ endif
all: ray_trace$(EXT)
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:
rm ray_trace ray_trace.exe
-3
View File
@@ -23,6 +23,3 @@ You should use a number of threads equal to the number of CPU cores. The `--init
![scene 0](assets/screenshot_1.png)
![scene 1](assets/screenshot_2.png)
![scene 2](assets/screenshot_3.png)
# 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
View File
@@ -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
View File
@@ -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);
+423
View File
@@ -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();
}
+49
View File
@@ -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);
+178 -1232
View File
File diff suppressed because it is too large Load Diff
-372
View File
@@ -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
View File
@@ -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);
+419
View File
@@ -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
View File
@@ -28,6 +28,8 @@ For more information, please refer to <http://unlicense.org/>
#include <stdbool.h>
#include "profile.h"
// TODO: Clean up this file
#ifdef _WIN32
#define WIN32_MEAN_AND_LEAN
#include <windows.h>
@@ -54,6 +56,10 @@ typedef struct {
os_condvar_t cond;
} 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_delete(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_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);
-97
View File
@@ -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);
}
}
-74
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
-74
View File
@@ -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;
}
-44
View File
@@ -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
View File
@@ -25,6 +25,7 @@ OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "utils.h"
@@ -55,3 +56,20 @@ char *load_file(const char *file, size_t *size)
if (size) *size = size2;
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;
}
+2
View File
@@ -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_digit(char c) { return c >= '0' && c <= '9'; }
float random_float(void);
+73 -270
View File
@@ -29,20 +29,93 @@ For more information, please refer to <http://unlicense.org/>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "utils.h"
#include "vector.h"
#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)
{
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)
{
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)
{
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]);
}
/*
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])
{
float inv[16], det;
+19
View File
@@ -61,7 +61,18 @@ typedef struct {
} Sphere;
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 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 identity_matrix(void);
@@ -92,4 +103,12 @@ Matrix4 dotm(Matrix4 a, Matrix4 b);
Matrix4 transpose(Matrix4 m);
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