Mirai's Miscellaneous Misadventures
M36 / core / animations.c
#include <mimimi/models.h>
#include <mimimi/appearances.h>
#include <mimimi/allocators.h>
#include <mimimi/animations.h>
#include <mimimi/assets.h>
#include <mimimi/poses.h>
#include "math.c"
static unsigned char mimimi_layer_pixel(struct mimimi_layer *layer, unsigned char y_angle, int x, int y)
{
int h = layer->count;
if (y < 0) return 0;
if (y >= h) return 0;
int w0 = layer->rows[y].width;
int w1 = layer->rows[y].height;
int s = 128 - mimimi_cosine[y_angle * 2 % 256];
int w = s * w0 + (256 - s) * w1;
w /= 512;
if (x < -w) return 0;
if (x >= w) return 0;
int count = layer->rows[y].count;
int a = y_angle + 64;
a *= count;
a += 128;
a /= 256;
a += x;
a = mimimi_mod(a, count);
return layer->rows[y].colors[a];
}
static void mimimi_swap(int *x, int *y)
{
int z = *x;
*x = *y;
*y = z;
}
static void mimimi_layer(struct mimimi_image *image, struct mimimi_layer *layer, int x0, int y0, struct mimimi_pose_layer *pose_layer, int behind)
{
int width = image->width;
int height = image->height;
unsigned char y_angle = pose_layer->y_angle;
int z_angle = mimimi_mod(pose_layer->z_angle, 4);
if (behind != 0) y_angle += 128;
if (z_angle == 0)
{
for (int y = 0 ; y < height ; y++)
for (int x = 0 ; x < width ; x++)
{
int x1 = x + layer->x - x0;
int y1 = y + layer->y - y0;
x1 += pose_layer->slant * (y - y0) / 256;
if (behind != 0) x1 = -x1 - 1;
unsigned char color = mimimi_layer_pixel(layer, y_angle, x1, y1);
if (color != 0) image->colors[x + y * width] = color;
}
}
if (z_angle == 1)
{
for (int y = 0 ; y < height ; y++)
for (int x = 0 ; x < width ; x++)
{
int x1 = x - layer->y - x0;
int y1 = y + layer->x - y0;
y1 -= pose_layer->slant * (x - x0) / 256;
if (behind != 0) y1 = -y1 - 1;
unsigned char color = mimimi_layer_pixel(layer, y_angle, y1, -x1);
if (color != 0) image->colors[x + y * width] = color;
}
return;
}
if (z_angle == 2)
{
for (int y = 0 ; y < height ; y++)
for (int x = 0 ; x < width ; x++)
{
int x1 = x - layer->x - x0;
int y1 = y - layer->y - y0;
x1 += pose_layer->slant * (y - y0) / 256;
if (behind != 0) x1 = -x1 + 1;
unsigned char color = mimimi_layer_pixel(layer, y_angle, -x1, -y1);
if (color != 0) image->colors[x + y * width] = color;
}
return;
}
if (z_angle == 3)
{
for (int y = 0 ; y < height ; y++)
for (int x = 0 ; x < width ; x++)
{
int x1 = x + layer->y - x0;
int y1 = y - layer->x - y0;
y1 -= pose_layer->slant * (x - x0) / 256;
if (behind != 0) y1 = -y1 + 1;
unsigned char color = mimimi_layer_pixel(layer, y_angle, -y1, x1);
if (color != 0) image->colors[x + y * width] = color;
}
return;
}
}
static void mimimi_apply_y_angle(struct mimimi_model *model, struct mimimi_layer *layers, struct mimimi_pose_layer *pose_layers)
{
for (int i = 0 ; i < model->count ; i++)
{
struct mimimi_layer *layer = layers + i;
struct mimimi_pose_layer *pose_layer = pose_layers + i;
int parent_index = model->layers[i].parent_index;
if (i == parent_index) continue;
int parent_y_angle = pose_layers[parent_index].y_angle;
pose_layer->y_angle += parent_y_angle;
unsigned char y_angle = parent_y_angle;
int sin = mimimi_sine[y_angle];
int cos = mimimi_cosine[y_angle];
int x = layer->x;
int z = layer->z;
layer->x = cos*x / 128 + sin*z / 128;
layer->z = cos*z / 128 - sin*x / 128;
}
}
static void mimimi_apply_z_angle(struct mimimi_model *model, struct mimimi_layer *layers, struct mimimi_pose_layer *pose_layers, struct mimimi_allocator *allocator)
{
int *xs = mimimi_allocate(allocator, sizeof *xs * model->count);
int *ys = mimimi_allocate(allocator, sizeof *ys * model->count);
for (int i = 0 ; i < model->count ; i++)
{
struct mimimi_layer *layer = layers + i;
struct mimimi_pose_layer *pose_layer = pose_layers + i;
int parent_index = model->layers[i].parent_index;
xs[i] = layer->x;
ys[i] = layer->y;
if (i == parent_index) continue;
xs[i] += xs[parent_index];
ys[i] += ys[parent_index];
int z_angle = mimimi_mod(pose_layer->z_angle, 4);
int x = xs[i];
int y = ys[i];
if (z_angle == 1) x = -x, mimimi_swap(&x, &y);
if (z_angle == 2) x = -x, y = -y;
if (z_angle == 3) y = -y, mimimi_swap(&x, &y);
layer->x = x - xs[parent_index];
layer->y = y - ys[parent_index];
if (z_angle == 1) xs[i] = -xs[i], mimimi_swap(xs + i, ys + i);
if (z_angle == 2) xs[i] = -xs[i], ys[i] = -ys[i];
if (z_angle == 3) ys[i] = -ys[i], mimimi_swap(xs + i, ys + i);
}
mimimi_deallocate(allocator, xs);
mimimi_deallocate(allocator, ys);
}
static void mimimi_apply_slant(struct mimimi_model *model, struct mimimi_layer *layers, struct mimimi_pose_layer *pose_layers, struct mimimi_allocator *allocator)
{
int *ys = mimimi_allocate(allocator, sizeof *ys * model->count);
for (int i = 0 ; i < model->count ; i++)
{
struct mimimi_layer *layer = layers + i;
struct mimimi_pose_layer *pose_layer = pose_layers + i;
int parent_index = model->layers[i].parent_index;
ys[i] = layer->y;
if (i == parent_index) continue;
ys[i] += ys[parent_index];
layer->x += pose_layer->slant * ys[i] / 256;
}
mimimi_deallocate(allocator, ys);
}
static void mimimi_absolutize(struct mimimi_model *model, struct mimimi_layer *layers, struct mimimi_pose_layer *pose_layers)
{
for (int i = 0 ; i < model->count ; i++)
{
struct mimimi_layer *layer = layers + i;
struct mimimi_pose_layer *pose_layer = pose_layers + i;
int parent_index = model->layers[i].parent_index;
if (i == parent_index) continue;
layer->x += layers[parent_index].x;
layer->y += layers[parent_index].y;
layer->z += layers[parent_index].z;
layer->amplifier = layer->amplifier * layers[parent_index].amplifier / 32;
pose_layer->slant += pose_layers[parent_index].slant;
pose_layer->z_angle += pose_layers[parent_index].z_angle;
pose_layer->z_angle = mimimi_mod(pose_layer->z_angle, 4);
}
}
void mimimi_pose(struct mimimi_image *image, struct mimimi_model *model, struct mimimi_pose *pose, struct mimimi_allocator *allocator)
{
struct mimimi_layer *layers = mimimi_allocate(allocator, sizeof *layers * model->count);
struct mimimi_pose_layer *pose_layers = mimimi_allocate(allocator, sizeof *pose_layers * model->count);
struct mimimi_half_layer { int behind; int z; struct mimimi_layer *layer; struct mimimi_pose_layer *pose_layer; };
struct mimimi_half_layer *half_layers = mimimi_allocate(allocator, sizeof *half_layers * model->count * 2);
for (int i = 0 ; i < model->count ; i++)
{
struct mimimi_layer *layer = layers + i;
*layer = *model->layers[i].layer;
struct mimimi_pose_layer *pose_layer = pose_layers + i;
if (i < pose->count)
{
*pose_layer = pose->layers[i];
}
else
{
pose_layer->slant = 0;
pose_layer->y_angle = 0;
pose_layer->z_angle = 0;
}
}
mimimi_apply_y_angle(model, layers, pose_layers);
mimimi_apply_z_angle(model, layers, pose_layers, allocator);
mimimi_apply_slant(model, layers, pose_layers, allocator);
mimimi_absolutize(model, layers, pose_layers);
for (int i = 0 ; i < model->count ; i++)
{
struct mimimi_layer *layer = layers + i;
struct mimimi_pose_layer *pose_layer = pose_layers + i;
int width = layer->width;
int z = layer->z * layer->amplifier / 32;
half_layers[i * 2 + 0].behind = 0;
half_layers[i * 2 + 0].z = z + width;
half_layers[i * 2 + 0].layer = layer;
half_layers[i * 2 + 0].pose_layer = pose_layer;
half_layers[i * 2 + 1].behind = 1;
half_layers[i * 2 + 1].z = z - width;
half_layers[i * 2 + 1].layer = layer;
half_layers[i * 2 + 1].pose_layer = pose_layer;
}
for (int i = 0 ; i < model->count * 2 ; i++)
{
int a = half_layers[i].z;
int k = i;
for (int j = i - 1 ; j >= 0 ; j--)
{
int b = half_layers[j].z;
if (a >= b) break;
struct mimimi_half_layer half_layer = half_layers[k];
half_layers[k] = half_layers[j];
half_layers[j] = half_layer;
k = j;
}
}
for (int y = 0 ; y < image->height ; y++)
for (int x = 0 ; x < image->width ; x++)
image->colors[x + y * image->width] = 0;
for (int i = 0 ; i < model->count * 2 ; i++)
{
struct mimimi_half_layer *half_layer = half_layers + i;
mimimi_layer(image, half_layer->layer, pose->x, pose->y, half_layer->pose_layer, half_layer->behind);
}
mimimi_deallocate(allocator, layers);
mimimi_deallocate(allocator, pose_layers);
mimimi_deallocate(allocator, half_layers);
}
static void mimimi_interpolate_pose_layer(struct mimimi_pose_layer *a, struct mimimi_pose_layer *b, int i, int count)
{
int slant = a->slant * (count - i) / count;
slant += mimimi_div(b->slant * i, count);
int y_angle = a->y_angle * (count - i) / count;
y_angle += mimimi_div(b->y_angle * i, count);
int z_angle = a->z_angle * (count - i) / count;
z_angle += mimimi_div(b->z_angle * i, count);
a->slant = slant;
a->y_angle = y_angle;
a->z_angle = z_angle;
}
static void mimimi_interpolate_pose(struct mimimi_pose *a, struct mimimi_pose *b, int i, int count)
{
if (count == 0) return;
int x = a->x * (count - i) / count;
x += mimimi_div(b->x * i, count);
int y = a->y * (count - i) / count;
y += mimimi_div(b->y * i, count);
a->x = x;
a->y = y;
for (int j = 0 ; j < a->count ; j++)
mimimi_interpolate_pose_layer(a->layers + j, b->layers + j, i, count);
}
void mimimi_movement(struct mimimi_animation *animation, struct mimimi_model *model, struct mimimi_movement *movement, struct mimimi_allocator *allocator)
{
if (movement->count == 1)
{
for (int i = 0 ; i < animation->count ; i++)
mimimi_pose(animation->images + i, model, movement->poses, allocator);
return;
}
for (int i = 0 ; i < animation->count ; i++)
{
int j = i * (movement->count - 1) / animation->count;
int k = i - j * animation->count / (movement->count - 1);
struct mimimi_pose pose = movement->poses[j];
pose.layers = mimimi_allocate(allocator, pose.count * sizeof *pose.layers);
for (int i = 0 ; i < pose.count ; i++) pose.layers[i] = movement->poses[j].layers[i];
mimimi_interpolate_pose(&pose, movement->poses + j + 1, k, animation->count / (movement->count - 1));
mimimi_pose(animation->images + i, model, &pose, allocator);
mimimi_deallocate(allocator, pose.layers);
}
}
static void mimimi_appearance_animations(struct mimimi_animation_set *animations, struct mimimi_model *model, int x, int y, int coefficient, struct mimimi_allocator *allocator)
{
int animation_count = animations->standing_animation_count;
for (int i = 0 ; i < animation_count ; i++)
{
struct mimimi_pose_layer layers[3][7] = {0};
int slant = -32 * i / animation_count * coefficient;
int y_angle = 64 + (20 + 20 * i / animation_count) * coefficient;
int arm_slant = 64 * i / animation_count;
layers[0][0].y_angle = y_angle;
layers[0][0].slant = +slant;
layers[0][1].slant = -slant;
layers[0][2].slant = -arm_slant;
layers[0][3].slant = +arm_slant;
layers[0][4].slant = +arm_slant;
layers[0][5].slant = -arm_slant;
layers[1][0].y_angle = y_angle;
layers[1][0].slant = +slant;
layers[1][1].slant = -slant;
layers[1][2].slant = +arm_slant;
layers[1][3].slant = -arm_slant;
layers[1][4].slant = -arm_slant;
layers[1][5].slant = +arm_slant;
layers[2][0].y_angle = y_angle;
layers[2][0].slant = +slant;
layers[2][1].slant = -slant;
layers[2][2].slant = -arm_slant;
layers[2][3].slant = +arm_slant;
layers[2][4].slant = +arm_slant;
layers[2][5].slant = -arm_slant;
struct mimimi_pose poses[] = {{x, y, 7, layers[0]}, {x, y, 7, layers[1]}, {x, y, 7, layers[2]}};
struct mimimi_movement movement = {3, poses};
mimimi_movement(animations->standing + i, model, &movement, allocator);
}
struct mimimi_pose_layer falling_layers[7] = {0};
falling_layers[0].z_angle = coefficient;
falling_layers[0].y_angle = 64 + 32 * coefficient;
falling_layers[2].slant = -64 * coefficient;
falling_layers[2].z_angle = coefficient;
falling_layers[3].slant = 64 * coefficient;
falling_layers[3].z_angle = coefficient;
falling_layers[4].slant = 64 * coefficient;
falling_layers[5].slant = 64 * coefficient;
struct mimimi_pose falling_poses[] = {{x - 12 * coefficient, y - 12, 7, falling_layers}};
struct mimimi_movement falling_movement = {1, falling_poses};
mimimi_movement(animations->falling, model, &falling_movement, allocator);
struct mimimi_pose_layer knocked_layers[1] = {0};
knocked_layers[0].z_angle = -coefficient;
knocked_layers[0].y_angle = 64 + 32 * coefficient;
struct mimimi_pose knocked_poses[] = {{x - 12 * coefficient, y - 20, 1, falling_layers}};
struct mimimi_movement knocked_movement = {1, knocked_poses};
mimimi_movement(animations->knocked, model, &knocked_movement, allocator);
int image_count = animations->jumping->count;
for (int i = 0 ; i < image_count ; i++)
{
struct mimimi_pose_layer layers[2] = {0};
int slant = -32 * i / image_count * coefficient;
int y_angle = 64 + (20 + 20 * i / image_count) * coefficient;
layers[0].y_angle = y_angle;
layers[0].slant = +slant;
layers[1].slant = -slant;
struct mimimi_pose pose = {x, y, 2, layers};
mimimi_pose(animations->jumping->images + i, model, &pose, allocator);
}
}
void mimimi_appearance(struct mimimi_appearance *appearance, struct mimimi_model *model, int x, int y, struct mimimi_allocator *allocator)
{
mimimi_appearance_animations(&appearance->left, model, x, y, 1, allocator);
mimimi_appearance_animations(&appearance->right, model, x, y, -1, allocator);
}