Initial commit
This commit is contained in:
288
code/platform.cpp
Normal file
288
code/platform.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
#include "rt_weekend.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
const char *WindowTitle = "Ray Tracing in a Weekend";
|
||||
|
||||
const uint32_t TARGET_FRAME_RATE = 30;
|
||||
const uint32_t TICKS_PER_FRAME = 1000 / TARGET_FRAME_RATE;
|
||||
const uint32_t WINDOW_WIDTH = 500;
|
||||
const uint32_t WINDOW_HEIGHT = 250;
|
||||
|
||||
global_variable bool Running = true;
|
||||
global_variable sdl_offscreen_buffer GlobalBackbuffer;
|
||||
|
||||
void PluginUpdateAndRenderStub(plugin_offscreen_buffer *Buffer)
|
||||
{
|
||||
}
|
||||
|
||||
internal sdl_window_dimension
|
||||
SDLGetWindowDimension(SDL_Window *Window)
|
||||
{
|
||||
sdl_window_dimension Result;
|
||||
SDL_GetWindowSize(Window, &Result.Width, &Result.Height);
|
||||
|
||||
return(Result);
|
||||
}
|
||||
|
||||
internal void
|
||||
SDLResizeTexture(sdl_offscreen_buffer *Buffer,
|
||||
SDL_Renderer *Renderer,
|
||||
int Width, int Height)
|
||||
{
|
||||
const int BytesPerPixel = 4;
|
||||
|
||||
if(Buffer->Memory)
|
||||
{
|
||||
munmap(Buffer->Memory,
|
||||
Buffer->Height * Buffer->Pitch);
|
||||
}
|
||||
|
||||
if(Buffer->Texture)
|
||||
{
|
||||
SDL_DestroyTexture(Buffer->Texture);
|
||||
}
|
||||
|
||||
Buffer->Texture = SDL_CreateTexture(Renderer,
|
||||
SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
Width, Height);
|
||||
Buffer->Width = Width;
|
||||
Buffer->Height = Height;
|
||||
Buffer->Pitch = Width * BytesPerPixel;
|
||||
Buffer->Memory = mmap(0, Width * Height * BytesPerPixel,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
}
|
||||
|
||||
internal void
|
||||
SDLUpdateWindow(SDL_Renderer *Renderer,
|
||||
sdl_offscreen_buffer Buffer)
|
||||
{
|
||||
SDL_UpdateTexture(Buffer.Texture,
|
||||
0,
|
||||
Buffer.Memory,
|
||||
Buffer.Pitch);
|
||||
|
||||
SDL_RenderCopy(Renderer, Buffer.Texture, 0, 0);
|
||||
|
||||
SDL_RenderPresent(Renderer);
|
||||
}
|
||||
|
||||
internal void
|
||||
HandleEvent(SDL_Event *Event)
|
||||
{
|
||||
switch(Event->type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
{
|
||||
Running = false;
|
||||
} break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
switch (Event->window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_EXPOSED:
|
||||
{
|
||||
SDL_Window *Window = SDL_GetWindowFromID(Event->window.windowID);
|
||||
SDL_Renderer *Renderer = SDL_GetRenderer(Window);
|
||||
SDLUpdateWindow(Renderer, GlobalBackbuffer);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline time_t
|
||||
SDLGetLastModificationTime(const char *Filename)
|
||||
{
|
||||
time_t LastModificationTime = 0;
|
||||
|
||||
// Find out plugin library's last modification time
|
||||
struct stat LibraryFileStat;
|
||||
if (stat(Filename, &LibraryFileStat) == 0)
|
||||
{
|
||||
LastModificationTime = LibraryFileStat.st_ctime;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Could not read plugin library: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
return(LastModificationTime);
|
||||
}
|
||||
|
||||
internal sdl_plugin_code
|
||||
SDLLoadPluginCode(const char *Filename)
|
||||
{
|
||||
sdl_plugin_code Result = {};
|
||||
Result.UpdateAndRender = PluginUpdateAndRenderStub;
|
||||
|
||||
Result.LastModificationTime = SDLGetLastModificationTime(Filename);
|
||||
|
||||
const char *error;
|
||||
|
||||
/* Load library */
|
||||
Result.CodeLibrary = dlopen(Filename, RTLD_LAZY);
|
||||
if (Result.CodeLibrary)
|
||||
{
|
||||
dlerror();
|
||||
Result.UpdateAndRender =
|
||||
(plugin_update_and_render *)dlsym(Result.CodeLibrary,
|
||||
"PluginUpdateAndRender");
|
||||
if ((error = dlerror()))
|
||||
{
|
||||
fprintf(stderr, "Couldn't find PluginUpdateAndRender: %s\n", error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Couldn't open plugin library: %s\n",
|
||||
dlerror());
|
||||
}
|
||||
|
||||
return(Result);
|
||||
}
|
||||
|
||||
internal void
|
||||
SDLUnloadPluginCode(sdl_plugin_code *Plugin)
|
||||
{
|
||||
if(Plugin->CodeLibrary)
|
||||
{
|
||||
dlclose(Plugin->CodeLibrary);
|
||||
}
|
||||
|
||||
Plugin->UpdateAndRender = PluginUpdateAndRenderStub;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Variables required to calculate framerate
|
||||
#define FPS_INTERVAL 1.0
|
||||
uint32_t LastFrameEndTime = SDL_GetTicks();
|
||||
uint32_t CurrentFPS = 0;
|
||||
uint32_t FramesElapsed = 0;
|
||||
uint32_t FramesMissed = 0;
|
||||
|
||||
// Initialize SDL
|
||||
SDL_Window *Window = NULL;
|
||||
SDL_Renderer *Renderer = NULL;
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0)
|
||||
{
|
||||
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create window
|
||||
Window = SDL_CreateWindow(WindowTitle,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
WINDOW_WIDTH,
|
||||
WINDOW_HEIGHT,
|
||||
SDL_WINDOW_OPENGL);
|
||||
if (Window == NULL)
|
||||
{
|
||||
printf("Could not create window: %s\n", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create renderer
|
||||
Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED);
|
||||
|
||||
if (Renderer == NULL)
|
||||
{
|
||||
printf("Could not create renderer: %s\n", SDL_GetError());
|
||||
SDL_DestroyWindow(Window);
|
||||
SDL_Quit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create texture
|
||||
sdl_window_dimension Dimension = SDLGetWindowDimension(Window);
|
||||
SDLResizeTexture(&GlobalBackbuffer, Renderer, Dimension.Width, Dimension.Height);
|
||||
|
||||
// Load plugin code
|
||||
const char *PluginLibraryFilename = "librt_weekend.so";
|
||||
sdl_plugin_code Plugin = SDLLoadPluginCode(PluginLibraryFilename);
|
||||
|
||||
// Main loop
|
||||
while (Running)
|
||||
{
|
||||
uint32_t FrameStartTime = SDL_GetTicks();
|
||||
|
||||
// Live reloading
|
||||
time_t NewLastModificationTime = SDLGetLastModificationTime(PluginLibraryFilename);
|
||||
if(NewLastModificationTime > Plugin.LastModificationTime)
|
||||
{
|
||||
SDLUnloadPluginCode(&Plugin);
|
||||
Plugin = SDLLoadPluginCode(PluginLibraryFilename);
|
||||
}
|
||||
|
||||
SDL_Event Event;
|
||||
while (SDL_PollEvent(&Event))
|
||||
{
|
||||
HandleEvent(&Event);
|
||||
}
|
||||
|
||||
// Display rendering information in Window title
|
||||
char WindowTitleBuffer[512];
|
||||
sprintf(WindowTitleBuffer,
|
||||
"%s - %d x %d - %u fps - %d frames missed",
|
||||
WindowTitle,
|
||||
WINDOW_WIDTH,
|
||||
WINDOW_HEIGHT,
|
||||
CurrentFPS,
|
||||
FramesMissed);
|
||||
|
||||
SDL_SetWindowTitle(Window, WindowTitleBuffer);
|
||||
|
||||
plugin_offscreen_buffer Buffer = {};
|
||||
Buffer.Memory = GlobalBackbuffer.Memory;
|
||||
Buffer.Width = GlobalBackbuffer.Width;
|
||||
Buffer.Height = GlobalBackbuffer.Height;
|
||||
Buffer.Pitch = GlobalBackbuffer.Pitch;
|
||||
Plugin.UpdateAndRender(&Buffer);
|
||||
|
||||
SDLUpdateWindow(Renderer, GlobalBackbuffer);
|
||||
|
||||
uint32_t FrameEndTime = SDL_GetTicks();
|
||||
// Calculate framerate
|
||||
if (LastFrameEndTime < FrameEndTime - FPS_INTERVAL * 1000)
|
||||
{
|
||||
LastFrameEndTime = FrameEndTime;
|
||||
CurrentFPS = FramesElapsed;
|
||||
FramesElapsed = 0;
|
||||
}
|
||||
FramesElapsed++;
|
||||
|
||||
// Cap framerate
|
||||
uint32_t FrameTicks = FrameEndTime - FrameStartTime;
|
||||
|
||||
if (FrameTicks < TICKS_PER_FRAME)
|
||||
{
|
||||
// Wait remaining time
|
||||
SDL_Delay(TICKS_PER_FRAME - FrameTicks);
|
||||
}
|
||||
else
|
||||
{
|
||||
FramesMissed++;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_DestroyRenderer(Renderer);
|
||||
SDL_DestroyWindow(Window);
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
28
code/platform.h
Normal file
28
code/platform.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef PLATFORM_H
|
||||
#define PLATFORM_H
|
||||
|
||||
typedef void plugin_update_and_render(plugin_offscreen_buffer *Buffer);
|
||||
|
||||
struct sdl_plugin_code
|
||||
{
|
||||
time_t LastModificationTime;
|
||||
void *CodeLibrary;
|
||||
plugin_update_and_render *UpdateAndRender;
|
||||
};
|
||||
|
||||
struct sdl_offscreen_buffer
|
||||
{
|
||||
SDL_Texture *Texture;
|
||||
void *Memory;
|
||||
int Width;
|
||||
int Height;
|
||||
int Pitch;
|
||||
};
|
||||
|
||||
struct sdl_window_dimension
|
||||
{
|
||||
int Width;
|
||||
int Height;
|
||||
};
|
||||
|
||||
#endif
|
||||
19
code/ray.h
Normal file
19
code/ray.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef RAYH
|
||||
#define RAYH
|
||||
|
||||
#include "vec3.h"
|
||||
|
||||
class ray
|
||||
{
|
||||
public:
|
||||
ray() {}
|
||||
ray(const vec3& a, const vec3& b) { A = a; B = b; }
|
||||
vec3 origin() const { return A; }
|
||||
vec3 direction() const { return B; }
|
||||
vec3 point_at_parameter(float t) const { return A + t * B; }
|
||||
|
||||
vec3 A;
|
||||
vec3 B;
|
||||
};
|
||||
|
||||
#endif
|
||||
50
code/rt_weekend.cpp
Normal file
50
code/rt_weekend.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "rt_weekend.h"
|
||||
#include "ray.h"
|
||||
|
||||
bool hit_sphere(const vec3& center, float radius, const ray& r)
|
||||
{
|
||||
vec3 oc = r.origin() - center;
|
||||
float a = dot(r.direction(), r.direction());
|
||||
float b = 2.0 * dot(oc, r.direction());
|
||||
float c = dot(oc, oc) - radius * radius;
|
||||
float discriminant = b * b - 4 * a * c;
|
||||
return (discriminant > 0);
|
||||
}
|
||||
|
||||
vec3 color(const ray& r)
|
||||
{
|
||||
if (hit_sphere(vec3(0, 0, -1), 0.5, r))
|
||||
return vec3(1, 0, 0);
|
||||
vec3 unit_direction = unit_vector(r.direction());
|
||||
float t = 0.5 * (unit_direction.y() + 1.0);
|
||||
return (1.0 - t) * vec3(1.0, 1.0, 1.0) + t * vec3(0.5, 0.7, 1.0);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
void
|
||||
PluginUpdateAndRender(plugin_offscreen_buffer *Buffer)
|
||||
{
|
||||
int Pitch = Buffer->Pitch;
|
||||
uint8_t *Row = (uint8_t *)Buffer->Memory;
|
||||
for(int Y = 0;
|
||||
Y < Buffer->Height;
|
||||
++Y)
|
||||
{
|
||||
uint32_t *Pixel = (uint32_t *)Row;
|
||||
for(int X = 0;
|
||||
X < Buffer->Width;
|
||||
++X)
|
||||
{
|
||||
float r = float(X) / float(Buffer->Width);
|
||||
float g = float(Y) / float(Buffer->Height);
|
||||
float b = 0.2;
|
||||
int ir = int(255.99 * r);
|
||||
int ig = int(255.99 * g);
|
||||
int ib = int(255.99 * b);
|
||||
|
||||
*Pixel++ = (ir << 24) + (ig << 16) + (ib << 8) + 0xFF;
|
||||
}
|
||||
|
||||
Row += Pitch;
|
||||
}
|
||||
}
|
||||
20
code/rt_weekend.h
Normal file
20
code/rt_weekend.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef RT_WEEKEND_H
|
||||
#define RT_WEEKEND_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define internal static
|
||||
#define local_persist static
|
||||
#define global_variable static
|
||||
|
||||
// Services that are being provided to the platform layer
|
||||
|
||||
struct plugin_offscreen_buffer
|
||||
{
|
||||
void *Memory;
|
||||
int Width;
|
||||
int Height;
|
||||
int Pitch;
|
||||
};
|
||||
|
||||
#endif
|
||||
163
code/vec3.h
Normal file
163
code/vec3.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#ifndef VEC3H
|
||||
#define VEC3H
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
class vec3
|
||||
{
|
||||
public:
|
||||
vec3() {}
|
||||
vec3(float e0, float e1, float e2) { e[0] = e0; e[1] = e1; e[2] = e2; }
|
||||
inline float x() const { return e[0]; }
|
||||
inline float y() const { return e[1]; }
|
||||
inline float z() const { return e[2]; }
|
||||
inline float r() const { return e[0]; }
|
||||
inline float g() const { return e[1]; }
|
||||
inline float b() const { return e[2]; }
|
||||
|
||||
inline const vec3& operator+() const { return *this; }
|
||||
inline vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }
|
||||
inline float operator[](int i) const { return e[i]; }
|
||||
inline float& operator[](int i) { return e[i]; }
|
||||
|
||||
inline vec3& operator+=(const vec3 &v2);
|
||||
inline vec3& operator-=(const vec3 &v2);
|
||||
inline vec3& operator*=(const vec3 &v2);
|
||||
inline vec3& operator/=(const vec3 &v2);
|
||||
inline vec3& operator*=(const float t);
|
||||
inline vec3& operator/=(const float t);
|
||||
|
||||
inline float length() const
|
||||
{
|
||||
return sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
|
||||
}
|
||||
inline float squared_length() const
|
||||
{
|
||||
return e[0] * e[0] + e[1] * e[1] + e[2] * e[2];
|
||||
}
|
||||
inline void make_unit_vector();
|
||||
|
||||
float e[3];
|
||||
};
|
||||
|
||||
// inline std::istream& operator>>(std::istream &is, vec3 &t)
|
||||
// {
|
||||
// is >> t.e[0] >> t.e[1] >> t.e[2];
|
||||
// return is;
|
||||
// }
|
||||
|
||||
// inline std::ostream& operator<<(std::ostream &os, const vec3 &t)
|
||||
// {
|
||||
// os << t.e[0] << " " << t.e[1] << " " << t.e[2];
|
||||
// return os;
|
||||
// }
|
||||
|
||||
inline void vec3::make_unit_vector()
|
||||
{
|
||||
float k = 1.0 / sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
|
||||
e[0] *= k;
|
||||
e[1] *= k;
|
||||
e[2] *= k;
|
||||
}
|
||||
|
||||
inline vec3 operator+(const vec3 &v1, const vec3 &v2)
|
||||
{
|
||||
return vec3(v1.e[0] + v2.e[0], v1.e[1] + v2.e[1], v1.e[2] + v2.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator-(const vec3 &v1, const vec3 &v2)
|
||||
{
|
||||
return vec3(v1.e[0] - v2.e[0], v1.e[1] - v2.e[1], v1.e[2] - v2.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator*(const vec3 &v1, const vec3 &v2)
|
||||
{
|
||||
return vec3(v1.e[0] * v2.e[0], v1.e[1] * v2.e[1], v1.e[2] * v2.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator/(const vec3 &v1, const vec3 &v2)
|
||||
{
|
||||
return vec3(v1.e[0] / v2.e[0], v1.e[1] / v2.e[1], v1.e[2] / v2.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator*(float t, const vec3 &v)
|
||||
{
|
||||
return vec3(t * v.e[0], t * v.e[1], t * v.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator/(vec3 v, float t)
|
||||
{
|
||||
return vec3(v.e[0]/t, v.e[1]/t, v.e[2]/t);
|
||||
}
|
||||
|
||||
inline vec3 operator*(const vec3 &v, float t)
|
||||
{
|
||||
return vec3(t * v.e[0], t * v.e[1], t * v.e[2]);
|
||||
}
|
||||
|
||||
inline float dot(const vec3 &v1, const vec3 &v2)
|
||||
{
|
||||
return v1.e[0] * v2.e[0] + v1.e[1] * v2.e[1] + v1.e[2] * v2.e[2];
|
||||
}
|
||||
|
||||
inline vec3 cross(const vec3 &v1, const vec3 &v2)
|
||||
{
|
||||
return vec3( (v1.e[1] * v2.e[2] - v1.e[2] * v2.e[1]),
|
||||
(- (v1.e[0] * v2.e[2] - v1.e[2] * v2.e[0])),
|
||||
(v1.e[0] * v2.e[1] - v1.e[1] * v2.e[0]) );
|
||||
}
|
||||
|
||||
inline vec3& vec3::operator+=(const vec3 &v)
|
||||
{
|
||||
e[0] += v.e[0];
|
||||
e[1] += v.e[1];
|
||||
e[2] += v.e[2];
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline vec3& vec3::operator*=(const vec3 &v)
|
||||
{
|
||||
e[0] *= v.e[0];
|
||||
e[1] *= v.e[1];
|
||||
e[2] *= v.e[2];
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline vec3& vec3::operator/=(const vec3 &v)
|
||||
{
|
||||
e[0] /= v.e[0];
|
||||
e[1] /= v.e[1];
|
||||
e[2] /= v.e[2];
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline vec3& vec3::operator-=(const vec3 &v)
|
||||
{
|
||||
e[0] -= v.e[0];
|
||||
e[1] -= v.e[1];
|
||||
e[2] -= v.e[2];
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline vec3& vec3::operator/=(const float t)
|
||||
{
|
||||
float k = 1.0 / t;
|
||||
|
||||
e[0] *= k;
|
||||
e[1] *= k;
|
||||
e[2] *= k;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline vec3 unit_vector(vec3 v)
|
||||
{
|
||||
return v / v.length();
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user