Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add geo command for cylindrical billboarding #856

Merged
merged 8 commits into from
Jan 25, 2025
38 changes: 28 additions & 10 deletions include/geo_commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,20 +335,38 @@ enum GeoLayoutCommands {
* 0x01: u8 params
* 0b1000_0000: if set, enable displayList field and drawingLayer
* 0b0000_1111: drawingLayer
* 0x02: s16 xTranslation
* 0x04: s16 yTranslation
* 0x06: s16 zTranslation
* 0x08: [u32 displayList: if MSbit of params is set, display list segmented address]
*/
* 0x02: u8 mode: if set, use cylindical billboarding
LucretiaArc marked this conversation as resolved.
Show resolved Hide resolved
* 0x03: unused
* 0x04: s16 xTranslation
* 0x06: s16 yTranslation
* 0x08: s16 zTranslation
* 0x0A: s16 xAxis
* 0x0C: s16 yAxis
* 0x0E: s16 zAxis
* 0x10: [u32 displayList: if MSbit of params is set, display list segmented address]
*/
#define GEO_BILLBOARD_WITH_MODE(layer, mode, tx, ty, tz, ax, ay, az) \
CMD_BBBB(GEO_CMD_NODE_BILLBOARD, layer, mode, 0x00), \
CMD_HH(tx, ty), \
CMD_HH(tz, ax), \
CMD_HH(ay, az)
#define GEO_BILLBOARD_WITH_MODE_AND_DL(layer, mode, tx, ty, tz, ax, ay, az, displayList) \
CMD_BBBB(GEO_CMD_NODE_BILLBOARD, (0x80 | layer), mode, 0x00), \
CMD_HH(tx, ty), \
CMD_HH(tz, ax), \
CMD_HH(ay, az), \
CMD_PTR(displayList)

#define GEO_BILLBOARD_WITH_PARAMS(layer, tx, ty, tz) \
CMD_BBH(GEO_CMD_NODE_BILLBOARD, layer, tx), \
CMD_HH(ty, tz)
GEO_BILLBOARD_WITH_MODE(layer, FALSE, tx, ty, tz, 0, 0, 0)
#define GEO_BILLBOARD_WITH_PARAMS_AND_DL(layer, tx, ty, tz, displayList) \
CMD_BBH(GEO_CMD_NODE_BILLBOARD, (layer | 0x80), tx), \
CMD_HH(ty, tz), \
CMD_PTR(displayList)
GEO_BILLBOARD_WITH_MODE_AND_DL(layer, FALSE, tx, ty, tz, 0, 0, 0, displayList)
#define GEO_BILLBOARD() \
GEO_BILLBOARD_WITH_PARAMS(0, 0, 0, 0)
#define GEO_BILLBOARD_CYLINDRICAL(ax, ay, az) \
GEO_BILLBOARD_WITH_MODE(0, TRUE, 0, 0, 0, ax, ay, az)
#define GEO_BILLBOARD_CYLINDRICAL_WITH_DL(layer, ax, ay, az, displayList) \
GEO_BILLBOARD_WITH_MODE_AND_DL(layer, TRUE, 0, 0, 0, ax, ay, az, displayList)

/**
* 0x15: Create plain display list scene graph node
Expand Down
18 changes: 12 additions & 6 deletions src/engine/geo_layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -580,28 +580,34 @@ void geo_layout_cmd_node_animated_part(void) {
cmd+0x01: u8 params
(params & 0x80): if set, enable displayList field and drawingLayer
(params & 0x0F): drawingLayer
cmd+0x02: s16 xTranslation
cmd+0x04: s16 yTranslation
cmd+0x06: s16 zTranslation
[cmd+0x08: void *displayList]
cmd+0x02: u8 mode: if set, use cylindrical billboarding
cmd+0x04: s16 xTranslation
cmd+0x06: s16 yTranslation
cmd+0x08: s16 zTranslation
cmd+0x0A: s16 xAxis
cmd+0x0C: s16 yAxis
cmd+0x0E: s16 zAxis
[cmd+0x10: void *displayList]
*/
void geo_layout_cmd_node_billboard(void) {
struct GraphNodeBillboard *graphNode;
Vec3s translation;
Vec3s axis;
s16 drawingLayer = LAYER_FIRST;
s16 params = cur_geo_cmd_u8(0x01);
s16 *cmdPos = (s16 *) gGeoLayoutCommand;
void *displayList = NULL;

cmdPos = read_vec3s(translation, &cmdPos[1]);
cmdPos = read_vec3s(translation, &cmdPos[2]);
cmdPos = read_vec3s(axis, &cmdPos[0]);

if (params & 0x80) {
displayList = *(void **) &cmdPos[0];
drawingLayer = params & 0x0F;
cmdPos += 2 << CMD_SIZE_SHIFT;
}

graphNode = init_graph_node_billboard(gGraphNodePool, NULL, drawingLayer, displayList, translation);
graphNode = init_graph_node_billboard(gGraphNodePool, NULL, drawingLayer, displayList, translation, axis, cur_geo_cmd_u8(0x02));

register_scene_graph_node(&graphNode->node);

Expand Down
6 changes: 5 additions & 1 deletion src/engine/graph_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,16 +361,20 @@ struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct AllocOnlyPool
struct GraphNodeBillboard *init_graph_node_billboard(struct AllocOnlyPool *pool,
struct GraphNodeBillboard *graphNode,
s32 drawingLayer, void *displayList,
Vec3s translation) {
Vec3s translation,
Vec3s axis,
u8 mode) {
if (pool != NULL) {
graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBillboard));
}

if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_BILLBOARD);
vec3s_copy(graphNode->translation, translation);
vec3s_copy(graphNode->axis, axis);
SET_GRAPH_NODE_LAYER(graphNode->node.flags, drawingLayer);
graphNode->displayList = displayList;
graphNode->mode = mode;
}

