diff --git a/include/reign.h b/include/reign.h index 801090c..90c30ad 100644 --- a/include/reign.h +++ b/include/reign.h @@ -105,6 +105,7 @@ struct RE_instance { struct model *model; struct motion *motion; struct motion *next_motion; + struct height_detector *height_detector; enum RE_instance_type type; vec3 pos; @@ -150,6 +151,7 @@ bool RE_instance_free_next_motion(struct RE_instance *instance); bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z); int RE_instance_get_bone_index(struct RE_instance *instance, const char *name); bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instance, int bone, vec3 offset, vec3 out); +float RE_instance_calc_height(struct RE_instance *instance, float x, float z); int RE_motion_get_state(struct motion *motion); bool RE_motion_set_state(struct motion *motion, int state); diff --git a/shaders/reign_shadow.f.glsl b/shaders/reign_shadow.f.glsl index 5a1cfa4..4290140 100644 --- a/shaders/reign_shadow.f.glsl +++ b/shaders/reign_shadow.f.glsl @@ -14,5 +14,8 @@ * along with this program; if not, see . */ +out uvec4 frag_color; + void main() { + frag_color.r = uint(gl_FragCoord.z * 65535.0); } diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 4172887..384afca 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -163,6 +163,9 @@ void motion_free(struct motion *motion); struct RE_renderer *RE_renderer_new(struct texture *texture); void RE_renderer_free(struct RE_renderer *r); 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); // parser.c diff --git a/src/3d/reign.c b/src/3d/reign.c index 92fe549..db9d236 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -66,6 +66,10 @@ static void unload_instance(struct RE_instance *instance) free(instance->bone_transforms); instance->bone_transforms = NULL; } + if (instance->height_detector) { + RE_renderer_free_height_detector(instance->height_detector); + instance->height_detector = NULL; + } } static void free_instance(struct RE_instance *instance) @@ -374,6 +378,17 @@ bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instan return true; } +float RE_instance_calc_height(struct RE_instance *instance, float x, float z) +{ + if (!instance || !instance->model) + return 0.0; + if (!instance->height_detector) { + instance->height_detector = RE_renderer_create_height_detector(instance->plugin->renderer, instance->model); + } + float y = RE_renderer_detect_height(instance->height_detector, x, z); + return y; +} + void RE_instance_update_local_transform(struct RE_instance *inst) { vec3 euler = { diff --git a/src/3d/renderer.c b/src/3d/renderer.c index 0c6511f..e36eda9 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -620,3 +620,97 @@ void RE_render(struct sact_sprite *sp) glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo); } + +struct height_detector { + GLushort *buf; + vec3 aabb[2]; +}; + +struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r, struct model *model) +{ + struct height_detector *hd = xcalloc(1, sizeof(struct height_detector)); + vec3 aabb[2]; + memcpy(aabb, model->aabb, sizeof(aabb)); + // Add some Y padding. + aabb[0][1] -= 1.0; + aabb[1][1] += 1.0; + memcpy(hd->aabb, aabb, sizeof(aabb)); + + // Set up an orthographic projection matrix looking down the instance from + // right above. + mat4 view_matrix, proj_matrix; + glm_mat4_identity(view_matrix); + glm_rotate_x(view_matrix, glm_rad(90.0), view_matrix); + glm_aabb_transform(aabb, view_matrix, aabb); + glm_ortho_aabb(aabb, proj_matrix); + + GLint orig_fbo, orig_viewport[4]; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &orig_fbo); + glGetIntegerv(GL_VIEWPORT, orig_viewport); + + // Since OpenGL ES does not allow reading from depth textures, create a + // color texture and render depth values into it. + GLuint color_texture; + glGenTextures(1, &color_texture); + glBindTexture(GL_TEXTURE_2D, color_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, NULL); + + glBindFramebuffer(GL_FRAMEBUFFER, r->shadow.fbo); + GLenum draw_buffers[1] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, draw_buffers); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_texture, 0); + + glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); + glClear(GL_DEPTH_BUFFER_BIT); + glUseProgram(r->shadow.program); + glUniformMatrix4fv(r->shadow.world_transform, 1, GL_FALSE, view_matrix[0]); + glUniformMatrix4fv(r->shadow.view_transform, 1, GL_FALSE, proj_matrix[0]); + glUniform1i(r->shadow.has_bones, GL_FALSE); + + glEnable(GL_DEPTH_TEST); + + // Render the model. + for (int i = 0; i < model->nr_meshes; i++) { + struct mesh *mesh = &model->meshes[i]; + glBindVertexArray(mesh->vao); + glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices); + } + glBindVertexArray(0); + glFinish(); + + hd->buf = xmalloc(SHADOW_WIDTH * SHADOW_HEIGHT * sizeof(GLushort)); + glReadPixels(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT, GL_RED_INTEGER, GL_UNSIGNED_SHORT, hd->buf); + + glDisable(GL_DEPTH_TEST); + glDrawBuffers(0, NULL); + glReadBuffer(GL_NONE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, orig_fbo); + glViewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]); + + glDeleteTextures(1, &color_texture); + return hd; +} + +void RE_renderer_free_height_detector(struct height_detector *hd) +{ + free(hd->buf); + free(hd); +} + +float RE_renderer_detect_height(struct height_detector *hd, float x, float z) +{ + float minx = hd->aabb[0][0]; + float maxx = hd->aabb[1][0]; + float miny = hd->aabb[0][1]; + float maxy = hd->aabb[1][1]; + float minz = hd->aabb[0][2]; + float maxz = hd->aabb[1][2]; + if (x < minx || x > maxx || z < minz || z > maxz) + return 0.0; + int px = glm_percent(minx, maxx, x) * SHADOW_WIDTH; + int pz = glm_percent(maxz, minz, z) * SHADOW_HEIGHT; + float py = hd->buf[pz * SHADOW_WIDTH + px] / 65535.0; + return glm_lerp(maxy, miny, py); +} diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index 1ad1a89..d89ca8f 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -523,7 +523,12 @@ static bool ReignEngine_TransInstanceLocalPosToWorldPosByBone(int plugin, int in //int ReignEngine_GetInstanceTextureMemorySize(int plugin, int instance); //void ReignEngine_GetInstanceInfoText(int plugin, int instance, struct string **pIText); //void ReignEngine_GetInstanceMaterialInfoText(int plugin, int instance, struct string **pIText); -HLL_WARN_UNIMPLEMENTED(0.0, float, ReignEngine, CalcInstanceHeightDetection, int plugin, int instance, float x, float z); + +static float ReignEngine_CalcInstanceHeightDetection(int plugin, int instance, float x, float z) +{ + return RE_instance_calc_height(get_instance(plugin, instance), x, -z); +} + //float ReignEngine_GetInstanceSoftFogEdgeLength(int plugin, int instance); HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceSoftFogEdgeLength, int plugin, int instance, float length);