    
local demo_dir = gh_utils.get_demo_dir() 		

local lib_dir = gh_utils.get_lib_dir() 		
dofile(lib_dir .. "lua/vk.lua")

    

    
winW, winH = gh_window.getsize(0)





-----------------------------------------------------------------------
--
-- In this demo, there are N uniform buffers and descriptor sets where N is the number of swapchain images.
--
--
swapchain_image_count = gh_vk.swapchain_get_image_count()
print("swapchain_buffer_count => " .. swapchain_image_count)









-----------------------------------------------------------------------
-- Perspective camera.
--
local aspect = winW / winH
camera_params = { fov=60, znear=1.0, zfar=1000.0 }
camera = gh_camera.create_persp(camera_params.fov, aspect, camera_params.znear, camera_params.zfar)
gh_camera.set_viewport(camera, 0, 0, winW, winH)
gh_camera.set_position(camera, 0, 0, 20)
gh_camera.set_lookat(camera, 0, 0, 0, 1)
gh_camera.set_upvec(camera, 0, 1, 0, 0)







-----------------------------------------------------------------------
--
--[[

Uniform buffer structure in the shader.

layout (std140, binding = 0) uniform uniforms_t
{ 
  mat4 ProjectionMatrix;
  mat4 ViewMatrix;
  vec4 uv_tiling;
  mat4 ModelMatrix[NUM];
} ub;
--]]


function UpdateCameraTransform(ub, cam)
  local buffer_offset_bytes = 0
  gh_gpu_buffer.set_matrix4x4(ub, buffer_offset_bytes, cam, "camera_projection_vk")
  
  buffer_offset_bytes = 64
  gh_gpu_buffer.set_matrix4x4(ub, buffer_offset_bytes, cam, "camera_view")  
end


function UpdateObjectTransform(ub, obj, index)
  local buffer_offset_bytes = 64 * 2 + 16 + index*64
  gh_gpu_buffer.set_matrix4x4(ub, buffer_offset_bytes, obj, "object_global_transform")
end


function UpdateUVTiling(ub, s, t)
  local buffer_offset_bytes = 64 * 2
  gh_gpu_buffer.set_value_4f(ub, buffer_offset_bytes, s, t, 0, 0)
end


g_num_x = 30
g_num_y = 30
local extra_space = 128
local ub_size = ((g_num_x+1) * (g_num_y+1) * 64) + (2*64) + extra_space

--ub1 = gh_gpu_buffer.create("UNIFORM", "NONE", ub_size, "")
--gh_gpu_buffer.bind(ub1)
--gh_gpu_buffer.map(ub1)
--UpdateCameraTransform(ub1, camera)
--UpdateUVTiling(ub1, 4.0, 2.0)




ubs = {} -- All uniform buffers are stored in this table.

for i=0, swapchain_image_count-1 do
  local ub = gh_gpu_buffer.create("UNIFORM", "NONE", ub_size, "")

  ubs[i+1] = ub

  gh_gpu_buffer.bind(ub)
  gh_gpu_buffer.map(ub)
  UpdateCameraTransform(ub, camera)
  UpdateUVTiling(ub, 6.0, 6.0)
  gh_gpu_buffer.unmap(ub)
end







-----------------------------------------------------------------------
--
local vertex_shader = demo_dir .. "spirv/08-vs.spv"
local pixel_shader = demo_dir .. "spirv/08-ps.spv"
phong_shader = gh_gpu_program.vk_create_from_spirv_module_file("phong_shader",   vertex_shader, "main",     pixel_shader, "main",    "", "",    "", "",     "", "",    "", "") 





-----------------------------------------------------------------------
--
local abs_path = 0
local PF_U8_RGB = 1
local PF_U8_RGBA = 3
local pixel_format = PF_U8_RGBA
local gen_mipmaps = 0
local compressed_texture = 0
local free_cpu_memory = 1
tex0 = gh_texture.create_from_file_v6(demo_dir .. "textures/t08.jpg", pixel_format, gen_mipmaps, compressed_texture)


