Reorganize files and remove unused code
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
+3
-3
@@ -23,7 +23,7 @@ typedef enum {
|
||||
} Direction;
|
||||
|
||||
Matrix4 camera_pov(void);
|
||||
void move_camera(Direction dir, float speed);
|
||||
void rotate_camera(double mouse_x, double mouse_y);
|
||||
void move_camera(Direction dir, float speed);
|
||||
void rotate_camera(double mouse_x, double mouse_y);
|
||||
Vector3 get_camera_pos(void);
|
||||
Ray ray_through_screen_at(float u, float v, float aspect_ratio);
|
||||
Ray ray_through_screen_at(float u, float v, float aspect_ratio);
|
||||
-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);
|
||||
+176
-1230
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 "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);
|
||||
@@ -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
+38
-20
@@ -25,33 +25,51 @@ 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"
|
||||
|
||||
char *load_file(const char *file, size_t *size)
|
||||
{
|
||||
FILE *stream = fopen(file, "rb");
|
||||
if (stream == NULL) return NULL;
|
||||
FILE *stream = fopen(file, "rb");
|
||||
if (stream == NULL) return NULL;
|
||||
|
||||
fseek(stream, 0, SEEK_END);
|
||||
long size2 = ftell(stream);
|
||||
fseek(stream, 0, SEEK_SET);
|
||||
fseek(stream, 0, SEEK_END);
|
||||
long size2 = ftell(stream);
|
||||
fseek(stream, 0, SEEK_SET);
|
||||
|
||||
char *dst = (char*) malloc(size2+1);
|
||||
if (dst == NULL) {
|
||||
fclose(stream);
|
||||
return NULL;
|
||||
}
|
||||
char *dst = (char*) malloc(size2+1);
|
||||
if (dst == NULL) {
|
||||
fclose(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fread(dst, 1, size2, stream);
|
||||
if (ferror(stream)) {
|
||||
free(dst);
|
||||
fclose(stream);
|
||||
return NULL;
|
||||
}
|
||||
dst[size2] = '\0';
|
||||
fread(dst, 1, size2, stream);
|
||||
if (ferror(stream)) {
|
||||
free(dst);
|
||||
fclose(stream);
|
||||
return NULL;
|
||||
}
|
||||
dst[size2] = '\0';
|
||||
|
||||
fclose(stream);
|
||||
if (size) *size = size2;
|
||||
return dst;
|
||||
fclose(stream);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user