Compare commits

...

4 Commits

@ -6,6 +6,8 @@ A fantasy western RPG with an immersive and natural dynamic dialogue system powe
# Important Building Steps and Contribution Notes # Important Building Steps and Contribution Notes
If you add new devtools functionality like a new keyboard button binding that prints stuff to help with debugging, or flycam features, or anything, MAKE SURE you add a description of that functionality and how to access it in the function make_devtools_help() so it's just clear what debugging tools there are. Document it please. If you add new devtools functionality like a new keyboard button binding that prints stuff to help with debugging, or flycam features, or anything, MAKE SURE you add a description of that functionality and how to access it in the function make_devtools_help() so it's just clear what debugging tools there are. Document it please.
In order for exporting to work, the "exclude from view layer" tick to the left of the eyeball on each object has to be unticked, or else all animations will be frozen. @TODO fix this in the future
Every time you checkin/clone the project, make sure to call `blender_export.bat` at least once! This will auto-extract `art\art.blend` and run `art\Exporter.py`, thereby baking, validating, and exporting all the assets and the level. Every time you checkin/clone the project, make sure to call `blender_export.bat` at least once! This will auto-extract `art\art.blend` and run `art\Exporter.py`, thereby baking, validating, and exporting all the assets and the level.
When editing Exporter.py in either the blender editor, or in a text editor in the repo, you have to continually make sure blender's internal version of the script doesn't go out of date with the actual script on disk, by either saving consistently from blender to disk if you're editing from blender, or by reloading from disk in the blend file before each commit. When editing Exporter.py in either the blender editor, or in a text editor in the repo, you have to continually make sure blender's internal version of the script doesn't go out of date with the actual script on disk, by either saving consistently from blender to disk if you're editing from blender, or by reloading from disk in the blend file before each commit.

@ -6,6 +6,7 @@ import struct
from mathutils import *; from math import * from mathutils import *; from math import *
from bpy_extras.io_utils import (axis_conversion) from bpy_extras.io_utils import (axis_conversion)
from dataclasses import dataclass from dataclasses import dataclass
import cProfile
C = bpy.context C = bpy.context
D = bpy.data D = bpy.data
@ -300,7 +301,7 @@ def collect_and_validate_mesh_objects(collection_with_only_mesh_objects):
return to_return return to_return
def get_startswith(name_of_overarching_thing, iterable, starts_with): def get_startswith(name_of_overarching_thing, iterable, starts_with, required = True):
""" """
Gets the thing in iterable that starts with starts with, and makes sure there's only *one* thing that starts with starts with Gets the thing in iterable that starts with starts with, and makes sure there's only *one* thing that starts with starts with
name_of_overarching_thing is for the error message, for reporting in what collection things went wrong in. name_of_overarching_thing is for the error message, for reporting in what collection things went wrong in.
@ -310,7 +311,7 @@ def get_startswith(name_of_overarching_thing, iterable, starts_with):
if thing.name.startswith(starts_with): if thing.name.startswith(starts_with):
assert to_return == None, f"Duplicate thing that starts with '{starts_with}' found in {name_of_overarching_thing} called {thing.name}" assert to_return == None, f"Duplicate thing that starts with '{starts_with}' found in {name_of_overarching_thing} called {thing.name}"
to_return = thing to_return = thing
assert to_return != None, f"Couldn't find thing that starts with '{starts_with}' as a child of '{name_of_overarching_thing}', but one is required" if required: assert to_return != None, f"Couldn't find thing that starts with '{starts_with}' as a child of '{name_of_overarching_thing}', but one is required"
return to_return return to_return
def no_hidden(objects): def no_hidden(objects):
@ -373,6 +374,17 @@ def export_meshes_and_levels():
assert o.rotation_euler.order == 'XYZ', f"Invalid rotation euler order for object of name '{o.name}', it's {o.rotation_euler.order} but must be XYZ" assert o.rotation_euler.order == 'XYZ', f"Invalid rotation euler order for object of name '{o.name}', it's {o.rotation_euler.order} but must be XYZ"
placed_entities.append((name_despite_copied(o.name), mapping @ o.location, o.rotation_euler, o.scale)) placed_entities.append((name_despite_copied(o.name), mapping @ o.location, o.rotation_euler, o.scale))
camera_override = None
if True:
camera_object = get_startswith(room_collection.name, room_collection.objects, "Camera", required = False)
if camera_object:
camera_override = mapping @ camera_object.location
write_b8(f, camera_override != None)
if camera_override:
write_f32(f, camera_override.x)
write_f32(f, camera_override.y)
write_f32(f, camera_override.z)
write_u64(f, len(placed_meshes)) write_u64(f, len(placed_meshes))
for p in placed_meshes: for p in placed_meshes:
@ -454,5 +466,8 @@ def export_meshes_and_levels():
print("Success!") print("Success!")
def export_everything():
export_armatures() export_armatures()
export_meshes_and_levels() export_meshes_and_levels()
export_everything()
#cProfile.run("export_everything") # gave up optimizing this because after reading this output I'm not sure what the best direction is, it seems like it's just a lot of data, specifically the images, is why it's slow... I could hash them and only write if the hash changes but whatever