return graphNode;
Expand Down
11 changes: 7 additions & 4 deletions src/engine/graph_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,17 @@ struct GraphNodeAnimatedPart {
};

/** A GraphNode that draws a display list rotated in a way to always face the
* camera. Note that if the entire object is a billboard (like a coin or 1-up)
* then it simply sets the billboard flag for the entire object, this node is
* used for billboard parts (like a chuckya or goomba body).
* camera. If mode is set, then cylindrical (axis-aligned) billboarding will
* be used instead. Note that if the entire object is a billboard (like a coin
* or 1-up) then it simply sets the billboard flag for the entire object, this
* node is used for billboard parts (like a chuckya or goomba body).
*/
struct GraphNodeBillboard {
/*0x00*/ struct GraphNode node;
/*0x14*/ void *displayList;
/*0x18*/ Vec3s translation;
/*0x1E*/ Vec3s axis;
/*0x24*/ u8 mode;
};

/** A GraphNode that simply draws a display list without doing any
Expand Down Expand Up @@ -376,7 +379,7 @@ struct GraphNodeScale *init_graph_node_scale (struct
struct GraphNodeObject *init_graph_node_object (struct AllocOnlyPool *pool, struct GraphNodeObject *graphNode, struct GraphNode *sharedChild, Vec3f pos, Vec3s angle, Vec3f scale);
struct GraphNodeCullingRadius *init_graph_node_culling_radius (struct AllocOnlyPool *pool, struct GraphNodeCullingRadius *graphNode, s16 radius);
struct GraphNodeAnimatedPart *init_graph_node_animated_part (struct AllocOnlyPool *pool, struct GraphNodeAnimatedPart *graphNode, s32 drawingLayer, void *displayList, Vec3s translation);
struct GraphNodeBillboard *init_graph_node_billboard (struct AllocOnlyPool *pool, struct GraphNodeBillboard *graphNode, s32 drawingLayer, void *displayList, Vec3s translation);
struct GraphNodeBillboard *init_graph_node_billboard (struct AllocOnlyPool *pool, struct GraphNodeBillboard *graphNode, s32 drawingLayer, void *displayList, Vec3s translation, Vec3s axis, u8 mode);
struct GraphNodeDisplayList *init_graph_node_display_list (struct AllocOnlyPool *pool, struct GraphNodeDisplayList *graphNode, s32 drawingLayer, void *displayList);
struct GraphNodeShadow *init_graph_node_shadow (struct AllocOnlyPool *pool, struct GraphNodeShadow *graphNode, s16 shadowScale, u8 shadowSolidity, u8 shadowType);
struct GraphNodeObjectParent *init_graph_node_object_parent (struct AllocOnlyPool *pool, struct GraphNodeObjectParent *graphNode, struct GraphNode *sharedChild);
Expand Down
57 changes: 57 additions & 0 deletions src/engine/math_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,63 @@ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, Vec3f scale, s16 angle)
dest[3][3] = 1.0f;
}

/**
* Set 'dest' to a transformation matrix that turns an object to face the camera using cylindrical billboarding.
* 'mtx' is the look-at matrix from the camera.
* 'camera' is the camera's look direction.
* 'axis' is the fixed axis around which the object will be rotated.
* 'position' is the position of the object in the world.
* 'scale' is the scale of the object.
* 'angle' rotates the object while still facing the camera.
*/
void mtxf_billboard_cylindrical(Mat4 dest, Mat4 mtx, Vec3f camera, Vec3f axis, Vec3f position, Vec3f scale, s16 angle) {
LucretiaArc marked this conversation as resolved.
Show resolved Hide resolved
PUPPYPRINT_ADD_COUNTER(gPuppyCallCounter.matrix);
register s32 i;
register f32 sx = scale[0];
register f32 sy = scale[1];
register f32 sz = scale[2];
LucretiaArc marked this conversation as resolved.
Show resolved Hide resolved

Vec3f rotAxis;
vec3f_copy(rotAxis, axis);
vec3f_normalize(rotAxis);
Vec3f rightAxis;
vec3f_cross(rightAxis, camera, rotAxis);
vec3f_normalize(rightAxis);
Vec3f forwardAxis;
vec3f_cross(forwardAxis, rightAxis, rotAxis);

vec3f_copy(dest[0], rightAxis);
vec3f_copy(dest[1], rotAxis);
vec3f_copy(dest[2], forwardAxis);

if (angle != 0x0) {
float m00 = dest[0][0];
float m01 = dest[0][1];
float m02 = dest[0][2];
float m10 = dest[1][0];
float m11 = dest[1][1];
float m12 = dest[1][2];
float cosa = coss(angle);
float sina = sins(angle);
dest[0][0] = cosa * m00 + sina * m10;
dest[0][1] = cosa * m01 + sina * m11;
dest[0][2] = cosa * m02 + sina * m12;
dest[1][0] = -sina * m00 + cosa * m10;
dest[1][1] = -sina * m01 + cosa * m11;
dest[1][2] = -sina * m02 + cosa * m12;
}
for (i = 0; i < 3; i++) {
dest[0][i] *= sx;
dest[1][i] *= sy;
dest[2][i] *= sz;
}

// Translation = input translation + position
vec3f_copy(dest[3], position);
vec3f_add(dest[3], mtx[3]);
dest[3][3] = 1.0f;
}

