techkaczynski.dev / code

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor.c207
-rw-r--r--ent.c184
-rw-r--r--env.c1
-rw-r--r--physics.h1
-rw-r--r--res.c27
-rw-r--r--res/mdl/psx-characters/male/rigged/male1.glbbin0 -> 409632 bytes
-rw-r--r--res/mdl/psx-characters/male/rigged/male1.raylib.glbbin0 -> 409632 bytes
-rw-r--r--res/mdl/psx-trees/dead_tree_rt_1.glbbin0 -> 302660 bytes
-rw-r--r--res/mdl/psx-trees/dead_tree_rt_2.glbbin0 -> 80152 bytes
-rw-r--r--res/mdl/psx-trees/small_tree_rt_1.glbbin0 -> 16040 bytes
-rw-r--r--res/mdl/psx-trees/tree_rt_1.glbbin0 -> 58664 bytes
-rw-r--r--res/mdl/psx-trees/tree_rt_2.glbbin0 -> 159940 bytes
-rw-r--r--res/mdl/psx-trees/tree_rt_2_1.glbbin0 -> 158828 bytes
-rw-r--r--res/mdl/psx-trees/tree_rt_3.glbbin0 -> 178696 bytes
-rw-r--r--res/mdl/psx-trees/tree_rt_4.glbbin0 -> 415296 bytes
-rw-r--r--res/save/scene.fgsbin14764 -> 172800 bytes
-rw-r--r--res/sdr/mdl_foliage.vs23
-rw-r--r--res/sdr/mdl_skinned_vtx_lit.vs59
18 files changed, 463 insertions, 39 deletions
diff --git a/editor.c b/editor.c
index 2591cc1..18cd918 100644
--- a/editor.c
+++ b/editor.c
@@ -3184,24 +3184,19 @@ panel_properties(EditorPanelContainer container, bool input) {
main_cam.input = false;
main_cam.update = false;
- u64 id = entity_id_from_pid(ent->pid);
-
- f32 view_height = 10.0f *
- tanf(main_cam.perspective_fov *
- DEG2RAD * 0.5f);
-
- cam_focus(entity_world_pos(id), 5.0f, view_height);
-
ed.physics_pid = ent->pid;
ed.physics_body = -1;
ed.physics_shape = -1;
ed.physics_scroll = (Vec2){0};
ed.prev_mode = ed.mode;
+ ed.panel[ED_PNL_LEFT].enabled = true;
+ ed.panel[ED_PNL_RIGHT].enabled = true;
ed.mode = ED_MODE_PHYSICS;
ed.panel[ED_PNL_LEFT].type =
ED_PNL_TYPE_PHYSICS_ELEMENTS;
ed.panel[ED_PNL_RIGHT].type =
ED_PNL_TYPE_PHYSICS_PROPERTIES;
+ editor_focus_selection();
edit.focus = 0;
add_open = false;
@@ -4251,6 +4246,66 @@ panel_physics_shape(PanelEdit *edit, Rect row,
}
static void
+physics_shape_model_geometry(
+ PhysicsShape *shape,
+ const Model *model,
+ u32 point_cnt,
+ u32 idx_cnt) {
+ bool trimesh = shape->tag == PHYS_SHAPE_STATIC_TRIMESH;
+
+ Vec3 *points = malloc(point_cnt * sizeof(*points));
+ u32 *indices = NULL;
+
+ if (trimesh) {
+ indices = malloc(idx_cnt * sizeof(*indices));
+ }
+
+ if (!points || (trimesh && !indices)) {
+ perror("model collision allocation failed");
+ exit(EXIT_FAILURE);
+ }
+
+ u32 point_offset = 0;
+ u32 index_offset = 0;
+
+ for (int i = 0; i < model->meshCount; ++i) {
+ const Mesh *mesh = &model->meshes[i];
+
+ for (int v = 0; v < mesh->vertexCount; ++v) {
+ points[point_offset + v] = (Vec3){
+ mesh->vertices[v * 3],
+ mesh->vertices[v * 3 + 1],
+ mesh->vertices[v * 3 + 2],
+ };
+ }
+
+ if (trimesh) {
+ u32 count = (u32)mesh->triangleCount * 3;
+
+ for (u32 k = 0; k < count; ++k) {
+ u32 index = mesh->indices
+ ? (u32)mesh->indices[k]
+ : k;
+
+ indices[index_offset++] = point_offset + index;
+ }
+ }
+
+ point_offset += (u32)mesh->vertexCount;
+ }
+
+ if (trimesh) {
+ shape->TriMesh.points = points;
+ shape->TriMesh.indices = indices;
+ shape->TriMesh.point_cnt = point_cnt;
+ shape->TriMesh.idx_cnt = idx_cnt;
+ } else {
+ shape->ConvexHull.points = points;
+ shape->ConvexHull.point_cnt = point_cnt;
+ }
+}
+
+static void
panel_physics_properties(EditorPanelContainer container, bool input) {
Rect bounds = ed.panel[container].bounds;
@@ -4518,10 +4573,20 @@ panel_physics_properties(EditorPanelContainer container, bool input) {
}
bool fit_model = GuiButton(info, "Add Model Box");
+
+ info.y += 30;
+ bool add_hull = GuiButton(info, "Add Model Hull");
+
+ info.y += 30;
+ if (body->mode != PHYS_MODE_STATIC) {
+ GuiSetState(STATE_DISABLED);
+ }
+
+ bool add_trimesh = GuiButton(info, "Add Model Trimesh");
GuiSetState(button_state);
Model *model = NULL;
- if (fit_model) {
+ if (fit_model || add_hull || add_trimesh) {
Asset *asset = asset_get(ent->mdl.key);
if (asset && asset->tag == ASSET_TAG_MODEL &&
asset->mdl.meshCount > 0) {
@@ -4529,6 +4594,35 @@ panel_physics_properties(EditorPanelContainer container, bool input) {
}
}
+ u32 point_cnt = 0;
+ u32 idx_cnt = 0;
+
+ if (model && (add_hull || add_trimesh)) {
+ for (int i = 0; i < model->meshCount; ++i) {
+ Mesh *mesh = &model->meshes[i];
+
+ if (!mesh->vertices ||
+ mesh->vertexCount <= 0) {
+ model = NULL;
+ break;
+ }
+
+ point_cnt += (u32)mesh->vertexCount;
+ idx_cnt += (u32)mesh->triangleCount * 3;
+ }
+
+ if (point_cnt < (add_trimesh ? 3u : 4u) ||
+ (add_trimesh && idx_cnt < 3)) {
+ model = NULL;
+ }
+
+ if (!model) {
+ TraceLog(LOG_WARNING,
+ "model has no usable "
+ "collision geometry");
+ }
+ }
+
if (add_box || add_sphere || add_capsule || add_cylinder ||
model) {
edit.focus = 0;
@@ -4584,7 +4678,7 @@ panel_physics_properties(EditorPanelContainer container, bool input) {
shape->Cylinder.height = 1.0f;
}
- if (model) {
+ if (model && fit_model) {
Model local_model = *model;
local_model.transform = MatrixIdentity();
@@ -4608,6 +4702,22 @@ panel_physics_properties(EditorPanelContainer container, bool input) {
shape->rot);
}
+ if (model && (add_hull || add_trimesh)) {
+ PhysicsShape *shape =
+ &body->shapes[body->shape_cnt];
+
+ shape->tag = add_trimesh
+ ? PHYS_SHAPE_STATIC_TRIMESH
+ : PHYS_SHAPE_CONVEX_HULL;
+
+ shape->rot = QuaternionInvert(body->rot);
+ shape->pos = Vector3RotateByQuaternion(
+ Vector3Negate(body->pos), shape->rot);
+
+ physics_shape_model_geometry(
+ shape, model, point_cnt, idx_cnt);
+ }
+
body->shape_cnt++;
action_commit(&action);
@@ -5435,6 +5545,36 @@ gizmo_scale(void) {
}
static void
+physics_draw_hull(b3ShapeId shape_id, Color colour) {
+ if (!b3Shape_IsValid(shape_id)) {
+ return;
+ }
+
+ const b3HullData *hull = b3Shape_GetHull(shape_id);
+ const b3Vec3 *points = b3GetHullPoints(hull);
+ const b3HullHalfEdge *edges = b3GetHullEdges(hull);
+ b3BodyId body_id = b3Shape_GetBody(shape_id);
+
+ for (int i = 0; i < hull->edgeCount; ++i) {
+ const b3HullHalfEdge *edge = &edges[i];
+
+ /* Each edge has two halves; draw it once. */
+ if (i > edge->twin) {
+ continue;
+ }
+
+ b3Pos a = b3Body_GetWorldPoint(
+ body_id, points[edge->origin]);
+ b3Pos b = b3Body_GetWorldPoint(
+ body_id, points[edges[edge->twin].origin]);
+
+ DrawLine3D(
+ (Vec3){a.x, a.y, a.z},
+ (Vec3){b.x, b.y, b.z}, colour);
+ }
+}
+
+static void
physics_draw_shapes(u64 id) {
Entity *ent = entity_get(id);
if (!ent) {
@@ -5496,6 +5636,53 @@ physics_draw_shapes(u64 id) {
QuaternionToMatrix(rotation),
MatrixTranslate(center.x, center.y, center.z));
+ if (shape->tag == PHYS_SHAPE_CONVEX_HULL) {
+ PhysicsBodyRuntime *bodies =
+ ent->phys.runtime.bodies;
+
+ if (bodies && bodies[i].shapes) {
+ physics_draw_hull(bodies[i].shapes[j],
+ colour);
+ }
+ continue;
+ }
+
+ if (shape->tag == PHYS_SHAPE_STATIC_TRIMESH) {
+ const Vec3 *points = shape->TriMesh.points;
+ const u32 *indices = shape->TriMesh.indices;
+ u32 point_cnt = shape->TriMesh.point_cnt;
+
+ if (!points || !indices) {
+ continue;
+ }
+
+ for (u32 k = 0;
+ k + 2 < shape->TriMesh.idx_cnt;
+ k += 3) {
+ u32 ia = indices[k];
+ u32 ib = indices[k + 1];
+ u32 ic = indices[k + 2];
+
+ if (ia >= point_cnt ||
+ ib >= point_cnt ||
+ ic >= point_cnt) {
+ continue;
+ }
+
+ Vec3 a = Vector3Transform(points[ia],
+ world);
+ Vec3 b = Vector3Transform(points[ib],
+ world);
+ Vec3 c = Vector3Transform(points[ic],
+ world);
+
+ DrawLine3D(a, b, colour);
+ DrawLine3D(b, c, colour);
+ DrawLine3D(c, a, colour);
+ }
+ continue;
+ }
+
rlPushMatrix();
rlMultMatrixf(MatrixToFloat(transform));
diff --git a/ent.c b/ent.c
index 2f61aa7..d8a3a16 100644
--- a/ent.c
+++ b/ent.c
@@ -275,7 +275,9 @@ entity_draw_mdl(u64 id) {
Vec2 uv_scale = {1, 1};
Vec2 uv_offset = {0, 0};
- cstr shader_key = "default";
+ cstr shader_key = mdl->meshes[i].boneCount > 0
+ ? "skinned"
+ : "default";
if (mat_idx < model->mat_cnt) {
const MaterialOverride *override;
@@ -292,7 +294,12 @@ entity_draw_mdl(u64 id) {
}
if (override->shader_key) {
- shader_key = override->shader_key;
+ if (strcmp(override->shader_key,
+ "default") != 0 &&
+ strcmp(shader_key,
+ "skinned") != 0) {
+ shader_key = override->shader_key;
+ }
}
}
@@ -326,18 +333,18 @@ entity_draw_mdl(u64 id) {
(u8)((int)colour.a * tint.a / 255),
};
- /* Preserve DrawModel's GPU skinning support. */
- // int loc = SHADER_LOC_MATRIX_BONETRANSFORMS;
- // if (material.shader.locs != NULL &&
- // material.shader.locs[loc] != -1 &&
- // mdl->boneMatrices != NULL) {
- // rlEnableShader(material.shader.id);
- // rlSetUniformMatrices(
- // material.shader.locs[loc],
- // mdl->boneMatrices,
- // mdl->skeleton.boneCount
- // );
- // }
+ if (strcmp(shader_key, "skinned") == 0 &&
+ mdl->boneMatrices != NULL) {
+ int loc = fun_shader_uniform(shader_key,
+ "boneMatrices");
+
+ rlEnableShader(material.shader.id);
+ rlSetUniformMatrices(
+ loc,
+ mdl->boneMatrices,
+ mdl->skeleton.boneCount
+ );
+ }
bool nocull =
strcmp(shader_key, "alpha_cutoff_nocull") == 0 ||
@@ -607,6 +614,18 @@ entity_physics_clear(PhysicsCmp *phys) {
if (b3Body_IsValid(bdy->id)) {
b3DestroyBody(bdy->id);
}
+
+ if (bdy->meshes) {
+ for (u32 j = 0;
+ j < phys->bodies[i].shape_cnt;
+ ++j) {
+ if (bdy->meshes[j]) {
+ b3DestroyMesh(bdy->meshes[j]);
+ }
+ }
+ }
+
+ free(bdy->meshes);
free(bdy->shapes);
}
}
@@ -629,10 +648,12 @@ physics_build_shapes(u64 id, u32 body_index) {
runtime->shapes = calloc(
body->shape_cnt, sizeof(*runtime->shapes));
+ runtime->meshes = calloc(
+ body->shape_cnt, sizeof(*runtime->meshes));
- if (!runtime->shapes) {
- perror("physics shape handles allocation failed");
- exit(EXIT_FAILURE);
+ if (!runtime->shapes || !runtime->meshes) {
+ perror("physics shape allocation failed");
+ exit(1);
}
Matrix entity_world = entity_world_matrix(id);
@@ -826,6 +847,135 @@ physics_build_shapes(u64 id, u32 body_index) {
break;
}
+ case PHYS_SHAPE_CONVEX_HULL:
+ case PHYS_SHAPE_STATIC_TRIMESH: {
+ bool trimesh = shape->tag == PHYS_SHAPE_STATIC_TRIMESH;
+ if (trimesh && body->mode != PHYS_MODE_STATIC) {
+ TraceLog(LOG_WARNING,
+ "trimesh requires a static body");
+ break;
+ }
+
+ Vec3 *source = trimesh
+ ? shape->TriMesh.points
+ : shape->ConvexHull.points;
+
+ u32 point_cnt = trimesh
+ ? shape->TriMesh.point_cnt
+ : shape->ConvexHull.point_cnt;
+
+ if (!source || point_cnt < (trimesh ? 3u : 4u) ||
+ point_cnt > INT32_MAX) {
+ break;
+ }
+
+ b3Vec3 *points = malloc(point_cnt * sizeof(*points));
+ if (!points) {
+ perror("physics points allocation failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Bake the full transform into body-local geometry. */
+ for (u32 k = 0; k < point_cnt; ++k) {
+ Vec3 point =
+ Vector3Transform(source[k], world);
+ point = Vector3RotateByQuaternion(
+ Vector3Subtract(point, pos),
+ inverse_rot);
+
+ points[k] = (b3Vec3){
+ point.x, point.y, point.z,
+ };
+ }
+
+ if (!trimesh) {
+ b3HullData *hull =
+ b3CreateHull(
+ points,
+ (int)point_cnt,
+ (int)point_cnt);
+
+ if (hull) {
+ runtime->shapes[j] =
+ b3CreateTransformedHullShape(
+ runtime->id,
+ &def,
+ hull,
+ b3Transform_identity,
+ (b3Vec3){1, 1, 1});
+
+ b3DestroyHull(hull);
+ } else {
+ TraceLog(LOG_WARNING,
+ "convex hull creation failed");
+ }
+
+ free(points);
+ break;
+ }
+
+ u32 idx_cnt = shape->TriMesh.idx_cnt;
+ if (!shape->TriMesh.indices ||
+ idx_cnt < 3 || idx_cnt % 3 != 0 ||
+ idx_cnt > INT32_MAX) {
+ free(points);
+ break;
+ }
+
+ int32_t *indices = malloc(idx_cnt * sizeof(*indices));
+ if (!indices) {
+ perror("physics indices allocation failed");
+ exit(EXIT_FAILURE);
+ }
+
+ u32 k = 0;
+ for (; k < idx_cnt; ++k) {
+ u32 index = shape->TriMesh.indices[k];
+ if (index >= point_cnt) {
+ break;
+ }
+ indices[k] = (int32_t)index;
+ }
+
+ if (k != idx_cnt) {
+ TraceLog(LOG_WARNING,
+ "triangle mesh index out of range");
+ free(indices);
+ free(points);
+ break;
+ }
+
+ b3MeshDef mesh_def = {0};
+ mesh_def.vertices = points;
+ mesh_def.vertexCount = (int)point_cnt;
+ mesh_def.indices = indices;
+ mesh_def.triangleCount = (int)(idx_cnt / 3);
+ mesh_def.identifyEdges = true;
+
+ /* Preserve outward winding when the transform mirrors geometry. */
+ mesh_def.clockWiseWinding = Vector3DotProduct(
+ Vector3CrossProduct(
+ (Vec3){world.m0, world.m1, world.m2},
+ (Vec3){world.m4, world.m5, world.m6}),
+ (Vec3){world.m8, world.m9, world.m10}) < 0;
+
+ b3MeshData *mesh = b3CreateMesh(&mesh_def, NULL, 0);
+
+ free(indices);
+ free(points);
+
+ if (!mesh) {
+ TraceLog(LOG_WARNING,
+ "triangle mesh creation failed");
+ break;
+ }
+
+ runtime->meshes[j] = mesh;
+ runtime->shapes[j] = b3CreateMeshShape(
+ runtime->id, &def, mesh, (b3Vec3){1, 1, 1});
+ break;
+ }
+
default:
break;
}
diff --git a/env.c b/env.c
index 31c7971..efcdfc2 100644
--- a/env.c
+++ b/env.c
@@ -267,6 +267,7 @@ void
env_update(void) {
sky_update_lighting();
env_update_shader("default");
+ env_update_shader("skinned");
env_update_shader("alpha_cutoff");
env_update_shader("alpha_cutoff_nocull");
env_update_shader("foliage");
diff --git a/physics.h b/physics.h
index 5a9a8d0..3fde638 100644
--- a/physics.h
+++ b/physics.h
@@ -141,6 +141,7 @@ typedef struct {
u64 entity_id;
b3BodyId id;
b3ShapeId *shapes; // count is stored in PhysicsCmp
+ b3MeshData **meshes; // same count, null for non mesh types
} PhysicsBodyRuntime;
typedef struct {
diff --git a/res.c b/res.c
index f4c8597..db74376 100644
--- a/res.c
+++ b/res.c
@@ -37,8 +37,29 @@ asset_load_data(cstr key) {
return false;
}
+ bool skinned = mdl.skeleton.boneCount > 0 &&
+ mdl.skeleton.bindPose != NULL;
+
+ cstr sdr_key = skinned ? "skinned" : "default";
+
for (int i = 0; i < mdl.materialCount; ++i) {
- mdl.materials[i].shader = shader_get("default")->sdr;
+ mdl.materials[i].shader = shader_get(sdr_key)->sdr;
+ }
+
+ if (skinned) {
+ int anim_count = 0;
+ ModelAnimation *anims =
+ LoadModelAnimations(key, &anim_count);
+
+ if (anim_count > 0) {
+ UpdateModelAnimation(mdl, anims[0], 0.0f);
+ }
+
+ UnloadModelAnimations(anims, anim_count);
+ }
+
+ for (int i = 0; i < mdl.materialCount; ++i) {
+ mdl.materials[i].shader = shader_get(sdr_key)->sdr;
}
asset->tag = ASSET_TAG_MODEL;
@@ -148,6 +169,10 @@ res_init(void) {
"res/sdr/mdl_vtx_lit.vs",
"res/sdr/mdl_vtx_lit.fs");
+ fun_shader_create("skinned",
+ "res/sdr/mdl_skinned_vtx_lit.vs",
+ "res/sdr/mdl_vtx_lit.fs");
+
fun_shader_create("alpha_cutoff",
"res/sdr/mdl_vtx_lit.vs",
"res/sdr/mdl_vtx_lit_cutoff.fs");
diff --git a/res/mdl/psx-characters/male/rigged/male1.glb b/res/mdl/psx-characters/male/rigged/male1.glb
new file mode 100644
index 0000000..69272c1
--- /dev/null
+++ b/res/mdl/psx-characters/male/rigged/male1.glb
Binary files differ
diff --git a/res/mdl/psx-characters/male/rigged/male1.raylib.glb b/res/mdl/psx-characters/male/rigged/male1.raylib.glb
new file mode 100644
index 0000000..283a3d2
--- /dev/null
+++ b/res/mdl/psx-characters/male/rigged/male1.raylib.glb
Binary files differ
diff --git a/res/mdl/psx-trees/dead_tree_rt_1.glb b/res/mdl/psx-trees/dead_tree_rt_1.glb
new file mode 100644
index 0000000..fec0486
--- /dev/null
+++ b/res/mdl/psx-trees/dead_tree_rt_1.glb
Binary files differ
diff --git a/res/mdl/psx-trees/dead_tree_rt_2.glb b/res/mdl/psx-trees/dead_tree_rt_2.glb
new file mode 100644
index 0000000..0523544
--- /dev/null
+++ b/res/mdl/psx-trees/dead_tree_rt_2.glb
Binary files differ
diff --git a/res/mdl/psx-trees/small_tree_rt_1.glb b/res/mdl/psx-trees/small_tree_rt_1.glb
new file mode 100644
index 0000000..632de9d
--- /dev/null
+++ b/res/mdl/psx-trees/small_tree_rt_1.glb
Binary files differ
diff --git a/res/mdl/psx-trees/tree_rt_1.glb b/res/mdl/psx-trees/tree_rt_1.glb
new file mode 100644
index 0000000..0c7fa93
--- /dev/null
+++ b/res/mdl/psx-trees/tree_rt_1.glb
Binary files differ
diff --git a/res/mdl/psx-trees/tree_rt_2.glb b/res/mdl/psx-trees/tree_rt_2.glb
new file mode 100644
index 0000000..881f7d8
--- /dev/null
+++ b/res/mdl/psx-trees/tree_rt_2.glb
Binary files differ
diff --git a/res/mdl/psx-trees/tree_rt_2_1.glb b/res/mdl/psx-trees/tree_rt_2_1.glb
new file mode 100644
index 0000000..8c9669c
--- /dev/null
+++ b/res/mdl/psx-trees/tree_rt_2_1.glb
Binary files differ
diff --git a/res/mdl/psx-trees/tree_rt_3.glb b/res/mdl/psx-trees/tree_rt_3.glb
new file mode 100644
index 0000000..2ec3314
--- /dev/null
+++ b/res/mdl/psx-trees/tree_rt_3.glb
Binary files differ
diff --git a/res/mdl/psx-trees/tree_rt_4.glb b/res/mdl/psx-trees/tree_rt_4.glb
new file mode 100644
index 0000000..19963e1
--- /dev/null
+++ b/res/mdl/psx-trees/tree_rt_4.glb
Binary files differ
diff --git a/res/save/scene.fgs b/res/save/scene.fgs
index ae27721..b07fae4 100644
--- a/res/save/scene.fgs
+++ b/res/save/scene.fgs
Binary files differ
diff --git a/res/sdr/mdl_foliage.vs b/res/sdr/mdl_foliage.vs
index 1198009..366290a 100644
--- a/res/sdr/mdl_foliage.vs
+++ b/res/sdr/mdl_foliage.vs
@@ -27,20 +27,21 @@ out vec4 frag_colour;
out vec3 frag_lighting;
void main() {
- vec3 world_normal = normalize((matNormal * vec4(vertexNormal, 0.0)).xyz);
+ vec3 canopy_center = vec3(0.0, 4.0, 0.0);
+ vec3 canopy_normal = vertexNormal;
+ vec3 offset = vertexPosition - canopy_center;
- // float sun_diffuse = abs(dot(world_normal, sun_direction));
- // float moon_diffuse = abs(dot(world_normal, moon_direction));
+ if (dot(offset, offset) > 0.0001) {
+ canopy_normal = normalize(offset);
+ }
- // for stronger shading, use 0.3 + 0.7 * abs(...)
- // float sun_diffuse = 0.5 + 0.5 * abs(dot(world_normal, sun_direction));
- // float moon_diffuse = 0.5 + 0.5 * abs(dot(world_normal, moon_direction));
- float sun_diffuse = abs(dot(world_normal, sun_direction));
- float moon_diffuse = abs(dot(world_normal, moon_direction));
+ vec3 world_normal = normalize((matNormal * vec4(canopy_normal, 0.0)).xyz);
- // hack to fix lighting at midday
- float midday = smoothstep(0.7, 1.0, sun_direction.y);
- sun_diffuse = mix(sun_diffuse, 0.65, midday);
+ float sun_facing = dot(world_normal, sun_direction);
+ float moon_facing = dot(world_normal, moon_direction);
+
+ float sun_diffuse = max(sun_facing, 0.0) + 0.1 * max(-sun_facing, 0.0);
+ float moon_diffuse = max(moon_facing, 0.0) + 0.1 * max(-moon_facing, 0.0);
vec3 ambient_light = ambient_colour * ambient_intensity;
vec3 sun_light = sun_colour * sun_intensity * sun_diffuse;
diff --git a/res/sdr/mdl_skinned_vtx_lit.vs b/res/sdr/mdl_skinned_vtx_lit.vs
new file mode 100644
index 0000000..a6da15e
--- /dev/null
+++ b/res/sdr/mdl_skinned_vtx_lit.vs
@@ -0,0 +1,59 @@
+#version 330
+
+in vec3 vertexPosition;
+in vec2 vertexTexCoord;
+in vec3 vertexNormal;
+in vec4 vertexColor;
+
+uniform mat4 mvp;
+uniform mat4 matNormal;
+
+uniform vec3 sun_direction;
+uniform vec3 sun_colour;
+uniform float sun_intensity;
+
+uniform vec3 moon_direction;
+uniform vec3 moon_colour;
+uniform float moon_intensity;
+
+uniform vec3 ambient_colour;
+uniform float ambient_intensity;
+
+uniform vec2 uv_tiling;
+uniform vec2 uv_offset;
+
+out vec2 frag_texcoord;
+out vec4 frag_colour;
+out vec3 frag_lighting;
+out vec3 frag_normal;
+
+in vec4 vertexBoneIndices;
+in vec4 vertexBoneWeights;
+
+uniform mat4 boneMatrices[64];
+
+void main() {
+ mat4 skin =
+ vertexBoneWeights.x * boneMatrices[int(vertexBoneIndices.x)] +
+ vertexBoneWeights.y * boneMatrices[int(vertexBoneIndices.y)] +
+ vertexBoneWeights.z * boneMatrices[int(vertexBoneIndices.z)] +
+ vertexBoneWeights.w * boneMatrices[int(vertexBoneIndices.w)];
+
+ vec4 position = skin * vec4(vertexPosition, 1.0);
+ vec3 normal = (skin * vec4(vertexNormal, 0.0)).xyz;
+ vec3 world_normal = normalize((matNormal * vec4(normal, 0.0)).xyz);
+
+ float sun_diffuse = max(dot(world_normal, sun_direction), 0.0);
+ float moon_diffuse = max(dot(world_normal, moon_direction), 0.0);
+
+ vec3 ambient_light = ambient_colour * ambient_intensity;
+ vec3 sun_light = sun_colour * sun_intensity * sun_diffuse;
+ vec3 moon_light = moon_colour * moon_intensity * moon_diffuse;
+
+ frag_lighting = ambient_light + sun_light + moon_light;
+ frag_texcoord = vertexTexCoord * uv_tiling + uv_offset;
+ frag_colour = vertexColor;
+ frag_normal = world_normal;
+
+ gl_Position = mvp * position;
+}