diff --git a/imgui/imgui_dx11.jai b/imgui/imgui_dx11.jai new file mode 100644 index 0000000..1732a81 --- /dev/null +++ b/imgui/imgui_dx11.jai @@ -0,0 +1,582 @@ +ImGui :: #import "ImGui"; + +// Shaders +IMGUI_VERTEX_SHADER :: #string END +cbuffer vertexBuffer : register(b0) +{ + float4x4 ProjectionMatrix; +}; +struct VS_INPUT +{ + float2 pos : POSITION; + float4 col : COLOR; + float2 uv : TEXCOORD0; +}; + +struct PS_INPUT +{ + float4 pos : SV_POSITION; + float4 col : COLOR; + float2 uv : TEXCOORD0; +}; + +PS_INPUT main_vs(float2 pos : POSITION, float4 col : COLOR, float2 uv : TEXCOORD0) +{ + PS_INPUT output; + output.pos = mul(float4(pos.xy, 0.f, 1.f), ProjectionMatrix); + output.col = col; + output.uv = uv; + return output; +} +END + +IMGUI_PIXEL_SHADER :: #string END +struct PS_INPUT +{ + float4 pos : SV_POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; +sampler sampler0; +Texture2D texture0; + +float4 main_ps(PS_INPUT input) : SV_Target +{ + float4 out_col = input.col * texture0.Sample(sampler0, input.uv); + return out_col; +} +END + +// Impl procedures +imgui_impldx11_new_frame :: () { + bd := imgui_impldx11_get_backend_data(); + assert(bd != null && "Did you call ImGui_ImplDX11_Init()?"); + + if !bd.font_sampler { + imgui_impldx11_create_device_objects(); + } +} + +imgui_impldx11_render_draw_data :: (draw_data: *ImGui.ImDrawData) { + // Avoid rendering when minimized + if draw_data.DisplaySize.x <= 0.0 || draw_data.DisplaySize.y <= 0.0 return; + + bd := imgui_impldx11_get_backend_data(); + ctx := bd.d3d_context; + + // Create and grow vertex/index buffers if needed + if !bd.vb || bd.vertex_buffer_size < xx draw_data.TotalVtxCount { + if bd.vb { IUnknown_Release(bd.vb); bd.vb = null; } + bd.vertex_buffer_size = cast(u32)draw_data.TotalVtxCount + 5000; + desc: D3D11_BUFFER_DESC; + memset(*desc, 0, size_of(D3D11_BUFFER_DESC)); + desc.Usage = .D3D11_USAGE_DYNAMIC; + desc.ByteWidth = bd.vertex_buffer_size * size_of(ImGui.ImDrawVert); + desc.BindFlags = .D3D11_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = .D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + if ID3D11Device_CreateBuffer(bd.d3d_device, *desc, null, *bd.vb) < 0 return; + } + + if !bd.ib || bd.index_buffer_size < xx draw_data.TotalIdxCount { + if bd.ib != null { IUnknown_Release(bd.ib); bd.ib = null; } + bd.index_buffer_size = cast(u32)draw_data.TotalIdxCount + 10000; + desc: D3D11_BUFFER_DESC; + memset(*desc, 0, size_of(D3D11_BUFFER_DESC)); + desc.Usage = .D3D11_USAGE_DYNAMIC; + desc.ByteWidth = bd.index_buffer_size * size_of(ImGui.ImDrawIdx); + desc.BindFlags = .D3D11_BIND_INDEX_BUFFER; + desc.CPUAccessFlags = .D3D11_CPU_ACCESS_WRITE; + if ID3D11Device_CreateBuffer(bd.d3d_device, *desc, null, *bd.ib) < 0 return; + } + + // Upload vertex/index data into a single contiguous GPU buffer + vtx_resource: D3D11_MAPPED_SUBRESOURCE; + idx_resource: D3D11_MAPPED_SUBRESOURCE; + if ID3D11DeviceContext_Map(ctx, bd.vb, 0, .D3D11_MAP_WRITE_DISCARD, 0, *vtx_resource) != S_OK return; + if ID3D11DeviceContext_Map(ctx, bd.ib, 0, .D3D11_MAP_WRITE_DISCARD, 0, *idx_resource) != S_OK return; + + vtx_dst := cast(*ImGui.ImDrawVert)vtx_resource.pData; + idx_dst := cast(*ImGui.ImDrawIdx)idx_resource.pData; + + for n: 0..draw_data.CmdListsCount - 1 { + cmd_list := draw_data.CmdLists[n]; + memcpy(vtx_dst, cmd_list.VtxBuffer.Data, cmd_list.VtxBuffer.Size * size_of(ImGui.ImDrawVert)); + memcpy(idx_dst, cmd_list.IdxBuffer.Data, cmd_list.IdxBuffer.Size * size_of(ImGui.ImDrawIdx)); + vtx_dst += cmd_list.VtxBuffer.Size; + idx_dst += cmd_list.IdxBuffer.Size; + } + ID3D11DeviceContext_Unmap(ctx, bd.vb, 0); + ID3D11DeviceContext_Unmap(ctx, bd.ib, 0); + + // Setup orthographic projection matrix into our constant buffer + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + { + mapped_resource: D3D11_MAPPED_SUBRESOURCE; + if ID3D11DeviceContext_Map(ctx, bd.vertex_constant_buffer, 0, .D3D11_MAP_WRITE_DISCARD, 0, *mapped_resource) != S_OK return; + + constant_buffer := cast(*Matrix4)mapped_resource.pData; + L := draw_data.DisplayPos.x; + R := draw_data.DisplayPos.x + draw_data.DisplaySize.x; + T := draw_data.DisplayPos.y; + B := draw_data.DisplayPos.y + draw_data.DisplaySize.y; + + m: Matrix4; + m._11 = 2.0 / (R-L); + m._12 = 0.0; + m._13 = 0.0; + m._14 = (R+L)/(L-R); + + m._21 = 0.0; + m._22 = 2.0 / (T-B); + m._23 = 0.0; + m._24 = (T+B)/(B-T); + + m._31 = 0.0; + m._32 = 0.0; + m._33 = 0.5; + m._34 = 0.5; + + m._41 = 0.0; + m._42 = 0.0; + m._43 = 0.0; + m._44 = 1.0; + + memcpy(constant_buffer, *m, size_of(Matrix4)); + ID3D11DeviceContext_Unmap(ctx, bd.vertex_constant_buffer, 0); + } + + // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) + BACKUP_DX11_STATE :: struct { + scissor_rects_count: u32; + viewports_count: u32; + + scissor_rects: [D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE] D3D11_RECT; + viewports: [D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE] D3D11_VIEWPORT; + rs: *ID3D11RasterizerState; + blend_state: *ID3D11BlendState; + blend_factor: [4] float; + sample_mask: u32; + stencil_ref: u32; + depth_stencil_state: *ID3D11DepthStencilState; + ps_shader_resource: *ID3D11ShaderResourceView; + ps_sampler: *ID3D11SamplerState; + ps: *ID3D11PixelShader; + vs: *ID3D11VertexShader; + gs: *ID3D11GeometryShader; + ps_instances_count: u32; + vs_instances_count: u32; + gs_instances_count: u32; + ps_instances: [256]*ID3D11ClassInstance; + vs_instances: [256]*ID3D11ClassInstance; + gs_instances: [256]*ID3D11ClassInstance; + primitive_topology: D3D11_PRIMITIVE_TOPOLOGY; + index_buffer: *ID3D11Buffer; + vertex_buffer: *ID3D11Buffer; + vs_constant_buffer: *ID3D11Buffer; + index_buffer_offset: u32; + vertex_buffer_stride: u32; + vertex_buffer_offset: u32; + index_buffer_format: DXGI_FORMAT; + input_layout: *ID3D11InputLayout; + }; + + old: BACKUP_DX11_STATE; + old.scissor_rects_count = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + old.viewports_count = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + ID3D11DeviceContext_RSGetScissorRects(ctx, *old.scissor_rects_count, old.scissor_rects.data); + ID3D11DeviceContext_RSGetViewports(ctx, *old.viewports_count, old.viewports.data); + ID3D11DeviceContext_RSGetState(ctx, *old.rs); + ID3D11DeviceContext_OMGetBlendState(ctx, *old.blend_state, *old.blend_factor, *old.sample_mask); + ID3D11DeviceContext_OMGetDepthStencilState(ctx, *old.depth_stencil_state, *old.stencil_ref); + ID3D11DeviceContext_PSGetShaderResources(ctx, 0, 1, *old.ps_shader_resource); + ID3D11DeviceContext_PSGetSamplers(ctx, 0, 1, *old.ps_sampler); + old.ps_instances_count = 256; + old.vs_instances_count = 256; + old.gs_instances_count = 256; + ID3D11DeviceContext_PSGetShader(ctx, *old.ps, old.ps_instances.data, *old.ps_instances_count); + ID3D11DeviceContext_VSGetShader(ctx, *old.vs, old.vs_instances.data, *old.vs_instances_count); + ID3D11DeviceContext_VSGetConstantBuffers(ctx, 0, 1, *old.vs_constant_buffer); + ID3D11DeviceContext_GSGetShader(ctx, *old.gs, old.gs_instances.data, *old.gs_instances_count); + + ID3D11DeviceContext_IAGetPrimitiveTopology(ctx, *old.primitive_topology); + ID3D11DeviceContext_IAGetIndexBuffer(ctx, *old.index_buffer, *old.index_buffer_format, *old.index_buffer_offset); + ID3D11DeviceContext_IAGetVertexBuffers(ctx, 0, 1, *old.vertex_buffer, *old.vertex_buffer_stride, *old.vertex_buffer_offset); + ID3D11DeviceContext_IAGetInputLayout(ctx, *old.input_layout); + + // Setup desired DX state + imgui_impldx11_setup_render_state(draw_data, ctx); + + // Render command lists + // (Because we merged all buffers into a single one, we maintain our own offset into them) + global_idx_offset : u32 = 0; + global_vtx_offset : s32 = 0; + clip_off := draw_data.DisplayPos; + for n: 0..draw_data.CmdListsCount-1 { + cmd_list := draw_data.CmdLists[n]; + for cmd_i: 0..cmd_list.CmdBuffer.Size-1 { + pcmd := *cmd_list.CmdBuffer.Data[cmd_i]; + if pcmd.UserCallback != null { + // @Incomplete + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + //if pcmd.UserCallback == ImGui.ImDrawCallback_ResetRenderState { + // imgui_impdx11_setup_render_state(draw_data, ctx); + //} else { + pcmd.UserCallback(cmd_list, pcmd); + //} + } else { + // Project scissor/clipping rectangles into framebuffer space + clip_min : ImGui.ImVec2; + clip_min.x = pcmd.ClipRect.x - clip_off.x; + clip_min.y = pcmd.ClipRect.y - clip_off.y; + + clip_max : ImGui.ImVec2; + clip_max.x = pcmd.ClipRect.z - clip_off.x; + clip_max.y = pcmd.ClipRect.w - clip_off.y; + + if clip_max.x <= clip_min.x || clip_max.y <= clip_min.y continue; + + // Apply scissor/clipping rectangle + r : D3D11_RECT; + r.left = xx clip_min.x; + r.top = xx clip_min.y; + r.right = xx clip_max.x; + r.bottom = xx clip_max.y; + ID3D11DeviceContext_RSSetScissorRects(ctx, 1, *r); + + // Bind texture, Draw + texture_srv := cast(*ID3D11ShaderResourceView)pcmd.TextureId; + ID3D11DeviceContext_PSSetShaderResources(ctx, 0, 1, *texture_srv); + ID3D11DeviceContext_DrawIndexed(ctx, pcmd.ElemCount, pcmd.IdxOffset + global_idx_offset, xx pcmd.VtxOffset + global_vtx_offset); + } + } + global_idx_offset += xx cmd_list.IdxBuffer.Size; + global_vtx_offset += xx cmd_list.VtxBuffer.Size; + } + + // Restore modified DX state + ID3D11DeviceContext_RSSetScissorRects(ctx, old.scissor_rects_count, old.scissor_rects.data); + ID3D11DeviceContext_RSSetViewports(ctx, old.viewports_count, old.viewports.data); + ID3D11DeviceContext_RSSetState(ctx, old.rs); if (old.rs != null) IUnknown_Release(old.rs); + ID3D11DeviceContext_OMSetBlendState(ctx, old.blend_state, *old.blend_factor, old.sample_mask); if old.blend_state != null IUnknown_Release(old.blend_state); + ID3D11DeviceContext_OMSetDepthStencilState(ctx, old.depth_stencil_state, old.stencil_ref); if old.depth_stencil_state != null IUnknown_Release(old.depth_stencil_state); + ID3D11DeviceContext_PSSetShaderResources(ctx, 0, 1, *old.ps_shader_resource); if old.ps_shader_resource != null IUnknown_Release(old.ps_shader_resource); + ID3D11DeviceContext_PSSetSamplers(ctx, 0, 1, *old.ps_sampler); if old.ps_sampler != null IUnknown_Release(old.ps_sampler); + ID3D11DeviceContext_PSSetShader(ctx, old.ps, old.ps_instances.data, old.ps_instances_count); if old.ps IUnknown_Release(old.ps); + + { + i := 0; + while i < old.ps_instances_count { + defer i += 1; + if old.ps_instances[i] != null IUnknown_Release(old.ps_instances[i]); + } + } + + ID3D11DeviceContext_VSSetShader(ctx, old.vs, old.vs_instances.data, old.vs_instances_count); if old.vs != null IUnknown_Release(old.vs); + ID3D11DeviceContext_VSSetConstantBuffers(ctx, 0, 1, *old.vs_constant_buffer); if old.vs_constant_buffer != null IUnknown_Release(old.vs_constant_buffer); + ID3D11DeviceContext_GSSetShader(ctx, old.gs, old.gs_instances.data, old.gs_instances_count); if old.gs != null IUnknown_Release(old.gs); + + { + i := 0; + while i < old.vs_instances_count { + defer i += 1; + if old.vs_instances[i] != null IUnknown_Release(old.vs_instances[i]); + } + } + + ID3D11DeviceContext_IASetPrimitiveTopology(ctx, old.primitive_topology); + ID3D11DeviceContext_IASetIndexBuffer(ctx, old.index_buffer, old.index_buffer_format, old.index_buffer_offset); if old.index_buffer != null IUnknown_Release(old.index_buffer); + ID3D11DeviceContext_IASetVertexBuffers(ctx, 0, 1, *old.vertex_buffer, *old.vertex_buffer_stride, *old.vertex_buffer_offset); if old.vertex_buffer != null IUnknown_Release(old.vertex_buffer); + ID3D11DeviceContext_IASetInputLayout(ctx, old.input_layout); if old.input_layout != null IUnknown_Release(old.input_layout); +} + +imgui_impldx11_setup_render_state :: (draw_data: *ImGui.ImDrawData, ctx: *ID3D11DeviceContext) { + bd := imgui_impldx11_get_backend_data(); + + vp: D3D11_VIEWPORT; + memset(*vp, 0, size_of(D3D11_VIEWPORT)); + vp.Width = draw_data.DisplaySize.x; + vp.Height = draw_data.DisplaySize.y; + vp.MinDepth = 0.0; + vp.MaxDepth = 1.0; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + ID3D11DeviceContext_RSSetViewports(ctx, 1, *vp); + + // Setup shader and vertex buffers + stride: u32 = xx size_of(ImGui.ImDrawVert); + offset: u32 = 0; + ID3D11DeviceContext_IASetInputLayout(ctx, bd.input_layout); + ID3D11DeviceContext_IASetVertexBuffers(ctx, 0, 1, *bd.vb, *stride, *offset); + + format : DXGI_FORMAT; + + if size_of(ImGui.ImDrawIdx) == 2 { + format = .DXGI_FORMAT_R16_UINT; + } else { + format = .DXGI_FORMAT_R32_UINT; + } + + ID3D11DeviceContext_IASetIndexBuffer(ctx, bd.ib, format, 0); + ID3D11DeviceContext_IASetPrimitiveTopology(ctx, .D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + ID3D11DeviceContext_VSSetShader(ctx, bd.vertex_shader, null, 0); + ID3D11DeviceContext_VSSetConstantBuffers(ctx, 0, 1, *bd.vertex_constant_buffer); + ID3D11DeviceContext_PSSetShader(ctx, bd.pixel_shader, null, 0); + ID3D11DeviceContext_PSSetSamplers(ctx, 0, 1, *bd.font_sampler); + ID3D11DeviceContext_GSSetShader(ctx, null, null, 0); + ID3D11DeviceContext_HSSetShader(ctx, null, null, 0); // In theory we should backup and restore this as well.. very infrequently used.. + ID3D11DeviceContext_DSSetShader(ctx, null, null, 0); // In theory we should backup and restore this as well.. very infrequently used.. + ID3D11DeviceContext_CSSetShader(ctx, null, null, 0); // In theory we should backup and restore this as well.. very infrequently used.. + + // Setup blend state + factor : [4] float; + ID3D11DeviceContext_OMSetBlendState(ctx, bd.blend_state, *factor, 0xffffffff); + ID3D11DeviceContext_OMSetDepthStencilState(ctx, bd.depth_stencil_state, 0); + ID3D11DeviceContext_RSSetState(ctx, bd.rasterizer_state); +} + +imgui_impldx11_invalidate_device_objects :: () { + bd := imgui_impldx11_get_backend_data(); + + if bd.d3d_device == null return; + + if bd.font_sampler { IUnknown_Release(bd.font_sampler); bd.font_sampler = null; } + if bd.font_texture_view { IUnknown_Release(bd.font_texture_view); bd.font_texture_view = null; ImGui.GetIO().Fonts.TexID = null; } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well. + if bd.ib { IUnknown_Release(bd.ib); bd.ib = null; } + if bd.vb { IUnknown_Release(bd.vb); bd.vb = null; } + if bd.blend_state { IUnknown_Release(bd.blend_state); bd.blend_state = null; } + if bd.depth_stencil_state { IUnknown_Release(bd.depth_stencil_state); bd.depth_stencil_state = null; } + if bd.rasterizer_state { IUnknown_Release(bd.rasterizer_state); bd.rasterizer_state = null; } + if bd.pixel_shader { IUnknown_Release(bd.pixel_shader); bd.pixel_shader = null; } + if bd.vertex_constant_buffer { IUnknown_Release(bd.vertex_constant_buffer); bd.vertex_constant_buffer = null; } + if bd.input_layout { IUnknown_Release(bd.input_layout); bd.input_layout = null; } + if bd.vertex_shader { IUnknown_Release(bd.vertex_shader); bd.vertex_shader = null; } +} + +imgui_impldx11_create_device_objects :: () -> bool { + bd := imgui_impldx11_get_backend_data(); + + if bd.font_sampler { + imgui_impldx11_invalidate_device_objects(); + } + + // Create the vertex shader + vertex_shader_blob, blob, result := compile_shader("imgui_vertex_shader", IMGUI_VERTEX_SHADER, "main_vs", "vs_4_0", string.[]); + if FAILED(result) { + return false; + } + + if ID3D11Device_CreateVertexShader(bd.d3d_device, vertex_shader_blob.data, cast(u64)vertex_shader_blob.count, null, *bd.vertex_shader) != S_OK { + free(vertex_shader_blob); + return false; + } + + { + // Create the input layout + layout : [3] Vertex_Data_Info; + layout[0] = .{0,.POSITION2D, 0}; + layout[1] = .{0,.TEXCOORD0, 0}; + layout[2] = .{0,.COLOR_WITH_ALPHA8, 0}; + + backend_shader : Backend_Shader; + backend_shader.bytecode = vertex_shader_blob; + backend_shader.vertex_shader = bd.vertex_shader; + bd.input_layout = create_backend_input_layout(engine.renderer.backend, layout, backend_shader); + + free(vertex_shader_blob); + } + + // Create the constant buffer + { + desc: D3D11_BUFFER_DESC; + desc.ByteWidth = size_of(Matrix4); + desc.Usage = .D3D11_USAGE_DYNAMIC; + desc.BindFlags = .CONSTANT_BUFFER; + desc.CPUAccessFlags = .WRITE; + desc.MiscFlags = 0; + hr := ID3D11Device_CreateBuffer(bd.d3d_device, *desc, null, *bd.vertex_constant_buffer); + if FAILED(hr) { + return false; + } + } + + // Create the pixel shader + { + pixel_shader_blob, blob, result := compile_shader("imgui_pixel_shader", IMGUI_PIXEL_SHADER, "main_ps", "ps_4_0", string.[]); + if FAILED(result) { + return false; + } + + if (ID3D11Device_CreatePixelShader(bd.d3d_device, pixel_shader_blob.data, cast(u64)pixel_shader_blob.count, null, *bd.pixel_shader) != S_OK) { + free(pixel_shader_blob); + return false; + } + free(pixel_shader_blob); + } + + // Create the blending setup + { + desc: D3D11_BLEND_DESC; + desc.AlphaToCoverageEnable = .FALSE; + desc.RenderTarget[0].BlendEnable = .TRUE; + desc.RenderTarget[0].SrcBlend = .D3D11_BLEND_SRC_ALPHA; + desc.RenderTarget[0].DestBlend = .D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOp = .D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlendAlpha = .D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlendAlpha = .D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOpAlpha = .D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].RenderTargetWriteMask = xx D3D11_COLOR_WRITE_ENABLE.ALL; + ID3D11Device_CreateBlendState(bd.d3d_device, *desc, *bd.blend_state); + } + + // Create the rasterizer state + { + desc: D3D11_RASTERIZER_DESC; + desc.FillMode = .D3D11_FILL_SOLID; + desc.CullMode = .D3D11_CULL_NONE; + desc.ScissorEnable = .TRUE; + desc.DepthClipEnable = .TRUE; + //desc.FrontCounterClockwise = .TRUE; + ID3D11Device_CreateRasterizerState(bd.d3d_device, *desc, *bd.rasterizer_state); + } + + // Create depth-stencil State + { + desc: D3D11_DEPTH_STENCIL_DESC; + desc.DepthEnable = .FALSE; + desc.DepthWriteMask = .D3D11_DEPTH_WRITE_MASK_ALL; + desc.DepthFunc = .D3D11_COMPARISON_ALWAYS; + desc.StencilEnable = .FALSE; + desc.FrontFace.StencilFailOp = .D3D11_STENCIL_OP_KEEP; + desc.FrontFace.StencilDepthFailOp = .D3D11_STENCIL_OP_KEEP; + desc.FrontFace.StencilPassOp = .D3D11_STENCIL_OP_KEEP; + desc.FrontFace.StencilFunc = .D3D11_COMPARISON_ALWAYS; + desc.BackFace = desc.FrontFace; + ID3D11Device_CreateDepthStencilState(bd.d3d_device, *desc, *bd.depth_stencil_state); + } + + imgui_impld11_create_fonts_texture(); + + return true; +} + +imgui_impld11_create_fonts_texture :: () { + // Build texture atlas + io := ImGui.GetIO(); + bd := imgui_impldx11_get_backend_data(); + + pixels: *u8; + width: s32; + height: s32; + + ImGui.ImFontAtlas.GetTexDataAsRGBA32(io.Fonts, *pixels, *width, *height); + + // Upload texture to graphics system + { + desc: D3D11_TEXTURE2D_DESC; + desc.Width = xx width; + desc.Height = xx height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = .DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.Usage = .D3D11_USAGE_DEFAULT; + desc.BindFlags = .D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + + texture: *ID3D11Texture2D; + sub_resource: D3D11_SUBRESOURCE_DATA; + sub_resource.pSysMem = pixels; + sub_resource.SysMemPitch = desc.Width * 4; + sub_resource.SysMemSlicePitch = 0; + ID3D11Device_CreateTexture2D(bd.d3d_device, *desc, *sub_resource, *texture); + assert(texture != null); + + // Create texture view + srv_desc: D3D11_SHADER_RESOURCE_VIEW_DESC; + srv_desc.Format = .DXGI_FORMAT_R8G8B8A8_UNORM; + srv_desc.ViewDimension = .D3D11_SRV_DIMENSION_TEXTURE2D; + srv_desc.Texture2D.MipLevels = xx desc.MipLevels; + srv_desc.Texture2D.MostDetailedMip = 0; + ID3D11Device_CreateShaderResourceView(bd.d3d_device, texture, *srv_desc, *bd.font_texture_view); + IUnknown_Release(texture); + } + + // Store our identifier + io.Fonts.TexID = cast(ImGui.ImTextureID)bd.font_texture_view; + + // Create texture sampler + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + { + desc: D3D11_SAMPLER_DESC; + desc.Filter = .D3D11_FILTER_MIN_MAG_MIP_LINEAR; + desc.AddressU = .D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressV = .D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressW = .D3D11_TEXTURE_ADDRESS_WRAP; + desc.MipLODBias = 0.0; + desc.ComparisonFunc = .D3D11_COMPARISON_ALWAYS; + desc.MinLOD = 0.0; + desc.MaxLOD = 0.0; + ID3D11Device_CreateSamplerState(bd.d3d_device, *desc, *bd.font_sampler); + } +} + + +ImGui_ImplDX11_Data :: struct { + d3d_device: *ID3D11Device; + d3d_context: *ID3D11DeviceContext; + factor: *IDXGIFactory1; + vb: *ID3D11Buffer; + ib: *ID3D11Buffer; + vertex_shader: *ID3D11VertexShader; + input_layout: *ID3D11InputLayout; + vertex_constant_buffer: *ID3D11Buffer; + pixel_shader: *ID3D11PixelShader; + font_sampler: *ID3D11SamplerState; + font_texture_view: *ID3D11ShaderResourceView; + rasterizer_state: *ID3D11RasterizerState; + blend_state: *ID3D11BlendState; + depth_stencil_state: *ID3D11DepthStencilState; + vertex_buffer_size: u32 = 5000; + index_buffer_size: u32 = 10000; +}; + +// ImGui +imgui_impldx11_init :: (device: *ID3D11Device, device_context: *ID3D11DeviceContext) -> bool { + io := ImGui.GetIO(); + assert(io.BackendRendererUserData == null && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + bd := New(ImGui_ImplDX11_Data); + io.BackendRendererUserData = xx bd; + io.BackendRendererName = copy_string("imgui_impl_dx11").data; + io.BackendFlags_ |= .ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + bd.d3d_device = device; + bd.d3d_context = device_context; + + return true; +} + +imgui_impldx11_get_backend_data :: () -> *ImGui_ImplDX11_Data { + ctx := ImGui.GetCurrentContext(); + + if ctx != null { + return cast(*ImGui_ImplDX11_Data)ImGui.GetIO().BackendRendererUserData; + } + + return null; +} + +imgui_impldx11_shutdown :: () { + bd := imgui_impldx11_get_backend_data(); + assert(bd != null && "No renderer backend to shutdown, or already shutdown?"); + io := ImGui.GetIO(); + + imgui_impldx11_invalidate_device_objects(); + + io.BackendRendererName = null; + io.BackendRendererUserData = null; + free(bd); // @Incomplete +} diff --git a/imgui/imgui_sdl.jai b/imgui/imgui_sdl.jai new file mode 100644 index 0000000..160e2f0 --- /dev/null +++ b/imgui/imgui_sdl.jai @@ -0,0 +1,146 @@ +cursor_arrow: *SDL_Cursor; +cursor_text_input: *SDL_Cursor; +cursor_move: *SDL_Cursor; +cursor_resize_ns: *SDL_Cursor; +cursor_resize_ew: *SDL_Cursor; +cursor_resize_nesw: *SDL_Cursor; +cursor_resize_nwse: *SDL_Cursor; +g_Time: u64 = 0; +g_MousePressed := bool.[false, false, false]; +g_MouseWheel: float = 0.0; + +ImGui_ImplSdl_NewFrame :: (window: *SDL_Window) { + //if !g_FontTexture ImGui_ImplSdl_CreateDeviceObjects(); + + io := ImGui.GetIO(); + + // Setup display size (every frame to accommodate for window resizing) + w, h: s32; + display_w, display_h: s32; + SDL_GetWindowSize(window, *w, *h); + SDL_GL_GetDrawableSize(window, *display_w, *display_h); + io.DisplaySize = .{xx w, xx h}; + io.DisplayFramebufferScale = .{cast(float) display_w / w, cast(float) display_h / h}; + + // Setup time step + frequency := SDL_GetPerformanceFrequency(); + current_time := SDL_GetPerformanceCounter(); + if g_Time > 0 { + io.DeltaTime = cast(float)((cast(float64)(current_time - g_Time)) / frequency); + } else { + io.DeltaTime = (1.0/60.0); + } + // if (io.DeltaTime <= 0.0) { + // io.DeltaTime = (1.0/60.0); + // } + g_Time = current_time; + + // Setup inputs + // (we already got mouse wheel, keyboard keys & characters from SDL_PollEvent()) + mx, my: s32; + mouseMask := SDL_GetMouseState(*mx, *my); + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_FOCUS) { + io.MousePos = .{xx mx, xx my}; // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) + } else { + io.MousePos = .{-FLOAT32_MAX,-FLOAT32_MAX}; + } + + io.MouseDown[0] = (g_MousePressed[0] || (mouseMask & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0); // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[1] = (g_MousePressed[1] || (mouseMask & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0); + io.MouseDown[2] = (g_MousePressed[2] || (mouseMask & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0); + g_MousePressed[0], g_MousePressed[1], g_MousePressed[2] = false, false, false; + + io.MouseWheel = g_MouseWheel; + g_MouseWheel = 0.0; + + // Hide OS mouse cursor if ImGui is drawing it + SDL_ShowCursor(xx (io.MouseDrawCursor == false)); +} + +ImGui_ImplSdl_Init :: (window: *SDL_Window) -> bool { + io := ImGui.GetIO(); + io.KeyMap[ImGui.Key.Tab] = xx SDLK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. + io.KeyMap[ImGui.Key.LeftArrow] = xx SDL_SCANCODE_LEFT; + io.KeyMap[ImGui.Key.RightArrow] = xx SDL_SCANCODE_RIGHT; + io.KeyMap[ImGui.Key.UpArrow] = xx SDL_SCANCODE_UP; + io.KeyMap[ImGui.Key.DownArrow] = xx SDL_SCANCODE_DOWN; + io.KeyMap[ImGui.Key.PageUp] = xx SDL_SCANCODE_PAGEUP; + io.KeyMap[ImGui.Key.PageDown] = xx SDL_SCANCODE_PAGEDOWN; + io.KeyMap[ImGui.Key.Home] = xx SDL_SCANCODE_HOME; + io.KeyMap[ImGui.Key.End] = xx SDL_SCANCODE_END; + io.KeyMap[ImGui.Key.Delete] = xx SDLK_DELETE; + io.KeyMap[ImGui.Key.Backspace] = xx SDLK_BACKSPACE; + io.KeyMap[ImGui.Key.Enter] = xx SDLK_RETURN; + io.KeyMap[ImGui.Key.Escape] = xx SDLK_ESCAPE; + io.KeyMap[ImGui.Key.A] = xx SDLK_a; + io.KeyMap[ImGui.Key.C] = xx SDLK_c; + io.KeyMap[ImGui.Key.V] = xx SDLK_v; + io.KeyMap[ImGui.Key.X] = xx SDLK_x; + io.KeyMap[ImGui.Key.Y] = xx SDLK_y; + io.KeyMap[ImGui.Key.Z] = xx SDLK_z; + + io.SetClipboardTextFn = ImGui_ImplSdl_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplSdl_GetClipboardText; + io.ClipboardUserData = null; + + #if OS == .WINDOWS { + wmInfo: SDL_SysWMinfo ; + SDL_VERSION(*wmInfo.version); + SDL_GetWindowWMInfo(window, *wmInfo); + io.ImeWindowHandle = wmInfo.info.win.window; + } + + cursor_arrow = SDL_CreateSystemCursor(.ARROW); + cursor_text_input = SDL_CreateSystemCursor(.IBEAM); + cursor_move = SDL_CreateSystemCursor(.HAND); + cursor_resize_ns = SDL_CreateSystemCursor(.SIZENS); + cursor_resize_ew = SDL_CreateSystemCursor(.SIZEWE); + cursor_resize_nesw = SDL_CreateSystemCursor(.SIZENESW); + cursor_resize_nwse = SDL_CreateSystemCursor(.SIZENWSE); + + return true; +} + +// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. +// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. +// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. +// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. +ImGui_ImplSdl_ProcessEvent :: (event: *SDL_Event) -> bool { + io := ImGui.GetIO(); + if event.type == { + case SDL_MOUSEWHEEL; + if event.wheel.y > 0 g_MouseWheel = 1; + if event.wheel.y < 0 g_MouseWheel = -1; + return true; + case SDL_MOUSEBUTTONDOWN; + if event.button.button == SDL_BUTTON_LEFT g_MousePressed[0] = true; + if event.button.button == SDL_BUTTON_RIGHT g_MousePressed[1] = true; + if event.button.button == SDL_BUTTON_MIDDLE g_MousePressed[2] = true; + return true; + case SDL_TEXTINPUT; + io.AddInputCharactersUTF8(io, xx event.text.text.data); + return true; + case SDL_KEYDOWN; #through; + case SDL_KEYUP; + //@@key := event.key.keysym.sym & ~SDLK_SCANCODE_MASK; + key := event.key.keysym.sym & (xx ~(1<<30)); + io.KeysDown[key] = (event.type == SDL_KEYDOWN); + kmod := SDL_GetModState(); + io.KeyShift = ((kmod & KMOD_SHIFT) != 0); + io.KeyCtrl = ((kmod & KMOD_CTRL) != 0); + io.KeyAlt = ((kmod & KMOD_ALT) != 0); + io.KeySuper = ((kmod & KMOD_GUI) != 0); + return true; + } + return false; +} + + +ImGui_ImplSdl_GetClipboardText :: (data: *void) -> *u8 #c_call { + return SDL_GetClipboardText(); +} + +ImGui_ImplSdl_SetClipboardText :: (data: *void, text: *u8) #c_call { + SDL_SetClipboardText(text); +} + diff --git a/metaprogram.jai b/metaprogram.jai index 86d46b9..69d69ed 100644 --- a/metaprogram.jai +++ b/metaprogram.jai @@ -245,7 +245,6 @@ generate_member_serialization :: (struct_type: *Type_Info_Struct, builder: *Stri generate_member_deserialization :: (struct_type: *Type_Info_Struct, builder: *String_Builder, path: string = "") { for struct_type.members { if should_serialize(struct_type, it) { - print("DESER %\n", struct_type.name); new_path : string; if path.count == 0 { new_path = it.name; diff --git a/module.jai b/module.jai index e019919..90bec13 100644 --- a/module.jai +++ b/module.jai @@ -22,6 +22,7 @@ Engine_Core :: struct { #if EDITOR { editor: Editor; + imgui_context: *ImGui.ImGuiContext; } window: *Window; @@ -68,6 +69,7 @@ Shader_Time :: struct { } coven_init :: (window_title: string, window_width: u32, window_height: u32, fullscreen: bool) { + engine.imgui_context = ImGui.CreateContext(); engine.window = create_window(window_title, window_width, window_height, fullscreen); engine.renderer = create_renderer(engine.window); engine.renderer.vsync = true; @@ -91,6 +93,12 @@ coven_run :: (game_update_proc: (float), game_editor_update_proc: (float), game_ } } + sdl_window := cast(*SDL_Window_Type)engine.window; + + ImGui_ImplSdl_Init(sdl_window.sdl_window); + ImGui.StyleColorsClassic(); + ImGui.GetIO(); + time = xx seconds_since_init(); while !quit { @@ -98,6 +106,13 @@ coven_run :: (game_update_proc: (float), game_editor_update_proc: (float), game_ frame_index += 1; + ImGui_ImplSdl_NewFrame((cast(*SDL_Window_Type)engine.window).sdl_window); + imgui_impldx11_new_frame(); + ImGui.NewFrame(); + + show: bool = true; + ImGui.ShowDemoWindow(*show); + update_input(); // @Incomplete @@ -156,9 +171,16 @@ coven_run :: (game_update_proc: (float), game_editor_update_proc: (float), game_ update_audio(dt); + ImGui.Render(); render(); } + imgui_impldx11_shutdown(); + + #if WITH_EDITOR { + ImGui.DestroyContext(engine.imgui_context); + } + SDL_DestroyWindow(engine.window); SDL_Quit(); diff --git a/physics/physics.jai b/physics/physics.jai index b533b58..b6e67bf 100644 --- a/physics/physics.jai +++ b/physics/physics.jai @@ -50,7 +50,9 @@ Collider :: struct { collides_with_layers: Collision_Layers = .LAYER1; override_aabb: bool; + render_aabb: bool; aabb: AABB; + union { sphere: Sphere; @DontSerialize mesh : Mesh_Collider; @DontSerialize diff --git a/renderer/dx11_renderer.jai b/renderer/dx11_renderer.jai index d9aed10..6dfdf50 100644 --- a/renderer/dx11_renderer.jai +++ b/renderer/dx11_renderer.jai @@ -3,6 +3,7 @@ #import "dxgi"; #import "File"; #import "Windows"; +#load "../imgui/imgui_dx11.jai"; Graphics_Backend :: D3D11_Backend; Backend_Vertex_Input :: *ID3D11InputLayout; @@ -77,7 +78,7 @@ create_backend :: (w: *Window) -> *D3D11_Backend { return null; } - //imgui_impldx11_init(backend.d3d_device, backend.d3d_context); + imgui_impldx11_init(backend.d3d_device, backend.d3d_context); return backend; } @@ -1439,6 +1440,10 @@ render :: (backend: *D3D11_Backend, command_buffer: *Render_Command_Buffer) { } ID3D11DeviceContext_DrawIndexedInstanced(d3d_context, xx it.draw_indexed_instanced.index_count, xx it.draw_indexed_instanced.instance_count, xx it.draw_indexed_instanced.start_index, xx it.draw_indexed_instanced.base_vertex, 0); + case .RENDER_IMGUI; { + ImGui.Render(); + imgui_impldx11_render_draw_data(ImGui.GetDrawData()); + } } } @@ -1515,5 +1520,4 @@ create_rasterizer_state :: (backend: *D3D11_Backend, fill: bool, cull_face: Cull return *backend.rasterizer_states[backend.rasterizer_states.count-1]; } -//#load "imgui_dx11.jai"; //#import "shader_parsing"; diff --git a/renderer/renderer.jai b/renderer/renderer.jai index 1b9431c..a9f2d59 100644 --- a/renderer/renderer.jai +++ b/renderer/renderer.jai @@ -434,6 +434,8 @@ Render_Command_Type :: enum { SET_DRAW_MODE; SET_CULL_FACE; SET_DEPTH_WRITE; + + RENDER_IMGUI; } MAX_RENDER_TARGETS :: 8; @@ -1108,6 +1110,12 @@ create_pipeline_state :: (using renderer: *Renderer, vs: Shader_Handle, ps: Shad return xx pipeline_states.count; } +push_cmd_render_imgui :: (using renderer: *Renderer) { + command : Render_Command; + command.type = .RENDER_IMGUI; + array_add(*engine.renderer.command_buffer.commands, command); +} + push_cmd_set_pipeline_state :: (using renderer: *Renderer, handle: Pipeline_State_Handle) { assert(handle > 0); @@ -1647,7 +1655,6 @@ render :: () { // #load "ui_system.jai"; // #load "ui.jai"; //} -//ImGui :: #import "ImGui"; #scope_module #load "dx11_renderer.jai"; diff --git a/windowing/window.jai b/windowing/window.jai index e8fbc26..0eecf03 100644 --- a/windowing/window.jai +++ b/windowing/window.jai @@ -13,6 +13,8 @@ SDL_Window_Type :: struct { // SDL #import "SDL"; +#load "../imgui/imgui_sdl.jai"; + create_window :: (title: string, width: u32, height: u32, fullscreen: bool) -> *Window { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);