/**
* Mostly the same as 'mtxf_align_terrain_normal', but also applies a scale and multiplication.
* 'upDir' is the terrain normal
Expand Down
1 change: 1 addition & 0 deletions src/engine/math_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,7 @@ void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f trans, Vec3s rot);
void mtxf_rotate_zxy_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Mat4 src);
void mtxf_rotate_xyz_and_translate_and_mul(Vec3s rot, Vec3f trans, Mat4 dest, Mat4 src);
void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, Vec3f scale, s16 angle);
void mtxf_billboard_cylindrical(Mat4 dest, Mat4 mtx, Vec3f camera, Vec3f axis, Vec3f position, Vec3f scale, s16 angle);
void mtxf_shadow(Mat4 dest, Vec3f upDir, Vec3f pos, Vec3f scale, s16 yaw);
void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw);
void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius);
Expand Down
10 changes: 9 additions & 1 deletion src/game/rendering_graph_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,17 +680,25 @@ void geo_process_scale(struct GraphNodeScale *node) {
*/
void geo_process_billboard(struct GraphNodeBillboard *node) {
Vec3f translation;
Vec3f axis;
Vec3f camera;
Vec3f scale = { 1.0f, 1.0f, 1.0f };

vec3s_to_vec3f(translation, node->translation);
linear_mtxf_mul_vec3(gMatStack[gMatStackIndex], axis, node->axis);
vec3f_diff(camera, gCurGraphNodeCamera->focus, gCurGraphNodeCamera->pos);

if (gCurGraphNodeHeldObject != NULL) {
vec3f_copy(scale, gCurGraphNodeHeldObject->objNode->header.gfx.scale);
} else if (gCurGraphNodeObject != NULL) {
vec3f_copy(scale, gCurGraphNodeObject->scale);
}

mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], translation, scale, gCurGraphNodeCamera->roll);
if (node->mode) {
mtxf_billboard_cylindrical(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], camera, axis, translation, scale, gCurGraphNodeCamera->roll);
} else {
mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], translation, scale, gCurGraphNodeCamera->roll);
}

inc_mat_stack();
append_dl_and_return((struct GraphNodeDisplayList *)node);
Expand Down