Files
nunuhara_xsystem4/src/parts/render.c
T
kichikuou 3496f45e39 parts: add basic implementation of Flash type
Sprite objects and sounds are not implemented yet.
2023-06-17 11:32:14 +09:00

242 lines
8.0 KiB
C

/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <limits.h>
#include <assert.h>
#include <cglm/cglm.h>
#include "system4.h"
#include "system4/hashtable.h"
#include "gfx/gfx.h"
#include "scene.h"
#include "xsystem4.h"
#include "parts_internal.h"
// Goat sprites are integrated into the scene via a single (virtual) sprite
static struct sprite goat_sprite;
static struct {
struct shader shader;
GLint blend_rate;
GLint bot_left;
GLint top_right;
GLint add_color;
GLint multiply_color;
} parts_shader;
static void parts_render_text(struct parts *parts, struct parts_common *common)
{
Rectangle rect = {
.x = parts->global.pos.x + common->origin_offset.x,
.y = parts->global.pos.y + common->origin_offset.y,
.w = common->w,
.h = common->h
};
gfx_render_texture(&parts->states[parts->state].common.texture, &rect);
}
static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rectangle *rect, float blend_rate, vec3 add_color, vec3 multiply_color)
{
mat4 wv_transform = WV_TRANSFORM(config.view_width, config.view_height);
struct gfx_render_job job = {
.shader = &parts_shader.shader,
.texture = texture->handle,
.world_transform = mw_transform[0],
.view_transform = wv_transform[0],
.data = texture,
};
gfx_prepare_job(&job);
glUniform1f(parts_shader.blend_rate, blend_rate);
glUniform2f(parts_shader.bot_left, rect->x, rect->y);
glUniform2f(parts_shader.top_right, rect->x + rect->w, rect->y + rect->h);
glUniform3fv(parts_shader.add_color, 1, add_color);
glUniform3fv(parts_shader.multiply_color, 1, multiply_color);
gfx_run_job(&job);
}
static void parts_render_cg(struct parts *parts, struct parts_common *common)
{
switch (parts->draw_filter) {
case 1:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
break;
default:
break;
}
mat4 mw_transform = GLM_MAT4_IDENTITY_INIT;
glm_translate(mw_transform, (vec3) { parts->global.pos.x, parts->global.pos.y, 0 });
// FIXME: need perspective for 3D rotate
//glm_rotate_x(mw_transform, parts->rotation.x, mw_transform);
//glm_rotate_y(mw_transform, parts->rotation.y, mw_transform);
glm_rotate_z(mw_transform, parts->local.rotation.z, mw_transform);
glm_scale(mw_transform, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0 });
glm_translate(mw_transform, (vec3){ common->origin_offset.x, common->origin_offset.y, 0 });
glm_scale(mw_transform, (vec3){ common->w, common->h, 1.0 });
Rectangle r = common->surface_area;
if (!r.w && !r.h) {
r = (Rectangle) { 0, 0, common->texture.w, common->texture.h };
}
vec3 add_color = {
parts->global.add_color.r / 255.0f,
parts->global.add_color.g / 255.0f,
parts->global.add_color.b / 255.0f
};
vec3 multiply_color = {
parts->global.multiply_color.r / 255.0f,
parts->global.multiply_color.g / 255.0f,
parts->global.multiply_color.b / 255.0f,
};
parts_render_texture(&common->texture, mw_transform, &r, parts->global.alpha / 255.0, add_color, multiply_color);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
}
static void parts_render_flash_shape(struct parts *parts, struct parts_flash *f, struct parts_flash_object *obj, struct swf_tag_define_shape *tag)
{
struct texture *src = ht_get_int(f->bitmaps, tag->fill_style.bitmap_id, NULL);
if (!src)
ERROR("undefined bitmap id %d", tag->fill_style.bitmap_id);
mat4 mw_transform = GLM_MAT4_IDENTITY_INIT;
glm_translate(mw_transform, (vec3) { parts->global.pos.x, parts->global.pos.y, 0 });
glm_rotate_z(mw_transform, parts->local.rotation.z, mw_transform);
glm_scale(mw_transform, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0 });
int x = f->common.origin_offset.x + (obj->matrix.translate_x + tag->bounds.x_min) / 20;
int y = f->common.origin_offset.y + (obj->matrix.translate_y + tag->bounds.y_min) / 20;
int width = (tag->bounds.x_max - tag->bounds.x_min) / 20 * (obj->matrix.scale_x / 65536.0f);
int height = (tag->bounds.y_max - tag->bounds.y_min) / 20 * (obj->matrix.scale_y / 65536.0f);
// TODO: consider matrix.roate_skew
glm_translate(mw_transform, (vec3){ x, y, 0 });
glm_scale(mw_transform, (vec3){ width, height, 1.0 });
Rectangle r = { 0, 0, width, height };
float blend_rate = (parts->global.alpha / 255.0f) * (obj->color_transform.mult_terms[3] / 256.0f);
vec3 add_color = {
(parts->global.add_color.r + obj->color_transform.add_terms[0]) / 255.0f,
(parts->global.add_color.g + obj->color_transform.add_terms[1]) / 255.0f,
(parts->global.add_color.b + obj->color_transform.add_terms[2]) / 255.0f
};
vec3 multiply_color = {
(parts->global.multiply_color.r / 255.0f) * (obj->color_transform.mult_terms[0] / 256.0f),
(parts->global.multiply_color.g / 255.0f) * (obj->color_transform.mult_terms[1] / 256.0f),
(parts->global.multiply_color.b / 255.0f) * (obj->color_transform.mult_terms[2] / 256.0f)
};
parts_render_texture(src, mw_transform, &r, blend_rate, add_color, multiply_color);
}
static void parts_render_flash(struct parts *parts, struct parts_flash *f)
{
struct parts_flash_object *obj;
TAILQ_FOREACH(obj, &f->display_list, entry) {
struct swf_tag *tag = ht_get_int(f->dictionary, obj->character_id, NULL);
if (!tag) {
WARNING("character %d is not defined", obj->character_id);
continue;
}
switch (tag->type) {
case TAG_DEFINE_SHAPE:
parts_render_flash_shape(parts, f, obj, (struct swf_tag_define_shape *)tag);
break;
default:
ERROR("unknown tag 0x%x", tag->type);
}
}
}
void parts_render(struct parts *parts)
{
if (!parts->global.show)
return;
if (parts->linked_to >= 0) {
struct parts *link_parts = parts_get(parts->linked_to);
struct parts_state *link_state = &link_parts->states[link_parts->state];
if (!SDL_PointInRect(&parts_prev_pos, &link_state->common.hitbox))
return;
}
// render
struct parts_state *state = &parts->states[parts->state];
switch (state->type) {
case PARTS_UNINITIALIZED:
break;
case PARTS_CG:
case PARTS_ANIMATION:
case PARTS_NUMERAL:
case PARTS_HGAUGE:
case PARTS_VGAUGE:
case PARTS_CONSTRUCTION_PROCESS:
if (state->common.texture.handle)
parts_render_cg(parts, &state->common);
break;
case PARTS_TEXT:
if (state->common.texture.handle)
parts_render_text(parts, &state->common);
break;
case PARTS_FLASH:
parts_render_flash(parts, &state->flash);
break;
}
}
void parts_engine_render(possibly_unused struct sprite *_)
{
struct parts *parts;
PARTS_LIST_FOREACH(parts) {
parts_render(parts);
}
}
void parts_engine_dirty(void)
{
scene_sprite_dirty(&goat_sprite);
}
void parts_dirty(possibly_unused struct parts *parts)
{
parts_engine_dirty();
}
static cJSON *_parts_engine_to_json(struct sprite *_, bool verbose)
{
return parts_engine_to_json(verbose);
}
void parts_render_init(void)
{
goat_sprite.z = 0;
goat_sprite.z2 = INT_MAX;
goat_sprite.has_pixel = true;
goat_sprite.has_alpha = true;
goat_sprite.render = parts_engine_render;
goat_sprite.to_json = _parts_engine_to_json;
scene_register_sprite(&goat_sprite);
gfx_load_shader(&parts_shader.shader, "shaders/render.v.glsl", "shaders/parts.f.glsl");
parts_shader.blend_rate = glGetUniformLocation(parts_shader.shader.program, "blend_rate");
parts_shader.bot_left = glGetUniformLocation(parts_shader.shader.program, "bot_left");
parts_shader.top_right = glGetUniformLocation(parts_shader.shader.program, "top_right");
parts_shader.add_color = glGetUniformLocation(parts_shader.shader.program, "add_color");
parts_shader.multiply_color = glGetUniformLocation(parts_shader.shader.program, "multiply_color");
}