Mirai's Miscellaneous Misadventures

M49 / core / animations.c

/* license: AGPLv3 or later */
/* copyright 2024 zamfofex */

#include <mimimi.h>

static unsigned char mimimi_layer_pixel(struct mimimi_layer *layer, unsigned char y_angle, int x, int y)
{
	int w, h;
	int w0, w1;
	int s;
	int count;
	int a;
	
	h = layer->count;
	
	if (y < 0) return 0;
	if (y >= h) return 0;
	
	w0 = layer->rows[y].width;
	w1 = layer->rows[y].height;
	
	s = 128 - mimimi_cosine[y_angle * 2 % 256];
	w = s * w0 + (256 - s) * w1;
	w /= 512;
	
	if (x < -w) return 0;
	if (x >= w) return 0;
	
	count = layer->rows[y].count;
	
	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;
	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, height;
	unsigned char y_angle;
	int z_angle;
	int x, y;
	int x1, y1;
	unsigned char color;
	
	width = image->width;
	height = image->height;
	
	y_angle = pose_layer->y_angle;
	z_angle = mimimi_mod(pose_layer->z_angle, 4);
	
	if (behind != 0) y_angle += 128;
	
	if (z_angle == 0)
	{
		for (y = 0 ; y < height ; y++)
		for (x = 0 ; x < width ; x++)
		{
			x1 = x + layer->x - x0;
			y1 = y + layer->y - y0;
			
			x1 += pose_layer->slant * (y - y0) / 256;
			
			if (behind != 0) x1 = -x1 - 1;
			
			color = mimimi_layer_pixel(layer, y_angle, x1, y1);
			if (color != 0) image->colors[x + y * width] = color;
		}
	}
	
	if (z_angle == 1)
	{
		for (y = 0 ; y < height ; y++)
		for (x = 0 ; x < width ; x++)
		{
			x1 = x - layer->y - x0;
			y1 = y + layer->x - y0;
			
			y1 -= pose_layer->slant * (x - x0) / 256;
			
			if (behind != 0) y1 = -y1 - 1;
			
			color = mimimi_layer_pixel(layer, y_angle, y1, -x1);
			if (color != 0) image->colors[x + y * width] = color;
		}
		
		return;
	}
	
	if (z_angle == 2)
	{
		for (y = 0 ; y < height ; y++)
		for (x = 0 ; x < width ; x++)
		{
			x1 = x - layer->x - x0;
			y1 = y - layer->y - y0;
			
			x1 += pose_layer->slant * (y - y0) / 256;
			
			if (behind != 0) x1 = -x1 + 1;
			
			color = mimimi_layer_pixel(layer, y_angle, -x1, -y1);
			if (color != 0) image->colors[x + y * width] = color;
		}
		
		return;
	}
	
	if (z_angle == 3)
	{
		for (y = 0 ; y < height ; y++)
		for (x = 0 ; x < width ; x++)
		{
			x1 = x + layer->y - x0;
			y1 = y - layer->x - y0;
			
			y1 -= pose_layer->slant * (x - x0) / 256;
			
			if (behind != 0) y1 = -y1 + 1;
			
			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)
{
	int i, parent_index;
	struct mimimi_layer *layer;
	struct mimimi_pose_layer *pose_layer;
	int parent_y_angle;
	unsigned char y_angle;
	int sin, cos;
	int x, z;
	
	for (i = 0 ; i < model->count ; i++)
	{
		layer = layers + i;
		pose_layer = pose_layers + i;
		parent_index = model->layers[i].parent_index;
		
		if (i == parent_index) continue;
		
		parent_y_angle = pose_layers[parent_index].y_angle;
		pose_layer->y_angle += parent_y_angle;
		
		y_angle = parent_y_angle;
		
		sin = mimimi_sine[y_angle];
		cos = mimimi_cosine[y_angle];
		
		x = layer->x;
		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)
{
	static int xs[0x80], ys[0x80];
	
	int i;
	struct mimimi_layer *layer;
	struct mimimi_pose_layer *pose_layer;
	int parent_index;
	int z_angle;
	int x, y;
	
	for (i = 0 ; i < model->count ; i++)
	{
		layer = layers + i;
		pose_layer = pose_layers + i;
		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];
		
		z_angle = mimimi_mod(pose_layer->z_angle, 4);
		
		x = xs[i];
		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);
	}
}

static void mimimi_apply_slant(struct mimimi_model *model, struct mimimi_layer *layers, struct mimimi_pose_layer *pose_layers)
{
	static int ys[0x80];
	
	int i;
	struct mimimi_layer *layer;
	struct mimimi_pose_layer *pose_layer;
	int parent_index;
	
	for (i = 0 ; i < model->count ; i++)
	{
		layer = layers + i;
		pose_layer = pose_layers + i;
		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;
	}
}

static void mimimi_absolutise(struct mimimi_model *model, struct mimimi_layer *layers, struct mimimi_pose_layer *pose_layers)
{
	int i;
	struct mimimi_layer *layer;
	struct mimimi_pose_layer *pose_layer;
	int parent_index;
	
	for (i = 0 ; i < model->count ; i++)
	{
		layer = layers + i;
		pose_layer = pose_layers + i;
		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);
	}
}

