block platforms in level 2
This commit is contained in:
parent
23716a7637
commit
b147f74822
43
src/game.cpp
43
src/game.cpp
|
@ -115,6 +115,49 @@ struct prect space_platform_position(struct platform const *p)
|
|||
return r;
|
||||
}
|
||||
|
||||
struct frect space_block_position(struct platform const *p)
|
||||
{
|
||||
vec3 r1(0, -2 * PLATFORM_HEIGHT_STEP, p->z);
|
||||
vec3 r2(0, -space_cylinder_quasiradius() + p->height, p->z);
|
||||
|
||||
int angle_l = p->face;
|
||||
int angle_r = (p->face + 1) % PLATFORM_COUNT;
|
||||
|
||||
struct frect f;
|
||||
f.tr = vec_rotate_around_z(r1, _platform_rotations[angle_r]);
|
||||
f.tl = vec_rotate_around_z(r1, _platform_rotations[angle_l]);
|
||||
f.br = vec_rotate_around_z(r2, _platform_rotations[angle_r]);
|
||||
f.bl = vec_rotate_around_z(r2, _platform_rotations[angle_l]);
|
||||
return f;
|
||||
}
|
||||
|
||||
struct prect space_block_top_position(struct platform const *p)
|
||||
{
|
||||
vec3 r1(0, -2 * PLATFORM_HEIGHT_STEP, p->z);
|
||||
int angle_l = p->face;
|
||||
int angle_r = (p->face + 1) % PLATFORM_COUNT;
|
||||
|
||||
struct prect t;
|
||||
t.nl = t.fl = vec_rotate_around_z(r1, _platform_rotations[angle_l]);
|
||||
t.nr = t.fr = vec_rotate_around_z(r1, _platform_rotations[angle_r]);
|
||||
t.fl.z += p->length;
|
||||
t.fr.z += p->length;
|
||||
return t;
|
||||
}
|
||||
|
||||
struct srect space_wall_position(struct platform const *p)
|
||||
{
|
||||
vec3 r1(0, -2 * PLATFORM_HEIGHT_STEP, p->z);
|
||||
vec3 r2(0, -space_cylinder_quasiradius() + p->height, p->z);
|
||||
|
||||
struct srect s;
|
||||
s.nt = s.ft = vec_rotate_around_z(r1, _platform_rotations[p->face]);
|
||||
s.nb = s.fb = vec_rotate_around_z(r2, _platform_rotations[p->face]);
|
||||
s.ft.z += p->length;
|
||||
s.fb.z += p->length;
|
||||
return s;
|
||||
}
|
||||
|
||||
//======= Game implementation =======//
|
||||
|
||||
void game_advance(struct game *game)
|
||||
|
|
|
@ -116,8 +116,11 @@ consteval num space_platform_arc()
|
|||
platform itself cuts into it. */
|
||||
num space_cylinder_quasiradius(void);
|
||||
|
||||
/* Position, in world units, of the provided flat platform. */
|
||||
/* Position, in world units, of the provided platform. */
|
||||
struct prect space_platform_position(struct platform const *p);
|
||||
struct frect space_block_position(struct platform const *p);
|
||||
struct prect space_block_top_position(struct platform const *p);
|
||||
struct srect space_wall_position(struct platform const *p);
|
||||
|
||||
//======= Game management =======//
|
||||
|
||||
|
|
|
@ -42,7 +42,10 @@ struct MultiPathCarver
|
|||
~MultiPathCarver();
|
||||
|
||||
/* Set initial positions. */
|
||||
void set_faces(int *faces /* size N */);
|
||||
void set_faces(int const *faces /* size N */);
|
||||
|
||||
/* Get current positions. */
|
||||
void get_faces(int *faces /* size N */);
|
||||
|
||||
/* Force the carver to generate all platforms and never skip. */
|
||||
void set_noskip(bool noskip);
|
||||
|
@ -130,6 +133,9 @@ struct AcceleronGenerator: public Generator
|
|||
~AcceleronGenerator() override = default;
|
||||
|
||||
num generate_ascending_funnel(struct level *level, num z);
|
||||
num generate_tunnel_block(struct level *level, num z, int sections);
|
||||
|
||||
void change_phase(void);
|
||||
|
||||
num m_z;
|
||||
bool m_initial;
|
||||
|
|
|
@ -163,7 +163,6 @@ num GeonGenerator::generate_random_tunnel(struct level *level, num z, int sec)
|
|||
return z;
|
||||
}
|
||||
|
||||
|
||||
void GeonGenerator::change_phase(void)
|
||||
{
|
||||
static int const freq_table[] = {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#define G_PLATFORM_LEN_INITIAL num(6.0)
|
||||
/* Probabilities */
|
||||
#define P_ASCF_RED num(0.2)
|
||||
#define P_TUNNEL_RED num(0.2)
|
||||
#define P_TUNNEL_RED_RING num(0.15)
|
||||
|
||||
AcceleronGenerator::AcceleronGenerator():
|
||||
m_checkerboard_carver{3}
|
||||
|
@ -39,9 +41,9 @@ AcceleronGenerator::AcceleronGenerator():
|
|||
....R..... [h=8]
|
||||
....|.....
|
||||
....|.....
|
||||
....|.....
|
||||
...B...... [h=5]
|
||||
...|.#.... [h=4]
|
||||
....|#.... [h=4]
|
||||
...B.|.... [h=5]
|
||||
...|.|....
|
||||
...|.|....
|
||||
....#..... [h=1]
|
||||
|
||||
|
@ -91,18 +93,18 @@ num AcceleronGenerator::generate_ascending_funnel(struct level *level, num z)
|
|||
p.length = G_PLATFORM_LEN * 3;
|
||||
p.height = 8 * PLATFORM_HEIGHT_STEP;
|
||||
add(level, p);
|
||||
z += G_PLATFORM_LEN * 3 + G_PLATFORM_SPC;
|
||||
z += G_PLATFORM_LEN * 2 + G_PLATFORM_SPC;
|
||||
|
||||
p.type = PLATFORM_BLUE;
|
||||
p.face = 3;
|
||||
p.z = z;
|
||||
p.z = z + G_PLATFORM_LEN;
|
||||
p.length = G_PLATFORM_LEN * 3;
|
||||
p.height = 5 * PLATFORM_HEIGHT_STEP;
|
||||
add(level, p);
|
||||
|
||||
p.type = PLATFORM_WHITE;
|
||||
p.face = 5;
|
||||
p.z = z + G_PLATFORM_LEN;
|
||||
p.z = z;
|
||||
p.length = G_PLATFORM_LEN * 2;
|
||||
p.height = 4 * PLATFORM_HEIGHT_STEP;
|
||||
add(level, p);
|
||||
|
@ -121,6 +123,79 @@ num AcceleronGenerator::generate_ascending_funnel(struct level *level, num z)
|
|||
return z;
|
||||
}
|
||||
|
||||
/* Generates a tunnel block, which is full ring alternating with half rings.
|
||||
The total length (which should be odd) is specified as a parameter.
|
||||
|
||||
0123456789
|
||||
########## [h=0]
|
||||
#.#.#.#.#. [h=0]
|
||||
|.|.|.|.|.
|
||||
########## [h=0]
|
||||
(...repeats)
|
||||
|
||||
There is a random probability for any of the platforms to be red, and also a
|
||||
random probability for entire rings to be red. */
|
||||
|
||||
num AcceleronGenerator::generate_tunnel_block(struct level *level, num z,
|
||||
int sec)
|
||||
{
|
||||
for(int i = 0; i < sec; i++) {
|
||||
num len = (1 + (i & 1)) * G_PLATFORM_LEN;
|
||||
platform_type_t type = PLATFORM_WHITE;
|
||||
if(num_rand() <= P_TUNNEL_RED_RING)
|
||||
type = PLATFORM_RED;
|
||||
|
||||
/* Generate all/even platforms on even/odd i */
|
||||
for(int j = 0; j < PLATFORM_COUNT; j++) {
|
||||
if((i & j) & 1)
|
||||
continue;
|
||||
|
||||
struct platform p;
|
||||
p.type = (num_rand() <= P_TUNNEL_RED) ? PLATFORM_RED : type;
|
||||
p.face = j;
|
||||
p.z = z;
|
||||
p.length = len;
|
||||
p.height = 0;
|
||||
add(level, p);
|
||||
}
|
||||
z += len;
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
void AcceleronGenerator::change_phase(void)
|
||||
{
|
||||
static int const freq_table[] = {
|
||||
0, /* Ascending funnel */
|
||||
1, /* Random paths */
|
||||
2, /* Tunnel block */
|
||||
3, /* Random path with walls */
|
||||
4, /* Incomplete tunnel block */
|
||||
};
|
||||
int const FREQ_TABLE_SIZE = sizeof freq_table / sizeof freq_table[0];
|
||||
|
||||
int p = m_phase;
|
||||
|
||||
while(1) {
|
||||
p = freq_table[rand() % FREQ_TABLE_SIZE];
|
||||
|
||||
/* Do not repeat the same phase twice in a row */
|
||||
if(p == m_phase)
|
||||
continue;
|
||||
/* Only allow ascending funnel after tunnel blocks */
|
||||
if(p == 0 && (m_phase != 2 && m_phase != 4))
|
||||
continue;
|
||||
/* Do not follow tunnel blocks together */
|
||||
if((p == 2 && m_phase == 4) || (p == 4 && m_phase == 2))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
m_phase = p;
|
||||
}
|
||||
|
||||
void AcceleronGenerator::generate(struct level *level)
|
||||
{
|
||||
/* Friendly platform at start of level */
|
||||
|
@ -135,14 +210,16 @@ void AcceleronGenerator::generate(struct level *level)
|
|||
add(level, p);
|
||||
m_z += p.length;
|
||||
m_initial = false;
|
||||
m_phase = 3;
|
||||
}
|
||||
|
||||
/* Phase #0: Ascending funnel and long/tight red platform */
|
||||
if(m_phase == 0) {
|
||||
m_z = this->generate_ascending_funnel(level, m_z);
|
||||
m_checkerboard_carver.force_to_match(4);
|
||||
m_phase = 1;
|
||||
this->change_phase();
|
||||
}
|
||||
|
||||
/* Phase #1: Checkerboard random paths */
|
||||
else if(m_phase == 1) {
|
||||
int length = 4 + 2 * (rand() % 2);
|
||||
|
@ -150,7 +227,90 @@ void AcceleronGenerator::generate(struct level *level)
|
|||
m_checkerboard_carver.next(level, m_z, G_PLATFORM_LEN);
|
||||
m_z += G_PLATFORM_LEN + G_PLATFORM_SPC;
|
||||
}
|
||||
m_phase = 0;
|
||||
this->change_phase();
|
||||
}
|
||||
|
||||
/* Phase #2: Simple tunnel block */
|
||||
else if(m_phase == 2) {
|
||||
int length = 5 + 2 * (rand() % 2);
|
||||
m_z = this->generate_tunnel_block(level, m_z, length);
|
||||
this->change_phase();
|
||||
}
|
||||
|
||||
/* Phase #3: Checkerboard random paths, with dividing walls */
|
||||
else if(m_phase == 3) {
|
||||
int length = 14;
|
||||
bool occupied[PLATFORM_COUNT];
|
||||
int faces[3];
|
||||
|
||||
for(int i = 0; i < length; i++) {
|
||||
/* Add walls at i=2,5,8,11 on any face where there is no platform
|
||||
at iteration i or iteration i+1. This is to prevent the
|
||||
following situation:
|
||||
|
||||
W#W
|
||||
#..
|
||||
|
||||
where you can't side-jump from the middle platform to the left
|
||||
one because the wall prevents it. */
|
||||
bool walls = (i == 2 || i == 5 || i == 8 || i == 1);
|
||||
|
||||
/* Start by excluding platforms from iteration #i */
|
||||
if(walls) {
|
||||
for(int j = 0; j < PLATFORM_COUNT; j++)
|
||||
occupied[j] = false;
|
||||
m_checkerboard_carver.get_faces(faces);
|
||||
for(int j = 0; j < 3; j++)
|
||||
occupied[faces[j]] = true;
|
||||
}
|
||||
|
||||
/* Advance */
|
||||
m_checkerboard_carver.next(level, m_z, G_PLATFORM_LEN);
|
||||
|
||||
/* Generate blocks and walls */
|
||||
if(walls) {
|
||||
/* Exclude platforms from iteration #i+1 */
|
||||
m_checkerboard_carver.get_faces(faces);
|
||||
for(int j = 0; j < 3; j++)
|
||||
occupied[faces[j]] = true;
|
||||
|
||||
for(int j = 0; j < PLATFORM_COUNT; j++) {
|
||||
/* Generate blocks in empty spaces */
|
||||
if(!occupied[j]) {
|
||||
struct platform p;
|
||||
p.type = PLATFORM_BLOCK;
|
||||
p.face = j;
|
||||
p.z = m_z;
|
||||
p.length = G_PLATFORM_LEN;
|
||||
p.height = 0.0;
|
||||
add(level, p);
|
||||
p.type = PLATFORM_BLOCK_TOP;
|
||||
add(level, p);
|
||||
}
|
||||
/* Generate dividing walls on edges */
|
||||
if(occupied[j] != occupied[(j+1) % PLATFORM_COUNT]) {
|
||||
struct platform p;
|
||||
p.type = PLATFORM_SEPARATING_WALL;
|
||||
p.face = (j+1) % PLATFORM_COUNT;
|
||||
p.z = m_z;
|
||||
p.length = G_PLATFORM_LEN;
|
||||
p.height = 0.0;
|
||||
p.wall_left_sided = !occupied[j];
|
||||
p.wall_right_sided = occupied[j];
|
||||
add(level, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_z += G_PLATFORM_LEN + G_PLATFORM_SPC;
|
||||
}
|
||||
this->change_phase();
|
||||
}
|
||||
|
||||
/* Phase #4: Incomplete tunnel block */
|
||||
else if(m_phase == 4) {
|
||||
// TODO
|
||||
this->change_phase();
|
||||
}
|
||||
|
||||
/* Phase #1: Almost-full tunnel with random holes? */
|
||||
|
|
|
@ -55,12 +55,19 @@ MultiPathCarver::~MultiPathCarver()
|
|||
delete[] m_paths;
|
||||
}
|
||||
|
||||
void MultiPathCarver::set_faces(int *faces)
|
||||
void MultiPathCarver::set_faces(int const *faces)
|
||||
{
|
||||
for(int i = 0; i < m_N; i++)
|
||||
m_paths[i].face = faces[i];
|
||||
}
|
||||
|
||||
void MultiPathCarver::get_faces(int *faces)
|
||||
{
|
||||
for(int i = 0; i < m_N; i++) {
|
||||
faces[i] = m_paths[i].face;
|
||||
}
|
||||
}
|
||||
|
||||
void MultiPathCarver::set_noskip(bool noskip)
|
||||
{
|
||||
m_noskip = noskip;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "generator.h"
|
||||
#include "util.h"
|
||||
#include <gint/display.h>
|
||||
#include <algorithm>
|
||||
|
||||
struct level level_create(int level)
|
||||
{
|
||||
|
@ -27,10 +28,51 @@ struct level level_create(int level)
|
|||
return l;
|
||||
}
|
||||
|
||||
void level_update(struct level *level, num z)
|
||||
static int local_depth(struct platform const &p)
|
||||
{
|
||||
if(p.type == PLATFORM_BLOCK)
|
||||
return 0;
|
||||
else if(p.type == PLATFORM_BLOCK_TOP)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int relative_distance(struct platform const &p, int face)
|
||||
{
|
||||
int d = abs(p.face - face) % PLATFORM_COUNT;
|
||||
return (d <= PLATFORM_COUNT / 2) ? d : PLATFORM_COUNT - d;
|
||||
}
|
||||
|
||||
/* Hack */
|
||||
static int reference_face = 0;
|
||||
|
||||
static bool compare_platforms(struct platform const &p1,
|
||||
struct platform const &p2)
|
||||
{
|
||||
if(p1.z != p2.z)
|
||||
return p1.z < p2.z;
|
||||
|
||||
int d1 = local_depth(p1);
|
||||
int d2 = local_depth(p2);
|
||||
|
||||
if(d1 != d2)
|
||||
return d1 < d2;
|
||||
|
||||
int rd1 = relative_distance(p1, reference_face);
|
||||
int rd2 = relative_distance(p2, reference_face);
|
||||
return rd1 < rd2;
|
||||
}
|
||||
|
||||
void level_update(struct level *level, num z, int face)
|
||||
{
|
||||
while(!level->platform_buffer.size()
|
||||
|| level->platform_buffer[level->platform_buffer.size() - 1].z
|
||||
< z + RENDER_DEPTH)
|
||||
level->gen->generate(level);
|
||||
|
||||
reference_face = face;
|
||||
std::stable_sort(level->platform_buffer.begin(),
|
||||
level->platform_buffer.end(),
|
||||
compare_platforms);
|
||||
}
|
||||
|
|
10
src/level.h
10
src/level.h
|
@ -10,7 +10,9 @@ typedef enum {
|
|||
PLATFORM_WHITE,
|
||||
PLATFORM_RED,
|
||||
PLATFORM_BLUE,
|
||||
PLATFORM_BLOCK
|
||||
PLATFORM_SEPARATING_WALL,
|
||||
PLATFORM_BLOCK,
|
||||
PLATFORM_BLOCK_TOP,
|
||||
} platform_type_t;
|
||||
|
||||
struct platform {
|
||||
|
@ -27,6 +29,10 @@ struct platform {
|
|||
/* Whether the platform is currently falling. This is only used for red
|
||||
platforms. */
|
||||
bool falling;
|
||||
/* Whether the SEPARATING_WALL platform is seen from the left side and/or
|
||||
seen from the right side. */
|
||||
bool wall_left_sided;
|
||||
bool wall_right_sided;
|
||||
};
|
||||
|
||||
struct Generator;
|
||||
|
@ -40,7 +46,7 @@ struct level {
|
|||
|
||||
|
||||
|
||||
extern void level_update(struct level *level, num z);
|
||||
extern void level_update(struct level *level, num z, int reference_face);
|
||||
extern struct level level_create(int level);
|
||||
|
||||
#endif /* __LEVEL_H__ */
|
||||
|
|
|
@ -137,7 +137,7 @@ int play_level(int level_id)
|
|||
player->energy_percent = 350.0;
|
||||
player->height = num(-10.0);
|
||||
game.t_death = num(10.0);
|
||||
level_update(&game.level, num(350.0));
|
||||
level_update(&game.level, num(350.0), 0);
|
||||
}
|
||||
|
||||
bool game_run = true;
|
||||
|
@ -152,7 +152,7 @@ int play_level(int level_id)
|
|||
prof_t perf_frame = prof_make();
|
||||
prof_enter_norec(perf_frame);
|
||||
|
||||
level_update(&game.level, player->z);
|
||||
level_update(&game.level, player->z, player->platform);
|
||||
camera_track(camera, player);
|
||||
|
||||
struct platform *standing_on = game_platform_under_player(&game,
|
||||
|
|
|
@ -73,6 +73,22 @@ void camera_project_prect(struct camera *camera, struct prect *p)
|
|||
p->fr.y = -p->fr.y * far_f + half_screen.y;
|
||||
}
|
||||
|
||||
void camera_project_frect(struct camera *camera, struct frect *f)
|
||||
{
|
||||
f->tr = camera_project_point(camera, f->tr);
|
||||
f->tl = camera_project_point(camera, f->tl);
|
||||
f->br = camera_project_point(camera, f->br);
|
||||
f->bl = camera_project_point(camera, f->bl);
|
||||
}
|
||||
|
||||
void camera_project_srect(struct camera *camera, struct srect *s)
|
||||
{
|
||||
s->nt = camera_project_point(camera, s->nt);
|
||||
s->nb = camera_project_point(camera, s->nb);
|
||||
s->ft = camera_project_point(camera, s->ft);
|
||||
s->fb = camera_project_point(camera, s->fb);
|
||||
}
|
||||
|
||||
int camera_platform_color(struct camera const *camera, int platform_id)
|
||||
{
|
||||
/* Accounting for the full angle is too precise and results in weird
|
||||
|
@ -137,6 +153,10 @@ uint16_t render_platform_color(struct platform const *p,
|
|||
return camera_platform_color(camera, p->face);
|
||||
if(p->type == PLATFORM_BLUE)
|
||||
return render_color_blue_platform(t);
|
||||
if(p->type == PLATFORM_SEPARATING_WALL || p->type == PLATFORM_BLOCK_TOP)
|
||||
return RGB24(0x909090);
|
||||
if(p->type == PLATFORM_BLOCK)
|
||||
return RGB24(0x808080);
|
||||
if(p->type == PLATFORM_RED) {
|
||||
return t.frac() >= num(0.6)
|
||||
? camera_platform_color(camera, p->face)
|
||||
|
@ -338,21 +358,72 @@ uint16_t render_blend(uint16_t c1, uint16_t c2, int t)
|
|||
|
||||
static void render_game_platforms(struct game &game)
|
||||
{
|
||||
num near = game.camera.pos.z + game.camera.near_plane();
|
||||
|
||||
for(auto it = game.level.platform_buffer.rbegin();
|
||||
it != game.level.platform_buffer.rend();
|
||||
++it) {
|
||||
int color = render_platform_color(&*it, &game.camera, game.t);
|
||||
struct prect p = space_platform_position(&*it);
|
||||
|
||||
/* Near plane clipping */
|
||||
num near = game.camera.pos.z + game.camera.near_plane();
|
||||
if(p.fl.z <= near) continue;
|
||||
if(p.nl.z <= near) p.nl.z = near;
|
||||
if(p.nr.z <= near) p.nr.z = near;
|
||||
camera_project_prect(&game.camera, &p);
|
||||
if(it->type == PLATFORM_SEPARATING_WALL) {
|
||||
struct srect side = space_wall_position(&*it);
|
||||
|
||||
render_triangle(&p.nl, &p.fr, &p.fl, color);
|
||||
render_triangle(&p.fr, &p.nl, &p.nr, color);
|
||||
/* Check if the wall should be rendered at all; it might be on the
|
||||
side of a block that's hiding it from the player's view */
|
||||
int d = (it->face - game.player.platform) % PLATFORM_COUNT;
|
||||
if(d > PLATFORM_COUNT / 2)
|
||||
d -= PLATFORM_COUNT;
|
||||
if(d <= -PLATFORM_COUNT / 2)
|
||||
d += PLATFORM_COUNT;
|
||||
|
||||
if((d < 0 && !it->wall_left_sided) ||
|
||||
(d > 0 && !it->wall_right_sided))
|
||||
continue;
|
||||
|
||||
if(side.ft.z > near) {
|
||||
if(side.nt.z <= near) side.nt.z = near;
|
||||
if(side.nb.z <= near) side.nb.z = near;
|
||||
camera_project_srect(&game.camera, &side);
|
||||
|
||||
render_triangle(&side.ft, &side.fb, &side.nb, color);
|
||||
render_triangle(&side.ft, &side.nb, &side.nt, color);
|
||||
}
|
||||
}
|
||||
else if(it->type == PLATFORM_BLOCK) {
|
||||
struct frect front = space_block_position(&*it);
|
||||
|
||||
if(front.tl.z > near) {
|
||||
camera_project_frect(&game.camera, &front);
|
||||
|
||||
render_triangle(&front.tr, &front.tl, &front.bl, color);
|
||||
render_triangle(&front.tr, &front.bl, &front.br, color);
|
||||
}
|
||||
}
|
||||
else if(it->type == PLATFORM_BLOCK_TOP) {
|
||||
struct prect top = space_block_top_position(&*it);
|
||||
|
||||
if(top.fl.z > near) {
|
||||
if(top.nl.z <= near) top.nl.z = near;
|
||||
if(top.nr.z <= near) top.nr.z = near;
|
||||
camera_project_prect(&game.camera, &top);
|
||||
|
||||
render_triangle(&top.nl, &top.fr, &top.fl, color);
|
||||
render_triangle(&top.fr, &top.nl, &top.nr, color);
|
||||
}
|
||||
}
|
||||
/* Normal, flat platforms */
|
||||
else {
|
||||
struct prect p = space_platform_position(&*it);
|
||||
|
||||
/* Near plane clipping */
|
||||
if(p.fl.z <= near) continue;
|
||||
if(p.nl.z <= near) p.nl.z = near;
|
||||
if(p.nr.z <= near) p.nr.z = near;
|
||||
camera_project_prect(&game.camera, &p);
|
||||
|
||||
render_triangle(&p.nl, &p.fr, &p.fl, color);
|
||||
render_triangle(&p.fr, &p.nl, &p.nr, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,8 +66,11 @@ void camera_track(struct camera *, struct player const *);
|
|||
member is kept unchanged. */
|
||||
vec3 camera_project_point(struct camera *, vec3 point);
|
||||
|
||||
/* Optimized camera_project_point() that projects an entire prect in-place. */
|
||||
/* Optimized camera_project_point() that projects an entire prect, frect or
|
||||
srect in-place. */
|
||||
void camera_project_prect(struct camera *, struct prect *rect);
|
||||
void camera_project_frect(struct camera *, struct frect *frect);
|
||||
void camera_project_srect(struct camera *, struct srect *srect);
|
||||
|
||||
/* Compute the platform's color based on the camera's angle. */
|
||||
int camera_platform_color(struct camera const *camera, int platform_id);
|
||||
|
|
14
src/util.h
14
src/util.h
|
@ -13,6 +13,20 @@ struct prect
|
|||
vec3 fl, fr; /* Far left and far right points */
|
||||
};
|
||||
|
||||
/* A side rectangle, aligned with the camera's view */
|
||||
struct srect
|
||||
{
|
||||
vec3 nt, nb; /* Near top and near bottom points */
|
||||
vec3 ft, fb; /* Far top and far bottom points */
|
||||
};
|
||||
|
||||
/* A flat rectangle, normal to the camera's view */
|
||||
struct frect
|
||||
{
|
||||
vec3 tl, tr; /* Top left an top right points */
|
||||
vec3 bl, br; /* Bottom left and bottom right points */
|
||||
};
|
||||
|
||||
/* Approximations of the sine and cosine of an angle in radians. */
|
||||
num num_cos(num a);
|
||||
num num_sin(num a);
|
||||
|
|
Loading…
Reference in New Issue