local demo_dir = gh_utils.get_demo_dir() 		

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




    
winW, winH = gh_window.getsize(0)




geexlab_ver = {x=0, y=0, z=0, w=0}
geexlab_ver.x, geexlab_ver.y, geexlab_ver.z, geexlab_ver.w = gh_utils.get_app_version()
geexlab_version = string.format("GeeXLab: %d.%d.%d.%d", geexlab_ver.x, geexlab_ver.y, geexlab_ver.z, geexlab_ver.w)




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





-----------------------------------------------------------------------
-- GPU buffer to store uniform data used by the shaders.
--


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)
  local buffer_offset_bytes = 64 * 2
  gh_gpu_buffer.set_matrix4x4(ub, buffer_offset_bytes, obj, "object_global_transform")
end


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


local ub_size = 512
--ub1 = gh_gpu_buffer.create("UNIFORM", "NONE", ub_size, "")
--gh_gpu_buffer.bind(ub1)
--gh_gpu_buffer.map(ub1)

--UpdateCameraTransform(ub1, camera)
--UpdateUVTiling(ub1, 2.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)
end




















-----------------------------------------------------------------------
-- SPIR-V shaders.
--
-- The SPIR-V shaders have been created from GLSL shaders using the 
-- glslangValidator.exe on Windows. This utility is available in the 
-- spirv/ folder.
--
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",    "", "",    "", "",     "", "",    "", "") 

phong_shader = gh_gpu_program.create_empty("phong_shader") 
gh_gpu_program.vk_add_spirv_module_file(phong_shader, vertex_shader, "main", GPU_SHADER_VERTEX) 
gh_gpu_program.vk_add_spirv_module_file(phong_shader, pixel_shader, "main", GPU_SHADER_PIXEL) 






-----------------------------------------------------------------------
--
local abs_path = 0
local PF_U8_RGB = 1
local PF_U8_RGBA = 3
local pixel_format = PF_U8_RGBA
local gen_mipmaps = 1
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)

num_mipmaps = gh_texture.get_num_mipmaps(tex0)
print("num_mipmaps for tex0: " .. num_mipmaps)


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






-----------------------------------------------------------------------
-- Descriptor set that describes what resources (GPU buffer and textures) 
-- will be used for drawing.
--
-- ds = gh_vk.descriptorset_create()

-- local binding_point = 0
-- gh_vk.descriptorset_add_resource_gpu_buffer(ds, ub1, binding_point, SHADER_STAGE_VERTEX)

-- binding_point = 1
-- tex_res_index = gh_vk.descriptorset_add_resource_texture(ds, tex0, sampler, binding_point, SHADER_STAGE_FRAGMENT)

-- gh_renderer.vk_descriptorset_build(ds)
-- gh_renderer.vk_descriptorset_update(ds)






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]


	local binding_point = 0
	gh_vk.descriptorset_add_resource_gpu_buffer(ds, ub, binding_point, SHADER_STAGE_VERTEX)

	binding_point = 1
	tex_res_index = gh_vk.descriptorset_add_resource_texture(ds, tex0, sampler, binding_point, SHADER_STAGE_FRAGMENT)

	gh_renderer.vk_descriptorset_build(ds)
	gh_renderer.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]

-----------------------------------------------------------------------
-- The pipeline object or PSO.
-- The PSO stores all graphics pipeline states (what shaders are used, depth state, blending, etc.)
-- in one place. The PSO is an immutable object: once created, it can not be changed. So if you need
-- to draw an object with solid polygon mode and another object in wireframe, you have to create 
-- two PSOs.
--
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 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

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

--UpdateObjectTransform(ub1, mesh)
--UpdateUVTiling(ub1, 2.0, 2.0)





for i=0, swapchain_image_count-1 do
  local ub = ubs[i+1]
  gh_gpu_buffer.bind(ub)
  gh_gpu_buffer.map(ub)
  UpdateCameraTransform(ub, camera)
	UpdateObjectTransform(ub, mesh)
	UpdateUVTiling(ub, 2.0, 2.0)
  gh_gpu_buffer.unmap(ub)
end







