a bit slow, but that's a start
This commit is contained in:
commit
933819a219
|
@ -0,0 +1,3 @@
|
|||
/build-*/
|
||||
*.g3a
|
||||
*.mp4
|
|
@ -0,0 +1,58 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(ChaosDrop VERSION 1.0 LANGUAGES CXX C ASM)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "$ENV{AZUR_PATH_${AZUR_PLATFORM}}/lib/cmake")
|
||||
find_package(Azur 0.1 REQUIRED)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
|
||||
set(SOURCES
|
||||
src/main.cc
|
||||
src/raytracing.cc)
|
||||
set(ASSETS)
|
||||
|
||||
if(AZUR_PLATFORM STREQUAL gint)
|
||||
list(APPEND SOURCES
|
||||
src/backend/gint/shader.cc)
|
||||
# list(APPEND ASSETS
|
||||
# ...)
|
||||
endif()
|
||||
|
||||
if(AZUR_PLATFORM STREQUAL linux)
|
||||
list(APPEND SOURCES
|
||||
src/backend/linux/render.cc
|
||||
src/backend/linux/programs.cc)
|
||||
endif()
|
||||
|
||||
# No emscripten backend for now
|
||||
if(AZUR_PLATFORM STREQUAL emscripten)
|
||||
# configure_file(backend/emscripten/index.html index.html)
|
||||
# list(APPEND SOURCES
|
||||
# src/backend/emscripten/render.cc)
|
||||
message(FATAL_ERROR "No emscripten build yet")
|
||||
endif()
|
||||
|
||||
add_executable(chaos-drop ${SOURCES} ${ASSETS})
|
||||
target_compile_options(chaos-drop PRIVATE -Wall -Wextra -O2)
|
||||
|
||||
if(AZUR_PLATFORM STREQUAL gint)
|
||||
find_package(Gint 2.10 REQUIRED)
|
||||
find_package(LibProf 2.4 REQUIRED)
|
||||
include(Fxconv)
|
||||
fxconv_declare_assets(${ASSETS} WITH_METADATA)
|
||||
|
||||
target_link_libraries(chaos-drop Azur::Azur LibProf::LibProf Gint::Gint -lm)
|
||||
target_link_options(chaos-drop PRIVATE
|
||||
-Wl,-Map=map -Wl,--print-memory-usage)
|
||||
|
||||
include(GenerateG3A)
|
||||
generate_g3a(TARGET chaos-drop
|
||||
NAME "Chaos Drop"
|
||||
OUTPUT "ChaosDrop.g3a"
|
||||
ICONS assets/icon-uns.png assets/icon-sel.png)
|
||||
endif()
|
||||
|
||||
if(AZUR_PLATFORM STREQUAL linux)
|
||||
target_link_libraries(chaos-drop Azur::Azur Azur::ImGui)
|
||||
endif()
|
|
@ -0,0 +1 @@
|
|||
A reckless attempt at making a game with real-time raytracing, because my brain is filled with ludicrous ideas that keep turning out to be barely possible.
|
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
@ -0,0 +1,56 @@
|
|||
#include "../../chaos-drop.h"
|
||||
#include <azur/gint/render.h>
|
||||
|
||||
uint8_t CD_RAYTRACE_SHADER_ID = -1;
|
||||
|
||||
struct cd_raytrace_cmd
|
||||
{
|
||||
/* Shader ID for Azur */
|
||||
uint8_t shader_id;
|
||||
uint8_t _[3];
|
||||
|
||||
/* Camera used for rendering */
|
||||
struct camera const *camera;
|
||||
/* Current y value */
|
||||
int y;
|
||||
};
|
||||
|
||||
#include <string.h>
|
||||
/* TODO: Write raytrace shader in assembler (MUCH NEEDED) */
|
||||
void cd_raytrace_shader(void *uniforms0, void *cmd0, void *fragment)
|
||||
{
|
||||
memset(fragment, 0x55, azrp_width * azrp_frag_height * 2);
|
||||
|
||||
uint32_t uniforms = (uint32_t)uniforms0;
|
||||
struct cd_raytrace_cmd *cmd = (struct cd_raytrace_cmd *)cmd0;
|
||||
|
||||
int frag_height = uniforms;
|
||||
int h = (frag_height > 112 - cmd->y) ? 112 - cmd->y : frag_height;
|
||||
render_fragment(cmd->camera, (uint16_t *)fragment, cmd->y, h);
|
||||
cmd->y += h;
|
||||
}
|
||||
|
||||
GCONSTRUCTOR
|
||||
static void register_shader(void)
|
||||
{
|
||||
CD_RAYTRACE_SHADER_ID = azrp_register_shader(cd_raytrace_shader);
|
||||
}
|
||||
|
||||
void cd_raytrace(struct camera const *camera)
|
||||
{
|
||||
prof_enter(azrp_perf_cmdgen);
|
||||
|
||||
struct cd_raytrace_cmd cmd;
|
||||
cmd.shader_id = CD_RAYTRACE_SHADER_ID;
|
||||
cmd.camera = camera;
|
||||
cmd.y = 0;
|
||||
|
||||
azrp_queue_command(&cmd, sizeof cmd, 0, azrp_frag_count);
|
||||
prof_leave(azrp_perf_cmdgen);
|
||||
}
|
||||
|
||||
void cd_raytrace_configure(void)
|
||||
{
|
||||
uint32_t value = azrp_frag_height;
|
||||
azrp_set_uniforms(CD_RAYTRACE_SHADER_ID, (void *)value);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#include "programs.h"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
//---
|
||||
// 2D Texture shader
|
||||
//---
|
||||
|
||||
ProgramTexture::ProgramTexture(): Program()
|
||||
{
|
||||
glBindVertexArray(this->vao);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
// TODO: Better way to access Azur's internal shaders
|
||||
extern char const *azur_glsl__vs_tex2d;
|
||||
extern char const *azur_glsl__fs_tex2d;
|
||||
|
||||
this->prog = azur::gl::loadProgramSources(
|
||||
GL_VERTEX_SHADER, azur_glsl__vs_tex2d,
|
||||
GL_FRAGMENT_SHADER, azur_glsl__fs_tex2d,
|
||||
0);
|
||||
}
|
||||
|
||||
void ProgramTexture::set_vertex_attributes() const
|
||||
{
|
||||
glVertexAttribPointer(glGetAttribLocation(this->prog, "a_vertex"),
|
||||
2, GL_FLOAT, GL_FALSE,
|
||||
sizeof(ProgramTexture_Attributes),
|
||||
(void *)offsetof(ProgramTexture_Attributes, vertex)
|
||||
);
|
||||
glVertexAttribPointer(glGetAttribLocation(this->prog, "a_texture_pos"),
|
||||
2, GL_FLOAT, GL_FALSE,
|
||||
sizeof(ProgramTexture_Attributes),
|
||||
(void *)offsetof(ProgramTexture_Attributes, uv)
|
||||
);
|
||||
}
|
||||
|
||||
void ProgramTexture::add_texture(int x, int y, int width, int height)
|
||||
{
|
||||
ProgramTexture_Attributes attr[4] = {
|
||||
{ glm::vec2(x, y), glm::vec2(0.0, 0.0) },
|
||||
{ glm::vec2(x+width, y), glm::vec2(1.0, 0.0) },
|
||||
{ glm::vec2(x, y+height), glm::vec2(0.0, 1.0) },
|
||||
{ glm::vec2(x+width, y+height), glm::vec2(1.0, 1.0) },
|
||||
};
|
||||
|
||||
this->vertices.push_back(attr[0]);
|
||||
this->vertices.push_back(attr[1]);
|
||||
this->vertices.push_back(attr[2]);
|
||||
this->vertices.push_back(attr[1]);
|
||||
this->vertices.push_back(attr[2]);
|
||||
this->vertices.push_back(attr[3]);
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
//-Program: wrapper for shader + VAO + VBO (from Magic Lab)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azur/gl/gl.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
template<typename VertexAttributes>
|
||||
struct Program
|
||||
{
|
||||
Program();
|
||||
virtual ~Program();
|
||||
|
||||
/* Since this holds non-trivial OpenGL objects, disable copy/move */
|
||||
Program(Program const &other) = delete;
|
||||
Program(Program &&other) = delete;
|
||||
|
||||
/* Adds a vertex to the vertex data buffer */
|
||||
void add_vertex(VertexAttributes const &attributes);
|
||||
|
||||
/* Set vertex attributes when the buffer is loaded */
|
||||
virtual void set_vertex_attributes() const = 0;
|
||||
|
||||
/* Update the VBO and draw */
|
||||
void prepare_draw();
|
||||
void draw();
|
||||
|
||||
/* Utilities to set uniforms */
|
||||
void set_uniform(char const *name, float f);
|
||||
void set_uniform(char const *name, float f1, float f2);
|
||||
void set_uniform(char const *name, float f1, float f2, float f3);
|
||||
void set_uniform(char const *name, glm::vec2 const &v2);
|
||||
void set_uniform(char const *name, glm::vec3 const &v3);
|
||||
void set_uniform(char const *name, glm::vec4 const &v4);
|
||||
void set_uniform(char const *name, glm::mat2 const &m2);
|
||||
void set_uniform(char const *name, glm::mat3 const &m3);
|
||||
void set_uniform(char const *name, glm::mat4 const &m4);
|
||||
|
||||
/* Program ID */
|
||||
GLuint prog;
|
||||
/* Vertex Array Object and Vertex Buffer Object with parameters */
|
||||
GLuint vao, vbo;
|
||||
|
||||
/* Vertex attributes to be loaded in the VBO */
|
||||
std::vector<VertexAttributes> vertices;
|
||||
/* Size of the VBO, in number of vertices */
|
||||
/* Number of spots currently used in [vertex_data] */
|
||||
size_t vbo_size;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Program<T>::Program(): vertices {}
|
||||
{
|
||||
prog = 0;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
vbo_size = 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::add_vertex(T const &attributes)
|
||||
{
|
||||
vertices.push_back(attributes);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::prepare_draw()
|
||||
{
|
||||
if(!vertices.size()) return;
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glUseProgram(prog);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
/* If the size of the VBO is too small or much larger than needed, resize
|
||||
it; otherwise, simply swap the data without reallocating */
|
||||
if(vbo_size < vertices.size() || vbo_size > vertices.size() * 4) {
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(T) * vertices.size(),
|
||||
vertices.data(), GL_DYNAMIC_DRAW);
|
||||
}
|
||||
else {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(T) * vertices.size(),
|
||||
vertices.data());
|
||||
}
|
||||
|
||||
this->set_vertex_attributes();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::draw()
|
||||
{
|
||||
prepare_draw();
|
||||
|
||||
// TODO: Do not hardcode the specific operation GL_TRIANGLES
|
||||
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, float f)
|
||||
{
|
||||
glUniform1f(glGetUniformLocation(this->prog, name), f);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, float f1, float f2)
|
||||
{
|
||||
glUniform2f(glGetUniformLocation(this->prog, name), f1, f2);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, float f1, float f2, float f3)
|
||||
{
|
||||
glUniform3f(glGetUniformLocation(this->prog, name), f1, f2, f3);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, glm::vec2 const &v2)
|
||||
{
|
||||
glUniform2fv(glGetUniformLocation(this->prog, name), 1, &v2[0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, glm::vec3 const &v3)
|
||||
{
|
||||
glUniform3fv(glGetUniformLocation(this->prog, name), 1, &v3[0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, glm::vec4 const &v4)
|
||||
{
|
||||
glUniform4fv(glGetUniformLocation(this->prog, name), 1, &v4[0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, glm::mat2 const &m2)
|
||||
{
|
||||
glUniformMatrix2fv(glGetUniformLocation(this->prog, name), 1, GL_FALSE,
|
||||
&m2[0][0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, glm::mat3 const &m3)
|
||||
{
|
||||
glUniformMatrix3fv(glGetUniformLocation(this->prog, name), 1, GL_FALSE,
|
||||
&m3[0][0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Program<T>::set_uniform(char const *name, glm::mat4 const &m4)
|
||||
{
|
||||
glUniformMatrix4fv(glGetUniformLocation(this->prog, name), 1, GL_FALSE,
|
||||
&m4[0][0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Program<T>::~Program()
|
||||
{
|
||||
glDeleteBuffers(1, &vbo);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
glDeleteProgram(prog);
|
||||
}
|
||||
|
||||
//---
|
||||
// 2D texture shader
|
||||
//---
|
||||
|
||||
/* 6 vertices (a quad) for each texture */
|
||||
struct ProgramTexture_Attributes
|
||||
{
|
||||
/* Location of the vertex */
|
||||
glm::vec2 vertex;
|
||||
/* Location within the texture */
|
||||
glm::vec2 uv;
|
||||
};
|
||||
|
||||
struct ProgramTexture: public Program<ProgramTexture_Attributes>
|
||||
{
|
||||
ProgramTexture();
|
||||
void set_vertex_attributes() const override;
|
||||
|
||||
/* Add a full texture of the specified size
|
||||
TODO: This always uses texture #0
|
||||
TODO: Specify sub-regions */
|
||||
void add_texture(int x, int y, int width, int height);
|
||||
};
|
|
@ -0,0 +1,85 @@
|
|||
/* The world is a drop chute, arranged horizontally. The coordinate system is
|
||||
right-handed, with the following orientation from the top view of the chute:
|
||||
|
||||
z
|
||||
^
|
||||
|
|
||||
|
|
||||
y (x)-----> x
|
||||
|
||||
The player primarily moves in the +y direction (... although in practice
|
||||
objects move towards the player to better use the limited range of fixed-
|
||||
point values).
|
||||
|
||||
We compute directions of rays based on points on the virtual screen placed
|
||||
in the world, in front of the camera. We make rays start at the camera
|
||||
position so the distance between the camera and screen does not matter; we
|
||||
arbitrarily place the screen at a distance which makes the screen height
|
||||
correspond to 2*HALF_SCREEN_HEIGHT world units. This is to help avoid
|
||||
precision loss with fixed-point numbers. */
|
||||
|
||||
#include <num/num.h>
|
||||
#include <num/vec.h>
|
||||
using namespace libnum;
|
||||
struct mat3;
|
||||
|
||||
/* Number of world units that we fix the screen's half height to. The screen is
|
||||
placed at the correct distance to make that happen. */
|
||||
#define HALF_SCREEN_HEIGHT 2
|
||||
/* World boundaries */
|
||||
#define WORLD_SIZE 8 /* (± 4) */
|
||||
/* Viewport size, in pixels */
|
||||
#define VWIDTH 198
|
||||
#define VHEIGHT 112
|
||||
|
||||
struct camera {
|
||||
num fov;
|
||||
num screen_distance;
|
||||
|
||||
/* Camera position (usually stays at z=0, objects move towards it) */
|
||||
vec3 pos;
|
||||
/* Angles of rotation */
|
||||
float yaw, pitch, roll;
|
||||
num cos_r, sin_r;
|
||||
num cos_p, sin_p;
|
||||
num cos_y, sin_y;
|
||||
/* Corresponding directions, in world coordinates */
|
||||
vec3 forward, right, up;
|
||||
|
||||
/* Game stuff... */
|
||||
|
||||
num neon_position;
|
||||
};
|
||||
|
||||
void camera_set_fov(struct camera *camera, float fov_degrees);
|
||||
mat3 matrix_camera2world(struct camera const *camera);
|
||||
void camera_update_angles(struct camera *camera);
|
||||
void render_fragment(struct camera const *camera, uint16_t *fragment,
|
||||
int y_start, int y_height);
|
||||
|
||||
//=== Azur shader wrapping the raytracing ===//
|
||||
|
||||
void cd_raytrace(struct camera const *camera);
|
||||
void cd_raytrace_configure(void);
|
||||
|
||||
//=== Input management ===//
|
||||
|
||||
struct input {
|
||||
bool left;
|
||||
bool right;
|
||||
bool up;
|
||||
bool down;
|
||||
bool OPTN;
|
||||
};
|
||||
|
||||
//=== Additions to libnum ===//
|
||||
|
||||
struct mat3
|
||||
{
|
||||
num x11, x12, x13;
|
||||
num x21, x22, x23;
|
||||
num x31, x32, x33;
|
||||
};
|
||||
|
||||
mat3 operator *(mat3 const &A, mat3 const &B);
|
||||
vec3 operator *(mat3 const &M, vec3 const &u);
|
|
@ -0,0 +1,269 @@
|
|||
#define __BSD_VISIBLE 1
|
||||
#include <azur/azur.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "chaos-drop.h"
|
||||
|
||||
#if defined(AZUR_TOOLKIT_SDL)
|
||||
#include <SDL2/SDL.h>
|
||||
#include <azur/gl/gl.h>
|
||||
#include <memory>
|
||||
#include "backend/linux/programs.h"
|
||||
|
||||
static uint16_t vram[VWIDTH * VHEIGHT];
|
||||
std::unique_ptr<ProgramTexture> shader_texture = nullptr;
|
||||
static GLuint tex_vram;
|
||||
|
||||
static void view_update(void)
|
||||
{
|
||||
SDL_Window *window = azur_sdl_window();
|
||||
int width, height;
|
||||
SDL_GetWindowSize(window, &width, &height);
|
||||
|
||||
/* Transform from pixel coordinates within the winfow to GL coordinates */
|
||||
glm::mat3 tr_pixel2gl(
|
||||
2.0f / width, 0.0f, 0.0f,
|
||||
0.0f, -2.0f / height, 0.0f,
|
||||
-1.0f, 1.0f, 1.0f);
|
||||
|
||||
glUseProgram(shader_texture->prog);
|
||||
shader_texture->set_uniform("u_transform", tr_pixel2gl);
|
||||
}
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
shader_texture = std::make_unique<ProgramTexture>();
|
||||
view_update();
|
||||
memset(vram, 0x55, sizeof vram);
|
||||
|
||||
glGenTextures(1, &tex_vram);
|
||||
glBindTexture(GL_TEXTURE_2D, tex_vram);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VWIDTH, VHEIGHT, 0, GL_RGB,
|
||||
GL_UNSIGNED_SHORT_5_6_5, vram);
|
||||
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_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
static void quit(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int platform_update(struct input *input)
|
||||
{
|
||||
SDL_Event e;
|
||||
*input = (struct input){};
|
||||
|
||||
while(SDL_PollEvent(&e)) {
|
||||
// ImGui_ImplSDL2_ProcessEvent(&e);
|
||||
// render_needed = std::max(render_needed, 1);
|
||||
|
||||
if(e.type == SDL_QUIT)
|
||||
return 1;
|
||||
if(e.type == SDL_WINDOWEVENT &&
|
||||
e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
||||
view_update();
|
||||
}
|
||||
}
|
||||
|
||||
Uint8 const *state = SDL_GetKeyboardState(NULL);
|
||||
input->up = state[SDL_SCANCODE_UP];
|
||||
input->down = state[SDL_SCANCODE_DOWN];
|
||||
input->left = state[SDL_SCANCODE_LEFT];
|
||||
input->right = state[SDL_SCANCODE_RIGHT];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void platform_render(void)
|
||||
{
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VWIDTH, VHEIGHT, 0, GL_RGB,
|
||||
GL_UNSIGNED_SHORT_5_6_5, vram);
|
||||
|
||||
SDL_Window *window = azur_sdl_window();
|
||||
|
||||
shader_texture->vertices.clear();
|
||||
shader_texture->add_texture(0, 0, 3*VWIDTH, 3*VHEIGHT);
|
||||
shader_texture->draw();
|
||||
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
#elif defined(AZUR_TOOLKIT_GINT)
|
||||
|
||||
#include <azur/gint/render.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/drivers/r61524.h>
|
||||
#include <libprof.h>
|
||||
#define vram gint_vram
|
||||
|
||||
static int platform_update(struct input *input)
|
||||
{
|
||||
key_event_t e;
|
||||
|
||||
while((e = pollevent()).type != KEYEV_NONE) {
|
||||
if(e.type == KEYEV_UP)
|
||||
continue;
|
||||
|
||||
if(e.key == KEY_EXIT)
|
||||
return 1;
|
||||
if(e.key == KEY_MENU)
|
||||
gint_osmenu();
|
||||
if(e.key == KEY_OPTN)
|
||||
input->OPTN = true;
|
||||
}
|
||||
|
||||
input->left = keydown(KEY_LEFT);
|
||||
input->right = keydown(KEY_RIGHT);
|
||||
input->up = keydown(KEY_UP);
|
||||
input->down = keydown(KEY_DOWN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void platform_render(void)
|
||||
{
|
||||
azrp_update();
|
||||
}
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
prof_init();
|
||||
azrp_config_scale(2);
|
||||
cd_raytrace_configure();
|
||||
}
|
||||
|
||||
static void quit(void)
|
||||
{
|
||||
prof_quit();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
mat3 operator *(mat3 const &A, mat3 const &B)
|
||||
{
|
||||
mat3 C;
|
||||
|
||||
C.x11 = A.x11 * B.x11 + A.x12 * B.x21 + A.x13 * B.x31;
|
||||
C.x12 = A.x11 * B.x12 + A.x12 * B.x22 + A.x13 * B.x32;
|
||||
C.x13 = A.x11 * B.x13 + A.x12 * B.x23 + A.x13 * B.x33;
|
||||
|
||||
C.x21 = A.x21 * B.x11 + A.x22 * B.x21 + A.x23 * B.x31;
|
||||
C.x22 = A.x21 * B.x12 + A.x22 * B.x22 + A.x23 * B.x32;
|
||||
C.x23 = A.x21 * B.x13 + A.x22 * B.x23 + A.x23 * B.x33;
|
||||
|
||||
C.x31 = A.x31 * B.x11 + A.x32 * B.x21 + A.x33 * B.x31;
|
||||
C.x32 = A.x31 * B.x12 + A.x32 * B.x22 + A.x33 * B.x32;
|
||||
C.x33 = A.x31 * B.x13 + A.x32 * B.x23 + A.x33 * B.x33;
|
||||
|
||||
return C;
|
||||
}
|
||||
|
||||
vec3 operator * (mat3 const &M, vec3 const &u)
|
||||
{
|
||||
vec3 v;
|
||||
|
||||
v.x = M.x11 * u.x + M.x12 * u.y + M.x13 * u.z;
|
||||
v.y = M.x21 * u.x + M.x22 * u.y + M.x23 * u.z;
|
||||
v.z = M.x31 * u.x + M.x32 * u.y + M.x33 * u.z;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static struct camera *camera = NULL;
|
||||
static bool debug = false;
|
||||
|
||||
void render(void)
|
||||
{
|
||||
#ifdef AZUR_TOOLKIT_GINT
|
||||
azrp_perf_clear();
|
||||
cd_raytrace(camera);
|
||||
platform_render();
|
||||
|
||||
if(debug) {
|
||||
drect(0, DHEIGHT-20, DWIDTH-1, DHEIGHT-1, C_WHITE);
|
||||
dprint(4, 209, C_BLACK, "render:%4d+%4dus",
|
||||
prof_time(azrp_perf_render) - prof_time(azrp_perf_r61524),
|
||||
prof_time(azrp_perf_r61524));
|
||||
r61524_display(gint_vram, DHEIGHT-20, 20, R61524_DMA_WAIT);
|
||||
}
|
||||
#else
|
||||
render_fragment(camera, vram, 0, VHEIGHT);
|
||||
platform_render();
|
||||
#endif
|
||||
}
|
||||
|
||||
int update(void)
|
||||
{
|
||||
struct input input = {};
|
||||
if(platform_update(&input))
|
||||
return 1;
|
||||
|
||||
/* Yes I'm aware I'm always changing the angles */
|
||||
bool changed = false;
|
||||
|
||||
static num const mv_speed = 0.35;
|
||||
static num const wall = WORLD_SIZE * 0.45; /* margin */
|
||||
static float const rot_speed = 0.01;
|
||||
static float const rot_snap = 0.6;
|
||||
static float const rot_max = 0.2;
|
||||
static num const neon_speed = 2;
|
||||
|
||||
if(input.left) {
|
||||
camera->pos.x = max(camera->pos.x - mv_speed, -wall);
|
||||
camera->yaw = fmaxf(camera->yaw - rot_speed, -rot_max);
|
||||
changed = true;
|
||||
}
|
||||
else if(input.right) {
|
||||
camera->pos.x = min(camera->pos.x + mv_speed, wall);
|
||||
camera->yaw = fminf(camera->yaw + rot_speed, rot_max);
|
||||
changed = true;
|
||||
}
|
||||
else {
|
||||
camera->yaw *= rot_snap;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if(input.up) {
|
||||
camera->pos.z = min(camera->pos.z + mv_speed, wall);
|
||||
camera->pitch = fmaxf(camera->pitch - rot_speed, -rot_max);
|
||||
changed = true;
|
||||
}
|
||||
else if(input.down) {
|
||||
camera->pos.z = max(camera->pos.z - mv_speed, -wall);
|
||||
camera->pitch = fminf(camera->pitch + rot_speed, rot_max);
|
||||
changed = true;
|
||||
}
|
||||
else {
|
||||
camera->pitch *= rot_snap;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if(changed)
|
||||
camera_update_angles(camera);
|
||||
|
||||
camera->neon_position =
|
||||
(camera->neon_position + num(32) - neon_speed) % num(32);
|
||||
|
||||
if(input.OPTN)
|
||||
debug = !debug;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
if(azur_init("Chaos Drop!", 3*VWIDTH, 3*VHEIGHT))
|
||||
return 1;
|
||||
|
||||
struct camera c = {};
|
||||
camera = &c;
|
||||
/* TODO: Why do I need such a low FOV?! */
|
||||
camera_set_fov(camera, 80.0);
|
||||
camera_update_angles(camera);
|
||||
|
||||
init();
|
||||
int rc = azur_main_loop(render, 30, update, -1, AZUR_MAIN_LOOP_TIED);
|
||||
quit();
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
#define __BSD_VISIBLE 1
|
||||
#include "chaos-drop.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
void camera_set_fov(struct camera *camera, float fov_degrees)
|
||||
{
|
||||
camera->fov = num(fov_degrees);
|
||||
float fov_radians = fov_degrees * 3.14159 / 180;
|
||||
/* Use FOV as the horizontal viewing angle */
|
||||
float sd = (VWIDTH * HALF_SCREEN_HEIGHT / VHEIGHT) / tanf(fov_radians / 2);
|
||||
/* The screen is at such a distance that 16 units is half the height. We
|
||||
don't care where it is placed as we'll always send rays from the camera
|
||||
itself. This is to ensure good ranges for fixed point values */
|
||||
camera->screen_distance = num(sd);
|
||||
}
|
||||
|
||||
mat3 matrix_camera2world(struct camera const *camera)
|
||||
{
|
||||
num cos_r = camera->cos_r;
|
||||
num sin_r = camera->sin_r;
|
||||
num cos_p = camera->cos_p;
|
||||
num sin_p = camera->sin_p;
|
||||
num cos_y = camera->cos_y;
|
||||
num sin_y = camera->sin_y;
|
||||
|
||||
#if 0
|
||||
mat3 m_roll = {
|
||||
cos_r, 0, sin_r,
|
||||
0, 1, 0,
|
||||
-sin_r, 0, cos_r,
|
||||
};
|
||||
mat3 m_pitch = {
|
||||
1, 0, 0,
|
||||
0, cos_p, +sin_p,
|
||||
0, -sin_p, cos_p,
|
||||
};
|
||||
mat3 m_yaw = {
|
||||
cos_y, +sin_y, 0,
|
||||
-sin_y, cos_y, 0,
|
||||
0, 0, 1,
|
||||
};
|
||||
mat3 M = m_roll * (m_pitch * m_yaw);
|
||||
#endif
|
||||
|
||||
mat3 m_anti_roll = {
|
||||
cos_r, 0, -sin_r,
|
||||
0, 1, 0,
|
||||
+sin_r, 0, cos_r,
|
||||
};
|
||||
mat3 m_anti_pitch = {
|
||||
1, 0, 0,
|
||||
0, cos_p, -sin_p,
|
||||
0, +sin_p, cos_p,
|
||||
};
|
||||
mat3 m_anti_yaw = {
|
||||
cos_y, -sin_y, 0,
|
||||
+sin_y, cos_y, 0,
|
||||
0, 0, 1,
|
||||
};
|
||||
return m_anti_yaw * (m_anti_pitch * m_anti_roll);
|
||||
}
|
||||
|
||||
void camera_update_angles(struct camera *camera)
|
||||
{
|
||||
float c, s;
|
||||
|
||||
sincosf(camera->roll, &s, &c);
|
||||
camera->sin_r = s;
|
||||
camera->cos_r = c;
|
||||
|
||||
sincosf(camera->pitch, &s, &c);
|
||||
camera->sin_p = s;
|
||||
camera->cos_p = c;
|
||||
|
||||
sincosf(camera->yaw, &s, &c);
|
||||
camera->sin_y = s;
|
||||
camera->cos_y = c;
|
||||
|
||||
mat3 c2w = matrix_camera2world(camera);
|
||||
camera->forward = c2w * vec3(0, 1, 0);
|
||||
camera->right = c2w * vec3(1, 0, 0);
|
||||
camera->up = c2w * vec3(0, 0, 1);
|
||||
}
|
||||
|
||||
using snum = num16;
|
||||
using svec3 = vec<snum,3>;
|
||||
svec3 svec3_of_vec3(vec3 v)
|
||||
{
|
||||
return svec3(snum(v.x), snum(v.y), snum(v.z));
|
||||
}
|
||||
|
||||
enum object {
|
||||
/* Wall sides */
|
||||
LEFT = 1, TOP, RIGHT, BOTTOM,
|
||||
};
|
||||
|
||||
static int cast_ray(svec3 const &origin, svec3 const &rayDir, snum *t)
|
||||
{
|
||||
snum tx_rx_rz, tz_rx_rz;
|
||||
int hitx = 0, hitz = 0;
|
||||
|
||||
if(rayDir.x > 0) {
|
||||
tx_rx_rz = (snum(WORLD_SIZE / 2) - origin.x) * rayDir.z;
|
||||
hitx = RIGHT;
|
||||
}
|
||||
else if(rayDir.x < 0) {
|
||||
tx_rx_rz = (snum(-WORLD_SIZE / 2) - origin.x) * rayDir.z;
|
||||
hitx = LEFT;
|
||||
}
|
||||
|
||||
if(rayDir.z > 0) {
|
||||
tz_rx_rz = (snum(WORLD_SIZE / 2) - origin.z) * rayDir.x;
|
||||
hitz = TOP;
|
||||
}
|
||||
else if(rayDir.z < 0) {
|
||||
tz_rx_rz = (snum(-WORLD_SIZE / 2) - origin.z) * rayDir.x;
|
||||
hitz = BOTTOM;
|
||||
}
|
||||
|
||||
// static int done = 0;
|
||||
// if(++done <= 4)
|
||||
// printf("tx=%f tz=%f rx=%f rz=%f tx_rx_rz=%f tz_rx_rz=%f\n",
|
||||
// (float)tx, (float)tz, (float)rayDir.x, (float)rayDir.z,
|
||||
// (float)tx_rx_rz, (float)tz_rx_rz);
|
||||
|
||||
int rx_rz_sign = (rayDir.x < 0) ^ (rayDir.z < 0);
|
||||
|
||||
if(hitz && (!hitx || (tz_rx_rz < tx_rx_rz) ^ rx_rz_sign)) {
|
||||
if(t) {
|
||||
if(hitz == TOP)
|
||||
*t = num16::div_positive(snum(WORLD_SIZE / 2) - origin.z,
|
||||
rayDir.z);
|
||||
else
|
||||
*t = num16::div_positive(origin.z + snum(WORLD_SIZE / 2),
|
||||
-rayDir.z);
|
||||
}
|
||||
return hitz;
|
||||
}
|
||||
else if(hitx) {
|
||||
if(t) {
|
||||
if(hitx == RIGHT)
|
||||
*t = num16::div_positive(snum(WORLD_SIZE / 2) - origin.x,
|
||||
rayDir.x);
|
||||
else
|
||||
*t = num16::div_positive(origin.x + snum(WORLD_SIZE / 2),
|
||||
-rayDir.x);
|
||||
}
|
||||
return hitx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void render_fragment(struct camera const *camera, uint16_t *fragment,
|
||||
int y_start, int y_height)
|
||||
{
|
||||
svec3 origin = svec3_of_vec3(camera->pos);
|
||||
svec3 forward = svec3_of_vec3(camera->forward);
|
||||
svec3 right = svec3_of_vec3(camera->right);
|
||||
svec3 up = svec3_of_vec3(camera->up);
|
||||
|
||||
/* Screen center in front of the camera */
|
||||
svec3 screen_center = origin + snum(camera->screen_distance) * forward;
|
||||
/* Unitary movements, in world coordinates, on screen placed in world,
|
||||
corresponding to individual pixel sizes */
|
||||
svec3 pixel_dy = snum(HALF_SCREEN_HEIGHT) * up / snum(VHEIGHT / 2);
|
||||
svec3 pixel_dx = snum(HALF_SCREEN_HEIGHT) * right / snum(VHEIGHT / 2);
|
||||
|
||||
svec3 rayDir_row = screen_center - origin
|
||||
+ snum(VHEIGHT/2 - y_start) * pixel_dy
|
||||
+ snum(0 - VWIDTH/2) * pixel_dx;
|
||||
/* printf("%f %f %f\n",
|
||||
(float)rayDir_row.x,
|
||||
(float)rayDir_row.y,
|
||||
(float)rayDir_row.z); */
|
||||
|
||||
for(int y = y_start; y < y_start + y_height; y++) {
|
||||
svec3 rayDir = rayDir_row;
|
||||
|
||||
for(int x = 0; x < VWIDTH; x++) {
|
||||
snum t;
|
||||
int obj = cast_ray(origin, rayDir, &t);
|
||||
int recolor = 0;
|
||||
snum collision_y;
|
||||
|
||||
if(obj == LEFT || obj == RIGHT) {
|
||||
svec3 collision;
|
||||
collision.z = origin.z + t * rayDir.z;
|
||||
|
||||
if(collision.z >= snum(-WORLD_SIZE / 4)
|
||||
&& collision.z < snum(WORLD_SIZE / 4)) {
|
||||
collision.x = origin.x + t * rayDir.x;
|
||||
collision.y = origin.y + t * rayDir.y;
|
||||
rayDir.x = -rayDir.x;
|
||||
obj = cast_ray(collision, rayDir, &t);
|
||||
recolor = 1 + (obj == RIGHT);
|
||||
collision_y = collision.y + t * rayDir.y;
|
||||
rayDir.x = -rayDir.x;
|
||||
}
|
||||
else {
|
||||
collision_y = origin.y + t * rayDir.y;
|
||||
}
|
||||
}
|
||||
else {
|
||||
collision_y = origin.y + t * rayDir.y;
|
||||
}
|
||||
|
||||
static uint16_t const colors[5] = {
|
||||
0x0000, 0xf800, 0x07e0, 0x001f, 0xffe0,
|
||||
};
|
||||
uint16_t color = colors[obj];
|
||||
|
||||
/* Don't show neons that are too far to avoid flickering */
|
||||
if(collision_y < 64) {
|
||||
snum neon_pos = snum(camera->neon_position);
|
||||
if(obj == TOP || obj == BOTTOM)
|
||||
neon_pos += snum(16);
|
||||
snum neon = collision_y - neon_pos;
|
||||
neon.v = neon.v & (snum(32).v - 1);
|
||||
/* Also make neons larger when they're far to further avoid
|
||||
flickering */
|
||||
snum neon_size = 1;
|
||||
if(collision_y > 20)
|
||||
neon_size += ((collision_y-snum(20)) * snum(1.0/4));
|
||||
if(neon <= neon_size)
|
||||
color = 0xffff;
|
||||
}
|
||||
|
||||
if(recolor == 1)
|
||||
color = ((color & 0xf7de) >> 1) + (0xf000 >> 1);
|
||||
if(recolor == 2)
|
||||
color = ((color & 0xf7de) >> 1) + (0x001f >> 1);
|
||||
|
||||
*fragment++ = color;
|
||||
rayDir += pixel_dx;
|
||||
}
|
||||
|
||||
rayDir_row -= pixel_dy;
|
||||
}
|
||||
|
||||
// printf("%f %f %f\n", (float)rayDir.x, (float)rayDir.y, (float)rayDir.z);
|
||||
}
|
Loading…
Reference in New Issue