Merge pull request #71 from kichikuou/particle

ReignEngine: Implement particle rendering
This commit is contained in:
Nunuhara Cabbage
2022-09-26 17:04:34 -07:00
committed by GitHub
7 changed files with 902 additions and 127 deletions
+7
View File
@@ -18,6 +18,7 @@
#define SYSTEM4_REIGN_H
#define RE_NR_BACK_CGS 16
#define RE_NR_INSTANCE_TARGETS 2
#include <cglm/types.h>
@@ -124,6 +125,7 @@ struct RE_instance {
struct height_detector *height_detector;
enum RE_instance_type type;
int target[RE_NR_INSTANCE_TARGETS];
vec3 pos;
float pitch, roll, yaw; // in degrees
vec3 scale;
@@ -137,6 +139,10 @@ struct RE_instance {
bool motion_blend;
float motion_blend_rate;
vec3 ambient;
vec3 column_pos;
float column_height;
float column_radius;
float column_angle; // around the Y axis, in degrees
// Lights
vec3 vec;
@@ -237,6 +243,7 @@ int RE_particle_get_num_damage(struct particle_object *po);
int RE_particle_get_damage(struct particle_object *po, int frame);
float RE_particle_get_offset_x(struct particle_object *po);
float RE_particle_get_offset_y(struct particle_object *po);
bool RE_effect_get_frame_range(struct RE_instance *instance, int *begin_frame, int *end_frame);
bool RE_back_cg_set(struct RE_back_cg *bcg, int no);
bool RE_back_cg_set_name(struct RE_back_cg *bcg, struct string *name, struct archive *aar);
+19 -10
View File
@@ -15,6 +15,11 @@
*/
#define NR_DIR_LIGHTS 3
#define DIFFUSE_EMISSIVE 0
#define DIFFUSE_NORMAL 1
#define DIFFUSE_LIGHT_MAP 2
#define FOG_LINEAR 1
#define FOG_LIGHT_SCATTERING 2
@@ -40,7 +45,7 @@ uniform float specular_shininess;
uniform bool use_specular_map;
uniform float rim_exponent;
uniform vec3 rim_color;
uniform bool use_light_map;
uniform int diffuse_type;
uniform bool use_shadow_map;
uniform float shadow_bias;
uniform int fog_type;
@@ -74,16 +79,20 @@ void main() {
vec3 frag_rgb = ambient;
// Diffuse lighting
vec3 diffuse = vec3(0.0);
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
float half_lambert = dot(norm, -light_dir[i]) * 0.5 + 0.5;
diffuse += mix(dir_lights[i].globe_diffuse, dir_lights[i].diffuse, half_lambert);
}
if (use_light_map) {
diffuse *= texture(light_texture, light_tex_coord).rgb;
}
vec4 texel = texture(tex, tex_coord);
frag_rgb += texel.rgb * diffuse;
if (diffuse_type == DIFFUSE_EMISSIVE) {
frag_rgb = texel.rgb;
} else {
vec3 diffuse = vec3(0.0);
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
float half_lambert = dot(norm, -light_dir[i]) * 0.5 + 0.5;
diffuse += mix(dir_lights[i].globe_diffuse, dir_lights[i].diffuse, half_lambert);
}
if (diffuse_type == DIFFUSE_LIGHT_MAP) {
diffuse *= texture(light_texture, light_tex_coord).rgb;
}
frag_rgb += texel.rgb * diffuse;
}
// Specular lighting
if (specular_strength > 0.0) {
+59 -12
View File
@@ -23,10 +23,16 @@
#include "gfx/gl.h"
#include "gfx/gfx.h"
#include "reign.h"
#define NR_DIR_LIGHTS 3
#define MAX_BONES 211 // Maximum in TT3
typedef struct vec3_range {
vec3 begin;
vec3 end;
} vec3_range;
struct model {
char *path;
int nr_meshes;
@@ -127,7 +133,7 @@ struct RE_renderer {
GLint rim_exponent;
GLint rim_color;
GLint camera_pos;
GLint use_light_map;
GLint diffuse_type;
GLint light_texture;
GLint use_normal_map;
GLint normal_texture;
@@ -151,6 +157,7 @@ struct RE_renderer {
struct billboard_texture {
GLuint texture;
bool has_alpha;
};
// reign.c
@@ -175,6 +182,7 @@ bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no);
struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r, struct model *model);
void RE_renderer_free_height_detector(struct height_detector *hd);
float RE_renderer_detect_height(struct height_detector *hd, float x, float z);
void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out);
// particle.c
@@ -191,6 +199,13 @@ enum particle_move_type {
PARTICLE_MOVE_EMITTER = 2,
};
enum particle_up_vector_type {
PARTICLE_UP_VECTOR_Y_AXIS = 0,
PARTICLE_UP_VECTOR_OBJECT_MOVE = 1,
PARTICLE_UP_VECTOR_INSTANCE_MOVE = 2,
PARTICLE_UP_VECTOR_TYPE_MAX = PARTICLE_UP_VECTOR_INSTANCE_MOVE
};
enum particle_blend_type {
PARTICLE_BLEND_NORMAL = 0,
PARTICLE_BLEND_ADDITIVE = 1,
@@ -201,6 +216,18 @@ enum particle_frame_ref_type {
PARTICLE_FRAME_REF_TARGET = 1,
};
enum particle_child_move_dir_type {
PARTICLE_CHILD_MOVE_DIR_NORMAL = 0,
PARTICLE_CHILD_MOVE_DIR_REVERSE = 1,
PARTICLE_CHILD_MOVE_DIR_TYPE_MAX = PARTICLE_CHILD_MOVE_DIR_REVERSE
};
enum particle_dir_type {
PARTICLE_DIR_TYPE_NORMAL = 0,
PARTICLE_DIR_TYPE_XY_PLANE = 1,
PARTICLE_DIR_TYPE_MAX = PARTICLE_DIR_TYPE_XY_PLANE
};
struct particle_position_unit {
enum {
PARTICLE_POS_UNIT_NONE = 0,
@@ -212,13 +239,13 @@ struct particle_position_unit {
} type;
union {
struct {
int n;
vec3 pos;
int index;
vec3 offset;
} target;
struct {
int n;
// char *name;
vec3 pos;
int target_index;
char *name;
vec3 offset;
} bone;
struct {
float f;
@@ -237,7 +264,7 @@ struct particle_object {
char *name;
enum particle_type type;
enum particle_move_type move_type;
int up_vector_type;
enum particle_up_vector_type up_vector_type;
float move_curve;
struct particle_position *position[NR_PARTICLE_POSITIONS];
float size[2];
@@ -269,9 +296,9 @@ struct particle_object {
int alpha_fadein_time;
float alpha_fadeout_frame;
int alpha_fadeout_time;
vec3 rotation[2];
vec3 revolution_angle[2];
vec3 revolution_distance[2];
vec3_range rotation;
vec3_range revolution_angle;
vec3_range revolution_distance;
vec3 curve_length;
int child_frame;
float child_length;
@@ -279,22 +306,42 @@ struct particle_object {
float child_end_slope;
float child_create_begin_frame;
float child_create_end_frame;
int child_move_dir_type;
int dir_type;
enum particle_child_move_dir_type child_move_dir_type;
enum particle_dir_type dir_type;
int nr_damages;
int *damages;
float offset_x;
float offset_y;
// Runtime data
struct model *model;
struct particle_instance *instances;
vec3_range pos;
vec3 move_vec, move_ortho;
};
struct particle_instance {
float begin_frame;
float end_frame;
vec3_range random_offset;
float roll_angle;
float pitch_angle;
};
struct particle_effect {
char *path;
struct hash_table *textures; // name -> struct billboard_texture*
int nr_objects;
struct particle_object *objects;
};
struct particle_effect *particle_effect_load(struct archive *aar, const char *path);
void particle_effect_free(struct particle_effect *effect);
void particle_effect_calc_frame_range(struct particle_effect *effect, struct motion *motion);
void particle_effect_update(struct RE_instance *inst);
void particle_object_calc_local_transform(struct RE_instance *inst, struct particle_object *po, struct particle_instance *pi, float frame, mat4 dest);
float particle_object_calc_alpha(struct particle_object *po, struct particle_instance *pi, float frame);
// parser.c
+505 -49
View File
@@ -14,12 +14,22 @@
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <limits.h>
#include <cglm/cglm.h>
#include "system4.h"
#include "system4/aar.h"
#include "system4/cg.h"
#include "system4/hashtable.h"
#include "system4/utfsjis.h"
#include "3d_internal.h"
static inline float randf(void)
{
return rand() / (float)RAND_MAX;
}
static void parse_error(const char *msg, char *input)
{
char *eol = strchr(input, '\n');
@@ -69,7 +79,7 @@ static enum particle_frame_ref_type parse_particle_frame_ref_type(const char *s,
return PARTICLE_FRAME_REF_EFFECT;
else if (sscanf(s, "ターゲット[%d]", param) == 1)
return PARTICLE_FRAME_REF_TARGET;
WARNING("unknown blend type \"%s\"", s);
WARNING("unknown frame ref type \"%s\"", s);
return (enum particle_frame_ref_type)-1;
}
@@ -148,7 +158,7 @@ static int *parse_int_list(char **ptext, int *nr)
return ints;
}
static char **parse_qstr_list(char **ptext, int *nr)
static char **parse_qstr_list(char **ptext, int *nr, bool ignore_empty)
{
char *text = *ptext;
int len;
@@ -161,14 +171,17 @@ static char **parse_qstr_list(char **ptext, int *nr)
cap *= 2;
strs = xrealloc(strs, cap * sizeof(char *));
}
strs[*nr] = parse_quoted_string(&text);
if (!strs[*nr]) {
char *s = parse_quoted_string(&text);
if (!s) {
for (int i = 0; i < *nr; i++)
free(strs[i]);
free(strs);
return NULL;
}
(*nr)++;
if (s[0] == '\0' && ignore_empty)
free(s);
else
strs[(*nr)++] = s;
} while (MATCH(" , "));
*ptext = text;
return strs;
@@ -196,17 +209,17 @@ static struct particle_position *parse_position(char **ptext)
} else if (MATCH("ボーン[ %d , "QUOTED_STRING" , %f , %f , %f ]", &n, string_buf, &x, &y, &z) ||
MATCH("ボーン[ %d , \"\" , %f , %f , %f ]", &n, &x, &y, &z)) {
unit->type = PARTICLE_POS_UNIT_BONE;
unit->bone.n = n;
// unit->bone.name = utf2sjis(string_buf, 0);
unit->bone.pos[0] = x;
unit->bone.pos[1] = y;
unit->bone.pos[2] = -z;
unit->bone.target_index = n;
unit->bone.name = utf2sjis(string_buf, 0);
unit->bone.offset[0] = x;
unit->bone.offset[1] = y;
unit->bone.offset[2] = -z;
} else if (MATCH("ターゲット[ %d , %f , %f , %f ]", &n, &x, &y, &z)) {
unit->type = PARTICLE_POS_UNIT_TARGET;
unit->target.n = n;
unit->target.pos[0] = x;
unit->target.pos[1] = y;
unit->target.pos[2] = -z;
unit->target.index = n;
unit->target.offset[0] = x;
unit->target.offset[1] = y;
unit->target.offset[2] = -z;
} else if (MATCH("ランダム方向( %f )", &x)) {
unit->type = PARTICLE_POS_UNIT_RAND;
unit->rand.f = x;
@@ -261,6 +274,8 @@ static struct particle_effect *parse_pae(char *text)
current_object->name = utf2sjis(string_buf, 0);
// Default values.
current_object->move_type = PARTICLE_MOVE_LINEAR;
current_object->texture_anime_frame = 1.0;
current_object->child_end_slope = 1.0;
} else {
parse_error("unknown toplevel.", text);
goto err;
@@ -281,6 +296,10 @@ static struct particle_effect *parse_pae(char *text)
if (current_object->move_type < 0)
goto err;
} else if (MATCH("アップベクトルタイプ = %d", &n)) {
if (n > PARTICLE_UP_VECTOR_TYPE_MAX) {
WARNING("unknown up vector type %d", n);
n = PARTICLE_UP_VECTOR_TYPE_MAX;
}
current_object->up_vector_type = n;
} else if (MATCH("移動カーブ = %f", &f)) {
current_object->move_curve = f;
@@ -324,7 +343,7 @@ static struct particle_effect *parse_pae(char *text)
if (!current_object->size_y_types)
goto err;
} else if (MATCH("テクスチャ = ")) {
current_object->textures = parse_qstr_list(&text, &current_object->nr_textures);
current_object->textures = parse_qstr_list(&text, &current_object->nr_textures, true);
if (!current_object->textures)
goto err;
} else if (MATCH("ブレンドタイプ = " KEYWORD, string_buf)) {
@@ -359,38 +378,38 @@ static struct particle_effect *parse_pae(char *text)
} else if (MATCH("アルファフェードアウト時間 = %d", &n)) {
current_object->alpha_fadeout_time = n;
} else if (MATCH("X軸自転角度 = %f , %f", &f, &f2)) {
current_object->rotation[0][0] = f;
current_object->rotation[1][0] = f2;
current_object->rotation.begin[0] = f;
current_object->rotation.end[0] = f2;
} else if (MATCH("Y軸自転角度 = %f , %f", &f, &f2)) {
current_object->rotation[0][1] = f;
current_object->rotation[1][1] = f2;
current_object->rotation.begin[1] = f;
current_object->rotation.end[1] = f2;
} else if (MATCH("Z軸自転角度 = %f , %f", &f, &f2)) {
current_object->rotation[0][2] = f;
current_object->rotation[1][2] = f2;
current_object->rotation.begin[2] = f;
current_object->rotation.end[2] = f2;
} else if (MATCH("X軸公転角度 = %f , %f", &f, &f2)) {
current_object->revolution_angle[0][0] = f;
current_object->revolution_angle[1][0] = f2;
current_object->revolution_angle.begin[0] = f;
current_object->revolution_angle.end[0] = f2;
} else if (MATCH("Y軸公転角度 = %f , %f", &f, &f2)) {
current_object->revolution_angle[0][1] = f;
current_object->revolution_angle[1][1] = f2;
current_object->revolution_angle.begin[1] = f;
current_object->revolution_angle.end[1] = f2;
} else if (MATCH("Z軸公転角度 = %f , %f", &f, &f2)) {
current_object->revolution_angle[0][2] = f;
current_object->revolution_angle[1][2] = f2;
current_object->revolution_angle.begin[2] = f;
current_object->revolution_angle.end[2] = f2;
} else if (MATCH("X軸公転距離 = %f , %f", &f, &f2)) {
current_object->revolution_distance[0][0] = f;
current_object->revolution_distance[1][0] = f2;
current_object->revolution_distance.begin[0] = f;
current_object->revolution_distance.end[0] = f2;
} else if (MATCH("Y軸公転距離 = %f , %f", &f, &f2)) {
current_object->revolution_distance[0][1] = f;
current_object->revolution_distance[1][1] = f2;
current_object->revolution_distance.begin[1] = f;
current_object->revolution_distance.end[1] = f2;
} else if (MATCH("Z軸公転距離 = %f , %f", &f, &f2)) {
current_object->revolution_distance[0][2] = f;
current_object->revolution_distance[1][2] = f2;
current_object->revolution_distance.begin[2] = f;
current_object->revolution_distance.end[2] = f2;
} else if (MATCH("カーブX距離 = %f", &f)) {
current_object->curve_length[0] = f;
} else if (MATCH("カーブY距離 = %f", &f)) {
current_object->curve_length[1] = f;
} else if (MATCH("カーブZ距離 = %f", &f)) {
current_object->curve_length[2] = f;
current_object->curve_length[2] = -f;
} else if (MATCH("子フレーム = %d", &n)) {
current_object->child_frame = n;
} else if (MATCH("子距離 = %f", &f)) {
@@ -404,8 +423,16 @@ static struct particle_effect *parse_pae(char *text)
} else if (MATCH("子生成終了フレーム = %f", &f)) {
current_object->child_create_end_frame = f;
} else if (MATCH("子移動方向タイプ = %d", &n)) {
if (n > PARTICLE_CHILD_MOVE_DIR_TYPE_MAX) {
WARNING("unknown child move dir type %d", n);
n = PARTICLE_CHILD_MOVE_DIR_TYPE_MAX;
}
current_object->child_move_dir_type = n;
} else if (MATCH("方向タイプ = %d", &n)) {
if (n > PARTICLE_DIR_TYPE_MAX) {
WARNING("unknown dir type %d", n);
n = PARTICLE_DIR_TYPE_MAX;
}
current_object->dir_type = n;
} else if (MATCH("ダメージ = ")) {
current_object->damages = parse_int_list(&text, &current_object->nr_damages);
@@ -426,6 +453,182 @@ static struct particle_effect *parse_pae(char *text)
return NULL;
}
static void load_textures(struct particle_effect *effect, struct archive *aar)
{
effect->textures = ht_create(16);
for (int i = 0; i < effect->nr_objects; i++) {
struct particle_object *po = &effect->objects[i];
for (int j = 0; j < po->nr_textures; j++) {
const char *name = po->textures[j];
if (ht_get(effect->textures, name, NULL))
continue; // already loaded.
struct archive_data *dfile = RE_get_aar_entry(aar, effect->path, name, ".bmp");
if (!dfile)
dfile = RE_get_aar_entry(aar, effect->path, name, ".tga");
if (!dfile) {
WARNING("cannot load texture %s\\%s", effect->path, name);
continue;
}
struct cg *cg = cg_load_data(dfile);
if (!cg) {
WARNING("cg_load_data failed: %s", dfile->name);
archive_free_data(dfile);
continue;
}
archive_free_data(dfile);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
struct billboard_texture *bt = xcalloc(1, sizeof(struct billboard_texture));
bt->texture = texture;
bt->has_alpha = cg->metrics.has_alpha;
ht_put(effect->textures, name, bt);
cg_free(cg);
}
}
}
static struct RE_instance *get_target(struct RE_instance *inst, int index)
{
int target_id = inst->target[index];
if (target_id < 0 || target_id >= inst->plugin->nr_instances)
return NULL;
return inst->plugin->instances[target_id];
}
static void eval_pos_for_object(struct RE_instance *inst, struct particle_position *pp, vec3 dest)
{
glm_vec3_zero(dest);
if (!pp)
return;
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
struct particle_position_unit *unit = &pp->units[i];
switch (unit->type) {
case PARTICLE_POS_UNIT_NONE:
break;
case PARTICLE_POS_UNIT_RAND:
case PARTICLE_POS_UNIT_RAND_POSITIVE_Y:
// These are computed per instance, in eval_pos_for_instance().
break;
case PARTICLE_POS_UNIT_ABSOLUTE:
glm_vec3_add(dest, unit->absolute, dest);
break;
case PARTICLE_POS_UNIT_BONE:
{
struct RE_instance *target = get_target(inst, unit->bone.target_index);
int bone = RE_instance_get_bone_index(target, unit->bone.name);
vec3 target_pos;
if (RE_instance_trans_local_pos_to_world_pos_by_bone(target, bone, unit->bone.offset, target_pos))
glm_vec3_add(dest, target_pos, dest);
else
glm_vec3_add(dest, unit->bone.offset, dest);
}
break;
case PARTICLE_POS_UNIT_TARGET:
{
struct RE_instance *target = get_target(inst, unit->target.index);
if (!target)
break;
vec3 scale = {
target->column_radius,
target->column_height,
target->column_radius
};
mat4 transform;
glm_scale_make(transform, scale);
glm_rotate_y(transform, -glm_rad(target->column_angle), transform);
vec3 target_pos;
glm_mat4_mulv3(transform, unit->target.offset, 1.0f, target_pos);
glm_vec3_addadd(target->pos, target->column_pos, target_pos);
glm_vec3_add(dest, target_pos, dest);
}
break;
}
}
}
static void eval_pos_for_instance(struct particle_position *pp, vec3 dest)
{
glm_vec3_zero(dest);
if (!pp)
return;
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
struct particle_position_unit *unit = &pp->units[i];
switch (unit->type) {
case PARTICLE_POS_UNIT_NONE:
break;
case PARTICLE_POS_UNIT_ABSOLUTE:
case PARTICLE_POS_UNIT_BONE:
case PARTICLE_POS_UNIT_TARGET:
// These are computed per object, in eval_pos_for_object().
break;
case PARTICLE_POS_UNIT_RAND:
dest[0] += (randf() * 2.0 - 1.0) * unit->rand.f;
dest[1] += (randf() * 2.0 - 1.0) * unit->rand.f;
dest[2] += (randf() * 2.0 - 1.0) * unit->rand.f;
break;
case PARTICLE_POS_UNIT_RAND_POSITIVE_Y:
dest[0] += (randf() * 2.0 - 1.0) * unit->rand.f;
dest[1] += randf() * unit->rand.f;
dest[2] += (randf() * 2.0 - 1.0) * unit->rand.f;
break;
}
}
}
static float eval_child_slope(struct particle_object *po)
{
float slope = glm_lerp(po->child_begin_slope, po->child_end_slope, randf());
if (slope > 1.0)
slope = 0.5 + 0.5 / slope; // ???
return (1.0 + slope) * GLM_PI_2;
}
static void interpolate_pos(vec3 begin, vec3 end, float move_curve, float t, vec3 dest)
{
if (move_curve == 0.0f) {
glm_vec3_copy(end, dest);
return;
}
float x = move_curve > 0.0f ? powf(t, move_curve) : 1.0f - powf(1.0f - t, -move_curve);
glm_vec3_lerp(begin, end, x, dest);
}
static void init_particle_instances(struct particle_object *po)
{
po->instances = xcalloc(po->nr_particles, sizeof(struct particle_instance));
for (int i = 0; i < po->nr_particles; i++) {
struct particle_instance *pi = &po->instances[i];
switch (po->move_type) {
case PARTICLE_MOVE_FIXED:
case PARTICLE_MOVE_LINEAR:
pi->begin_frame = po->begin_frame;
pi->end_frame = po->end_frame;
break;
case PARTICLE_MOVE_EMITTER:
pi->begin_frame = glm_lerp(po->child_create_begin_frame, po->child_create_end_frame, (float)i / po->nr_particles);
pi->end_frame = pi->begin_frame + po->child_frame;
pi->roll_angle = eval_child_slope(po);
pi->pitch_angle = randf() * (2.0f * GLM_PIf);
break;
}
eval_pos_for_instance(po->position[0], pi->random_offset.begin);
eval_pos_for_instance(po->position[1], pi->random_offset.end);
}
}
struct particle_effect *particle_effect_load(struct archive *aar, const char *path)
{
const char *basename = strrchr(path, '\\');
@@ -460,30 +663,283 @@ struct particle_effect *particle_effect_load(struct archive *aar, const char *pa
archive_free_data(dfile);
effect->path = strdup(path);
load_textures(effect, aar);
for (int i = 0; i < effect->nr_objects; i++) {
struct particle_object *po = &effect->objects[i];
if (po->polygon_name && *po->polygon_name) {
char *pol_path = xmalloc(strlen(path) + strlen(po->polygon_name) + 2);
sprintf(pol_path, "%s\\%s", path, po->polygon_name);
po->model = model_load(aar, pol_path);
free(pol_path);
}
init_particle_instances(po);
}
return effect;
}
static void free_billboard_texture(void *value)
{
struct billboard_texture *bt = value;
glDeleteTextures(1, &bt->texture);
free(bt);
}
static void particle_position_free(struct particle_position *pos)
{
if (!pos)
return;
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
if (pos->units[i].type == PARTICLE_POS_UNIT_BONE)
free(pos->units[i].bone.name);
}
free(pos);
}
void particle_effect_free(struct particle_effect *effect)
{
for (int i = 0; i < effect->nr_objects; i++) {
struct particle_object *obj = &effect->objects[i];
free(obj->name);
free(obj->position[0]);
free(obj->position[1]);
free(obj->sizes2);
free(obj->sizes_x);
free(obj->sizes_y);
free(obj->size_types);
free(obj->size_x_types);
free(obj->size_y_types);
for (int j = 0; j < obj->nr_textures; j++)
free(obj->textures[j]);
free(obj->textures);
free(obj->polygon_name);
free(obj->damages);
struct particle_object *po = &effect->objects[i];
free(po->name);
for (int j = 0; j < NR_PARTICLE_POSITIONS; j++)
particle_position_free(po->position[j]);
free(po->sizes2);
free(po->sizes_x);
free(po->sizes_y);
free(po->size_types);
free(po->size_x_types);
free(po->size_y_types);
for (int j = 0; j < po->nr_textures; j++)
free(po->textures[j]);
free(po->textures);
free(po->polygon_name);
free(po->damages);
if (po->model)
model_free(po->model);
free(po->instances);
}
ht_foreach_value(effect->textures, free_billboard_texture);
ht_free(effect->textures);
free(effect->objects);
free(effect->path);
free(effect);
}
void particle_effect_calc_frame_range(struct particle_effect *effect, struct motion *motion)
{
int begin_frame = INT_MAX;
int end_frame = INT_MIN;
for (int i = 0; i < effect->nr_objects; i++) {
struct particle_object *po = &effect->objects[i];
int obj_begin = po->begin_frame;
int obj_end = max(po->end_frame, po->child_create_end_frame + po->child_frame);
if (begin_frame > obj_begin)
begin_frame = obj_begin;
if (end_frame < obj_end)
end_frame = obj_end;
}
motion->frame_begin = begin_frame;
motion->frame_end = end_frame;
}
void particle_effect_update(struct RE_instance *inst)
{
struct particle_effect *effect = inst->effect;
if (!effect)
return;
for (int i = 0; i < effect->nr_objects; i++) {
struct particle_object *po = &effect->objects[i];
// These cannot be precomputed, because they may depend on the target's
// *current* bone matrix.
eval_pos_for_object(inst, po->position[0], po->pos.begin);
eval_pos_for_object(inst, po->position[1], po->pos.end);
glm_vec3_sub(po->pos.end, po->pos.begin, po->move_vec);
switch (po->dir_type) {
case PARTICLE_DIR_TYPE_NORMAL:
glm_vec3_normalize(po->move_vec);
glm_vec3_ortho(po->move_vec, po->move_ortho);
glm_vec3_normalize(po->move_ortho);
break;
case PARTICLE_DIR_TYPE_XY_PLANE:
po->move_vec[2] = 0.0f;
glm_vec3_normalize(po->move_vec);
glm_vec3_copy(GLM_ZUP, po->move_ortho);
break;
}
}
}
static void calc_emitter_pos(struct particle_object *po, struct particle_instance *pi, float t, vec3 dest, vec3 move_vec)
{
vec3 begin, end;
glm_vec3_add(po->pos.begin, pi->random_offset.begin, begin);
glm_vec3_add(po->pos.end, pi->random_offset.end, end);
interpolate_pos(begin, end, po->move_curve, t, dest);
if (move_vec)
glm_vec3_sub(end, begin, move_vec);
// Orbital rotation.
vec3 angle, dist;
glm_vec3_lerp(po->revolution_angle.begin, po->revolution_angle.end, t, angle);
glm_vec3_lerp(po->revolution_distance.begin, po->revolution_distance.end, t, dist);
glm_vec3_scale(angle, GLM_PIf / 180.0f, angle); // deg -> rad
if (po->revolution_angle.begin[0] != 0.0f || po->revolution_angle.end[0] != 0.0f) {
dest[1] += dist[0] * sinf(angle[0]);
dest[2] -= dist[0] * cosf(angle[0]);
}
if (po->revolution_angle.begin[1] != 0.0f || po->revolution_angle.end[1] != 0.0f) {
dest[0] += dist[1] * sinf(angle[1]);
dest[2] -= dist[1] * cosf(angle[1]);
}
if (po->revolution_angle.begin[2] != 0.0f || po->revolution_angle.end[2] != 0.0f) {
dest[1] += dist[2] * sinf(angle[2]);
dest[0] += dist[2] * cosf(angle[2]);
}
// Apply the curve.
glm_vec3_muladds(po->curve_length, 4.9f * t * (1.0f - t), dest);
}
static void calc_pos_and_up_vector(struct particle_object *po, struct particle_instance *pi, float t, vec3 pos, vec3 up_vector)
{
switch (po->move_type) {
case PARTICLE_MOVE_FIXED:
glm_vec3_add(po->pos.begin, pi->random_offset.begin, pos);
glm_vec3_copy(GLM_YUP, up_vector);
break;
case PARTICLE_MOVE_LINEAR:
calc_emitter_pos(po, pi, t, pos, up_vector);
switch (po->up_vector_type) {
case PARTICLE_UP_VECTOR_Y_AXIS:
case PARTICLE_UP_VECTOR_INSTANCE_MOVE:
glm_vec3_copy(GLM_YUP, up_vector);
break;
case PARTICLE_UP_VECTOR_OBJECT_MOVE:
if (glm_vec3_norm2(up_vector) == 0.0f)
glm_vec3_copy(GLM_XUP, up_vector);
else
glm_vec3_normalize(up_vector);
break;
}
break;
case PARTICLE_MOVE_EMITTER:
{
// Calculate the instance's initial and final position.
float t0 = glm_clamp_zo(glm_percent(po->begin_frame, po->end_frame, pi->begin_frame));
vec3_range inst_pos;
calc_emitter_pos(po, pi, t0, inst_pos.begin, NULL);
vec3 inst_move;
glm_vec3_copy(po->move_vec, inst_move);
switch (po->dir_type) {
case PARTICLE_DIR_TYPE_NORMAL:
glm_vec3_rotate(inst_move, pi->roll_angle, po->move_ortho);
glm_vec3_rotate(inst_move, pi->pitch_angle, po->move_vec);
break;
case PARTICLE_DIR_TYPE_XY_PLANE:
glm_vec3_rotate(inst_move, (pi->pitch_angle < GLM_PIf ? -1.0f : 1.0f) * pi->roll_angle, po->move_ortho);
break;
}
glm_vec3_copy(inst_pos.begin, inst_pos.end);
switch (po->child_move_dir_type) {
case PARTICLE_CHILD_MOVE_DIR_NORMAL:
glm_vec3_muladds(inst_move, po->child_length, inst_pos.end);
break;
case PARTICLE_CHILD_MOVE_DIR_REVERSE:
glm_vec3_muladds(inst_move, po->child_length, inst_pos.begin);
glm_vec3_negate(inst_move);
break;
}
// Current position of the instance.
glm_vec3_lerp(inst_pos.begin, inst_pos.end, t, pos);
// Calculate the up vector.
switch (po->up_vector_type) {
case PARTICLE_UP_VECTOR_Y_AXIS:
glm_vec3_copy(GLM_YUP, up_vector);
break;
case PARTICLE_UP_VECTOR_OBJECT_MOVE:
glm_vec3_copy(po->move_vec, up_vector);
break;
case PARTICLE_UP_VECTOR_INSTANCE_MOVE:
if (po->child_length != 0.0f)
glm_vec3_copy(inst_move, up_vector);
else
glm_vec3_copy(GLM_XUP, up_vector);
break;
}
if (po->dir_type == PARTICLE_DIR_TYPE_XY_PLANE) {
mat3 rot = {{ inst_move[1], -inst_move[0], 0.0f },
{ inst_move[0], inst_move[1], 0.0f },
{ 0.0f, 0.0f, 1.0f }};
glm_mat3_mulv(rot, up_vector, up_vector);
}
}
break;
}
}
static void calc_scale(struct particle_object *po, int frame, vec3 dest)
{
glm_vec3_broadcast(po->sizes2[min(frame, po->nr_sizes2 - 1)], dest);
if (po->nr_sizes_x)
dest[0] *= po->sizes_x[min(frame, po->nr_sizes_x - 1)];
if (po->nr_sizes_y)
dest[1] *= po->sizes_y[min(frame, po->nr_sizes_y - 1)];
}
void particle_object_calc_local_transform(struct RE_instance *inst, struct particle_object *po, struct particle_instance *pi, float frame, mat4 dest)
{
float t = glm_percent(pi->begin_frame, pi->end_frame, frame);
vec3 pos, up_vector;
calc_pos_and_up_vector(po, pi, t, pos, up_vector);
float rel_frame = frame - pi->begin_frame;
int cur_step = rel_frame;
int next_step = ceilf(rel_frame);
float step_frac = rel_frame - cur_step;
glm_translate_make(dest, pos);
if (po->type == PARTICLE_TYPE_BILLBOARD) {
mat4 view_mat;
RE_calc_view_matrix(&inst->plugin->camera, up_vector, view_mat);
mat3 rot;
glm_mat4_pick3t(view_mat, rot);
glm_mat4_ins3(rot, dest);
}
vec3 angles;
glm_vec3_lerp(po->rotation.begin, po->rotation.end, t, angles);
angles[0] *= -GLM_PIf / 180.0f;
angles[1] *= -GLM_PIf / 180.0f;
angles[2] *= GLM_PIf / 180.0f;
mat4 rotation_mat;
glm_euler_zxy(angles, rotation_mat);
glm_mat4_mul(dest, rotation_mat, dest);
if (po->nr_sizes2 > 0) {
vec3 scale, scale2;
calc_scale(po, cur_step, scale);
calc_scale(po, next_step, scale2);
glm_vec3_lerp(scale, scale2, step_frac, scale);
glm_scale(dest, scale);
} else {
glm_scale_uni(dest, glm_lerp(po->size[0], po->size[1], t));
}
glm_translate_x(dest, -po->offset_x);
glm_translate_y(dest, -po->offset_y);
}
float particle_object_calc_alpha(struct particle_object *po, struct particle_instance *pi, float frame)
{
frame -= pi->begin_frame;
if (frame < po->alpha_fadein_frame)
return glm_clamp_zo(frame / po->alpha_fadein_frame);
float total_frames = pi->end_frame - pi->begin_frame;
if (total_frames - po->alpha_fadeout_frame < frame)
return glm_clamp_zo((total_frames - frame) / po->alpha_fadeout_frame);
return 1.0;
}
+60 -28
View File
@@ -37,12 +37,16 @@ static struct RE_instance *create_instance(struct RE_plugin *plugin)
{
struct RE_instance *instance = xcalloc(1, sizeof(struct RE_instance));
instance->plugin = plugin;
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
instance->target[i] = -1;
instance->scale[0] = 1.0;
instance->scale[1] = 1.0;
instance->scale[2] = 1.0;
instance->alpha = 1.0;
instance->draw_bump = true;
instance->fps = 30.0;
instance->column_height = 1.0;
instance->column_radius = 1.0;
glm_mat4_identity(instance->local_transform);
glm_mat3_identity(instance->normal_transform);
return instance;
@@ -106,6 +110,8 @@ static void update_motion(struct motion *m, float delta_frame)
if (!m || m->state == RE_MOTION_STATE_STOP)
return;
m->current_frame += delta_frame;
if (m->current_frame < m->frame_begin)
m->current_frame = m->frame_begin;
switch (m->state) {
case RE_MOTION_STATE_NOLOOP:
if (m->current_frame > m->frame_end) {
@@ -131,13 +137,9 @@ static void calc_motion_frame(struct motion *m, int bone, struct mot_frame *out)
interpolate_motion_frame(&frames[cur_frame], &frames[next_frame], t, out);
}
static void animate(struct RE_instance *inst, int elapsed_ms)
static void update_bones(struct RE_instance *inst)
{
float delta_frame = inst->fps * elapsed_ms / 1000.0;
update_motion(inst->motion, delta_frame);
update_motion(inst->next_motion, delta_frame);
if (inst->type != RE_ITYPE_SKINNED || !inst->motion)
if (!inst->motion)
return;
mat4 parent_transforms[MAX_BONES];
@@ -235,8 +237,24 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
return false;
for (int i = 0; i < plugin->nr_instances; i++) {
if (plugin->instances[i])
animate(plugin->instances[i], elapsed_ms);
struct RE_instance *inst = plugin->instances[i];
if (!inst)
continue;
float delta_frame = inst->fps * elapsed_ms / 1000.0;
update_motion(inst->motion, delta_frame);
update_motion(inst->next_motion, delta_frame);
if (inst->type == RE_ITYPE_SKINNED)
update_bones(inst);
}
// Update particle effects in a second pass, because it uses the results of
// update_bones().
for (int i = 0; i < plugin->nr_instances; i++) {
struct RE_instance *inst = plugin->instances[i];
if (inst && inst->type == RE_ITYPE_PARTICLE_EFFECT)
particle_effect_update(inst);
}
return true;
}
@@ -329,6 +347,11 @@ bool RE_instance_load(struct RE_instance *instance, const char *name)
return !!instance->model;
case RE_ITYPE_PARTICLE_EFFECT:
instance->effect = particle_effect_load(instance->plugin->aar, name);
if (!instance->motion) {
instance->motion = xcalloc(1, sizeof(struct motion));
instance->motion->instance = instance;
particle_effect_calc_frame_range(instance->effect, instance->motion);
}
return !!instance->effect;
default:
WARNING("Invalid instance type %d", instance->type);
@@ -573,9 +596,9 @@ int RE_particle_get_pos_unit_index(struct particle_object *po, int pos, int unit
return 0;
switch (u->type) {
case PARTICLE_POS_UNIT_BONE:
return u->bone.n;
return u->bone.target_index;
case PARTICLE_POS_UNIT_TARGET:
return u->target.n;
return u->target.index;
default:
return 0;
}
@@ -739,72 +762,72 @@ void RE_particle_get_x_rotation_angle(struct particle_object *po, float *begin_a
{
if (!po)
return;
*begin_angle = po->rotation[0][0];
*end_angle = po->rotation[1][0];
*begin_angle = po->rotation.begin[0];
*end_angle = po->rotation.end[0];
}
void RE_particle_get_y_rotation_angle(struct particle_object *po, float *begin_angle, float *end_angle)
{
if (!po)
return;
*begin_angle = po->rotation[0][1];
*end_angle = po->rotation[1][1];
*begin_angle = po->rotation.begin[1];
*end_angle = po->rotation.end[1];
}
void RE_particle_get_z_rotation_angle(struct particle_object *po, float *begin_angle, float *end_angle)
{
if (!po)
return;
*begin_angle = po->rotation[0][2];
*end_angle = po->rotation[1][2];
*begin_angle = po->rotation.begin[2];
*end_angle = po->rotation.end[2];
}
void RE_particle_get_x_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle)
{
if (!po)
return;
*begin_angle = po->revolution_angle[0][0];
*end_angle = po->revolution_angle[1][0];
*begin_angle = po->revolution_angle.begin[0];
*end_angle = po->revolution_angle.end[0];
}
void RE_particle_get_x_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance)
{
if (!po)
return;
*begin_distance = po->revolution_distance[0][0];
*end_distance = po->revolution_distance[1][0];
*begin_distance = po->revolution_distance.begin[0];
*end_distance = po->revolution_distance.end[0];
}
void RE_particle_get_y_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle)
{
if (!po)
return;
*begin_angle = po->revolution_angle[0][1];
*end_angle = po->revolution_angle[1][1];
*begin_angle = po->revolution_angle.begin[1];
*end_angle = po->revolution_angle.end[1];
}
void RE_particle_get_y_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance)
{
if (!po)
return;
*begin_distance = po->revolution_distance[0][1];
*end_distance = po->revolution_distance[1][1];
*begin_distance = po->revolution_distance.begin[1];
*end_distance = po->revolution_distance.end[1];
}
void RE_particle_get_z_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle)
{
if (!po)
return;
*begin_angle = po->revolution_angle[0][2];
*end_angle = po->revolution_angle[1][2];
*begin_angle = po->revolution_angle.begin[2];
*end_angle = po->revolution_angle.end[2];
}
void RE_particle_get_z_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance)
{
if (!po)
return;
*begin_distance = po->revolution_distance[0][2];
*end_distance = po->revolution_distance[1][2];
*begin_distance = po->revolution_distance.begin[2];
*end_distance = po->revolution_distance.end[2];
}
bool RE_particle_get_curve_length(struct particle_object *po, vec3 out)
@@ -877,6 +900,15 @@ float RE_particle_get_offset_y(struct particle_object *po)
return po ? po->offset_y : 0.0;
}
bool RE_effect_get_frame_range(struct RE_instance *instance, int *begin_frame, int *end_frame)
{
if (!instance || !instance->effect || !instance->motion)
return false;
*begin_frame = instance->motion->frame_begin;
*end_frame = instance->motion->frame_end;
return true;
}
bool RE_back_cg_set(struct RE_back_cg *bcg, int no)
{
if (!bcg)
+157 -12
View File
@@ -37,6 +37,12 @@ enum {
SHADOW_TEXTURE_UNIT,
};
enum {
DIFFUSE_EMISSIVE = 0,
DIFFUSE_NORMAL = 1,
DIFFUSE_LIGHT_MAP = 2,
};
static GLuint load_shader(const char *vertex_shader_path, const char *fragment_shader_path)
{
GLuint program = glCreateProgram();
@@ -108,10 +114,10 @@ static void init_billboard_mesh(struct RE_renderer *r)
{
static const GLfloat vertices[] = {
// x, y, z, u, v
-1.0, 2.0, 0.0, 0.0, 0.0,
-1.0, 0.0, 0.0, 0.0, 1.0,
1.0, 2.0, 0.0, 1.0, 0.0,
1.0, 0.0, 0.0, 1.0, 1.0,
-1.0, 1.0, 0.0, 0.0, 0.0,
-1.0, -1.0, 0.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0, 0.0,
1.0, -1.0, 0.0, 1.0, 1.0,
};
glGenVertexArrays(1, &r->billboard_vao);
glBindVertexArray(r->billboard_vao);
@@ -177,7 +183,7 @@ struct RE_renderer *RE_renderer_new(struct texture *texture)
r->rim_exponent = glGetUniformLocation(r->program, "rim_exponent");
r->rim_color = glGetUniformLocation(r->program, "rim_color");
r->camera_pos = glGetUniformLocation(r->program, "camera_pos");
r->use_light_map = glGetUniformLocation(r->program, "use_light_map");
r->diffuse_type = glGetUniformLocation(r->program, "diffuse_type");
r->light_texture = glGetUniformLocation(r->program, "light_texture");
r->use_normal_map = glGetUniformLocation(r->program, "use_normal_map");
r->normal_texture = glGetUniformLocation(r->program, "normal_texture");
@@ -224,6 +230,7 @@ bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
bt->has_alpha = cg->metrics.has_alpha;
cg_free(cg);
ht_put_int(r->billboard_textures, cg_no, bt);
@@ -248,7 +255,7 @@ void RE_renderer_free(struct RE_renderer *r)
free(r);
}
static void calc_view_matrix(struct RE_camera *camera, mat4 out)
void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out)
{
vec3 front = { 0.0, 0.0, -1.0 };
vec3 euler = {
@@ -259,7 +266,6 @@ static void calc_view_matrix(struct RE_camera *camera, mat4 out)
mat4 rot;
glm_euler(euler, rot);
glm_mat4_mulv3(rot, front, 0.0, front);
vec3 up = {0.0, 1.0, 0.0};
glm_look(camera->pos, front, up, out);
}
@@ -316,12 +322,12 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r)
glUniform1i(r->texture, COLOR_TEXTURE_UNIT);
if (material->light_map && inst->plugin->light_map_mode) {
glUniform1i(r->use_light_map, GL_TRUE);
glUniform1i(r->diffuse_type, DIFFUSE_LIGHT_MAP);
glActiveTexture(GL_TEXTURE0 + LIGHT_TEXTURE_UNIT);
glBindTexture(GL_TEXTURE_2D, material->light_map);
glUniform1i(r->light_texture, LIGHT_TEXTURE_UNIT);
} else {
glUniform1i(r->use_light_map, GL_FALSE);
glUniform1i(r->diffuse_type, DIFFUSE_NORMAL);
}
if (material->normal_map && inst->draw_bump && inst->plugin->bump_mode) {
@@ -342,12 +348,16 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r)
static void render_static_model(struct RE_instance *inst, struct RE_renderer *r)
{
if (!inst->draw)
return;
glUniform1i(r->has_bones, GL_FALSE);
render_model(inst, r);
}
static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r)
{
if (!inst->draw)
return;
struct model *model = inst->model;
if (!model)
return;
@@ -363,6 +373,8 @@ static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r
static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat)
{
if (!inst->draw)
return;
int cg_no = inst->motion->current_frame;
struct billboard_texture *bt = ht_get_int(r->billboard_textures, cg_no, NULL);
if (!bt)
@@ -374,6 +386,8 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma
glm_mat4_pick3t(view_mat, rot);
glm_mat4_ins3(rot, local_transform);
glm_scale(local_transform, inst->scale);
// Billboard instances are bottomed at y=0.
glm_translate_y(local_transform, 1.0);
mat3 normal_transform;
// This should be safe because billboards do not have non-uniform scaling.
glm_mat4_pick3(local_transform, normal_transform);
@@ -390,8 +404,9 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma
glUniform1f(r->specular_shininess, 0.0);
glUniform1i(r->use_specular_map, GL_FALSE);
glUniform1f(r->rim_exponent, 0.0);
glUniform1i(r->use_light_map, GL_FALSE);
glUniform1i(r->diffuse_type, DIFFUSE_NORMAL);
glUniform1i(r->use_normal_map, GL_FALSE);
glUniform1i(r->use_shadow_map, GL_FALSE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, bt->texture);
@@ -404,6 +419,133 @@ static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, ma
glBindTexture(GL_TEXTURE_2D, 0);
}
static void render_billboard_particles(struct RE_renderer *r, struct RE_instance *inst, struct particle_object *po, float frame)
{
if (po->nr_textures == 0)
return;
glUniform1i(r->diffuse_type, DIFFUSE_EMISSIVE);
glUniform1i(r->fog_type, 0);
glActiveTexture(GL_TEXTURE0);
glUniform1i(r->texture, 0);
glBindVertexArray(r->billboard_vao);
for (int index = 0; index < po->nr_particles; index++) {
struct particle_instance *pi = &po->instances[index];
if (frame < pi->begin_frame || pi->end_frame < frame)
continue;
int step = (int)((frame - pi->begin_frame) / po->texture_anime_frame);
struct billboard_texture *bt = ht_get(inst->effect->textures, po->textures[step % po->nr_textures], NULL);
if (!bt)
continue;
glBindTexture(GL_TEXTURE_2D, bt->texture);
float alpha = particle_object_calc_alpha(po, pi, frame);
if (po->blend_type == PARTICLE_BLEND_ADDITIVE && !bt->has_alpha)
alpha *= alpha; // why...
glUniform1f(r->alpha_mod, alpha);
mat4 local_transform;
particle_object_calc_local_transform(inst, po, pi, frame, local_transform);
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]);
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, local_transform[0]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glUniform1i(r->fog_type, inst->plugin->fog_type);
}
static void render_polygon_particles(struct RE_renderer *r, struct RE_instance *inst, struct particle_object *po, float frame)
{
struct model *model = po->model;
if (!model)
return;
glUniform1i(r->diffuse_type, DIFFUSE_NORMAL);
for (int index = 0; index < po->nr_particles; index++) {
struct particle_instance *pi = &po->instances[index];
if (frame < pi->begin_frame || pi->end_frame < frame)
continue;
float alpha = particle_object_calc_alpha(po, pi, frame);
glUniform1f(r->alpha_mod, alpha);
mat4 local_transform;
particle_object_calc_local_transform(inst, po, pi, frame, local_transform);
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]);
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, local_transform[0]);
for (int i = 0; i < model->nr_meshes; i++) {
struct mesh *mesh = &model->meshes[i];
struct material *material = &model->materials[mesh->material];
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, material->color_map);
glUniform1i(r->texture, 0);
glBindVertexArray(mesh->vao);
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
}
static void render_particle_effect(struct RE_instance *inst, struct RE_renderer *r)
{
// NOTE: inst->draw flag has no effect for particle effects.
if (!inst->effect)
return;
vec3 ambient;
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
glUniform3fv(r->ambient, 1, ambient);
glUniform1i(r->has_bones, GL_FALSE);
glUniform1f(r->specular_strength, 0.0);
glUniform1f(r->specular_shininess, 0.0);
glUniform1i(r->use_specular_map, GL_FALSE);
glUniform1f(r->rim_exponent, 0.0);
glUniform1i(r->use_normal_map, GL_FALSE);
glUniform1i(r->use_shadow_map, GL_FALSE);
glDepthMask(GL_FALSE);
for (int i = 0; i < inst->effect->nr_objects; i++) {
struct particle_object *po = &inst->effect->objects[i];
switch (po->blend_type) {
case PARTICLE_BLEND_NORMAL:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
break;
case PARTICLE_BLEND_ADDITIVE:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
break;
}
switch (po->type) {
case PARTICLE_TYPE_BILLBOARD:
render_billboard_particles(r, inst, po, inst->motion->current_frame);
break;
case PARTICLE_TYPE_POLYGON_OBJECT:
render_polygon_particles(r, inst, po, inst->motion->current_frame);
break;
case PARTICLE_TYPE_SWORD_BLUR: // unused
break;
}
}
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_TRUE);
}
static bool calc_shadow_light_transform(struct RE_plugin *plugin, mat4 dest)
{
// Compute AABB of shadow casters.
@@ -579,7 +721,7 @@ void RE_render(struct sact_sprite *sp)
glEnable(GL_CULL_FACE);
mat4 view_transform;
calc_view_matrix(&plugin->camera, view_transform);
RE_calc_view_matrix(&plugin->camera, GLM_YUP, view_transform);
// Tweak the projection transform so that the rendering result is vertically
// flipped. If we render the scene normally, the resulting image will be
@@ -621,7 +763,7 @@ void RE_render(struct sact_sprite *sp)
for (int i = 0; i < plugin->nr_instances; i++) {
struct RE_instance *inst = plugin->instances[i];
if (!inst || !inst->draw)
if (!inst)
continue;
switch (inst->type) {
case RE_ITYPE_STATIC:
@@ -633,6 +775,9 @@ void RE_render(struct sact_sprite *sp)
case RE_ITYPE_BILLBOARD:
render_billboard(inst, r, view_transform);
break;
case RE_ITYPE_PARTICLE_EFFECT:
render_particle_effect(inst, r);
break;
default:
break;
}
+95 -16
View File
@@ -482,22 +482,97 @@ static bool ReignEngine_FreeInstanceNextMotion(int plugin, int instance)
//bool ReignEngine_SaveInstanceAddMaterialData(int plugin, int instance);
//bool ReignEngine_SetInstancePointPos(int plugin, int instance, int index, float x, float y, float z);
//bool ReignEngine_GetInstancePointPos(int plugin, int instance, int index, float *pfX, float *pfY, float *pfZ);
//bool ReignEngine_GetInstanceColumnPos(int plugin, int instance, float *fX, float *fY, float *fZ);
//float ReignEngine_GetInstanceColumnHeight(int plugin, int instance);
//float ReignEngine_GetInstanceColumnRadius(int plugin, int instance);
//float ReignEngine_GetInstanceColumnAngle(int plugin, int instance);
static bool ReignEngine_GetInstanceColumnPos(int plugin, int instance, float *x, float *y, float *z)
{
struct RE_instance *ri = get_instance(plugin, instance);
if (!ri)
return false;
*x = ri->column_pos[0];
*y = ri->column_pos[1];
*z = -ri->column_pos[2];
return true;
}
static float ReignEngine_GetInstanceColumnHeight(int plugin, int instance)
{
struct RE_instance *ri = get_instance(plugin, instance);
return ri ? ri->column_height : 0.0;
}
static float ReignEngine_GetInstanceColumnRadius(int plugin, int instance)
{
struct RE_instance *ri = get_instance(plugin, instance);
return ri ? ri->column_radius : 0.0;
}
static float ReignEngine_GetInstanceColumnAngle(int plugin, int instance)
{
struct RE_instance *ri = get_instance(plugin, instance);
return ri ? ri->column_angle : 0.0;
}
//float ReignEngine_GetInstanceColumnAngleP(int plugin, int instance);
//float ReignEngine_GetInstanceColumnAngleB(int plugin, int instance);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceColumnPos, int plugin, int instance, float x, float y, float z);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceColumnHeight, int plugin, int instance, float height);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceColumnRadius, int plugin, int instance, float radius);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceColumnAngle, int plugin, int instance, float angle);
static bool ReignEngine_SetInstanceColumnPos(int plugin, int instance, float x, float y, float z)
{
struct RE_instance *ri = get_instance(plugin, instance);
if (!ri)
return false;
ri->column_pos[0] = x;
ri->column_pos[1] = y;
ri->column_pos[2] = -z;
return true;
}
static bool ReignEngine_SetInstanceColumnHeight(int plugin, int instance, float height)
{
struct RE_instance *ri = get_instance(plugin, instance);
if (!ri)
return false;
ri->column_height = height;
return true;
}
static bool ReignEngine_SetInstanceColumnRadius(int plugin, int instance, float radius)
{
struct RE_instance *ri = get_instance(plugin, instance);
if (!ri)
return false;
ri->column_radius = radius;
return true;
}
static bool ReignEngine_SetInstanceColumnAngle(int plugin, int instance, float angle)
{
struct RE_instance *ri = get_instance(plugin, instance);
if (!ri)
return false;
ri->column_angle = angle;
return true;
}
//bool ReignEngine_SetInstanceColumnAngleP(int plugin, int instance, float fAngleP);
//bool ReignEngine_SetInstanceColumnAngleB(int plugin, int instance, float fAngleB);
//bool ReignEngine_GetInstanceDrawColumn(int plugin, int instance);
//bool ReignEngine_SetInstanceDrawColumn(int plugin, int instance, bool bDraw);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceTarget, int plugin, int instance, int index, int target);
//int ReignEngine_GetInstanceTarget(int plugin, int instance, int index);
static bool ReignEngine_SetInstanceTarget(int plugin, int instance, int index, int target)
{
struct RE_instance *ri = get_instance(plugin, instance);
if (!ri || index < 0 || index >= RE_NR_INSTANCE_TARGETS)
return false;
ri->target[index] = target;
return true;
}
static int ReignEngine_GetInstanceTarget(int plugin, int instance, int index)
{
struct RE_instance *ri = get_instance(plugin, instance);
if (!ri || index < 0 || index >= RE_NR_INSTANCE_TARGETS)
return -1;
return ri->target[index];
}
static float ReignEngine_GetInstanceFPS(int plugin, int instance)
{
@@ -561,7 +636,11 @@ static bool ReignEngine_SetInstanceShadowVolumeBoneRadius(int plugin, int instan
//bool ReignEngine_GetInstanceDebugDrawShadowVolume(int plugin, int instance);
//bool ReignEngine_SetInstanceDebugDrawShadowVolume(int plugin, int instance, bool flag);
//bool ReignEngine_SaveEffect(int plugin, int instance);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, GetEffectFrameRange, int plugin, int instance, int *begin_frame, int *end_frame);
static bool ReignEngine_GetEffectFrameRange(int plugin, int instance, int *begin_frame, int *end_frame)
{
return RE_effect_get_frame_range(get_instance(plugin, instance), begin_frame, end_frame);
}
static int ReignEngine_GetEffectObjectType(int plugin, int instance, int object)
{
@@ -1765,10 +1844,10 @@ HLL_LIBRARY(ReignEngine,
HLL_TODO_EXPORT(SaveInstanceAddMaterialData, ReignEngine_SaveInstanceAddMaterialData),
HLL_TODO_EXPORT(SetInstancePointPos, ReignEngine_SetInstancePointPos),
HLL_TODO_EXPORT(GetInstancePointPos, ReignEngine_GetInstancePointPos),
HLL_TODO_EXPORT(GetInstanceColumnPos, ReignEngine_GetInstanceColumnPos),
HLL_TODO_EXPORT(GetInstanceColumnHeight, ReignEngine_GetInstanceColumnHeight),
HLL_TODO_EXPORT(GetInstanceColumnRadius, ReignEngine_GetInstanceColumnRadius),
HLL_TODO_EXPORT(GetInstanceColumnAngle, ReignEngine_GetInstanceColumnAngle),
HLL_EXPORT(GetInstanceColumnPos, ReignEngine_GetInstanceColumnPos),
HLL_EXPORT(GetInstanceColumnHeight, ReignEngine_GetInstanceColumnHeight),
HLL_EXPORT(GetInstanceColumnRadius, ReignEngine_GetInstanceColumnRadius),
HLL_EXPORT(GetInstanceColumnAngle, ReignEngine_GetInstanceColumnAngle),
HLL_TODO_EXPORT(GetInstanceColumnAngleP, ReignEngine_GetInstanceColumnAngleP),
HLL_TODO_EXPORT(GetInstanceColumnAngleB, ReignEngine_GetInstanceColumnAngleB),
HLL_EXPORT(SetInstanceColumnPos, ReignEngine_SetInstanceColumnPos),
@@ -1780,7 +1859,7 @@ HLL_LIBRARY(ReignEngine,
HLL_TODO_EXPORT(GetInstanceDrawColumn, ReignEngine_GetInstanceDrawColumn),
HLL_TODO_EXPORT(SetInstanceDrawColumn, ReignEngine_SetInstanceDrawColumn),
HLL_EXPORT(SetInstanceTarget, ReignEngine_SetInstanceTarget),
HLL_TODO_EXPORT(GetInstanceTarget, ReignEngine_GetInstanceTarget),
HLL_EXPORT(GetInstanceTarget, ReignEngine_GetInstanceTarget),
HLL_EXPORT(GetInstanceFPS, ReignEngine_GetInstanceFPS),
HLL_EXPORT(SetInstanceFPS, ReignEngine_SetInstanceFPS),
HLL_EXPORT(GetInstanceBoneIndex, ReignEngine_GetInstanceBoneIndex),