Mirai's Miscellaneous Misadventures

M18 / core / appearances.c

1// copyright 2022 zamfofex
2// license: AGPLv3 or later
3
4#include <mimimi/models.h>
5#include <mimimi/appearances.h>
6#include <mimimi/allocators.h>
7
8#include "appearances.h"
9
10#include "math.c"
11
12static unsigned char mimimi_rotating_image_pixel(struct mimimi_rotating_image *rotating_image, unsigned char angle, int x, int y)
13{
14	y += rotating_image->oy;
15	
16	int h = rotating_image->count;
17	if (y < 0) return 0;
18	if (y >= h) return 0;
19	
20	int w = rotating_image->rows[y].size;
21	if (x < -w / 2) return 0;
22	if (x >= w / 2) return 0;
23	
24	x += w / 2;
25	
26	int count = rotating_image->rows[y].count;
27	
28	int a = angle;
29	a *= count;
30	a += 128;
31	a /= 256;
32	a += x;
33	a %= count;
34	
35	return rotating_image->rows[y].colors[a];
36}
37
38static void mimimi_rotating_image_stamp(struct mimimi_image *image, struct mimimi_rotating_image *rotating_image, unsigned char y_angle, unsigned char z_angle, int x0, int y0, int behind)
39{
40	int width = image->width;
41	int height = image->height;
42	
43	x0 += rotating_image->oz * mimimi_cosine[y_angle] / 256;
44	
45	if (behind != 0) y_angle += 128;
46	
47	for (int x = 0 ; x < width ; x++)
48	for (int y = 0 ; y < height ; y++)
49	{
50		if (behind != 0 && image->colors[x + y * width] != 0) continue;
51		
52		int x1 = x - x0;
53		int y1 = y - y0;
54		
55		int x2 = x1 + y1 * mimimi_sine[z_angle] / 127;
56		
57		if (behind != 0) x2 = -x2 - 1;
58		
59		unsigned char color = mimimi_rotating_image_pixel(rotating_image, y_angle, x2, y1);
60		if (color == 0) continue;
61		
62		image->colors[x + y * width] = color;
63	}
64}
65
66static void mimimi_animation_layer(struct mimimi_image *image, struct mimimi_model_layer *layer, unsigned char y_angle, unsigned char z_angle, int x0, int y0)
67{
68	for (int i = 0 ; i < layer->count ; i++)
69	{
70		mimimi_rotating_image_stamp(image, layer->images[i], y_angle, z_angle, x0, y0, 0);
71		mimimi_rotating_image_stamp(image, layer->images[i], y_angle, z_angle, x0, y0, 1);
72	}
73}
74
75static void mimimi_animation_frame(struct mimimi_sprite_image *sprite_image, struct mimimi_model *model, unsigned char y_angle, unsigned char z_angle, unsigned char arm_angle)
76{
77	struct mimimi_image *image = &sprite_image->image;
78	
79	image->colors = sprite_image->colors;
80	image->width = 26;
81	image->height = 36;
82	
83	for (int x = 0 ; x < image->width ; x++)
84	for (int y = 0 ; y < image->height ; y++)
85		image->colors[x + y * image->width] = 0;
86	
87	int x = image->width / 2;
88	
89	int head_y = 13;
90	int head_x = x + mimimi_sine[z_angle] * (image->height - head_y) / 127;
91	
92	int arm_y = 16;
93	int arm_x = x + mimimi_sine[z_angle] * (image->height - arm_y) / 127;
94	int left_arm_x = arm_x + 10 * mimimi_sine[y_angle] / 256;
95	int right_arm_x = arm_x - 10 * mimimi_sine[y_angle] / 256;
96	
97	int leg_y = 24;
98	int leg_x = x + mimimi_sine[z_angle] * (image->height - leg_y) / 127;
99	int left_leg_x = leg_x + 6 * mimimi_sine[y_angle] / 256;
100	int right_leg_x = leg_x - 6 * mimimi_sine[y_angle] / 256;
101	
102	int torso_y = 24;
103	int torso_x = x + mimimi_sine[z_angle] * (image->height - torso_y) / 127;
104	
105	mimimi_animation_layer(image, model->left_leg, y_angle, -arm_angle, left_leg_x, leg_y);
106	mimimi_animation_layer(image, model->right_leg, y_angle, arm_angle, right_leg_x, leg_y);
107	if ((y_angle + 64) % 256 < 128)
108	{
109		mimimi_animation_layer(image, model->left_arm, y_angle, arm_angle, left_arm_x, arm_y);
110		mimimi_animation_layer(image, model->torso, y_angle, z_angle, torso_x, torso_y);
111		mimimi_animation_layer(image, model->right_arm, y_angle, -arm_angle, right_arm_x, arm_y);
112	}
113	else
114	{
115		mimimi_animation_layer(image, model->right_arm, y_angle, -arm_angle, right_arm_x, arm_y);
116		mimimi_animation_layer(image, model->torso, y_angle, z_angle, torso_x, torso_y);
117		mimimi_animation_layer(image, model->left_arm, y_angle, arm_angle, left_arm_x, arm_y);
118	}
119	mimimi_animation_layer(image, model->head, y_angle, 0, head_x, head_y);
120}
121
122static void mimimi_animation(struct mimimi_animation *animation, struct mimimi_model *model, unsigned char y_angle, unsigned char z_angle, unsigned char arm_angle)
123{
124	int count = mimimi_image_count;
125	for (int i = 0 ; i < count ; i++)
126		mimimi_animation_frame(animation->images + i, model, y_angle, z_angle, arm_angle * mimimi_sine[i * 255 / count] / 127);
127}
128
129static void mimimi_animation_set(struct mimimi_animation *animations, struct mimimi_model *model, int coefficient, unsigned char z_angle, unsigned char arm_angle)
130{
131	int count = mimimi_animation_count;
132	for (int i = 0 ; i < count ; i++)
133		mimimi_animation(animations + i, model, 64 + (12 + 34 * i / count) * coefficient, -z_angle * i / count * coefficient, arm_angle * i / count);
134}
135
136// adapted from https://rosettacode.org/wiki/Matrix_transposition#C
137static void mimimi_transpose_image(struct mimimi_image *image)
138{
139	int width = image->width;
140	int height = image->height;
141	image->width = height;
142	image->height = width;
143	
144	for (int start = 0 ; start < width * height ; start++)
145	{
146		int next = start;
147		int i = 0;
148		for (;;)
149		{
150			i++;
151			next = (next % height) * width + next / height;
152			if (next <= start) break;
153		}
154		
155		if (i == 1) continue;
156		if (next < start) continue;
157		
158		char tmp = image->colors[next = start];
159		for (;;)
160		{
161			int i = (next % height) * width + next / height;
162			image->colors[next] = (i == start) ? tmp : image->colors[i];
163			next = i;
164			if (next <= start) break;
165		}
166	}
167}
168
169static void mimimi_flip_image(struct mimimi_image *image)
170{
171	for (int x = 0 ; x < image->width / 2 ; x++)
172	for (int y = 0 ; y < image->height ; y++)
173	{
174		unsigned char color = image->colors[image->width - x - 1 + y * image->width];
175		image->colors[image->width - x - 1 + y * image->width] = image->colors[x + y * image->width];
176		image->colors[x + y * image->width] = color;
177	}
178}
179
180static void mimimi_rotate_image_cw(struct mimimi_image *image)
181{
182	mimimi_transpose_image(image);
183	mimimi_flip_image(image);
184}
185
186static void mimimi_rotate_image_ccw(struct mimimi_image *image)
187{
188	mimimi_flip_image(image);
189	mimimi_transpose_image(image);
190}
191
192struct mimimi_appearance *mimimi_appearance(struct mimimi_model *model, struct mimimi_allocator *allocator)
193{
194	struct mimimi_appearance *appearance = (*allocator->allocate)(sizeof *appearance);
195	
196	unsigned char z_angle = 9;
197	unsigned char arm_angle = 16;
198	unsigned char arm_flail_angle = 32;
199	
200	mimimi_animation_set(appearance->left.standing, model, 1, z_angle, arm_angle);
201	mimimi_animation_set(appearance->right.standing, model, -1, z_angle, arm_angle);
202	
203	mimimi_animation(&appearance->left.knocked, model, 64 + 16, 0, arm_flail_angle);
204	mimimi_animation(&appearance->right.knocked, model, 64 - 16, 0, arm_flail_angle);
205	
206	for (int i = 0 ; i < mimimi_image_count ; i++)
207	{
208		mimimi_rotate_image_cw(&appearance->left.knocked.images[i].image);
209		mimimi_rotate_image_ccw(&appearance->right.knocked.images[i].image);
210	}
211	
212	return appearance;
213}
214
215static struct mimimi_appearance mimimi_empty_appearance_value = {};
216struct mimimi_appearance *mimimi_empty_appearance = &mimimi_empty_appearance_value;