From 4d370b072ccb6834d6e251c0c2b5d3cac8cc4f94 Mon Sep 17 00:00:00 2001 From: LifeKILLED Date: Sun, 22 Jan 2023 03:45:29 +0400 Subject: [PATCH] vk rt denoiser: add motion vectors --- ref_vk/shaders/denoiser.comp | 6 ++ ref_vk/shaders/ray_common.glsl | 1 + ref_vk/shaders/ray_interop.h | 4 ++ ref_vk/shaders/ray_kusochki.glsl | 1 + ref_vk/shaders/ray_primary.rchit | 1 + ref_vk/shaders/ray_primary.rgen | 1 + ref_vk/shaders/ray_primary_common.glsl | 1 + ref_vk/shaders/rt_geometry.glsl | 8 +++ ref_vk/vk_brush.c | 46 ++++++++++++++ ref_vk/vk_geometry.h | 1 + ref_vk/vk_ray_model.c | 3 + ref_vk/vk_render.c | 8 +++ ref_vk/vk_render.h | 5 ++ ref_vk/vk_rtx.c | 10 ++- ref_vk/vk_studio.c | 86 ++++++++++++++++++++++---- 15 files changed, 168 insertions(+), 14 deletions(-) diff --git a/ref_vk/shaders/denoiser.comp b/ref_vk/shaders/denoiser.comp index 293e9718..59a789a9 100644 --- a/ref_vk/shaders/denoiser.comp +++ b/ref_vk/shaders/denoiser.comp @@ -17,6 +17,9 @@ layout(set = 0, binding = 4, rgba16f) uniform readonly image2D light_point_diffu layout(set = 0, binding = 5, rgba16f) uniform readonly image2D light_point_specular; layout(set = 0, binding = 6, rgba16f) uniform readonly image2D emissive; +layout(set = 0, binding = 7, rgba32f) uniform readonly image2D position_t; +layout(set = 0, binding = 8, rgba32f) uniform readonly image2D prev_position_t; + //layout(set = 0, binding = 7, rgba32f) uniform readonly image2D position_t; //layout(set = 0, binding = 8, rgba16f) uniform readonly image2D normals_gs; @@ -204,6 +207,9 @@ void main() { } #endif + // DEBUG motion vectors + //colour = vec3(length(imageLoad(position_t, pix).rgb - imageLoad(prev_position_t, pix).rgb)); + imageStore(out_dest, pix, vec4(colour, 0.)); //imageStore(out_dest, pix, imageLoad(light_poly, pix)); } diff --git a/ref_vk/shaders/ray_common.glsl b/ref_vk/shaders/ray_common.glsl index 652317a8..bf9ad41f 100644 --- a/ref_vk/shaders/ray_common.glsl +++ b/ref_vk/shaders/ray_common.glsl @@ -7,6 +7,7 @@ struct RayPayloadOpaque { float t_offset, pixel_cone_spread_angle; vec4 hit_pos_t; + vec4 prev_pos_t; vec3 normal; vec3 geometry_normal; vec3 base_color; diff --git a/ref_vk/shaders/ray_interop.h b/ref_vk/shaders/ray_interop.h index f0cd2622..64de99ea 100644 --- a/ref_vk/shaders/ray_interop.h +++ b/ref_vk/shaders/ray_interop.h @@ -18,6 +18,7 @@ X(12, normals_gs, rgba16f) \ X(13, material_rmxx, rgba8) \ X(14, emissive, rgba16f) \ + X(15, prev_position_t, rgba32f) \ #define RAY_LIGHT_DIRECT_INPUTS(X) \ X(10, position_t, rgba32f) \ @@ -103,6 +104,8 @@ struct Kusok { float roughness; float metalness; PAD(2) + + mat4 prev_transform; }; struct PointLight { @@ -153,6 +156,7 @@ struct PushConstants { struct UniformBuffer { mat4 inv_proj, inv_view; + mat4 prev_inv_proj, prev_inv_view; float ray_cone_width; uint random_seed; PAD(2) diff --git a/ref_vk/shaders/ray_kusochki.glsl b/ref_vk/shaders/ray_kusochki.glsl index 1b016ff1..9ae612b9 100644 --- a/ref_vk/shaders/ray_kusochki.glsl +++ b/ref_vk/shaders/ray_kusochki.glsl @@ -9,6 +9,7 @@ struct Vertex { vec3 pos; + vec3 prev_pos; vec3 normal; vec3 tangent; vec2 gl_tc; diff --git a/ref_vk/shaders/ray_primary.rchit b/ref_vk/shaders/ray_primary.rchit index c2073d47..4e411b6e 100644 --- a/ref_vk/shaders/ray_primary.rchit +++ b/ref_vk/shaders/ray_primary.rchit @@ -27,6 +27,7 @@ void main() { Geometry geom = readHitGeometry(); payload.hit_t = vec4(geom.pos, gl_HitTEXT); + payload.prev_pos_t = vec4(geom.prev_pos, 0.); const Kusok kusok = getKusok(geom.kusok_index); const uint tex_base_color = kusok.tex_base_color; diff --git a/ref_vk/shaders/ray_primary.rgen b/ref_vk/shaders/ray_primary.rgen index 117268e1..8639f481 100644 --- a/ref_vk/shaders/ray_primary.rgen +++ b/ref_vk/shaders/ray_primary.rgen @@ -40,4 +40,5 @@ void main() { imageStore(out_normals_gs, ivec2(gl_LaunchIDEXT.xy), payload.normals_gs); imageStore(out_material_rmxx, ivec2(gl_LaunchIDEXT.xy), payload.material_rmxx); imageStore(out_emissive, ivec2(gl_LaunchIDEXT.xy), payload.emissive); + imageStore(out_prev_position_t, ivec2(gl_LaunchIDEXT.xy), payload.prev_pos_t); } diff --git a/ref_vk/shaders/ray_primary_common.glsl b/ref_vk/shaders/ray_primary_common.glsl index fd593ee0..4611b843 100644 --- a/ref_vk/shaders/ray_primary_common.glsl +++ b/ref_vk/shaders/ray_primary_common.glsl @@ -6,6 +6,7 @@ struct RayPayloadPrimary { vec4 hit_t; + vec4 prev_pos_t; vec4 base_color_a; vec4 normals_gs; vec4 material_rmxx; diff --git a/ref_vk/shaders/rt_geometry.glsl b/ref_vk/shaders/rt_geometry.glsl index f232035e..e1980b6d 100644 --- a/ref_vk/shaders/rt_geometry.glsl +++ b/ref_vk/shaders/rt_geometry.glsl @@ -40,6 +40,7 @@ vec4 computeAnisotropicEllipseAxes(in vec3 P, in vec3 f, struct Geometry { vec3 pos; + vec3 prev_pos; vec2 uv; vec4 uv_lods; @@ -69,6 +70,12 @@ Geometry readHitGeometry() { gl_ObjectToWorldEXT * vec4(getVertex(vi3).pos, 1.f), }; + const vec3 prev_pos[3] = { + (kusok.prev_transform * vec4(getVertex(vi1).prev_pos, 1.f)).xyz, + (kusok.prev_transform * vec4(getVertex(vi2).prev_pos, 1.f)).xyz, + (kusok.prev_transform * vec4(getVertex(vi3).prev_pos, 1.f)).xyz, + }; + const vec2 uvs[3] = { getVertex(vi1).gl_tc, getVertex(vi2).gl_tc, @@ -76,6 +83,7 @@ Geometry readHitGeometry() { }; geom.pos = baryMix(pos[0], pos[1], pos[2], bary); + geom.prev_pos = baryMix(prev_pos[0], prev_pos[1], prev_pos[2], bary); geom.uv = baryMix(uvs[0], uvs[1], uvs[2], bary); //TODO or not TODO? const vec2 texture_uv = texture_uv_stationary + push_constants.time * kusok.uv_speed; diff --git a/ref_vk/vk_brush.c b/ref_vk/vk_brush.c index 43d9ed0a..cdfd59ed 100644 --- a/ref_vk/vk_brush.c +++ b/ref_vk/vk_brush.c @@ -33,6 +33,16 @@ static struct { int rtable[MOD_FRAMES][MOD_FRAMES]; } g_brush; + +#define MAX_BRUSH_ENTITIES_PREV_STATES 1024 + +typedef struct { + matrix4x4 prev_model_transform; + float prev_time; +} brush_entity_prev_state_t; + +static brush_entity_prev_state_t g_brush_prev_states[MAX_BRUSH_ENTITIES_PREV_STATES]; + void VK_InitRandomTable( void ) { int tu, tv; @@ -82,6 +92,7 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo { const float time = gpGlobals->time; float *v, nv, waveHeight; + float prev_nv, prev_time; float s, t, os, ot; glpoly_t *p; int i; @@ -90,6 +101,11 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo uint16_t *indices; r_geometry_buffer_lock_t buffer; + if (ent->index < MAX_BRUSH_ENTITIES_PREV_STATES) { + prev_time = g_brush_prev_states[ent->index].prev_time; + g_brush_prev_states[ent->index].prev_time = time; + } else gEngine.Con_Printf(S_ERROR "Brush entities previous frame states pool is overflow, increase it. Index is %s\n", ent->index ); + #define MAX_WATER_VERTICES 16 vk_vertex_t poly_vertices[MAX_WATER_VERTICES]; @@ -135,6 +151,10 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo nv = r_turbsin[(int)(time * 160.0f + v[1] + v[0]) & 255] + 8.0f; nv = (r_turbsin[(int)(v[0] * 5.0f + time * 171.0f - v[1]) & 255] + 8.0f ) * 0.8f + nv; nv = nv * waveHeight + v[2]; + + prev_nv = r_turbsin[(int)(prev_time * 160.0f + v[1] + v[0]) & 255] + 8.0f; + prev_nv = (r_turbsin[(int)(v[0] * 5.0f + prev_time * 171.0f - v[1]) & 255] + 8.0f ) * 0.8f + prev_nv; + prev_nv = prev_nv * waveHeight + v[2]; } else nv = v[2]; @@ -151,6 +171,10 @@ static void EmitWaterPolys( const cl_entity_t *ent, const msurface_t *warp, qboo poly_vertices[i].pos[1] = v[1]; poly_vertices[i].pos[2] = nv; + poly_vertices[i].prev_pos[0] = v[0]; + poly_vertices[i].prev_pos[1] = v[1]; + poly_vertices[i].prev_pos[2] = prev_nv; + poly_vertices[i].gl_tc[0] = s; poly_vertices[i].gl_tc[1] = t; @@ -265,9 +289,17 @@ void XVK_DrawWaterSurfaces( const cl_entity_t *ent ) EmitWaterPolys( ent, surf, false ); } + int entity_id = ent->index; + + if (entity_id < MAX_BRUSH_ENTITIES_PREV_STATES) + Matrix4x4_Copy( *VK_RenderGetLastFrameTransform(), g_brush_prev_states[entity_id].prev_model_transform ); + // submit as dynamic model VK_RenderModelDynamicCommit(); + if (entity_id < MAX_BRUSH_ENTITIES_PREV_STATES) + Matrix4x4_Copy( g_brush_prev_states[entity_id].prev_model_transform, *VK_RenderGetLastFrameTransform() ); + // TODO: // - upload water geometry only once, animate in compute/vertex shader } @@ -360,8 +392,18 @@ void VK_BrushModelDraw( const cl_entity_t *ent, int render_mode, const matrix4x4 } } + int entity_id = ent->index; + if (entity_id < MAX_BRUSH_ENTITIES_PREV_STATES) { + Matrix4x4_Copy( bmodel->render_model.prev_transform, + g_brush_prev_states[entity_id].prev_model_transform ); + } else gEngine.Con_Printf(S_ERROR "Brush entities previous frame states pool is overflow, increase it. Index is %s\n", ent->index ); + bmodel->render_model.render_mode = render_mode; VK_RenderModelDraw(ent, &bmodel->render_model); + + if (entity_id >= 0 && entity_id < MAX_BRUSH_ENTITIES_PREV_STATES) { + Matrix4x4_Copy( g_brush_prev_states[entity_id].prev_model_transform, bmodel->render_model.prev_transform ); + } } static qboolean renderableSurface( const msurface_t *surf, int i ) { @@ -576,6 +618,10 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) { {in_vertex->position[0], in_vertex->position[1], in_vertex->position[2]}, }; + vertex.prev_pos[0] = in_vertex->position[0]; + vertex.prev_pos[1] = in_vertex->position[1]; + vertex.prev_pos[2] = in_vertex->position[2]; + float s = DotProduct( in_vertex->position, surf->texinfo->vecs[0] ) + surf->texinfo->vecs[0][3]; float t = DotProduct( in_vertex->position, surf->texinfo->vecs[1] ) + surf->texinfo->vecs[1][3]; diff --git a/ref_vk/vk_geometry.h b/ref_vk/vk_geometry.h index 41165d2b..24388443 100644 --- a/ref_vk/vk_geometry.h +++ b/ref_vk/vk_geometry.h @@ -15,6 +15,7 @@ typedef struct vk_vertex_s { // TODO padding needed for storage buffer reading, figure out how to fix in GLSL/SPV side vec3_t pos; float p0_; + vec3_t prev_pos; float p01_; vec3_t normal; uint32_t flags; vec3_t tangent; uint32_t p1_; vec2_t gl_tc; //float p2_[2]; diff --git a/ref_vk/vk_ray_model.c b/ref_vk/vk_ray_model.c index e360d986..b0586b11 100644 --- a/ref_vk/vk_ray_model.c +++ b/ref_vk/vk_ray_model.c @@ -301,6 +301,7 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) { const vec3_t color = {1, 1, 1}; applyMaterialToKusok(kusochki + i, mg, color, false); + Matrix4x4_LoadIdentity(kusochki[i].prev_transform); } R_VkStagingUnlock(kusok_staging.handle); @@ -493,6 +494,8 @@ void VK_RayFrameAddModel( vk_ray_model_t *model, const vk_render_model_t *render for (int i = 0; i < render_model->num_geometries; ++i) { const vk_render_geometry_t *geom = render_model->geometries + i; applyMaterialToKusok(kusochki + i, geom, color, HACK_reflective); + + Matrix4x4_Copy((kusochki + i)->prev_transform, render_model->prev_transform); } /* gEngine.Con_Reportf("model %s: geom=%d kuoffs=%d kustoff=%d kustsz=%d sthndl=%d\n", */ diff --git a/ref_vk/vk_render.c b/ref_vk/vk_render.c index ac7df43e..7271db89 100644 --- a/ref_vk/vk_render.c +++ b/ref_vk/vk_render.c @@ -87,6 +87,7 @@ static qboolean createPipelines( void ) {.binding = 0, .location = 3, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(vk_vertex_t, lm_tc)}, {.binding = 0, .location = 4, .format = VK_FORMAT_R8G8B8A8_UNORM, .offset = offsetof(vk_vertex_t, color)}, {.binding = 0, .location = 5, .format = VK_FORMAT_R32_UINT, .offset = offsetof(vk_vertex_t, flags)}, + {.binding = 0, .location = 6, .format = VK_FORMAT_R32G32B32_SFLOAT, .offset = offsetof(vk_vertex_t, prev_pos)}, }; const vk_shader_stage_t shader_stages[] = { @@ -682,6 +683,9 @@ void VK_RenderModelDraw( const cl_entity_t *ent, vk_render_model_t* model ) { if (g_render_state.current_frame_is_ray_traced) { VK_RayFrameAddModel(model->ray_model, model, (const matrix3x4*)g_render_state.model, g_render_state.dirty_uniform_data.color, ent ? ent->curstate.rendercolor : (color24){255,255,255}); + + // store current transform here before it puts to entity history + Matrix4x4_Copy( model->prev_transform, g_render_state.model ); return; } @@ -747,6 +751,10 @@ static struct { vk_render_geometry_t geometries[MAX_DYNAMIC_GEOMETRY]; } g_dynamic_model = {0}; +matrix4x4 *VK_RenderGetLastFrameTransform() { + return &g_dynamic_model.model.prev_transform; +} + void VK_RenderModelDynamicBegin( int render_mode, const char *debug_name_fmt, ... ) { va_list argptr; va_start( argptr, debug_name_fmt ); diff --git a/ref_vk/vk_render.h b/ref_vk/vk_render.h index 5f3323fb..0bdf4f24 100644 --- a/ref_vk/vk_render.h +++ b/ref_vk/vk_render.h @@ -82,6 +82,9 @@ typedef struct vk_render_model_s { struct vk_ray_model_s *ray_model; struct rt_light_add_polygon_s *polylights; int polylights_count; + + // previous frame ObjectToWorld (model) matrix + matrix4x4 prev_transform; } vk_render_model_t; qboolean VK_RenderModelInit( vk_render_model_t* model ); @@ -100,3 +103,5 @@ void VK_RenderEnd( VkCommandBuffer cmdbuf ); void VK_RenderEndRTX( VkCommandBuffer cmdbuf, VkImageView img_dst_view, VkImage img_dst, uint32_t w, uint32_t h ); void VK_Render_FIXME_Barrier( VkCommandBuffer cmdbuf ); + +matrix4x4* VK_RenderGetLastFrameTransform(); diff --git a/ref_vk/vk_rtx.c b/ref_vk/vk_rtx.c index 51261609..e873177d 100644 --- a/ref_vk/vk_rtx.c +++ b/ref_vk/vk_rtx.c @@ -120,6 +120,13 @@ static void prepareUniformBuffer( const vk_ray_frame_render_args_t *args, int fr Matrix4x4_Invert_Full(view_inv, *args->view); Matrix4x4_ToArrayFloatGL(view_inv, (float*)ubo->inv_view); + // last frame matrices + static matrix4x4 prev_inv_proj, prev_inv_view; + Matrix4x4_ToArrayFloatGL(prev_inv_proj, (float*)ubo->prev_inv_proj); + Matrix4x4_ToArrayFloatGL(prev_inv_view, (float*)ubo->prev_inv_view); + Matrix4x4_Copy(prev_inv_view, view_inv); + Matrix4x4_Copy(prev_inv_proj, proj_inv); + ubo->ray_cone_width = atanf((2.0f*tanf(DEG2RAD(fov_angle_y) * 0.5f)) / (float)FRAME_HEIGHT); ubo->random_seed = (uint32_t)gEngine.COM_RandomLong(0, INT32_MAX); } @@ -273,6 +280,7 @@ static void performTracing(VkCommandBuffer cmdbuf, const perform_tracing_args_t* R_VkImageBlit( cmdbuf, &blit_args ); } + DEBUG_END(cmdbuf); } @@ -280,7 +288,7 @@ void VK_RayFrameEnd(const vk_ray_frame_render_args_t* args) { const VkCommandBuffer cmdbuf = args->cmdbuf; const xvk_ray_frame_images_t* current_frame = g_rtx.frames + (g_rtx.frame_number % 2); - + ASSERT(vk_core.rtx); // ubo should contain two matrices // FIXME pass these matrices explicitly to let RTX module handle ubo itself diff --git a/ref_vk/vk_studio.c b/ref_vk/vk_studio.c index 72b9ffc4..435e8c18 100644 --- a/ref_vk/vk_studio.c +++ b/ref_vk/vk_studio.c @@ -47,6 +47,11 @@ typedef struct sortedmesh_s int flags; // face flags } sortedmesh_t; +typedef struct { + matrix3x4 worldtransform[MAXSTUDIOBONES]; + matrix4x4 prev_transform; +} studio_entity_prev_state_t; + typedef struct { double time; @@ -74,6 +79,8 @@ typedef struct vec3_t verts[MAXSTUDIOVERTS]; vec3_t norms[MAXSTUDIOVERTS]; + vec3_t prev_verts[MAXSTUDIOVERTS]; // last frame state for motion vectors + // lighting state float ambientlight; float shadelight; @@ -118,6 +125,9 @@ static cvar_t *cl_himodels; static r_studio_interface_t *pStudioDraw; static studio_draw_state_t g_studio; // global studio state +#define MAX_ENTITIES_PREV_STATES_STUDIO 1024 +static studio_entity_prev_state_t g_entity_prev_states[MAX_ENTITIES_PREV_STATES_STUDIO]; + // global variables static qboolean m_fDoRemap; mstudiomodel_t *m_pSubModel; @@ -254,7 +264,7 @@ static qboolean R_StudioComputeBBox( vec3_t bbox[8] ) return true; // visible } -void R_StudioComputeSkinMatrix( mstudioboneweight_t *boneweights, matrix3x4 result ) +void R_StudioComputeSkinMatrix( mstudioboneweight_t *boneweights, matrix3x4 *worldtransform, matrix3x4 result ) { float flWeight0, flWeight1, flWeight2, flWeight3; int i, numbones = 0; @@ -268,10 +278,10 @@ void R_StudioComputeSkinMatrix( mstudioboneweight_t *boneweights, matrix3x4 resu if( numbones == 4 ) { - vec4_t *boneMat0 = (vec4_t *)g_studio.worldtransform[boneweights->bone[0]]; - vec4_t *boneMat1 = (vec4_t *)g_studio.worldtransform[boneweights->bone[1]]; - vec4_t *boneMat2 = (vec4_t *)g_studio.worldtransform[boneweights->bone[2]]; - vec4_t *boneMat3 = (vec4_t *)g_studio.worldtransform[boneweights->bone[3]]; + vec4_t *boneMat0 = (vec4_t *)worldtransform[boneweights->bone[0]]; + vec4_t *boneMat1 = (vec4_t *)worldtransform[boneweights->bone[1]]; + vec4_t *boneMat2 = (vec4_t *)worldtransform[boneweights->bone[2]]; + vec4_t *boneMat3 = (vec4_t *)worldtransform[boneweights->bone[3]]; flWeight0 = boneweights->weight[0] / 255.0f; flWeight1 = boneweights->weight[1] / 255.0f; flWeight2 = boneweights->weight[2] / 255.0f; @@ -295,9 +305,9 @@ void R_StudioComputeSkinMatrix( mstudioboneweight_t *boneweights, matrix3x4 resu } else if( numbones == 3 ) { - vec4_t *boneMat0 = (vec4_t *)g_studio.worldtransform[boneweights->bone[0]]; - vec4_t *boneMat1 = (vec4_t *)g_studio.worldtransform[boneweights->bone[1]]; - vec4_t *boneMat2 = (vec4_t *)g_studio.worldtransform[boneweights->bone[2]]; + vec4_t *boneMat0 = (vec4_t *)worldtransform[boneweights->bone[0]]; + vec4_t *boneMat1 = (vec4_t *)worldtransform[boneweights->bone[1]]; + vec4_t *boneMat2 = (vec4_t *)worldtransform[boneweights->bone[2]]; flWeight0 = boneweights->weight[0] / 255.0f; flWeight1 = boneweights->weight[1] / 255.0f; flWeight2 = boneweights->weight[2] / 255.0f; @@ -320,8 +330,8 @@ void R_StudioComputeSkinMatrix( mstudioboneweight_t *boneweights, matrix3x4 resu } else if( numbones == 2 ) { - vec4_t *boneMat0 = (vec4_t *)g_studio.worldtransform[boneweights->bone[0]]; - vec4_t *boneMat1 = (vec4_t *)g_studio.worldtransform[boneweights->bone[1]]; + vec4_t *boneMat0 = (vec4_t *)worldtransform[boneweights->bone[0]]; + vec4_t *boneMat1 = (vec4_t *)worldtransform[boneweights->bone[1]]; flWeight0 = boneweights->weight[0] / 255.0f; flWeight1 = boneweights->weight[1] / 255.0f; flTotal = flWeight0 + flWeight1; @@ -343,7 +353,7 @@ void R_StudioComputeSkinMatrix( mstudioboneweight_t *boneweights, matrix3x4 resu } else { - Matrix3x4_Copy( result, g_studio.worldtransform[boneweights->bone[0]] ); + Matrix3x4_Copy( result, worldtransform[boneweights->bone[0]] ); } } @@ -1062,6 +1072,26 @@ static void R_StudioSaveBones( void ) } } +/* +==================== +SaveTransformsForNextFrame + +==================== +*/ + +static void R_StudioSaveTransformsForNextFrame( matrix3x4* bones_transforms ) +{ + if (RI.currententity->index >= MAX_ENTITIES_PREV_STATES_STUDIO) + return; + + studio_entity_prev_state_t *prev_state = &g_entity_prev_states[RI.currententity->index]; + + for( int i = 0; i < m_pStudioHeader->numbones; i++ ) + { + Matrix3x4_Copy(prev_state->worldtransform[i], bones_transforms[i]); + } +} + /* ==================== StudioBuildNormalTable @@ -1946,6 +1976,7 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float *dst_vtx = (vk_vertex_t){0}; VectorCopy(g_studio.verts[ptricmds[0]], dst_vtx->pos); + VectorCopy(g_studio.prev_verts[ptricmds[0]], dst_vtx->prev_pos); VectorCopy(g_studio.norms[ptricmds[0]], dst_vtx->normal); dst_vtx->lm_tc[0] = dst_vtx->lm_tc[1] = 0.f; @@ -2127,6 +2158,14 @@ static void R_StudioDrawPoints( void ) pskinref = (short *)((byte *)m_pStudioHeader + m_pStudioHeader->skinindex); if( m_skinnum != 0 ) pskinref += (m_skinnum * m_pStudioHeader->numskinref); + studio_entity_prev_state_t *prev_frame_state = &g_entity_prev_states[RI.currententity->index]; + + if (RI.currententity->index >= MAX_ENTITIES_PREV_STATES_STUDIO) + { + gEngine.Con_Printf(S_ERROR "Studio entities previous frame states pool is overflow, increase it. Index is %s\n", RI.currententity->index); + prev_frame_state = &g_entity_prev_states[MAX_ENTITIES_PREV_STATES_STUDIO - 1]; // fallback to last element + } + if( FBitSet( m_pStudioHeader->flags, STUDIO_HAS_BONEWEIGHTS ) && m_pSubModel->blendvertinfoindex != 0 && m_pSubModel->blendnorminfoindex != 0 ) { mstudioboneweight_t *pvertweight = (mstudioboneweight_t *)((byte *)m_pStudioHeader + m_pSubModel->blendvertinfoindex); @@ -2135,24 +2174,35 @@ static void R_StudioDrawPoints( void ) for( i = 0; i < m_pSubModel->numverts; i++ ) { - R_StudioComputeSkinMatrix( &pvertweight[i], skinMat ); + R_StudioComputeSkinMatrix( &pvertweight[i], g_studio.worldtransform, skinMat ); Matrix3x4_VectorTransform( skinMat, pstudioverts[i], g_studio.verts[i] ); R_LightStrength( pvertbone[i], pstudioverts[i], g_studio.lightpos[i] ); } + for( i = 0; i < m_pSubModel->numverts; i++ ) + { + R_StudioComputeSkinMatrix( &pvertweight[i], prev_frame_state->worldtransform, skinMat ); + Matrix3x4_VectorTransform( skinMat, pstudioverts[i], g_studio.prev_verts[i] ); + } + for( i = 0; i < m_pSubModel->numnorms; i++ ) { - R_StudioComputeSkinMatrix( &pnormweight[i], skinMat ); + R_StudioComputeSkinMatrix( &pnormweight[i], g_studio.worldtransform, skinMat ); Matrix3x4_VectorRotate( skinMat, pstudionorms[i], g_studio.norms[i] ); } + + R_StudioSaveTransformsForNextFrame (g_studio.worldtransform ); } else { for( i = 0; i < m_pSubModel->numverts; i++ ) { Matrix3x4_VectorTransform( g_studio.bonestransform[pvertbone[i]], pstudioverts[i], g_studio.verts[i] ); + Matrix3x4_VectorTransform( prev_frame_state->worldtransform[pvertbone[i]], pstudioverts[i], g_studio.prev_verts[i] ); R_LightStrength( pvertbone[i], pstudioverts[i], g_studio.lightpos[i] ); } + + R_StudioSaveTransformsForNextFrame( g_studio.bonestransform ); } // generate shared normals for properly scaling glowing shell @@ -2278,7 +2328,17 @@ static void R_StudioDrawPoints( void ) */ } + int entity_id = RI.currententity->index; + + if (entity_id < MAX_ENTITIES_PREV_STATES_STUDIO) { + Matrix4x4_Copy( *VK_RenderGetLastFrameTransform(), g_entity_prev_states[entity_id].prev_transform ); + } else gEngine.Con_Printf(S_ERROR "Studio entities last states pool is overflow, increase it. Index is %s\n", entity_id ); + VK_RenderModelDynamicCommit(); + + if (entity_id < MAX_ENTITIES_PREV_STATES_STUDIO) { + Matrix4x4_Copy( g_entity_prev_states[entity_id].prev_transform, *VK_RenderGetLastFrameTransform() ); + } } static void R_StudioSetRemapColors( int newTop, int newBottom )