Particles, post effects and the camera
Lesson 9.6
Your mod can paint on the world: sparks, scorch marks on the floor, a red haze over the screen, thick fog, a tilted camera. This lesson shows each tool once. The guides have every option: Particles and Decals, Post-processing, Fog, Sky and Camera, Menus and Themes.
Particles
Every option struct has an _INIT macro with the defaults, so you only set what you need:
#include <clight_easy.h>
static void on_register(void* payload, void* user) {
(void)payload, (void)user;
cl_particle_desc d = CL_PARTICLE_DESC_INIT;
d.texture = CL_STR("my_mod:textures/particle/spark.png");
d.frames = 4;
d.columns = 2;
d.size = 0.12f;
d.size_end = 0.02f;
d.gravity = 0.02f;
d.color = 0xFFFFD080u; /* ARGB */
d.emissive = true;
d.additive = true;
d.fallback = CL_STR("minecraft:flame");
cl_render_particle_type(CL_STR("spark"), &d);
}
static cl_result mod_main(void) { return CL_ON(CL_EV_LIFECYCLE_REGISTER, on_register); }
CLIGHT_MOD_ENTRY(mod_main)
Spawning takes a level, a type and a cl_particle_spawn:
cl_particle_spawn s = CL_PARTICLE_SPAWN_INIT;
s.pos = pos;
s.vel = (cl_vec3){0, 0.1, 0};
s.count = 20;
s.spread = (cl_vec3){0.3, 0.3, 0.3};
cl_level_particle(level, CL_STR("my_mod:spark"), &s);
On the server everyone nearby sees them (players without your mod get the fallback); on the client only that player.
Decals
cl_decal_desc d = CL_DECAL_DESC_INIT;
d.texture = CL_STR("my_mod:textures/decal/scorch.png");
d.pos = hit_pos;
d.normal = hit_normal;
d.size = 2.0f;
cl_level_decal(level, &d);
A post effect
The shader is a GLSL file in your assets with one function:
vec4 cl_post(vec2 uv) {
vec4 c = cl_scene(uv);
return vec4(mix(c.rgb, vec3(c.r, 0.0, 0.0), amount), 1.0);
}
Register it on the client, then set its uniform whenever you like, for example every frame in CL_EV_RENDER_FRAME:
static cl_post_pass red;
static float amount = 0.0f;
static void on_frame(void* payload, void* user) {
(void)payload, (void)user;
cl_post_pass_set_uniform(red, CL_STR("amount"), amount);
}
static void on_client_setup(void* payload, void* user) {
(void)payload, (void)user;
cl_post_pass_desc d = CL_POST_PASS_DESC_INIT;
d.id = CL_STR("red");
d.shader = CL_STR("my_mod:shaders/post/red.glsl");
d.uniforms = CL_STR("amount");
if (cl_render_post_pass(&d, &red) == CL_OK) CL_ON(CL_EV_RENDER_FRAME, on_frame);
}
A shader that does not compile returns CL_E_PARSE; cl_last_error_message has the compiler’s message.
Fog and the camera
static void on_fog(void* payload, void* user) {
cl_ev_render_fog* e = CL_EVENT(cl_ev_render_fog, payload);
(void)user;
e->render_distance_end = 24.0f;
e->red = 0.2f, e->green = 0.2f, e->blue = 0.25f;
}
/* CL_ON(CL_EV_RENDER_FOG, on_fog); */
cl_camera_effects fx = CL_CAMERA_EFFECTS_INIT;
fx.fov_mult = 0.9f;
fx.roll = 5.0f;
fx.vignette = 0.5f;
cl_camera_set_effects(&fx);
The camera effects follow the player’s accessibility settings; shake needs the player’s Mod Camera Shake option.
Try it
Make water murkier: under water (fog_type == 1), halve environmental_end and tint the fog green.
Show a solution
static void on_fog(void* payload, void* user) {
cl_ev_render_fog* e = CL_EVENT(cl_ev_render_fog, payload);
(void)user;
if (e->fog_type != 1) return;
e->environmental_end *= 0.5f;
e->red = 0.1f, e->green = 0.35f, e->blue = 0.15f;
}
Recap: cl_render_particle_type and cl_level_particle for particles, cl_level_decal for marks, cl_render_post_pass plus cl_post_pass_set_uniform for screen effects, CL_EV_RENDER_FOG for fog, cl_camera_set_effects for the camera.