-----------------------------------------------------------------------
--
local anisotropy = 1.0
sampler = gh_vk.sampler_create("LINEAR", "WRAP", anisotropy, 0)





-----------------------------------------------------------------------
--

--[[
VK_SHADER_STAGE_VERTEX = 1
VK_SHADER_STAGE_TESSELLATION_CONTROL = 2
VK_SHADER_STAGE_TESSELLATION_EVALUATION = 4
VK_SHADER_STAGE_GEOMETRY = 8
VK_SHADER_STAGE_FRAGMENT = 16
VK_SHADER_STAGE_COMPUTE = 32

0010 0000 = 32
0001 0000 = 16
0000 1000 = 8
0000 0100 = 4
0000 0010 = 2
0000 0001 = 1
--]]



dss = {} -- All descriptor sets are stored in this table.

for i=0, swapchain_image_count-1 do
  local ds = gh_vk.descriptorset_create()
  dss[i+1] = ds

  local ub = ubs[i+1]

  ub_binding_point = 0
  gh_vk.descriptorset_add_resource_gpu_buffer(ds, ub, ub_binding_point, SHADER_STAGE_VERTEX)
  
  tex_binding_point = 1
  tex_res_index = gh_vk.descriptorset_add_resource_texture(ds, tex0, sampler, tex_binding_point, SHADER_STAGE_FRAGMENT)
  
  
  --[[
  layout(push_constant) uniform push_block_t
  {
    int index;
  } cb;
  --]]
  
  local constant_size_bytes = 4
  local constant_offset_bytes = 0
  push_constant_shader_flags = SHADER_STAGE_VERTEX
  gh_vk.descriptorset_add_push_constant_range(ds, constant_size_bytes, constant_offset_bytes, push_constant_shader_flags)
  
  gh_vk.descriptorset_build(ds)
  gh_vk.descriptorset_update(ds)
end



-- All descriptor sets have the same layout, we can then create only one pipeline that use one of those DSs. 
--
local ds = dss[1]










-----------------------------------------------------------------------
--
pso01 = gh_vk.pipeline_create("pso01", phong_shader, "")
gh_vk.pipeline_set_attrib_4i(pso01, "DEPTH_TEST", 1, 0, 0, 0)
gh_vk.pipeline_set_attrib_4i(pso01, "FILL_MODE", POLYGON_MODE_SOLID, 0, 0, 0)
gh_vk.pipeline_set_attrib_4i(pso01, "PRIMITIVE_TYPE", PRIMITIVE_TRIANGLE, 0, 0, 0)
--gh_vk.pipeline_set_attrib_4i(pso01, "CULL_MODE", POLYGON_FACE_NONE, 0, 0, 0)
gh_vk.pipeline_set_attrib_4i(pso01, "CULL_MODE", POLYGON_FACE_BACK, 0, 0, 0)
gh_vk.pipeline_set_attrib_4i(pso01, "CCW", 1, 0, 0, 0)

pso_valid = gh_vk.pipeline_build(pso01, ds)
if (pso_valid == 0) then
	print("ERROR: pipeline state pso01 is not valid.")
end






-----------------------------------------------------------------------
--
--mesh = gh_mesh.create_torus(6.0, 2.0, 10) -- 200f / 121v
--mesh = gh_mesh.create_torus(6.0, 2.0, 20) -- 800f / 441v
--mesh = gh_mesh.create_torus(6.0, 2.0, 40) -- 3200f / 1681v
--mesh = gh_mesh.create_torus(6.0, 2.0, 50) -- 5000f / 2601v
--mesh = gh_mesh.create_torus(6.0, 2.0, 100) -- 20'000f / 10'201v
--mesh = gh_mesh.create_torus(6.0, 2.0, 200) -- 80'000f / 40'401v
--mesh = gh_mesh.create_torus(6.0, 2.0, 400) -- 320'000f / 160'801v
--mesh = gh_mesh.create_torus(6.0, 2.0, 500) -- 500'000f / 251'001v
--mesh = gh_mesh.create_torus(6.0, 2.0, 1000) -- 2'000'000f / 1'002'001v