struct mimimi_half_layer
{
	int behind;
	int z;
	struct mimimi_layer *layer;
	struct mimimi_pose_layer *pose_layer;
};

void mimimi_pose(struct mimimi_image *image, struct mimimi_model *model, struct mimimi_pose *pose)
{
	static struct mimimi_layer layers[0x100];
	static struct mimimi_pose_layer pose_layers[0x100];
	static struct mimimi_half_layer half_layers[0x200];
	
	struct mimimi_layer *layer;
	struct mimimi_pose_layer *pose_layer;
	struct mimimi_half_layer *half_layer, half_layer0;
	int i, j, k;
	int x, y;
	int a, b;
	
	for (i = 0 ; i < model->count ; i++)
	{
		layer = layers + i;
		*layer = *model->layers[i].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);
	mimimi_apply_slant(model, layers, pose_layers);
	mimimi_absolutise(model, layers, pose_layers);
	
	for (i = 0 ; i < model->count ; i++)
	{
		layer = layers + i;
		pose_layer = pose_layers + i;
		
		half_layers[i * 2 + 0].behind = 0;
		half_layers[i * 2 + 0].z = layer->z * layer->amplifier + layer->width * 32;
		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 = layer->z * layer->amplifier - layer->width * 32;
		half_layers[i * 2 + 1].layer = layer;
		half_layers[i * 2 + 1].pose_layer = pose_layer;
	}
	
	/* z ordering */
	for (i = 0 ; i < model->count * 2 ; i++)
	{
		a = half_layers[i].z;
		
		k = i;
		for (j = i - 1 ; j >= 0 ; j--)
		{
			b = half_layers[j].z;
			if (a >= b) break;
			
			half_layer0 = half_layers[k];
			half_layers[k] = half_layers[j];
			half_layers[j] = half_layer0;
			
			k = j;
		}
	}
	
	for (y = 0 ; y < image->height ; y++)
	for (x = 0 ; x < image->width ; x++)
		image->colors[x + y * image->width] = 0;
	
	for (i = 0 ; i < model->count * 2 ; i++)
	{
		half_layer = half_layers + i;
		mimimi_layer(image, half_layer->layer, pose->x, pose->y, half_layer->pose_layer, half_layer->behind);
	}
}

static void mimimi_interpolate_pose_layer(struct mimimi_pose_layer *a, struct mimimi_pose_layer *b, int i, int count)
{
	int slant, y_angle, z_angle;
	
	slant = a->slant * (count - i) / count;
	slant += mimimi_div(b->slant * i, count);
	
	y_angle = a->y_angle * (count - i) / count;
	y_angle += mimimi_div(b->y_angle * i, count);
	
	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)
{
	int x, y;
	int j;
	
	if (count == 0) return;
	
	x = a->x * (count - i) / count;
	x += mimimi_div(b->x * i, count);
	
	y = a->y * (count - i) / count;
	y += mimimi_div(b->y * i, count);
	
	a->x = x;
	a->y = y;
	
	for (j = 0 ; j < a->count ; j++)
		mimimi_interpolate_pose_layer(a->layers + j, b->layers + j, i, count);
}

void mimimi_movement(struct mimimi_video *video, struct mimimi_model *model, struct mimimi_movement *movement)
{
	static struct mimimi_pose pose;
	
	int h, i, j, k;
	
	if (movement->count == 1)
	{
		for (i = 0 ; i < video->count ; i++)
			mimimi_pose(video->images + i, model, movement->poses);
		return;
	}
	
	for (i = 0 ; i < video->count ; i++)
	{
		j = i * (movement->count - 1) / video->count;
		k = i - j * video->count / (movement->count - 1);
		
		pose = movement->poses[j];
		for (h = 0 ; h < pose.count ; h++) pose.layers[h] = movement->poses[j].layers[h];
		
		mimimi_interpolate_pose(&pose, movement->poses + j + 1, k, video->count / (movement->count - 1));
		mimimi_pose(video->images + i, model, &pose);
	}
}

