Last active
June 19, 2019 03:27
-
-
Save chromedays/93b310087bdb67a298940f213cf03675 to your computer and use it in GitHub Desktop.
뭔가 directx (5시간 걸림)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct VertexIn | |
{ | |
float3 pos : POSITION; | |
float4 color : COLOR; | |
float3 normal : TEXCOORD; | |
}; | |
struct VertexOut | |
{ | |
float4 pos : SV_POSITION; | |
float4 color : COLOR; | |
float3 normal : TEXCOORD0; | |
float3 world_pos : TEXCOORD1; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "default.hlsli" | |
float4 main(VertexOut i) : SV_Target | |
{ | |
return i.color; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "default.hlsli" | |
VertexOut main(VertexIn i) | |
{ | |
VertexOut o; | |
#if 0 | |
o.pos = mul(float4(i.pos, 1.f), g_mvp); | |
o.color = i.color; | |
o.normal = normalize(mul(float4(i.normal, 0.f), g_invt_model).xyz); | |
o.world_pos = mul(float4(i.pos, 1.f), g_model).xyz; | |
#endif | |
o.pos = float4(i.pos, 1.f); | |
o.color = i.color; | |
o.normal = float3(0.f, 0.f, 0.f); | |
o.world_pos = float3(0.f, 0.f, 0.f); | |
return o; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _CRTDBG_MAP_ALLOC | |
#include <stdlib.h> | |
#include <crtdbg.h> | |
#include <SDL.h> | |
#include <SDL_syswm.h> | |
#include <d3d11.h> | |
#include <d3dcompiler.h> | |
#include <algorithm> | |
#include <type_traits> | |
#include <assert.h> | |
#pragma comment(lib, "d3d11.lib") | |
#pragma comment(lib, "dxgi.lib") | |
#pragma comment(lib, "d3dcompiler.lib") | |
#ifdef _WIN32 | |
#define PLATFORM_WINDOWS | |
#endif | |
#define ASSERT(expression) \ | |
do \ | |
{ \ | |
assert((expression)); \ | |
} while (0) | |
using uint = unsigned int; | |
template<typename Fn> | |
struct ScopeGuard | |
{ | |
Fn func; | |
bool active; | |
ScopeGuard(const Fn& func) : func(func), active(true) | |
{ | |
} | |
ScopeGuard(Fn&& func) : func(std::move(func)), active(true) | |
{ | |
} | |
ScopeGuard(ScopeGuard&& other) | |
: func(std::move(other.func)), active(other.active) | |
{ | |
other.active = false; | |
} | |
~ScopeGuard() | |
{ | |
if (active) | |
func(); | |
} | |
ScopeGuard(const ScopeGuard&) = delete; | |
ScopeGuard& operator=(const ScopeGuard&) = delete; | |
}; | |
#define DO_STRING_JOIN(a, b) a##b | |
#define STRING_JOIN(a, b) DO_STRING_JOIN(a, b) | |
#define SCOPE_EXIT(code) \ | |
ScopeGuard STRING_JOIN(scope_guard_, __LINE__)([&]() { code; }) | |
using Size = ptrdiff_t; | |
struct StringView | |
{ | |
using Char = char; | |
const Char* buf; | |
Size len; | |
}; | |
StringView string_view(const StringView::Char* c_str) | |
{ | |
StringView result = {.buf = c_str, .len = (Size)strlen(c_str)}; | |
return result; | |
} | |
struct String | |
{ | |
using Char = char; | |
const Char* buf; | |
Size len; | |
}; | |
String string_create(const String::Char* c_str) | |
{ | |
Size len = (Size)strlen(c_str); | |
Size buf_size = (len + 1) * sizeof(String::Char); | |
String::Char* buf = (String::Char*)malloc(buf_size); | |
memcpy_s(buf, buf_size, c_str, buf_size); | |
String result = {.buf = buf, .len = len}; | |
return result; | |
} | |
String string_create(const String::Char* a, const String::Char* b) | |
{ | |
Size new_len = (Size)strlen(a) + (Size)strlen(b); | |
Size new_buf_size = (new_len + 1) * sizeof(String::Char); | |
String::Char* new_buf = (String::Char*)malloc(new_buf_size); | |
strcpy_s(new_buf, new_buf_size, a); | |
strcpy_s(new_buf + strlen(a), | |
new_buf_size - strlen(a) * sizeof(String::Char), b); | |
new_buf[new_buf_size - 1] = '\0'; | |
String result = {.buf = new_buf, .len = new_len}; | |
return result; | |
} | |
void string_destroy(String* str) | |
{ | |
if (str->buf) | |
free(const_cast<String::Char*>(str->buf)); | |
} | |
String operator+(String str, const String::Char* buf) | |
{ | |
Size new_len = str.len + (Size)strlen(buf); | |
Size new_buf_size = (new_len + 1) * sizeof(String::Char); | |
String::Char* new_buf = (String::Char*)malloc(new_buf_size); | |
strcpy_s(new_buf, new_buf_size, str.buf); | |
strcpy_s(new_buf + str.len, new_buf_size - (str.len * sizeof(String::Char)), | |
buf); | |
new_buf[new_buf_size - 1] = '\0'; | |
String result = {.buf = new_buf, .len = new_len}; | |
return result; | |
} | |
String& operator+=(String& str, const String::Char* buf) | |
{ | |
String temp = str; | |
str = str + buf; | |
string_destroy(&temp); | |
return str; | |
} | |
template<typename T> | |
bool is_in_range_inclusive(T value, T min, T max) | |
{ | |
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type!"); | |
bool result = (value >= min) && (value <= max); | |
return result; | |
} | |
template<typename T> | |
bool is_in_range_exclusive(T value, T min, T max) | |
{ | |
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type!"); | |
bool result = (value >= min) && (value < max); | |
return result; | |
} | |
template<typename T> | |
inline constexpr bool is_pod_v = | |
std::is_trivial_v<T>&& std::is_standard_layout_v<T>; | |
template<typename T> | |
struct Span | |
{ | |
T* buf; | |
Size size; | |
T& operator[](Size index) | |
{ | |
ASSERT(is_in_range_exclusive<Size>(index, 0, size)); | |
return buf[index]; | |
} | |
const T& operator[](Size index) const | |
{ | |
ASSERT(is_in_range_exclusive<Size>(index, 0, size)); | |
return buf[index]; | |
} | |
T* begin() | |
{ | |
return buf; | |
} | |
T* end() | |
{ | |
return buf + size; | |
} | |
const T* begin() const | |
{ | |
return buf; | |
} | |
const T* end() const | |
{ | |
return buf + size; | |
} | |
}; | |
static_assert(is_pod_v<Span<int>>); | |
template<typename T, Size N> | |
Span<T> span(T (&arr)[N]) | |
{ | |
Span result = {.buf = arr, .size = (Size)std::size(arr)}; | |
return result; | |
} | |
template<typename T> | |
struct Array | |
{ | |
static_assert(is_pod_v<T>); | |
T* buf; | |
Size cap; | |
Size size; | |
operator Span<T>() | |
{ | |
Span<T> result = {.buf = buf, .size = size}; | |
return result; | |
} | |
T& operator[](Size index) | |
{ | |
ASSERT(is_in_range_exclusive<Size>(index, 0, size)); | |
return buf[index]; | |
} | |
const T& operator[](Size index) const | |
{ | |
ASSERT(is_in_range_exclusive<Size>(index, 0, size)); | |
return buf[index]; | |
} | |
T* begin() | |
{ | |
return buf; | |
} | |
T* end() | |
{ | |
return buf + size; | |
} | |
const T* begin() const | |
{ | |
return buf; | |
} | |
const T* end() const | |
{ | |
return buf + size; | |
} | |
}; | |
static_assert(is_pod_v<Array<int>>); | |
template<typename T> | |
Array<T> array_create(Size size = 0) | |
{ | |
Array<T> result = {}; | |
if (size > 0) | |
{ | |
result.buf = (T*)malloc(size * sizeof(T)); | |
result.cap = size; | |
result.size = size; | |
} | |
return result; | |
} | |
template<typename T> | |
Size array_size_bytes(const Array<T>* arr); | |
template<typename T> | |
Array<T> array_create(std::initializer_list<T> init_list) | |
{ | |
Array<T> result = array_create<T>((Size)init_list.size()); | |
errno_t err = memcpy_s(result.buf, array_size_bytes(&result), | |
init_list.begin(), init_list.size() * sizeof(T)); | |
ASSERT(err == 0); | |
return result; | |
} | |
template<typename T> | |
void array_destroy(Array<T>* arr) | |
{ | |
if (arr->buf) | |
free(arr->buf); | |
} | |
template<typename T> | |
Size array_size_bytes(const Array<T>* arr) | |
{ | |
Size result = arr->size * sizeof(T); | |
return result; | |
} | |
struct IntVector2 | |
{ | |
int x; | |
int y; | |
}; | |
struct Vector3 | |
{ | |
float x; | |
float y; | |
float z; | |
}; | |
Vector3 operator+(Vector3 a, Vector3 b) | |
{ | |
Vector3 result = {.x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z}; | |
return result; | |
} | |
Vector3 operator-(Vector3 a, Vector3 b) | |
{ | |
Vector3 result = {.x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z}; | |
return result; | |
} | |
Vector3 operator*(Vector3 a, Vector3 b) | |
{ | |
Vector3 result = {.x = a.x * b.x, .y = a.y * b.y, .z = a.z * b.z}; | |
return result; | |
} | |
Vector3 operator/(Vector3 a, Vector3 b) | |
{ | |
Vector3 result = {.x = a.x / b.x, .y = a.y / b.y, .z = a.z / b.z}; | |
return result; | |
} | |
Vector3 operator/(Vector3 v, float s) | |
{ | |
Vector3 result = {.x = v.x / s, .y = v.y / s, .z = v.z / s}; | |
return result; | |
} | |
Vector3& operator+=(Vector3& a, Vector3 b) | |
{ | |
a = a + b; | |
return a; | |
} | |
Vector3& operator-=(Vector3& a, Vector3 b) | |
{ | |
a = a - b; | |
return a; | |
} | |
Vector3& operator*=(Vector3& a, Vector3 b) | |
{ | |
a = a * b; | |
return a; | |
} | |
Vector3& operator/=(Vector3& a, Vector3 b) | |
{ | |
a = a / b; | |
return a; | |
} | |
Vector3& operator/=(Vector3& v, float s) | |
{ | |
v = v / s; | |
return v; | |
} | |
Vector3 cross(Vector3 a, Vector3 b) | |
{ | |
Vector3 result = {.x = a.y * b.z - a.z * b.y, | |
.y = a.z * b.x - a.x * b.z, | |
.z = a.x * b.y - a.y * b.x}; | |
return result; | |
} | |
float dot(Vector3 a, Vector3 b) | |
{ | |
float result = a.x * b.x + a.y * b.y + a.z * b.z; | |
return result; | |
} | |
float length_squared(Vector3 v) | |
{ | |
float result = dot(v, v); | |
return result; | |
} | |
float length(Vector3 v) | |
{ | |
float result = sqrtf(length_squared(v)); | |
return result; | |
} | |
Vector3 normalize(Vector3 v) | |
{ | |
Vector3 result = v / length(v); | |
return result; | |
} | |
struct Vector4 | |
{ | |
float x; | |
float y; | |
float z; | |
float w; | |
}; | |
struct Matrix | |
{ | |
float e[4][4]; | |
}; | |
struct Vertex | |
{ | |
Vector3 pos; | |
Vector4 color; | |
Vector3 normal; | |
}; | |
namespace color | |
{ | |
constexpr Vector4 white{1.f, 1.f, 1.f, 1.f}; | |
constexpr Vector4 black{0.f, 0.f, 0.f, 1.f}; | |
constexpr Vector4 red{1.f, 0.f, 0.f, 1.f}; | |
constexpr Vector4 green{0.f, 1.f, 0.f, 1.f}; | |
constexpr Vector4 blue{0.f, 0.f, 1.f, 1.f}; | |
constexpr Vector4 yellow{1.f, 1.f, 0.f, 1.f}; | |
constexpr Vector4 cyan{0.f, 1.f, 1.f, 1.f}; | |
constexpr Vector4 magenta{1.f, 0.f, 1.f, 1.f}; | |
} | |
struct Mesh | |
{ | |
Array<Vertex> vertices; | |
Array<uint> indices; | |
}; | |
void mesh_destroy(Mesh* mesh) | |
{ | |
array_destroy(&mesh->vertices); | |
array_destroy(&mesh->indices); | |
} | |
void set_average_interpolated_normals(Span<Vertex> vertices, Span<uint> indices) | |
{ | |
for (Vertex& v : vertices) | |
v.normal = {}; | |
for (int i = 0; i < indices.size / 3; ++i) | |
{ | |
uint i0 = indices[i * 3 + 0]; | |
uint i1 = indices[i * 3 + 1]; | |
uint i2 = indices[i * 3 + 2]; | |
Vector3 v0 = vertices[i0].pos; | |
Vector3 v1 = vertices[i1].pos; | |
Vector3 v2 = vertices[i2].pos; | |
Vector3 e0 = v1 - v0; | |
Vector3 e1 = v2 - v0; | |
Vector3 n = cross(e0, e1); | |
vertices[i0].normal += n; | |
vertices[i0].normal += n; | |
vertices[i0].normal += n; | |
} | |
for (Vertex& v : vertices) | |
v.normal = normalize(v.normal); | |
} | |
Mesh create_triangle_mesh() | |
{ | |
Mesh result = {.vertices = | |
array_create<Vertex>({{{-0.5f, -0.5f, 0.f}, color::red}, | |
{{0.5f, -0.5f, 0.f}, color::red}, | |
{{0.f, 0.5f, 0.f}, color::red}}), | |
.indices = array_create<uint>({0, 1, 2})}; | |
set_average_interpolated_normals(result.vertices, result.indices); | |
return result; | |
} | |
struct Camera | |
{ | |
Vector3 eye; | |
float yaw; | |
float pitch; | |
}; | |
struct NativeHandle | |
{ | |
uint64_t value; | |
}; | |
Array<uint8_t> read_binary_file(const char* filename); | |
namespace d3d | |
{ | |
struct Context | |
{ | |
ID3D11Device* device; | |
ID3D11DeviceContext* device_context; | |
IDXGISwapChain* swap_chain; | |
ID3D11RenderTargetView* render_target_view; | |
ID3D11Texture2D* depth_stencil_buffer; | |
ID3D11DepthStencilView* depth_stencil_view; | |
D3D_FEATURE_LEVEL feature_level; | |
uint msaa4x_quality; | |
ID3D11InputLayout* input_layout; | |
}; | |
template<typename T, typename U> | |
long query_interface(T* ptr, U** out) | |
{ | |
return ptr->QueryInterface(__uuidof(U), (void**)out); | |
} | |
Context context_create(NativeHandle window, IntVector2 window_size) | |
{ | |
ASSERT(window_size.x > 0 && window_size.y > 0); | |
HWND hwnd = *(HWND*)&window; | |
Context result = {}; | |
// Create device and device context | |
{ | |
D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, | |
D3D11_CREATE_DEVICE_DEBUG, nullptr, 0, | |
D3D11_SDK_VERSION, &result.device, | |
&result.feature_level, &result.device_context); | |
// TODO: handle the case when msaa4x_quality receives zero | |
result.device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, | |
5, &result.msaa4x_quality); | |
} | |
// Create swap chain | |
{ | |
DXGI_SWAP_CHAIN_DESC desc = { | |
.BufferDesc = | |
{ | |
.Width = (UINT)window_size.x, | |
.Height = (UINT)window_size.y, | |
.RefreshRate = {.Numerator = 60, .Denominator = 1}, | |
.Format = DXGI_FORMAT_R8G8B8A8_UNORM, | |
.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, | |
.Scaling = DXGI_MODE_SCALING_UNSPECIFIED, | |
}, | |
.SampleDesc = | |
{ | |
.Count = 4, | |
.Quality = result.msaa4x_quality - 1, | |
}, | |
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, | |
.BufferCount = 1, | |
.OutputWindow = hwnd, | |
.Windowed = true, | |
.SwapEffect = DXGI_SWAP_EFFECT_DISCARD, | |
.Flags = 0}; | |
IDXGIDevice* dxgi_device; | |
IDXGIAdapter* dxgi_adapter; | |
IDXGIFactory* dxgi_factory; | |
query_interface(result.device, &dxgi_device); | |
dxgi_device->GetAdapter(&dxgi_adapter); | |
dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory)); | |
dxgi_factory->CreateSwapChain(result.device, &desc, &result.swap_chain); | |
dxgi_factory->Release(); | |
dxgi_adapter->Release(); | |
dxgi_device->Release(); | |
} | |
// Create render target view | |
{ | |
ID3D11Texture2D* back_buffer; | |
result.swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer)); | |
result.device->CreateRenderTargetView(back_buffer, nullptr, | |
&result.render_target_view); | |
back_buffer->Release(); | |
} | |
// Create depth/stencil buffer | |
{ | |
D3D11_TEXTURE2D_DESC desc = { | |
.Width = (UINT)window_size.x, | |
.Height = (UINT)window_size.y, | |
.MipLevels = 1, | |
.ArraySize = 1, | |
.Format = DXGI_FORMAT_D24_UNORM_S8_UINT, | |
.SampleDesc = {.Count = 4, .Quality = result.msaa4x_quality - 1}, | |
.Usage = D3D11_USAGE_DEFAULT, | |
.BindFlags = D3D11_BIND_DEPTH_STENCIL, | |
.CPUAccessFlags = 0, | |
.MiscFlags = 0, | |
}; | |
result.device->CreateTexture2D(&desc, nullptr, | |
&result.depth_stencil_buffer); | |
result.device->CreateDepthStencilView( | |
result.depth_stencil_buffer, nullptr, &result.depth_stencil_view); | |
} | |
// Bind views to output merger | |
result.device_context->OMSetRenderTargets(1, &result.render_target_view, | |
result.depth_stencil_view); | |
// Create input layout | |
{ | |
constexpr D3D11_INPUT_ELEMENT_DESC vertex_layout[3] = { | |
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, | |
offsetof(Vertex, pos), D3D11_INPUT_PER_VERTEX_DATA, 0}, | |
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, | |
offsetof(Vertex, color), D3D11_INPUT_PER_VERTEX_DATA, 0}, | |
{"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, | |
offsetof(Vertex, normal), D3D11_INPUT_PER_VERTEX_DATA, 0}}; | |
StringView dummy_hlsl_source = string_view(R"( | |
struct VertexIn | |
{ | |
float3 pos : POSITION; | |
float4 color : COLOR; | |
float3 normal : TEXCOORD; | |
}; | |
float4 main(VertexIn i) : SV_POSITION | |
{ | |
return float4(0.f, 0.f, 0.f, 0.f); | |
} | |
)"); | |
D3D_SHADER_MACRO macros[] = {{}}; | |
ID3DBlob* bytecode; | |
D3DCompile(dummy_hlsl_source.buf, dummy_hlsl_source.len, nullptr, | |
macros, nullptr, "main", "vs_5_0", 0, 0, &bytecode, nullptr); | |
result.device->CreateInputLayout( | |
vertex_layout, (UINT)std::size(vertex_layout), | |
bytecode->GetBufferPointer(), bytecode->GetBufferSize(), | |
&result.input_layout); | |
bytecode->Release(); | |
} | |
return result; | |
} | |
void context_destroy(Context* context) | |
{ | |
context->device->Release(); | |
context->device_context->Release(); | |
context->swap_chain->Release(); | |
context->render_target_view->Release(); | |
context->depth_stencil_buffer->Release(); | |
context->depth_stencil_view->Release(); | |
context->input_layout->Release(); | |
} | |
struct Renderable | |
{ | |
const Mesh* mesh; | |
ID3D11Buffer* vb; | |
ID3D11Buffer* ib; | |
}; | |
Renderable renderable_create(const Mesh* mesh, const Context* d3d) | |
{ | |
ASSERT(mesh->vertices.size > 0 && mesh->indices.size > 0); | |
ID3D11Buffer* vb; | |
D3D11_BUFFER_DESC vb_desc = {.ByteWidth = | |
(UINT)array_size_bytes(&mesh->vertices), | |
.Usage = D3D11_USAGE_DEFAULT, | |
.BindFlags = D3D11_BIND_VERTEX_BUFFER, | |
.CPUAccessFlags = 0, | |
.MiscFlags = 0, | |
.StructureByteStride = 0}; | |
D3D11_SUBRESOURCE_DATA vb_init_data = {.pSysMem = mesh->vertices.buf}; | |
d3d->device->CreateBuffer(&vb_desc, &vb_init_data, &vb); | |
ID3D11Buffer* ib; | |
D3D11_BUFFER_DESC ib_desc = {.ByteWidth = | |
(UINT)array_size_bytes(&mesh->indices), | |
.Usage = D3D11_USAGE_DEFAULT, | |
.BindFlags = D3D11_BIND_INDEX_BUFFER, | |
.CPUAccessFlags = 0, | |
.MiscFlags = 0, | |
.StructureByteStride = 0}; | |
D3D11_SUBRESOURCE_DATA ib_init_data = {.pSysMem = mesh->indices.buf}; | |
d3d->device->CreateBuffer(&ib_desc, &ib_init_data, &ib); | |
Renderable result = {.mesh = mesh, .vb = vb, .ib = ib}; | |
return result; | |
} | |
void renderable_destroy(Renderable* renderable) | |
{ | |
renderable->vb->Release(); | |
renderable->ib->Release(); | |
} | |
struct Shader | |
{ | |
ID3D11VertexShader* vs; | |
ID3D11PixelShader* ps; | |
}; | |
Shader shader_create(const char* base_dir_path, | |
const char* shader_name, | |
Context* d3d) | |
{ | |
String shader_path_without_suffix = | |
string_create(base_dir_path, shader_name); | |
String vs_path = shader_path_without_suffix + "_v.cso"; | |
String ps_path = shader_path_without_suffix + "_p.cso"; | |
Array<uint8_t> vs_source = read_binary_file(vs_path.buf); | |
Array<uint8_t> ps_source = read_binary_file(ps_path.buf); | |
SCOPE_EXIT({ | |
array_destroy(&ps_source); | |
array_destroy(&vs_source); | |
string_destroy(&ps_path); | |
string_destroy(&vs_path); | |
string_destroy(&shader_path_without_suffix); | |
}); | |
ID3D11VertexShader* vs; | |
d3d->device->CreateVertexShader(vs_source.buf, vs_source.size, nullptr, | |
&vs); | |
ID3D11PixelShader* ps; | |
d3d->device->CreatePixelShader(ps_source.buf, ps_source.size, nullptr, &ps); | |
Shader result = {.vs = vs, .ps = ps}; | |
return result; | |
} | |
void shader_destroy(Shader* shader) | |
{ | |
shader->vs->Release(); | |
shader->ps->Release(); | |
} | |
} | |
struct App | |
{ | |
IntVector2 window_size; | |
SDL_Window* window_handle; | |
}; | |
App app_create(const char* window_title, IntVector2 window_size) | |
{ | |
SDL_Init(SDL_INIT_VIDEO); | |
App result = {.window_size = window_size, | |
.window_handle = SDL_CreateWindow( | |
window_title, SDL_WINDOWPOS_CENTERED, | |
SDL_WINDOWPOS_CENTERED, window_size.x, window_size.y, 0)}; | |
return result; | |
} | |
void app_destroy(App* app) | |
{ | |
SDL_DestroyWindow(app->window_handle); | |
SDL_Quit(); | |
} | |
NativeHandle get_native_window_handle(const App* app) | |
{ | |
NativeHandle result{}; | |
#if defined(PLATFORM_WINDOWS) | |
static_assert((sizeof(decltype(SDL_SysWMinfo::info.win.window)) <= | |
sizeof(NativeHandle))); | |
SDL_SysWMinfo wminfo{}; | |
SDL_VERSION(&wminfo.version); | |
SDL_GetWindowWMInfo(app->window_handle, &wminfo); | |
memcpy(&result, &wminfo.info.win.window, sizeof(wminfo.info.win.window)); | |
#else | |
#error This platform is not supported | |
#endif | |
return result; | |
} | |
StringView get_exe_dir_path() | |
{ | |
return string_view(SDL_GetBasePath()); | |
} | |
struct Timer | |
{ | |
int64_t frequency; | |
int64_t counter; | |
}; | |
int64_t get_current_perf_counter() | |
{ | |
LARGE_INTEGER curr_counter{}; | |
QueryPerformanceCounter(&curr_counter); | |
return curr_counter.QuadPart; | |
} | |
int64_t get_perf_frequency() | |
{ | |
LARGE_INTEGER freq{}; | |
QueryPerformanceFrequency(&freq); | |
return freq.QuadPart; | |
} | |
Timer timer() | |
{ | |
Timer result = {.frequency = get_perf_frequency(), | |
.counter = get_current_perf_counter()}; | |
return result; | |
} | |
void reset(Timer* timer) | |
{ | |
timer->counter = get_current_perf_counter(); | |
} | |
float get_elapsed_seconds(const Timer* timer) | |
{ | |
int64_t curr_counter = get_current_perf_counter(); | |
float result = | |
(float)(curr_counter - timer->counter) / (float)timer->frequency; | |
return result; | |
} | |
Array<uint8_t> read_binary_file(const char* filename) | |
{ | |
HANDLE file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, | |
OPEN_EXISTING, 0, nullptr); | |
ASSERT(file != INVALID_HANDLE_VALUE); | |
LARGE_INTEGER file_size = {}; | |
GetFileSizeEx(file, &file_size); | |
Array<uint8_t> result = array_create<uint8_t>((Size)file_size.QuadPart); | |
DWORD bytes_read = 0; | |
ReadFile(file, result.buf, (DWORD)file_size.QuadPart, &bytes_read, nullptr); | |
ASSERT(file_size.QuadPart == bytes_read); | |
return result; | |
} | |
void process_os_event(bool* running) | |
{ | |
SDL_Event e = {}; | |
while (SDL_PollEvent(&e)) | |
{ | |
if (e.type == SDL_QUIT) | |
*running = false; | |
else | |
{ | |
} | |
} | |
} | |
void run() | |
{ | |
using namespace d3d; | |
App app = app_create("DemoEngine", {1280, 720}); | |
SCOPE_EXIT(app_destroy(&app)); | |
Context d3d = | |
context_create(get_native_window_handle(&app), app.window_size); | |
SCOPE_EXIT(d3d::context_destroy(&d3d)); | |
Mesh triangle_mesh = create_triangle_mesh(); | |
SCOPE_EXIT(mesh_destroy(&triangle_mesh)); | |
Renderable triangle = renderable_create(&triangle_mesh, &d3d); | |
SCOPE_EXIT(renderable_destroy(&triangle)); | |
StringView base_path = get_exe_dir_path(); | |
Shader default_shader = shader_create(base_path.buf, "default", &d3d); | |
SCOPE_EXIT(shader_destroy(&default_shader)); | |
ID3D11RasterizerState* rs; | |
{ | |
D3D11_RASTERIZER_DESC desc = {.FillMode = D3D11_FILL_SOLID, | |
.CullMode = D3D11_CULL_BACK, | |
.FrontCounterClockwise = true, | |
.DepthBias = 0, | |
.DepthBiasClamp = 0.f, | |
.SlopeScaledDepthBias = 0.f, | |
.DepthClipEnable = true, | |
.ScissorEnable = false, | |
.MultisampleEnable = true, | |
.AntialiasedLineEnable = true}; | |
d3d.device->CreateRasterizerState(&desc, &rs); | |
} | |
SCOPE_EXIT(rs->Release()); | |
Timer tm = timer(); | |
bool running = true; | |
while (running) | |
{ | |
float dt = get_elapsed_seconds(&tm); | |
reset(&tm); | |
process_os_event(&running); | |
// Begin render | |
{ | |
D3D11_VIEWPORT vp = {.TopLeftX = 0.f, | |
.TopLeftY = 0.f, | |
.Width = (float)app.window_size.x, | |
.Height = (float)app.window_size.y, | |
.MinDepth = 0.f, | |
.MaxDepth = 1.f}; | |
d3d.device_context->RSSetViewports(1, &vp); | |
FLOAT clear_color[] = {1.f, 0.7f, 1.f, 1.f}; | |
d3d.device_context->ClearRenderTargetView(d3d.render_target_view, | |
clear_color); | |
d3d.device_context->ClearDepthStencilView( | |
d3d.depth_stencil_view, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, | |
1.f, 0); | |
d3d.device_context->IASetInputLayout(d3d.input_layout); | |
} | |
d3d.device_context->IASetPrimitiveTopology( | |
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); | |
d3d.device_context->RSSetState(rs); | |
d3d.device_context->VSSetShader(default_shader.vs, nullptr, 0); | |
d3d.device_context->PSSetShader(default_shader.ps, nullptr, 0); | |
UINT stride = sizeof(Vertex); | |
UINT offset = 0; | |
d3d.device_context->IASetVertexBuffers(0, 1, &triangle.vb, &stride, | |
&offset); | |
d3d.device_context->IASetIndexBuffer(triangle.ib, DXGI_FORMAT_R32_UINT, | |
0); | |
d3d.device_context->DrawIndexed((UINT)triangle.mesh->indices.size, 0, | |
0); | |
d3d.swap_chain->Present(1, 0); | |
} | |
} | |
int main(int, char**) | |
{ | |
run(); | |
_CrtDumpMemoryLeaks(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment