    
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)





-----------------------------------------------------------------------
-- Perspective camera.
--
local aspect = winW / winH
camera_params = { fov=60, znear=0.1, 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, 12)
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")
  gh_gpu_buffer.set_matrix4x4(ub, buffer_offset_bytes, cam, "camera_projection")
  
  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)



-----------------------------------------------------------------------
--
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 abs_path = 1
local gen_mipmaps = 0
local compressed_texture = 0
local free_cpu_memory = 1
--gh_texture.set_current_image_codec("FreeImage")

tex0 = gh_texture.create_from_file_v3(demo_dir .. "data/t08.jpg", pixel_format, abs_path, gen_mipmaps, compressed_texture, free_cpu_memory)
--]]


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







-----------------------------------------------------------------------
--
ds = gh_vk.descriptorset_create()

ub_binding_point = 0
gh_vk.descriptorset_add_resource_gpu_buffer(ds, ub1, 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)






-----------------------------------------------------------------------
--
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(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(num_x, num_y, time)

  local size_x = 40.0
  local size_y = 20.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(ub1, 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)
  UpdateCameraTransform(ub1, camera)  
  Update_Meshes(g_num_x, g_num_y, time)
end  



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

  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()



swapchain_image_count = gh_vk.swapchain_get_image_count()
print("swapchain_image_count => " .. swapchain_image_count)


--[[
for i=0, swapchain_image_count-1 do
	gh_vk.frame_set_active_command_buffer_index(i)
	build_command_buffer(winW, winH)
end
gh_vk.frame_set_active_command_buffer_index(0)
--]]