static void mimimi_appearance_videos(struct mimimi_video_set *videos, struct mimimi_model *model, int x, int y, int coefficient)
{
	static struct mimimi_pose_layer layer0 = {0};
	static struct mimimi_pose poses[3], pose;
	static struct mimimi_movement movement;
	static struct mimimi_pose_layer layers[2][10];
	
	int video_count;
	int i, j;
	int slant, y_angle;
	
	video_count = videos->standing_video_count;
	for (i = 0 ; i < video_count ; i++)
	{
		slant = 512 * i / video_count * coefficient;
		y_angle = 64 + (20 + 20 * i / video_count) * coefficient;
		
		for (j = 0 ; j < 10 ; j++)
		{
			layers[0][j] = layer0;
			layers[1][j] = layer0;
		}
		
		layers[0][0].y_angle = y_angle;
		layers[1][0].y_angle = y_angle;
		
		layers[0][0].slant = -slant / 8;
		layers[1][0].slant = -slant / 8;
		
		layers[0][1].slant = slant / 8;
		layers[1][1].slant = slant / 8;
		
		layers[1][4].slant = slant / 3;
		layers[1][8].slant = -slant / 3;
		layers[0][8].slant = -slant / 8;
		
		layers[0][5].slant = slant / 3;
		layers[0][9].slant = -slant / 3;
		layers[1][9].slant = -slant / 8;
		
		layers[0][2].slant = slant / 5;
		layers[1][2].slant = -slant / 3;
		layers[0][6].slant = slant / 4;
		layers[1][6].slant = slant / 2;
		
		layers[1][3].slant = slant / 5;
		layers[0][3].slant = -slant / 3;
		layers[1][7].slant = slant / 4;
		layers[0][7].slant = slant / 2;
		
		for (j = 0 ; j < 3 ; j++)
		{
			poses[j].x = x;
			poses[j].y = y;
			poses[j].count = 10;
		}
		
		for (j = 0 ; j < 10 ; j++)
		{
			poses[0].layers[j] = layers[0][j];
			poses[1].layers[j] = layers[1][j];
			poses[2].layers[j] = layers[0][j];
		}
		
		movement.count = 3;
		movement.poses = poses;
		mimimi_movement(videos->standing + i, model, &movement);
	}
	
	for (i = 0 ; i < 7 ; i++) pose.layers[i] = layer0;
	
	pose.layers[0].z_angle = coefficient;
	pose.layers[0].y_angle = 64 + 32 * coefficient;
	pose.layers[4].slant = 96 * coefficient;
	pose.layers[5].slant = 96 * coefficient;
	pose.layers[2].z_angle = coefficient;
	pose.layers[2].slant = -96;
	pose.layers[3].z_angle = coefficient;
	pose.layers[3].slant = 96;
	
	pose.x = x - 12 * coefficient;
	pose.y = y - 12;
	pose.count = 7;
	
	movement.count = 1;
	movement.poses = &pose;
	
	mimimi_movement(videos->falling, model, &movement);
	
	pose.layers[0] = layer0;
	pose.layers[0].slant = 0;
	pose.layers[0].z_angle = coefficient;
	pose.layers[0].y_angle = 64 + 32 * coefficient;
	
	pose.x = x - 12 * coefficient;
	pose.y = y - 20;
	pose.count = 1;
	
	movement.count = 1;
	movement.poses = &pose;
	
	mimimi_movement(videos->knocked, model, &movement);
	
	video_count = videos->jumping_video_count;
	for (i = 0 ; i < video_count ; i++)
	{
		for (j = 0 ; j < 6 ; j++)
		{
			poses[0].layers[j] = layer0;
			poses[1].layers[j] = layer0;
		}
		
		slant = 64 * i / videos->jumping[i].count * coefficient;
		y_angle = 64 + (20 + 20 * i / videos->jumping[i].count) * coefficient;
		
		poses[0].x = x;
		poses[0].y = y;
		poses[0].count = 6;
		poses[0].layers[0].y_angle = y_angle;
		poses[0].layers[0].slant = -slant * 2;
		poses[0].layers[1].slant = slant * 2;
		poses[0].layers[4].slant = -slant / 2;
		poses[0].layers[5].slant = -slant / 2;
		
		poses[1].x = x;
		poses[1].y = y;
		poses[1].count = 6;
		poses[1].layers[0].y_angle = y_angle;
		poses[1].layers[2].slant = -slant * 2;
		poses[1].layers[3].slant = -slant * 2;
		poses[1].layers[4].slant = slant * 2;
		poses[1].layers[5].slant = slant * 2;
		
		movement.count = 2;
		movement.poses = poses;
		
		mimimi_movement(videos->jumping + i, model, &movement);
	}
}

void mimimi_appearance(struct mimimi_appearance *appearance, struct mimimi_model *model, int x, int y)
{
	mimimi_appearance_videos(&appearance->left, model, x, y, 1);
	mimimi_appearance_videos(&appearance->right, model, x, y, -1);
}