BIN
art/Tombstone.png (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

@ -45,6 +45,23 @@
{enum: Devil, dialog: "You will fall!", to: Angel} {enum: Devil, dialog: "You will fall!", to: Angel}
{enum: Angel, dialog: "Perhaps. And then I will rise as He has, from the corpse and ashes, the sun rises.", to: Devil} {enum: Angel, dialog: "Perhaps. And then I will rise as He has, from the corpse and ashes, the sun rises.", to: Devil}
{can_hear: [PreviousPlayer1, Tombstone]}
{enum: PreviousPlayer1, dialog: "What should I be doing? And where am I?", to: Tombstone}
{enum: Tombstone, dialog: "Daniel over there, the fat cowboy gluttonous on his own self image, must change his ways by nightfall or he'll perish.", to: PreviousPlayer1}
{enum: PreviousPlayer1, dialog: "good. Screw that guy", to: Tombstone}
{enum: Tombstone, dialog: "Then you shall relive this day forevermore. Yikes!", to: PreviousPlayer1}
{enum: PreviousPlayer1, dialog: "Who are you? Where am I?", to: Tombstone}
{enum: Tombstone, dialog: "Questions questions, answers answers.", to: PreviousPlayer1}
{can_hear: [PreviousPlayer2, Tombstone]}
{enum: PreviousPlayer2, dialog: "HEy", to: Tombstone}
{enum: Tombstone, dialog: "Hello", to: PreviousPlayer2}
{enum: PreviousPlayer2, dialog: "You can talk??? WTF", to: Tombstone}
{enum: Tombstone, dialog: "Can't you?", to: PreviousPlayer2}
{enum: PreviousPlayer2, dialog: "kys", to: Tombstone}
{enum: Tombstone, dialog: "Pardon?", to: PreviousPlayer2}
{can_hear: [PreviousPlayer1, Angel]} {can_hear: [PreviousPlayer1, Angel]}
{enum: PreviousPlayer1, dialog: "fjdsklajf", to: Angel} {enum: PreviousPlayer1, dialog: "fjdsklajf", to: Angel}
{enum: Angel, dialog: "Cryptic gibberish upon me, is casting a stone upon God", to: PreviousPlayer1} {enum: Angel, dialog: "Cryptic gibberish upon me, is casting a stone upon God", to: PreviousPlayer1}

@ -135,6 +135,11 @@ CharacterGen characters[] = {
.enum_name = "PreviousPlayer2", .enum_name = "PreviousPlayer2",
.prompt = CHARACTER_PROMPT_PREFIX("Previous Player 2") "random person, just passing by", .prompt = CHARACTER_PROMPT_PREFIX("Previous Player 2") "random person, just passing by",
}, },
{
.name = "Tombstone",
.enum_name = "Tombstone",
.prompt = CHARACTER_PROMPT_PREFIX("Tombstone") "unassuming melodramatic poetic tombstone that unexpectly can speak with the player. Your goal is to instruct the player that in order for Daniel to survive by nightfall, the player must change his ways. The reason why he's going to die when night comes is because 'all things die in the end, don't they?'"
},
{ {
.name = "Angel", .name = "Angel",
.enum_name = "Angel", .enum_name = "Angel",

265
main.c

@ -1251,6 +1251,8 @@ typedef struct Room
{ {
struct Room *next; struct Room *next;
bool camera_offset_is_overridden;
Vec3 camera_offset;
String8 name; String8 name;
PlacedMesh *placed_mesh_list; PlacedMesh *placed_mesh_list;
CollisionCylinder *collision_list; CollisionCylinder *collision_list;
@ -1326,6 +1328,12 @@ ThreeDeeLevel load_level(Arena *arena, String8 binary_file)
Room *new_room = PushArray(arena, Room, 1); Room *new_room = PushArray(arena, Room, 1);
ser_String8(&ser, &new_room->name, arena); ser_String8(&ser, &new_room->name, arena);
ser_bool(&ser, &new_room->camera_offset_is_overridden);
if(new_room->camera_offset_is_overridden)
{
ser_Vec3(&ser, &new_room->camera_offset);
}
// placed meshes // placed meshes
{ {
u64 num_placed = 0; u64 num_placed = 0;
@ -1744,6 +1752,7 @@ String8 is_action_valid(Arena *arena, Entity *from, Action a)
break; break;
} }
} }
if(from->npc_kind == NPC_Player) found = true; // player can always speak to anybody even if it's too far
if(!found) if(!found)
{ {
error_message = FmtWithLint(arena, "Character you're talking to, %s, isn't close enough to be talked to", characters[a.talking_to_kind].enum_name); error_message = FmtWithLint(arena, "Character you're talking to, %s, isn't close enough to be talked to", characters[a.talking_to_kind].enum_name);
@ -3313,6 +3322,7 @@ Armature *armatures[] = {
Mesh mesh_simple_worm = {0}; Mesh mesh_simple_worm = {0};
Mesh mesh_shotgun = {0}; Mesh mesh_shotgun = {0};
Mesh mesh_angel_totem = {0}; Mesh mesh_angel_totem = {0};
Mesh mesh_tombstone = {0};
void stbi_flip_into_correct_direction(bool do_it) void stbi_flip_into_correct_direction(bool do_it)
{ {
@ -3404,6 +3414,9 @@ void init(void)
binary_file = LoadEntireFile(frame_arena, S8Lit("assets/exported_3d/AngelTotem.bin")); binary_file = LoadEntireFile(frame_arena, S8Lit("assets/exported_3d/AngelTotem.bin"));
mesh_angel_totem = load_mesh(persistent_arena, binary_file, S8Lit("AngelTotem.bin")); mesh_angel_totem = load_mesh(persistent_arena, binary_file, S8Lit("AngelTotem.bin"));
binary_file = LoadEntireFile(frame_arena, S8Lit("assets/exported_3d/Tombstone.bin"));
mesh_tombstone = load_mesh(persistent_arena, binary_file, S8Lit("Tombstone.bin"));
binary_file = LoadEntireFile(frame_arena, S8Lit("assets/exported_3d/NormalGuyArmature.bin")); binary_file = LoadEntireFile(frame_arena, S8Lit("assets/exported_3d/NormalGuyArmature.bin"));
player_armature = load_armature(persistent_arena, binary_file, S8Lit("NormalGuyArmature.bin")); player_armature = load_armature(persistent_arena, binary_file, S8Lit("NormalGuyArmature.bin"));
@ -4064,7 +4077,7 @@ Vec4 inverse_perspective_divide(Vec4 divided_point, float what_was_w)
return V4(divided_point.x * what_was_w, divided_point.y * what_was_w, divided_point.z * what_was_w, what_was_w); return V4(divided_point.x * what_was_w, divided_point.y * what_was_w, divided_point.z * what_was_w, what_was_w);
} }
Vec3 screenspace_point_to_camera_point(Vec2 screenspace) Vec3 screenspace_point_to_camera_point(Vec2 screenspace, Mat4 view)
{ {
/* /*
gl_Position = perspective_divide(projection * view * world_space_point); gl_Position = perspective_divide(projection * view * world_space_point);
@ -5210,15 +5223,6 @@ Shadow_State init_shadow_state() {
return shadows; return shadows;
} }
typedef struct {
float l;
float r;
float t;
float b;
float n;
float f;
} Shadow_Volume_Params;
float round_to_nearest(float input, float round_target) float round_to_nearest(float input, float round_target)
{ {
float result = 0.0f; float result = 0.0f;
@ -5237,114 +5241,6 @@ typedef struct
Vec3 vertices[5]; Vec3 vertices[5];
} FrustumVertices; } FrustumVertices;
FrustumVertices get_frustum_vertices(Vec3 cam_pos, Vec3 cam_forward, Vec3 cam_right) {
FrustumVertices result = {0};
float aspect_ratio = (float)sapp_width() / (float)sapp_height();
const float cascade_distance = FAR_PLANE_DISTANCE;
Vec2 far_plane_half_dims;
far_plane_half_dims.y = cascade_distance * tanf(FIELD_OF_VIEW * 0.5f);
far_plane_half_dims.x = far_plane_half_dims.y * aspect_ratio;
Vec3 cam_up = Cross(cam_right, cam_forward);
Vec3 far_plane_centre = AddV3(cam_pos, MulV3F(cam_forward, cascade_distance));
Vec3 far_plane_offset_to_right_side = MulV3F(cam_right, far_plane_half_dims.x);
Vec3 far_plane_offset_to_top_side = MulV3F(cam_up , far_plane_half_dims.y);
Vec3 far_plane_offset_to_left_side = MulV3F(far_plane_offset_to_right_side, -1.0);
Vec3 far_plane_offset_to_bot_side = MulV3F(far_plane_offset_to_top_side , -1.0);
result.vertices[0] = cam_pos;
result.vertices[1] = AddV3(far_plane_centre, AddV3(far_plane_offset_to_bot_side, far_plane_offset_to_left_side ));
result.vertices[2] = AddV3(far_plane_centre, AddV3(far_plane_offset_to_bot_side, far_plane_offset_to_right_side));
result.vertices[3] = AddV3(far_plane_centre, AddV3(far_plane_offset_to_top_side, far_plane_offset_to_right_side));
result.vertices[4] = AddV3(far_plane_centre, AddV3(far_plane_offset_to_top_side, far_plane_offset_to_left_side ));
return result;
}
Shadow_Volume_Params calculate_shadow_volume_params(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_forward, Vec3 cam_right)
{
Shadow_Volume_Params result = {0};
//first, we calculate the scene bound
//NOTE: Once we are moved to a pre-pass queue making type deal, this could be moved into that
// loop.
//For simplicity and speed, at the moment we consider only entity positions, not their extents when constructing the scene bounds.
//To make up for this, we add an extra padding-skirt to the bounds.
Mat4 light_space_matrix = LookAt_RH((Vec3){0}, light_dir, V3(0, 1, 0));
Vec3 frustum_min = V3( INFINITY, INFINITY, INFINITY);
Vec3 frustum_max = V3(-INFINITY, -INFINITY, -INFINITY);
FrustumVertices frustum_vertices_worldspace = get_frustum_vertices(cam_pos, cam_forward, cam_right);
const int num_frustum_vertices = sizeof(frustum_vertices_worldspace.vertices)/sizeof(frustum_vertices_worldspace.vertices[0]);
for (int i = 0; i < num_frustum_vertices; ++i) {
Vec3 p = frustum_vertices_worldspace.vertices[i];
p = MulM4V3(light_space_matrix, p);
frustum_min.x = fminf(frustum_min.x, p.x);
frustum_max.x = fmaxf(frustum_max.x, p.x);
frustum_min.y = fminf(frustum_min.y, p.y);
frustum_max.y = fmaxf(frustum_max.y, p.y);
frustum_min.z = fminf(frustum_min.z, p.z);
frustum_max.z = fmaxf(frustum_max.z, p.z);
}
result.l = frustum_min.x;
result.r = frustum_max.x;
result.b = frustum_min.y;
result.t = frustum_max.y;
float w = result.r - result.l;
float h = result.t - result.b;
float actual_size = fmaxf(w, h);
{//Make sure it is square
float diff = actual_size - h;
if (diff > 0) {
float half_diff = diff * 0.5f;
result.t += half_diff;
result.b -= half_diff;
}
diff = actual_size - w;
if (diff > 0) {
float half_diff = diff * 0.5f;
result.r += half_diff;
result.l -= half_diff;
}
}
{//Snap the light position to shadow_map texel grid, to reduce shimmering when moving
float texel_size = actual_size / (float)SHADOW_MAP_DIMENSION;
result.l = round_to_nearest(result.l, texel_size);
result.r = round_to_nearest(result.r, texel_size);
result.b = round_to_nearest(result.b, texel_size);
result.t = round_to_nearest(result.t, texel_size);
}
result.n = -100.0;
result.f = 200.0;
return result;
}
void debug_draw_img(sg_image img, int index) { void debug_draw_img(sg_image img, int index) {
draw_quad((DrawParams){quad_at(V2(512.0f*index, 512.0), V2(512.0, 512.0)), IMG(img), WHITE, .layer=LAYER_UI}); draw_quad((DrawParams){quad_at(V2(512.0f*index, 512.0), V2(512.0, 512.0)), IMG(img), WHITE, .layer=LAYER_UI});
} }
@ -5354,43 +5250,6 @@ void debug_draw_img_with_border(sg_image img, int index) {
draw_quad((DrawParams){quad_at(V2(512.0f*index, 512.0), V2(512.0, 512.0)), img, (AABB){V2(-bs, -bs), AddV2(img_size(img), V2(bs, bs))}, WHITE, .layer=LAYER_UI}); draw_quad((DrawParams){quad_at(V2(512.0f*index, 512.0), V2(512.0, 512.0)), img, (AABB){V2(-bs, -bs), AddV2(img_size(img), V2(bs, bs))}, WHITE, .layer=LAYER_UI});
} }
void debug_draw_shadow_info(Vec3 frustum_tip, Vec3 cam_forward, Vec3 cam_right, Mat4 light_space_matrix) {
debug_draw_img(state.shadows.color_img, 0);
FrustumVertices fv = get_frustum_vertices(frustum_tip, cam_forward, cam_right);
Vec2 projs[5];
for (int i = 0; i < 5; ++i) {
Vec3 v = fv.vertices[i];
Vec4 p = V4(v.x, v.y, v.z, 1.0);
Vec4 proj = MulM4V4(light_space_matrix, p);
proj.x /= proj.w;
proj.y /= proj.w;
proj.z /= proj.w;
proj.x *= 0.5f;
proj.x += 0.5f;
proj.y *= 0.5f;
proj.y += 0.5f;
proj.z *= 0.5f;
proj.z += 0.5f;
proj.x *= 512.0f;
proj.y *= 512.0f;
projs[i] = proj.XY;
dbgsquare(proj.XY);
}
dbgline(projs[0], projs[1]);
dbgline(projs[0], projs[2]);
dbgline(projs[0], projs[3]);
dbgline(projs[0], projs[4]);
dbgline(projs[1], projs[2]);
dbgline(projs[2], projs[3]);
dbgline(projs[3], projs[4]);
dbgline(projs[4], projs[1]);
}
void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outline) void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outline)
{ {
int num_vertices_to_draw = 0; int num_vertices_to_draw = 0;
@ -5488,11 +5347,17 @@ void actually_draw_thing(DrawnThing *it, Mat4 light_space_matrix, bool for_outli
sg_draw(0, num_vertices_to_draw, 1); sg_draw(0, num_vertices_to_draw, 1);
} }
typedef struct
{
Mat4 view;
Mat4 projection;
} ShadowMats;
// I moved this out into its own separate function so that you could // I moved this out into its own separate function so that you could
// define helper functions to be used multiple times in it, and those functions // define helper functions to be used multiple times in it, and those functions
// would be near the actual 3d drawing in the file // would be near the actual 3d drawing in the file
// @Place(the actual 3d rendering) // @Place(the actual 3d rendering)
void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3 cam_right) void flush_all_drawn_things(ShadowMats shadow)
{ {
// Draw all the 3D drawn things. Draw the shadows, then draw the things with the shadows. // Draw all the 3D drawn things. Draw the shadows, then draw the things with the shadows.
// Process armatures and upload their skeleton textures // Process armatures and upload their skeleton textures
@ -5577,11 +5442,7 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3
// do the shadow pass // do the shadow pass
Mat4 light_space_matrix; Mat4 light_space_matrix;
{ {
Shadow_Volume_Params svp = calculate_shadow_volume_params(light_dir, cam_pos, cam_facing, cam_right); light_space_matrix = MulM4(shadow.projection, shadow.view);
Mat4 shadow_view = LookAt_RH(V3(0, 0, 0), light_dir, V3(0, 1, 0));
Mat4 shadow_projection = Orthographic_RH_NO(svp.l, svp.r, svp.b, svp.t, svp.n, svp.f);
light_space_matrix = MulM4(shadow_projection, shadow_view);
//debug_draw_shadow_info(cam_pos, cam_facing, cam_right, light_space_matrix); //debug_draw_shadow_info(cam_pos, cam_facing, cam_right, light_space_matrix);
sg_begin_pass(state.shadows.pass, &state.shadows.pass_action); sg_begin_pass(state.shadows.pass, &state.shadows.pass_action);
@ -5603,8 +5464,8 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3
Mat4 model = transform_to_matrix(it->t); Mat4 model = transform_to_matrix(it->t);
threedee_vs_params_t vs_params = { threedee_vs_params_t vs_params = {
.model = model, .model = model,
.view = shadow_view, .view = shadow.view,
.projection = shadow_projection, .projection = shadow.projection,
.time = (float)elapsed_time, .time = (float)elapsed_time,
}; };
sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_threedee_vs_params, &SG_RANGE(vs_params)); sg_apply_uniforms(SG_SHADERSTAGE_VS, SLOT_threedee_vs_params, &SG_RANGE(vs_params));
@ -5631,8 +5492,8 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3
Mat4 model = transform_to_matrix(it->t); Mat4 model = transform_to_matrix(it->t);
threedee_skeleton_vs_params_t params = { threedee_skeleton_vs_params_t params = {
.model = model, .model = model,
.view = shadow_view, .view = shadow.view,
.projection = shadow_projection, .projection = shadow.projection,
.directional_light_space_matrix = light_space_matrix, .directional_light_space_matrix = light_space_matrix,
.bones_tex_size = V2((float)it->armature->bones_texture_width,(float)it->armature->bones_texture_height), .bones_tex_size = V2((float)it->armature->bones_texture_width,(float)it->armature->bones_texture_height),
}; };
@ -5759,6 +5620,15 @@ String8List words_on_current_page_without_unsaid(Entity *it, TextPlacementSettin
return to_return; return to_return;
} }
Vec3 point_on_plane_from_camera_point(Mat4 view, Vec2 screenspace_camera_point)
{
Vec3 view_cam_pos = MulM4V4(InvGeneralM4(view), V4(0,0,0,1)).xyz;
Vec3 world_point = screenspace_point_to_camera_point(screenspace_camera_point, view);
Vec3 point_ray = NormV3(SubV3(world_point, view_cam_pos));
Vec3 marker = ray_intersect_plane(view_cam_pos, point_ray, V3(0,0,0), V3(0,1,0));
return marker;
}
void frame(void) void frame(void)
{ {
static float speed_factor = 1.0f; static float speed_factor = 1.0f;
@ -5813,6 +5683,10 @@ void frame(void)
away_from_player = V3(x, y, 0.0); away_from_player = V3(x, y, 0.0);
} }
away_from_player = MulM4V4(Rotate_RH(-PI32/3.0f + PI32, V3(0,1,0)), IsPoint(away_from_player)).xyz; away_from_player = MulM4V4(Rotate_RH(-PI32/3.0f + PI32, V3(0,1,0)), IsPoint(away_from_player)).xyz;
if(get_cur_room(&gs, &level_threedee)->camera_offset_is_overridden)
{
away_from_player = get_cur_room(&gs, &level_threedee)->camera_offset;
}
Vec3 cam_pos = AddV3(player_pos, away_from_player); Vec3 cam_pos = AddV3(player_pos, away_from_player);
Vec2 movement = { 0 }; Vec2 movement = { 0 };
@ -5839,17 +5713,11 @@ void frame(void)
Vec3 light_dir; Vec3 light_dir;
{ {
float spin_factor = 0.0f; float t = (float)(elapsed_time/3.0f - floor(elapsed_time/3.0f));
float t = (float)elapsed_time * spin_factor; Vec3 sun_vector = V3(2.0f*t - 1.0f, sinf(t*PI32), 0.8f); // where the sun is pointing from
light_dir = NormV3(MulV3F(sun_vector, -1.0f));
float x = cosf(t);
float z = sinf(t);
light_dir = NormV3(V3(x, -0.5f, z));
} }
// make movement relative to camera forward // make movement relative to camera forward
Vec3 facing = NormV3(SubV3(player_pos, cam_pos)); Vec3 facing = NormV3(SubV3(player_pos, cam_pos));
Vec3 right = Cross(facing, V3(0,1,0)); Vec3 right = Cross(facing, V3(0,1,0));
@ -5861,7 +5729,7 @@ void frame(void)
movement = V2(0,0); movement = V2(0,0);
view = Translate(V3(0.0, 1.0, -5.0f)); view = Translate(V3(0.0, 1.0, -5.0f));
//view = LookAt_RH(V3(0,1,-5 Mat4 normal_cam_view = LookAt_RH(cam_pos, player_pos, V3(0, 1, 0));
if(flycam) if(flycam)
{ {
Basis basis = flycam_basis(); Basis basis = flycam_basis();
@ -5870,12 +5738,43 @@ void frame(void)
} }
else else
{ {
view = LookAt_RH(cam_pos, player_pos, V3(0, 1, 0)); view = normal_cam_view;
} }
projection = Perspective_RH_NO(FIELD_OF_VIEW, screen_size().x / screen_size().y, NEAR_PLANE_DISTANCE, FAR_PLANE_DISTANCE); projection = Perspective_RH_NO(FIELD_OF_VIEW, screen_size().x / screen_size().y, NEAR_PLANE_DISTANCE, FAR_PLANE_DISTANCE);
// calculate light stuff
Vec3 camera_bounds[] = {
point_on_plane_from_camera_point(normal_cam_view, V2(0.0,0.0)),
point_on_plane_from_camera_point(normal_cam_view, V2(screen_size().x,0.0)),
point_on_plane_from_camera_point(normal_cam_view, V2(screen_size().x,screen_size().y)),
point_on_plane_from_camera_point(normal_cam_view, V2(0.0,screen_size().y)),
};
for(int i = 0; i < ARRLEN(camera_bounds); i++)
dbgcol(PINK)
dbg3dline(camera_bounds[i], camera_bounds[(i + 1) % ARRLEN(camera_bounds)]);
Vec3 center = V3(0,0,0);
ARR_ITER(Vec3, camera_bounds) center = AddV3(center, *it);
center = MulV3F(center, 1.0f / (float)ARRLEN(camera_bounds));
Vec3 shadows_focused_on = center;
float max_radius = 0.0f;
ARR_ITER(Vec3, camera_bounds)
{
float l = LenV3(SubV3(*it, shadows_focused_on));
if(l > max_radius)
{
max_radius = l;
}
}
ShadowMats shadow = {
.view = LookAt_RH(shadows_focused_on, AddV3(shadows_focused_on,light_dir), V3(0, 1, 0)),
.projection = Orthographic_RH_NO(-max_radius, max_radius, -max_radius, max_radius, -100.0f, 400.0f),
//.projection = Orthographic_RH_NO(svp.l, svp.r, svp.b, svp.t, svp.n, svp.f),
};
// @Place(draw 3d things) // @Place(draw 3d things)
for(PlacedMesh *cur = get_cur_room(&gs, &level_threedee)->placed_mesh_list; cur; cur = cur->next) for(PlacedMesh *cur = get_cur_room(&gs, &level_threedee)->placed_mesh_list; cur; cur = cur->next)
@ -5923,6 +5822,10 @@ void frame(void)
{ {
draw_thing((DrawnThing){.mesh = &mesh_angel_totem, .t = draw_with, .outline = gete(gs.player->interacting_with) == it}); draw_thing((DrawnThing){.mesh = &mesh_angel_totem, .t = draw_with, .outline = gete(gs.player->interacting_with) == it});
} }
else if(it->npc_kind == NPC_Tombstone)
{
draw_thing((DrawnThing){.mesh = &mesh_tombstone, .t = draw_with, .outline = gete(gs.player->interacting_with) == it});
}
else else
{ {
Armature *to_use = 0; Armature *to_use = 0;
@ -5942,6 +5845,7 @@ void frame(void)
break; break;
case NPC_nobody: case NPC_nobody:
case NPC_AngelTotem: case NPC_AngelTotem:
case NPC_Tombstone:
case NPC_Devil: case NPC_Devil:
case NPC_PreviousPlayer1: case NPC_PreviousPlayer1:
case NPC_PreviousPlayer2: case NPC_PreviousPlayer2:
@ -5949,11 +5853,6 @@ void frame(void)
break; break;
} }
if (it->killed) if (it->killed)
{
assert(false);
break;
}
if (it->killed)
{ {
to_use->go_to_animation = S8Lit("Die Backwards"); to_use->go_to_animation = S8Lit("Die Backwards");
to_use->next_animation_isnt_looping = true; to_use->next_animation_isnt_looping = true;
@ -6048,7 +5947,7 @@ void frame(void)
} }
} }
flush_all_drawn_things(light_dir, cam_pos, facing, right); flush_all_drawn_things(shadow);
// draw the 3d render // draw the 3d render
draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.threedee_pass_resolve_image), WHITE, .layer = LAYER_WORLD, .custom_pipeline = state.twodee_colorcorrect_pip }); draw_quad((DrawParams){quad_at(V2(0.0, screen_size().y), screen_size()), IMG(state.threedee_pass_resolve_image), WHITE, .layer = LAYER_WORLD, .custom_pipeline = state.twodee_colorcorrect_pip });
@ -7348,7 +7247,7 @@ ISANERROR("Don't know how to do this stuff on this platform.")
//if(view_cam_pos.y >= 4.900f) // causes nan if not true... not good... //if(view_cam_pos.y >= 4.900f) // causes nan if not true... not good...
if(true) if(true)
{ {
Vec3 world_mouse = screenspace_point_to_camera_point(mouse_pos); Vec3 world_mouse = screenspace_point_to_camera_point(mouse_pos, view);
Vec3 mouse_ray = NormV3(SubV3(world_mouse, view_cam_pos)); Vec3 mouse_ray = NormV3(SubV3(world_mouse, view_cam_pos));
Vec3 marker = ray_intersect_plane(view_cam_pos, mouse_ray, V3(0,0,0), V3(0,1,0)); Vec3 marker = ray_intersect_plane(view_cam_pos, mouse_ray, V3(0,0,0), V3(0,1,0));
Vec2 mouse_on_floor = point_plane(marker); Vec2 mouse_on_floor = point_plane(marker);

@ -343,6 +343,17 @@ void fill_available_actions(GameState *gs, Entity *it, AvailableActions *a)
*a = (AvailableActions) { 0 }; *a = (AvailableActions) { 0 };
BUFF_APPEND(a, ACT_none); BUFF_APPEND(a, ACT_none);
if(it->npc_kind == NPC_Angel)
{
BUFF_APPEND(a, ACT_assign_gameplay_objective);
return;
}
if(it->npc_kind == NPC_Tombstone)
{
return;
}
if(gete_specified(gs, it->joined)) if(gete_specified(gs, it->joined))
{ {
BUFF_APPEND(a, ACT_leave) BUFF_APPEND(a, ACT_leave)
@ -352,11 +363,6 @@ void fill_available_actions(GameState *gs, Entity *it, AvailableActions *a)
BUFF_APPEND(a, ACT_join) BUFF_APPEND(a, ACT_join)
} }
if(it->npc_kind == NPC_Angel)
{
BUFF_APPEND(a, ACT_assign_gameplay_objective);
}
if(it->npc_kind != NPC_Angel) if(it->npc_kind != NPC_Angel)
{ {
BUFF_APPEND(a, ACT_end_conversation); BUFF_APPEND(a, ACT_end_conversation);
@ -470,7 +476,8 @@ String8 generate_chatgpt_prompt(Arena *arena, GameState *gs, Entity *e, CanTalkT
AddFmt("\n"); AddFmt("\n");
// @TODO unhardcode this, this will be a description of where the character is right now // @TODO unhardcode this, this will be a description of where the character is right now
AddFmt("You're currently standing in Daniel's farm's barn, a run-down structure that barely serves its purpose. Daniel's mighty protective of it though.\n"); //AddFmt("You're currently standing in Daniel's farm's barn, a run-down structure that barely serves its purpose. Daniel's mighty protective of it though.\n");
AddFmt("You and everybody you're talking to is in a small sparse forest near Daniel's farm. There are some mysterious mechanical parts strewn about on the floor that Daniel seems relunctant and fearful to talk about.\n");
AddFmt("\n"); AddFmt("\n");

@ -26,6 +26,7 @@ Urgent:
Long distance: Long distance:
- nocodegen instead of codegen argument - nocodegen instead of codegen argument
- Blur game on bitmap modal input mode
- Polygon and circle collision with cutec2 probably for the player being unable to collide with the camera bounds, and non axis aligned collision rects - Polygon and circle collision with cutec2 probably for the player being unable to collide with the camera bounds, and non axis aligned collision rects
- set the game in oregon (suggestion by phillip) - set the game in oregon (suggestion by phillip)
- Room system where characters can go to rooms. camera constrained to room bounds, and know which rooms are near them to go to - Room system where characters can go to rooms. camera constrained to room bounds, and know which rooms are near them to go to

@ -9,7 +9,7 @@
#define PERCEPTION_HEARING_RAGE (TILE_SIZE*4.0f) #define PERCEPTION_HEARING_RAGE (TILE_SIZE*4.0f)
#define CHARACTERS_PER_SEC 45.0f #define CHARACTERS_PER_SEC 45.0f
#define ANGEL_CHARACTERS_PER_SEC 35.0f #define ANGEL_CHARACTERS_PER_SEC 35.0f
#define PROPAGATE_ACTIONS_RADIUS 10.0f #define PROPAGATE_ACTIONS_RADIUS 4.0f
#define SWORD_SWIPE_RADIUS (TILE_SIZE*3.0f) #define SWORD_SWIPE_RADIUS (TILE_SIZE*3.0f)
#define ARROW_SPEED 200.0f #define ARROW_SPEED 200.0f
#define SECONDS_PER_ARROW 1.3f #define SECONDS_PER_ARROW 1.3f
@ -65,9 +65,9 @@
//Rendering //Rendering
#define FIELD_OF_VIEW (PI32/4.0f) #define FIELD_OF_VIEW (0.6911112070083618) // FOV
#define NEAR_PLANE_DISTANCE (0.01f) #define NEAR_PLANE_DISTANCE (0.01f)
#define FAR_PLANE_DISTANCE (45.0f) #define FAR_PLANE_DISTANCE (70.0f)
#define SHADOW_MAP_DIMENSION (2048) #define SHADOW_MAP_DIMENSION (2048)
// Post-processing // Post-processing

Loading…
Cancel
Save