-----------------------------------------------------------------------
-- Wait for GPU: be sure that all init commands are done.
--
gh_renderer.wait_for_gpu()


gh_vk.init("gpu_features_info_disabled")
--gh_vk.init("")


display_imgui = true



function show_imgui()

	imgui_frame_begin()


	gh_imgui.set_color(IMGUI_WINDOW_BG_COLOR, 0.1, 0.1, 0.1, 0.70)
	gh_imgui.set_color(IMGUI_TEXT_COLOR, 1.0, 1.0, 1.0, 1.0)
	
	gh_imgui.set_color(IMGUI_TITLE_BG_COLOR, 0.35, 0.35, 0.35, 1.0)
	gh_imgui.set_color(IMGUI_TITLE_BG_ACTIVE_COLOR, 0.5, 0.5, 0.5, 1.0)
	
	gh_imgui.set_color(IMGUI_FRAME_BG_COLOR, 0.6, 0.6, 0.6, 0.90)
	gh_imgui.set_color(IMGUI_FRAME_BG_HOVERED_COLOR, 0.7, 0.7, 0.7, 0.9)
	
	gh_imgui.set_color(IMGUI_BUTTON_COLOR, 0.3, 0.3, 0.3, 0.8)
	gh_imgui.set_color(IMGUI_BUTTON_HOVERED_COLOR, 0.4, 0.4, 0.4, 0.8)
	
	gh_imgui.set_color(IMGUI_HEADER_COLOR, 0.3, 0.3, 0.3, 0.8)
	gh_imgui.set_color(IMGUI_HEADER_HOVERED_COLOR, 0.4, 0.4, 0.4, 0.8)
	

	local is_open = imgui_window_begin_pos_size_always("Control panel", 300, winH, 0, 0)
	if (is_open == 1) then

		local fps = gh_utils.get_fps_cur()
		gh_imgui.text(fps .. " FPS")

	
		gh_imgui.text("Vulkan GPUs:")

		gh_imgui.spacing()

		local num_gpus = gh_vk.get_num_gpus()

		for i=1, num_gpus do
			local gpu_index = i-1
			local gpu_name = gh_vk.gpu_get_name(gpu_index)
			local major, minor, patch = gh_vk.gpu_get_api_version(gpu_index)
			local vendor_id, device_id = gh_vk.gpu_get_device_id(gpu_index)
			local num_extensions = gh_vk.gpu_get_num_extensions(gpu_index)

	
			local flags = 0
	
			if (i == 1) then
				-- Opens header for the first GPU.
				flags = ImGuiTreeNodeFlags_DefaultOpen
	    end

	    if (gh_imgui.collapsing_header(string.format("GPU %d", gpu_index), flags) == 1) then
				gh_imgui.text(string.format("%s", gpu_name))
				gh_imgui.text(string.format("Vulkan API: %d.%d.%d", major, minor, patch))
				gh_imgui.text(string.format("DeviceID: %.4X-%.4X (%d-%d)", vendor_id, device_id, vendor_id, device_id))
				gh_imgui.text(string.format("Num device extensions: %d", num_extensions))
      end
			gh_imgui.spacing()
			gh_imgui.spacing()

		end

		gh_imgui.separator()

		gh_imgui.spacing()
		gh_imgui.spacing()
	
		gh_imgui.text(geexlab_version)

		if (gh_imgui.button("homepage", 130, 20) == 1) then gh_utils.open_url("https://geeks3d.com/geexlab/") end

	end 
		
	imgui_window_end()
	imgui_frame_end()
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]

	-- Binds the descriptor set and PSO.
	--
	gh_vk.descriptorset_bind(ds)
	gh_vk.pipeline_bind(pso01)

	-- Render the object. Keep in mind that the object is not really rendered:
	-- only the drawing command is recorded in the command buffer. The object 
	-- will be rendered later when the command buffer will be executed.  
	--
	gh_object.render(mesh)

end



function build_command_buffer(w, h)

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

  draw_scene(w, h)

	if (display_imgui) then
		show_imgui()
	end
	
	gh_vk.frame_command_buffer_end()
end








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)