--mesh = gh_mesh.create_torus(2.0, 0.5, 50) -- 5000f / 2601v
--mesh = gh_mesh.create_torus(0.6, 0.1, 20) -- 800f / 441v
--mesh = gh_mesh.create_torus(0.6, 0.1, 50) -- 5'000f / 2601v
mesh = gh_mesh.create_torus(1.0, 0.2, 50) -- 5'000f / 2601v
--mesh = gh_mesh.create_torus(0.6, 0.1, 50) -- 5'000f / 2601v
--mesh = gh_mesh.create_torus(0.6, 0.1, 100) -- 20'000f / 10'201v


gh_mesh.set_vertices_color(mesh, 1.0, 1.0, 0.0, 1.0)



function Update_Meshes(ub, num_x, num_y, time)

  local size_x = 80.0
  local size_y = 40.0
  local step_x = size_x / num_x
  local step_y = size_y / num_y

  local pos_y = -size_y / 2.0

  local index = 0

  for y=0, num_y do

    local pos_x = -size_x / 2.0

    for x=0, num_x do

      gh_object.set_euler_angles(mesh, time * 3.0 + x, time * 7.0 + y, time * 11.0)
      gh_object.set_position(mesh, pos_x, pos_y, 0.0)

      UpdateObjectTransform(ub, mesh, index)
      index = index + 1

      pos_x = pos_x + step_x      

    end

    pos_y = pos_y + step_y

  end
end





-----------------------------------------------------------------------
--

-- 0 => single command buffer - rebuilt every frame, executed every frame
-- 1 => one command buffer per swapchain image - rebuilt every frame, executed every frame
-- 2 => one command buffer per swapchain image - built once, executed every frame
--
render_scene_mode = 2




function update_scene(time)
  gh_camera.set_position(camera, 0, 0, 20)
  gh_camera.set_lookat(camera, 0, 0, 0, 1)

  local frame_index = gh_vk.frame_get_active_command_buffer_index()
  local ub = ubs[frame_index+1]
  gh_gpu_buffer.map(ub)

  UpdateCameraTransform(ub, camera)  
  Update_Meshes(ub, g_num_x, g_num_y, time)

  gh_gpu_buffer.unmap(ub)
end  



function draw_scene(w, h)
	
  gh_vk.set_viewport_scissor(0, 0, w, h)
  

  local frame_index = gh_vk.frame_get_active_command_buffer_index()
  local ds = dss[frame_index+1]

  gh_vk.pipeline_bind(pso01)
  gh_vk.descriptorset_bind(ds)

  local index = 0
  
  ---[[
  for y=0, g_num_y do
    for x=0, g_num_x do

      local constant_offset_bytes = 0
      gh_vk.descriptorset_push_constant_1i(ds, index, constant_offset_bytes, SHADER_STAGE_VERTEX)
      index = index + 1

      gh_object.render(mesh)
    end
  end
  --]]
  

  --[[
  gh_object.render_geometry_prepare(mesh)

  for y=0, g_num_y do
    for x=0, g_num_x do

      local constant_offset_bytes = 0
      gh_vk.descriptorset_push_constant_1i(ds, index, constant_offset_bytes, SHADER_STAGE_VERTEX)
      index = index + 1

      gh_object.render_geometry_draw(mesh)
    end
  end

  gh_object.render_geometry_finish(mesh)
  --]]
  
end





function build_command_buffer(w, h)

  gh_vk.clear_color_depth_buffers(0.2, 0.25, 0.30, 1.0, 1.0)
	gh_vk.frame_command_buffer_begin()

  draw_scene(w, h)
	
	gh_vk.frame_command_buffer_end()
end





gh_vk.wait_for_gpu()




