summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md14
-rw-r--r--examples/demo/demo.c22
-rw-r--r--scripts/apidocs/coldark_prism.css317
-rw-r--r--scripts/apidocs/doc_styles.css76
-rw-r--r--scripts/apidocs/gen_apidocs.py109
-rw-r--r--scripts/apidocs/index.html236
-rw-r--r--scripts/apidocs/modules.md (renamed from scripts/api_gen/modules.md)0
-rw-r--r--scripts/apidocs/prism.css3
-rw-r--r--scripts/apidocs/prism.js6
-rw-r--r--scripts/apidocs/template.html (renamed from scripts/api_gen/template.html)0
-rw-r--r--scripts/gen_amalgamation.py24
-rw-r--r--scripts/gen_amalgamation.sh5
-rw-r--r--scripts/gen_apidocs.sh3
-rw-r--r--src/core/core.c2
-rw-r--r--src/core/core.h4
-rw-r--r--src/maths/primitives.c18
-rw-r--r--src/new_render/immdraw.h14
-rw-r--r--src/new_render/render.c107
-rw-r--r--src/new_render/render.h7
-rw-r--r--src/new_render/render_types.h2
-rw-r--r--src/ral/backends/opengl/backend_opengl.c284
-rw-r--r--src/ral/backends/opengl/backend_opengl.h6
-rw-r--r--src/ral/ral_common.c46
-rw-r--r--src/ral/ral_common.h20
-rw-r--r--src/ral/ral_impl.h56
-rw-r--r--src/ral/ral_types.h5
-rw-r--r--src/render/backends/opengl/backend_opengl.c16
-rw-r--r--src/render/bind_group_layouts.h30
-rw-r--r--src/render/immediate.h19
-rw-r--r--src/render/ral.h40
-rw-r--r--src/render/static_pipeline.h30
-rw-r--r--src/resources/gltf.c96
-rw-r--r--src/resources/loaders.h4
-rw-r--r--src/resources/obj.c6
-rw-r--r--src/std/buf.h6
-rw-r--r--xmake.lua6
36 files changed, 1382 insertions, 257 deletions
diff --git a/README.md b/README.md
index b5305ef..5ec3d37 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
# celeritas-core
-Celeritas is an engine written in C that acts as an extendable base layer for creating custom game-specific "engines" (frameworks) that includes helpful wrappers around input handling, cameras, rendering abstractions, and other helpful APIs
-for making games and binding to other languages' FFIs.
+Celeritas is an engine 'core' written in C that acts as an extendable base layer for creating custom game-specific "engines" (frameworks). It includes helpful wrappers around input handling, cameras, rendering abstractions, and other helpful APIs for making games and binding to other languages' FFIs.
+
+Higher-level concepts can then be overlaid on top of this core!
![Backpack model with lighting](examples/obj_loading/backpack_screenshot.png)
@@ -9,6 +10,15 @@ for making games and binding to other languages' FFIs.
All third-party dependencies are licensed under their own license.
+## Project Structure
+
+```
+/bindgen - bindings code generation for other languages
+/deps - third-party dependencies
+/docs - high-level documentation
+/scripts
+```
+
## Developing
#### Handy commands
diff --git a/examples/demo/demo.c b/examples/demo/demo.c
deleted file mode 100644
index 3b2354a..0000000
--- a/examples/demo/demo.c
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <glfw3.h>
-
-#include "core.h"
-#include "render.h"
-
-int main() {
- core* core = core_bringup();
-
- // Main loop
- while (!glfwWindowShouldClose(core->renderer.window)) {
- input_update(&core->input);
- threadpool_process_results(&core->threadpool, 1);
-
- render_frame_begin(&core->renderer);
-
- // insert work here
-
- render_frame_end(&core->renderer);
- }
-
- return 0;
-}
diff --git a/scripts/apidocs/coldark_prism.css b/scripts/apidocs/coldark_prism.css
new file mode 100644
index 0000000..39dd470
--- /dev/null
+++ b/scripts/apidocs/coldark_prism.css
@@ -0,0 +1,317 @@
+/**
+ * Coldark Theme for Prism.js
+ * Theme variation: Dark
+ * Tested with HTML, CSS, JS, JSON, PHP, YAML, Bash script
+ * @author Armand Philippot <contact@armandphilippot.com>
+ * @homepage https://github.com/ArmandPhilippot/coldark-prism
+ * @license MIT
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+ color: #e3eaf2;
+ background: none;
+ font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+ background: #3c526d;
+}
+
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+ background: #3c526d;
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+ padding: 1em;
+ margin: 0.5em 0;
+ overflow: auto;
+}
+
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+ background: #111b27;
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+ padding: 0.1em 0.3em;
+ border-radius: 0.3em;
+ white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: #8da1b9;
+}
+
+.token.punctuation {
+ color: #e3eaf2;
+}
+
+.token.delimiter.important,
+.token.selector .parent,
+.token.tag,
+.token.tag .token.punctuation {
+ color: #66cccc;
+}
+
+.token.attr-name,
+.token.boolean,
+.token.boolean.important,
+.token.number,
+.token.constant,
+.token.selector .token.attribute {
+ color: #e6d37a;
+}
+
+.token.class-name,
+.token.key,
+.token.parameter,
+.token.property,
+.token.property-access,
+.token.variable {
+ color: #6cb8e6;
+}
+
+.token.attr-value,
+.token.inserted,
+.token.color,
+.token.selector .token.value,
+.token.string,
+.token.string .token.url-link {
+ color: #91d076;
+}
+
+.token.builtin,
+.token.keyword-array,
+.token.package,
+.token.regex {
+ color: #f4adf4;
+}
+
+.token.function,
+.token.selector .token.class,
+.token.selector .token.id {
+ color: #c699e3;
+}
+
+.token.atrule .token.rule,
+.token.combinator,
+.token.keyword,
+.token.operator,
+.token.pseudo-class,
+.token.pseudo-element,
+.token.selector,
+.token.unit {
+ color: #e9ae7e;
+}
+
+.token.deleted,
+.token.important {
+ color: #cd6660;
+}
+
+.token.keyword-this,
+.token.this {
+ color: #6cb8e6;
+}
+
+.token.important,
+.token.keyword-this,
+.token.this,
+.token.bold {
+ font-weight: bold;
+}
+
+.token.delimiter.important {
+ font-weight: inherit;
+}
+
+.token.italic {
+ font-style: italic;
+}
+
+.token.entity {
+ cursor: help;
+}
+
+.language-markdown .token.title,
+.language-markdown .token.title .token.punctuation {
+ color: #6cb8e6;
+ font-weight: bold;
+}
+
+.language-markdown .token.blockquote.punctuation {
+ color: #f4adf4;
+}
+
+.language-markdown .token.code {
+ color: #66cccc;
+}
+
+.language-markdown .token.hr.punctuation {
+ color: #6cb8e6;
+}
+
+.language-markdown .token.url .token.content {
+ color: #91d076;
+}
+
+.language-markdown .token.url-link {
+ color: #e6d37a;
+}
+
+.language-markdown .token.list.punctuation {
+ color: #f4adf4;
+}
+
+.language-markdown .token.table-header {
+ color: #e3eaf2;
+}
+
+.language-json .token.operator {
+ color: #e3eaf2;
+}
+
+.language-scss .token.variable {
+ color: #66cccc;
+}
+
+/* overrides color-values for the Show Invisibles plugin
+ * https://prismjs.com/plugins/show-invisibles/
+ */
+.token.token.tab:not(:empty):before,
+.token.token.cr:before,
+.token.token.lf:before,
+.token.token.space:before {
+ color: #8da1b9;
+}
+
+/* overrides color-values for the Toolbar plugin
+ * https://prismjs.com/plugins/toolbar/
+ */
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > a,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > button {
+ color: #111b27;
+ background: #6cb8e6;
+}
+
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus {
+ color: #111b27;
+ background: #6cb8e6da;
+ text-decoration: none;
+}
+
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > span,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover,
+div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus {
+ color: #111b27;
+ background: #8da1b9;
+}
+
+/* overrides color-values for the Line Highlight plugin
+ * http://prismjs.com/plugins/line-highlight/
+ */
+.line-highlight.line-highlight {
+ background: #3c526d5f;
+ background: linear-gradient(to right, #3c526d5f 70%, #3c526d55);
+}
+
+.line-highlight.line-highlight:before,
+.line-highlight.line-highlight[data-end]:after {
+ background-color: #8da1b9;
+ color: #111b27;
+ box-shadow: 0 1px #3c526d;
+}
+
+pre[id].linkable-line-numbers.linkable-line-numbers span.line-numbers-rows > span:hover:before {
+ background-color: #8da1b918;
+}
+
+/* overrides color-values for the Line Numbers plugin
+ * http://prismjs.com/plugins/line-numbers/
+ */
+.line-numbers.line-numbers .line-numbers-rows {
+ border-right: 1px solid #0b121b;
+ background: #0b121b7a;
+}
+
+.line-numbers .line-numbers-rows > span:before {
+ color: #8da1b9da;
+}
+
+/* overrides color-values for the Match Braces plugin
+ * https://prismjs.com/plugins/match-braces/
+ */
+.rainbow-braces .token.token.punctuation.brace-level-1,
+.rainbow-braces .token.token.punctuation.brace-level-5,
+.rainbow-braces .token.token.punctuation.brace-level-9 {
+ color: #e6d37a;
+}
+
+.rainbow-braces .token.token.punctuation.brace-level-2,
+.rainbow-braces .token.token.punctuation.brace-level-6,
+.rainbow-braces .token.token.punctuation.brace-level-10 {
+ color: #f4adf4;
+}
+
+.rainbow-braces .token.token.punctuation.brace-level-3,
+.rainbow-braces .token.token.punctuation.brace-level-7,
+.rainbow-braces .token.token.punctuation.brace-level-11 {
+ color: #6cb8e6;
+}
+
+.rainbow-braces .token.token.punctuation.brace-level-4,
+.rainbow-braces .token.token.punctuation.brace-level-8,
+.rainbow-braces .token.token.punctuation.brace-level-12 {
+ color: #c699e3;
+}
+
+/* overrides color-values for the Diff Highlight plugin
+ * https://prismjs.com/plugins/diff-highlight/
+ */
+pre.diff-highlight > code .token.token.deleted:not(.prefix),
+pre > code.diff-highlight .token.token.deleted:not(.prefix) {
+ background-color: #cd66601f;
+}
+
+pre.diff-highlight > code .token.token.inserted:not(.prefix),
+pre > code.diff-highlight .token.token.inserted:not(.prefix) {
+ background-color: #91d0761f;
+}
+
+/* overrides color-values for the Command Line plugin
+ * https://prismjs.com/plugins/command-line/
+ */
+.command-line .command-line-prompt {
+ border-right: 1px solid #0b121b;
+}
+
+.command-line .command-line-prompt > span:before {
+ color: #8da1b9da;
+}
diff --git a/scripts/apidocs/doc_styles.css b/scripts/apidocs/doc_styles.css
new file mode 100644
index 0000000..ee0851b
--- /dev/null
+++ b/scripts/apidocs/doc_styles.css
@@ -0,0 +1,76 @@
+/*
+ Josh's Custom CSS Reset
+ https://www.joshwcomeau.com/css/custom-css-reset/
+*/
+*, *::before, *::after {
+ box-sizing: border-box;
+}
+* {
+ margin: 0;
+}
+body {
+ line-height: 1.5;
+ -webkit-font-smoothing: antialiased;
+}
+img, picture, video, canvas, svg {
+ display: block;
+ max-width: 100%;
+}
+input, button, textarea, select {
+ font: inherit;
+}
+p, h1, h2, h3, h4, h5, h6 {
+ overflow-wrap: break-word;
+}
+#root, #__next {
+ isolation: isolate;
+}
+
+/* Styles */
+
+html {
+ -webkit-text-size-adjust: 100%;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */
+ font-feature-settings: normal;
+ font-variation-settings: normal;
+}
+
+pre {
+ margin: 0;
+ padding: 0;
+}
+code {
+ margin: 0;
+ padding: 6px 0 !important
+}
+
+:root {
+ /* TODO: create basic greyscale colours */
+}
+
+main {
+ padding: 12px;
+}
+
+h1 {
+ font-size: 18px;
+ margin-bottom: 20px;
+ /* font-weight: 700; */
+}
+
+h3 {
+ font-size: 16px;
+ font-weight: 700;
+ text-transform: uppercase;
+}
+
+.category-list {
+ padding: 10px 20px;
+ list-style-type: none;
+}
+
+.signature {
+}
diff --git a/scripts/apidocs/gen_apidocs.py b/scripts/apidocs/gen_apidocs.py
new file mode 100644
index 0000000..390a081
--- /dev/null
+++ b/scripts/apidocs/gen_apidocs.py
@@ -0,0 +1,109 @@
+# Generates a static webpage for the public C-API of `celeritas-core`
+import re
+import os
+from pathlib import Path
+
+# --- HTML Fragments
+page_start = """
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="">
+ <link rel="stylesheet" href="doc_styles.css">
+ <!-- <link rel="stylesheet" href="prism.css"> -->
+ <!-- <script src="prism.js"></script> -->
+
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
+ <!-- and it's easy to individually load additional languages -->
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js"></script>
+ <script>hljs.highlightAll();</script>
+
+ <title>Celeritas core API</title>
+</head>
+<body>
+<main>
+"""
+
+page_header = """
+<header>
+ <h1>CELERITAS CORE DOCS</h1>
+</header>
+"""
+
+page_footer = """
+<footer>
+</footer>
+"""
+
+page_end = """
+</main>
+</body>
+</html>
+"""
+
+def emit_function_sig(signature: str) -> str:
+ return f"""
+ <li class="signature">
+ <pre><code class="language-c">{signature}</code></pre>
+ </li>
+ """
+
+categories = {
+ "RAL": "src/ral",
+ "Render": "src/new_render"
+}
+
+def find_pub_functions_in_folder(folder_path):
+ functions = []
+ for filename in os.listdir(folder_path):
+ filepath = os.path.join(folder_path, filename)
+ if os.path.isfile(filepath):
+ file_funcs = find_pub_functions_in_file(filepath)
+ functions.extend(file_funcs)
+
+ return functions
+
+def find_pub_functions_in_file(file_path):
+ pattern = r'PUB\s+(\w+\s+)*(\w+)\s+(\w+)\s*\((.*?)\)'
+
+ with open(file_path, 'r') as file:
+ content = file.read()
+
+ matches = re.finditer(pattern, content, re.MULTILINE)
+
+ # Collect all the functions into an array
+ functions = []
+ for match in matches:
+ signature = match.group(0)
+ if signature.startswith("PUB "):
+ signature = signature[4:]
+
+ print(signature)
+ functions.append(signature)
+
+ return functions
+
+def generate_html():
+ html_filepath = "index.html"
+
+ script_dir = Path(__file__).resolve().parent
+ grandparent_dir = script_dir.parents[1]
+
+ with open(html_filepath, 'w') as export_file:
+ export_file.write(page_start)
+ export_file.write(page_header)
+ # TODO: make the actual content
+ for category in categories.keys():
+ folder = os.path.join(grandparent_dir, categories[category])
+ category_funcs = find_pub_functions_in_folder(folder)
+ export_file.write(f"<h3>{category}</h3>")
+ export_file.write("<ul class=\"category-list\">")
+ for func in category_funcs:
+ export_file.write(emit_function_sig(func))
+ export_file.write("</ul>")
+ export_file.write(page_end)
+
+if __name__ == "__main__":
+ generate_html()
diff --git a/scripts/apidocs/index.html b/scripts/apidocs/index.html
new file mode 100644
index 0000000..878b7f9
--- /dev/null
+++ b/scripts/apidocs/index.html
@@ -0,0 +1,236 @@
+
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="description" content="">
+ <link rel="stylesheet" href="doc_styles.css">
+ <!-- <link rel="stylesheet" href="prism.css"> -->
+ <!-- <script src="prism.js"></script> -->
+
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
+ <!-- and it's easy to individually load additional languages -->
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js"></script>
+ <script>hljs.highlightAll();</script>
+
+ <title>Celeritas core API</title>
+</head>
+<body>
+<main>
+
+<header>
+ <h1>CELERITAS CORE</h1>
+</header>
+<h3>RAL</h3><ul class="category-list">
+ <li class="signature">
+ <pre><code class="language-c">void GPU_Renderpass_Destroy(GPU_Renderpass* pass)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GraphicsPipeline_Destroy(GPU_Pipeline* pipeline)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">GPU_CmdEncoder GPU_CmdEncoder_Create()</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_CmdEncoder_Destroy(GPU_CmdEncoder* encoder)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_CmdEncoder_BeginRender(GPU_CmdEncoder* encoder, GPU_Renderpass* renderpass)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_CmdEncoder_EndRender(GPU_CmdEncoder* encoder)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_QueueSubmit(GPU_CmdBuffer* cmd_buffer)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">BufferHandle GPU_BufferCreate(u64 size, GPU_BufferType buf_type, GPU_BufferFlags flags, const void* data)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_BufferDestroy(BufferHandle handle)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_BufferUpload(BufferHandle buffer, size_t n_bytes, const void* data)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">TextureHandle GPU_TextureCreate(TextureDesc desc, bool create_view, const void* data)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_TextureDestroy(TextureHandle handle)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_TextureUpload(TextureHandle handle, size_t n_bytes, const void* data)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_EncodeBindPipeline(GPU_CmdEncoder* encoder, GPU_Pipeline* pipeline)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_EncodeBindShaderData(GPU_CmdEncoder* encoder, u32 group, ShaderData* data)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_EncodeSetVertexBuffer(GPU_CmdEncoder* encoder, BufferHandle buf)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_EncodeSetIndexBuffer(GPU_CmdEncoder* encoder, BufferHandle buf)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_EncodeDraw(GPU_CmdEncoder* encoder, u64 count)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_EncodeDrawIndexed(GPU_CmdEncoder* encoder, u64 index_count)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">bool GPU_Backend_BeginFrame()</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void GPU_Backend_EndFrame()</code></pre>
+ </li>
+ </ul><h3>Render</h3><ul class="category-list">
+ <li class="signature">
+ <pre><code class="language-c">void EncodeDrawModel(Handle model, Mat4 transform)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void EncodeDrawMesh(Mesh* mesh, Material* material, Mat4 affine)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Immdraw_Init(Immdraw_Storage* storage)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Immdraw_Shutdown(Immdraw_Storage* storage)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Immdraw_Plane(Transform tf, Vec4 colour, bool wireframe)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Immdraw_Cuboid(Transform tf, Vec4 colour, bool wireframe)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Immdraw_Sphere(Transform tf, f32 size, Vec4 colour, bool wireframe)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Immdraw_TransformGizmo(Transform tf, f32 size)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void PBR_Init(PBR_Storage* storage)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">bool Renderer_Init(RendererConfig config, Renderer* renderer)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Renderer_Shutdown(Renderer* renderer)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">size_t Renderer_GetMemReqs()</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Render_FrameBegin(Renderer* renderer)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Render_FrameEnd(Renderer* renderer)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Render_RenderEntities(RenderEnt* entities, size_t entity_count)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">TextureHandle TextureUpload()</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">ModelHandle ModelLoad(const char* debug_name, const char* filepath)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">Mesh Mesh_Create(Geometry* geometry, bool free_on_upload)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Mesh_Delete(Mesh* mesh)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void DrawMesh(Mesh* mesh, Material* material, Mat4 model)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Render_DrawTerrain()</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void SetCamera(Camera camera)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void SetPointLight(PointLight light)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Shadow_Init(Shadow_Storage* storage)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Shadow_Run(Shadow_Storage* storage, RenderEnt* entities, size_t entity_count)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">Handle Shadow_GetShadowMapTexture(Shadow_Storage* storage)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void CubeMapData_Load(const char** face_paths, int n)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void CubeMapData_Free(CubeMapData* cubemap)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">TextureHandle CubeMapData_Upload(CubeMapData* cubemap)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">Skybox Skybox_Create(const char** face_paths, int n)</code></pre>
+ </li>
+
+ <li class="signature">
+ <pre><code class="language-c">void Skybox_Draw(Skybox* skybox)</code></pre>
+ </li>
+ </ul>
+</main>
+</body>
+</html>
diff --git a/scripts/api_gen/modules.md b/scripts/apidocs/modules.md
index 7298844..7298844 100644
--- a/scripts/api_gen/modules.md
+++ b/scripts/apidocs/modules.md
diff --git a/scripts/apidocs/prism.css b/scripts/apidocs/prism.css
new file mode 100644
index 0000000..333e985
--- /dev/null
+++ b/scripts/apidocs/prism.css
@@ -0,0 +1,3 @@
+/* PrismJS 1.29.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike */
+code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
diff --git a/scripts/apidocs/prism.js b/scripts/apidocs/prism.js
new file mode 100644
index 0000000..d0b4c05
--- /dev/null
+++ b/scripts/apidocs/prism.js
@@ -0,0 +1,6 @@
+/* PrismJS 1.29.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike */
+var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case"Object":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)&&(r[l]=e(n[l],t));return r;case"Array":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return"none"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,"gi"),""),e.classList.add("language-"+t)},currentScript:function(){if("undefined"==typeof document)return null;if("currentScript"in document)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName("script");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r="no-"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)&&(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u&&n!=e&&(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);"Object"!==u||i[l(s)]?"Array"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};a.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run("before-all-elements-highlight",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o&&"pre"===o.nodeName.toLowerCase()&&a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run("before-insert",s),s.element.innerHTML=s.highlightedCode,a.hooks.run("after-highlight",s),a.hooks.run("complete",s),r&&r.call(s.element)}if(a.hooks.run("before-sanity-check",s),(o=s.element.parentElement)&&"pre"===o.nodeName.toLowerCase()&&!o.hasAttribute("tabindex")&&o.setAttribute("tabindex","0"),!s.code)return a.hooks.run("complete",s),void(r&&r.call(s.element));if(a.hooks.run("before-highlight",s),s.grammar)if(t&&e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run("before-tokenize",r),!r.grammar)throw new Error('The language "'+r.language+'" has no grammar.');return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run("after-tokenize",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t&&t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d<h.length;++d){if(g&&g.cause==f+","+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y&&!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+"g")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail&&!(g&&A>=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(j<O||"string"==typeof C.value);C=C.next)L++,j+=C.value.length;L--,E=e.slice(A,j),P.index-=A}else if(!(P=l(b,0,E,m)))continue;S=P.index;var N=P[0],_=E.slice(0,S),M=E.slice(S+N.length),W=A+E.length;g&&W>g.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if("string"==typeof n)return n;if(Array.isArray(n)){var r="";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:"span",classes:["token",n.type],attributes:{},language:t},l=n.alias;l&&(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=" "+s+'="'+(i.attributes[s]||"").replace(/"/g,"&quot;")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+o+">"+i.content+"</"+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
+Prism.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&amp;/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^<!\[CDATA\[|\]\]>$/i;var t={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:<!\\[CDATA\\[(?:[^\\]]|\\](?!\\]>))*\\]\\]>|(?!<!\\[CDATA\\[)[^])*?(?=</__>)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
+!function(s){var e=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:[^;{\\s\"']|\\s+(?!\\s)|"+e.source+")*?(?:;|(?=\\s*\\{))"),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+e.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+e.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+e.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism);
+Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
diff --git a/scripts/api_gen/template.html b/scripts/apidocs/template.html
index 1e1c7a7..1e1c7a7 100644
--- a/scripts/api_gen/template.html
+++ b/scripts/apidocs/template.html
diff --git a/scripts/gen_amalgamation.py b/scripts/gen_amalgamation.py
new file mode 100644
index 0000000..45a1c21
--- /dev/null
+++ b/scripts/gen_amalgamation.py
@@ -0,0 +1,24 @@
+# Generates a single amalgamation C header file that includes all public types and functions.
+#
+# This makes including and linking Celeritas very easy.
+import re
+import sys
+
+def find_pub_functions(filepath):
+ pattern = r'PUB\s+(\w+\s+)*(\w+)\s+(\w+)\s*\((.*?)\)'
+
+ with open(filepath, 'r') as file:
+ content = file.read()
+
+ matches = re.finditer(pattern, content, re.MULTILINE)
+
+ for match in matches:
+ print(match.group(0))
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ print("Usage: python script.py <path_to_c_file>")
+ sys.exit(1)
+
+ file_path = sys.argv[1]
+ find_pub_functions(file_path)
diff --git a/scripts/gen_amalgamation.sh b/scripts/gen_amalgamation.sh
deleted file mode 100644
index 56df62e..0000000
--- a/scripts/gen_amalgamation.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# Generates a single amalgamation C header file that includes all public types and functions.
-#
-# This makes including and linking Celeritas very easy.
diff --git a/scripts/gen_apidocs.sh b/scripts/gen_apidocs.sh
deleted file mode 100644
index afad30e..0000000
--- a/scripts/gen_apidocs.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-# Generates a static webpage for the public C-API of `celeritas-core`
diff --git a/src/core/core.c b/src/core/core.c
index 602d35c..385479d 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -34,6 +34,7 @@ void Core_Bringup() {
.scr_height = SCR_HEIGHT,
.clear_colour = (Vec3){ .08, .08, .1 } };
+ g_core.renderer = malloc(Renderer_GetMemReqs());
// initialise all subsystems
if (!Renderer_Init(conf, g_core.renderer)) {
// FATAL("Failed to start renderer");
@@ -61,6 +62,7 @@ void Core_Bringup() {
void Core_Shutdown() {
Input_Shutdown(&g_core.input);
Renderer_Shutdown(g_core.renderer);
+ free(g_core.renderer);
}
bool ShouldExit() {
diff --git a/src/core/core.h b/src/core/core.h
index 7916143..78dcd14 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -9,6 +9,8 @@
typedef struct Core Core;
+struct Renderer;
+
Core* get_global_core();
/** @brief Throws error if the core cannot be instantiated */
@@ -16,6 +18,8 @@ void Core_Bringup();
void Core_Shutdown();
bool ShouldExit();
+struct Renderer* Core_GetRenderer(Core* core);
+
void Frame_Begin();
void Frame_Draw();
void Frame_End();
diff --git a/src/maths/primitives.c b/src/maths/primitives.c
index def2712..e74afcb 100644
--- a/src/maths/primitives.c
+++ b/src/maths/primitives.c
@@ -9,7 +9,7 @@
#define VERT_3D(arr, pos, norm, uv) \
{ \
Vertex v = { .static_3d = { .position = pos, .normal = norm, .tex_coords = uv } }; \
- vertex_darray_push(arr, v); \
+ Vertex_darray_push(arr, v); \
}
void push_triangle(u32_darray* arr, u32 i0, u32 i1, u32 i2) {
@@ -18,12 +18,6 @@ void push_triangle(u32_darray* arr, u32 i0, u32 i1, u32 i2) {
u32_darray_push(arr, i2);
}
-// TODO: move to another file
-void geo_free_data(Geometry* geo) {
- vertex_darray_free(geo->vertices);
- geo->vertices = NULL;
-}
-
Vec3 plane_vertex_positions[] = {
(Vec3){ -0.5, 0, -0.5 },
(Vec3){ 0.5, 0, -0.5 },
@@ -32,7 +26,7 @@ Vec3 plane_vertex_positions[] = {
};
Geometry geo_create_plane(f32x2 extents) {
- Vertex_darray* vertices = vertex_darray_new(4);
+ Vertex_darray* vertices = Vertex_darray_new(4);
u32_darray* indices = u32_darray_new(vertices->len);
Vec3 vert_pos[4];
@@ -69,7 +63,7 @@ static const Vec3 FRONT_TOP_LEFT = (Vec3){ 0, 1, 1 };
static const Vec3 FRONT_TOP_RIGHT = (Vec3){ 1, 1, 1 };
Geometry geo_create_cuboid(f32x3 extents) {
- Vertex_darray* vertices = vertex_darray_new(36);
+ Vertex_darray* vertices = Vertex_darray_new(36);
// back faces
VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_NEG_Z, vec2(1, 0));
@@ -148,7 +142,7 @@ Geometry geo_create_uvsphere(f32 radius, u32 north_south_lines, u32 east_west_li
assert(east_west_lines >= 3); // sphere will be degenerate and look gacked without at least 3
assert(north_south_lines >= 3);
- Vertex_darray* vertices = vertex_darray_new(2 + (east_west_lines - 1) * north_south_lines);
+ Vertex_darray* vertices = Vertex_darray_new(2 + (east_west_lines - 1) * north_south_lines);
// Create a UV sphere with spherical coordinates
// a point P on the unit sphere can be represented P(r, theta, phi)
@@ -180,7 +174,7 @@ Geometry geo_create_uvsphere(f32 radius, u32 north_south_lines, u32 east_west_li
vec3_normalise(position), // normal vector on sphere is same as position
.tex_coords = vec2(0, 0) // TODO
} };
- vertex_darray_push(vertices, v);
+ Vertex_darray_push(vertices, v);
}
}
@@ -188,7 +182,7 @@ Geometry geo_create_uvsphere(f32 radius, u32 north_south_lines, u32 east_west_li
Vertex bot = { .static_3d = { .position = vec3(0, -radius, 0),
.normal = vec3_normalise(vec3(0, -radius, 0)),
.tex_coords = vec2(0, 0) } };
- vertex_darray_push(vertices, bot);
+ Vertex_darray_push(vertices, bot);
u32_darray* indices = u32_darray_new(1);
diff --git a/src/new_render/immdraw.h b/src/new_render/immdraw.h
index c2c3a24..46c5add 100644
--- a/src/new_render/immdraw.h
+++ b/src/new_render/immdraw.h
@@ -6,9 +6,17 @@
#include "defines.h"
#include "maths_types.h"
+typedef struct Immdraw_Storage {
+
+} Immdraw_Storage;
+
// --- Public API
+PUB void Immdraw_Init(Immdraw_Storage* storage);
+PUB void Immdraw_Shutdown(Immdraw_Storage* storage);
-void Immdraw_Cuboid(Transform tf);
-void Immdraw_Sphere(Transform tf, f32 size);
-void Immdraw_TransformGizmo(Transform tf, f32 size);
+// These functions cause a pipeline switch and so aren't optimised for performance
+PUB void Immdraw_Plane(Transform tf, Vec4 colour, bool wireframe);
+PUB void Immdraw_Cuboid(Transform tf, Vec4 colour, bool wireframe);
+PUB void Immdraw_Sphere(Transform tf, f32 size, Vec4 colour, bool wireframe);
+PUB void Immdraw_TransformGizmo(Transform tf, f32 size);
diff --git a/src/new_render/render.c b/src/new_render/render.c
index f5547d5..cfd0b11 100644
--- a/src/new_render/render.c
+++ b/src/new_render/render.c
@@ -2,13 +2,23 @@
* @brief
*/
+#include <glfw3.h>
#include "render.h"
+#include "core.h"
+#include "camera.h"
+#include "colours.h"
+#include "log.h"
+#include "maths.h"
#include "maths_types.h"
#include "pbr.h"
#include "ral_common.h"
+#include "ral_impl.h"
#include "render_scene.h"
+#include "render_types.h"
#include "shadows.h"
+extern Core g_core;
+
struct Renderer {
struct GLFWwindow* window;
RendererConfig config;
@@ -21,14 +31,105 @@ struct Renderer {
Shadow_Storage* shadows;
// Terrain_Storage terrain;
// Text_Storage text;
- struct ResourcePools* resource_pools;
+ ResourcePools* resource_pools;
};
-bool Renderer_Init(RendererConfig config, Renderer* renderer) {
+bool Renderer_Init(RendererConfig config, Renderer* ren) {
+ INFO("Renderer init");
+
+ // init resource pools
+ DEBUG("Initialise GPU resource pools");
+ arena pool_arena = arena_create(malloc(1024 * 1024), 1024 * 1024);
+ ren->resource_pools = arena_alloc(&pool_arena, sizeof(struct ResourcePools));
+ ResourcePools_Init(&pool_arena, ren->resource_pools);
+
+ // GLFW window creation
+
+ // NOTE: all platforms use GLFW at the moment but thats subject to change
+ glfwInit();
+
+ #if defined(CEL_REND_BACKEND_OPENGL)
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+ #elif defined(CEL_REND_BACKEND_VULKAN)
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+ #endif
+
+ GLFWwindow* window = glfwCreateWindow(config.scr_width, config.scr_height,
+ config.window_name, NULL, NULL);
+ if (window == NULL) {
+ ERROR("Failed to create GLFW window\n");
+ glfwTerminate();
+ return false;
+ }
+ ren->window = window;
+
+ glfwMakeContextCurrent(ren->window);
+
// set the RAL backend up
+ if (!GPU_Backend_Init(config.window_name, window, ren->resource_pools)) {
+ return false;
+ }
+
+ GPU_Device_Create(&ren->device);
+ GPU_Swapchain_Create(&ren->swapchain);
+
+
+ // set up default scene
+ Camera default_cam =
+ Camera_Create(vec3(0.0, 2.0, 4.0), vec3_normalise(vec3(0.0, -2.0, -4.0)), VEC3_Y, 45.0);
+ SetCamera(default_cam);
+ PointLight default_light = { /* TODO */ };
+ SetPointLight(default_light);
// create our renderpasses
- Shadow_Init(renderer->shadows);
+ Shadow_Init(ren->shadows);
return true;
}
+
+void Renderer_Shutdown(Renderer* renderer) { }
+size_t Renderer_GetMemReqs() { return sizeof(Renderer); }
+
+void Render_FrameBegin(Renderer *renderer) {
+ renderer->frame_aborted = false;
+ if (GPU_Backend)
+
+}
+void Render_FrameEnd(Renderer* renderer) {
+
+}
+void Render_RenderEntities(RenderEnt* entities, size_t entity_count) {
+ Renderer* ren = Core_GetRenderer(&g_core);
+
+ GPU_CmdEncoder* enc = GPU_GetDefaultEncoder();
+ // bind shadow
+ GPU_EncodeBindPipeline(enc, ren->shadows)
+
+}
+
+Mesh Mesh_Create(Geometry* geometry, bool free_on_upload) {
+ Mesh m = { 0 };
+
+ // Create and upload vertex buffer
+ size_t vert_bytes = geometry->vertices->len * sizeof(Vertex);
+ INFO("Creating vertex buffer with size %d (%d x %d)", vert_bytes, geometry->vertices->len,
+ sizeof(Vertex));
+ m.vertex_buffer = GPU_BufferCreate(vert_bytes, BUFFER_VERTEX, BUFFER_FLAG_GPU,
+ geometry->vertices->data);
+
+ // Create and upload index buffer
+ size_t index_bytes = geometry->indices->len * sizeof(u32);
+ INFO("Creating index buffer with size %d (len: %d)", index_bytes, geometry->indices->len);
+ m.index_buffer = GPU_BufferCreate(index_bytes, BUFFER_INDEX, BUFFER_FLAG_GPU,
+ geometry->indices->data);
+
+ m.is_uploaded = true;
+ m.geometry = geometry;
+ if (free_on_upload) {
+ Geometry_Destroy(geometry);
+ }
+ return m;
+}
diff --git a/src/new_render/render.h b/src/new_render/render.h
index b0b5495..6e9f380 100644
--- a/src/new_render/render.h
+++ b/src/new_render/render.h
@@ -24,6 +24,10 @@ typedef struct RenderCtx {
PUB bool Renderer_Init(RendererConfig config, Renderer* renderer);
PUB void Renderer_Shutdown(Renderer* renderer);
+PUB size_t Renderer_GetMemReqs();
+
+// internal init functions
+void DefaultPipelinesInit(Renderer* renderer);
// NOTE: All of these functions grab the Renderer instance off the global Core
PUB void Render_FrameBegin(Renderer* renderer);
@@ -37,12 +41,13 @@ PUB void Render_RenderEntities(RenderEnt* entities, size_t entity_count);
// --- Resources
PUB TextureHandle TextureUpload();
-PUB ModelHandle ModelLoad(const char* name, const char* filepath);
+PUB ModelHandle ModelLoad(const char* debug_name, const char* filepath);
// --- Rendering Data
PUB Mesh Mesh_Create(Geometry* geometry, bool free_on_upload);
PUB void Mesh_Delete(Mesh* mesh);
+void Geometry_Destroy(Geometry* geometry);
// --- Drawing
diff --git a/src/new_render/render_types.h b/src/new_render/render_types.h
index b27bf2f..1cf6c7e 100644
--- a/src/new_render/render_types.h
+++ b/src/new_render/render_types.h
@@ -27,7 +27,7 @@ typedef struct u32_opt {
} u32_opt;
typedef struct Mesh {
- BufferHandle vextex_buffer;
+ BufferHandle vertex_buffer;
BufferHandle index_buffer;
Geometry* geometry; // NULL means it has been freed CPU-side
bool is_uploaded; // has the data been uploaded to the GPU
diff --git a/src/ral/backends/opengl/backend_opengl.c b/src/ral/backends/opengl/backend_opengl.c
new file mode 100644
index 0000000..2c7c411
--- /dev/null
+++ b/src/ral/backends/opengl/backend_opengl.c
@@ -0,0 +1,284 @@
+#include <assert.h>
+#include "backend_opengl.h"
+#include "log.h"
+#include "mem.h"
+#include "ral_common.h"
+#include "ral_impl.h"
+#include "ral_types.h"
+
+#include <glad/glad.h>
+#include <glfw3.h>
+
+typedef struct OpenglCtx {
+ GLFWwindow* window;
+ arena pool_arena;
+ GPU_CmdBuffer main_cmd_buffer;
+ GPU_BackendPools gpu_pools;
+ ResourcePools* resource_pools;
+} OpenglCtx;
+
+static OpenglCtx context;
+
+bool GPU_Backend_Init(const char* window_name, struct GLFWwindow *window, struct ResourcePools* res_pools) {
+ INFO("loading OpenGL backend");
+
+ memset(&context, 0, sizeof(context));
+ context.window = window;
+
+ size_t pool_buffer_size = 1024 * 1024;
+ context.pool_arena = arena_create(malloc(pool_buffer_size), pool_buffer_size);
+
+ BackendPools_Init(&context.pool_arena, &context.gpu_pools);
+ context.resource_pools = res_pools;
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+
+ // glad: load all opengl function pointers
+ if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
+ ERROR("Failed to initialise GLAD \n");
+ return false;
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+
+ return true;
+}
+
+// All of these are no-ops in OpenGL
+void GPU_Backend_Shutdown() {}
+bool GPU_Device_Create(GPU_Device* out_device) { return true; }
+void GPU_Device_Destroy(GPU_Device* device) {}
+bool GPU_Swapchain_Create(GPU_Swapchain* out_swapchain) { return true; }
+void GPU_Swapchain_Destroy(GPU_Swapchain* swapchain) {}
+void GPU_CmdEncoder_Destroy(GPU_CmdEncoder* encoder) {}
+void GPU_CmdEncoder_BeginRender(GPU_CmdEncoder* encoder, GPU_Renderpass* renderpass) {}
+void GPU_CmdEncoder_EndRender(GPU_CmdEncoder* encoder) {}
+GPU_CmdEncoder* GPU_GetDefaultEncoder() {
+ return context.
+}
+void GPU_QueueSubmit(GPU_CmdBuffer* cmd_buffer) {}
+
+GPU_Renderpass* GPU_Renderpass_Create(GPU_RenderpassDesc description) {
+ // allocate new pass
+ GPU_Renderpass* renderpass = Renderpass_pool_alloc(&context.gpu_pools.renderpasses, NULL);
+ renderpass->description = description;
+
+ if (!description.default_framebuffer) {
+ // If we're not using the default framebuffer we need to generate a new one
+ GLuint gl_fbo_id;
+ glGenFramebuffers(1, &gl_fbo_id);
+ renderpass->fbo = gl_fbo_id;
+ } else {
+ renderpass->fbo = OPENGL_DEFAULT_FRAMEBUFFER;
+ assert(!description.has_color_target);
+ assert(!description.has_depth_stencil);
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, renderpass->fbo);
+
+ if (description.has_color_target && !description.default_framebuffer) {
+ GPU_Texture* colour_attachment = TEXTURE_GET(description.color_target);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ colour_attachment->id, 0);
+ }
+ if (description.has_depth_stencil && !description.default_framebuffer) {
+ GPU_Texture* depth_attachment = TEXTURE_GET(description.depth_stencil);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_attachment->id,
+ 0);
+ }
+
+ if (description.has_depth_stencil && !description.has_color_target) {
+ glDrawBuffer(GL_NONE);
+ glReadBuffer(GL_NONE);
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0); // reset to default framebuffer
+
+ return renderpass;
+}
+
+void GPU_Renderpass_Destroy(GPU_Renderpass* pass) { glDeleteFramebuffers(1, &pass->fbo); }
+
+GPU_Pipeline* GPU_GraphicsPipeline_Create(GraphicsPipelineDesc description, GPU_Renderpass* renderpass) {
+ GPU_Pipeline* pipeline = Pipeline_pool_alloc(&context.gpu_pools.pipelines, NULL);
+
+ // Create shader program
+ u32 shader_id = shader_create_separate(description.vs.filepath.buf, description.fs.filepath.buf);
+ pipeline->shader_id = shader_id;
+
+ // Vertex format
+ pipeline->vertex_desc = description.vertex_desc;
+
+ // Allocate uniform buffers if needed
+ u32 ubo_count = 0;
+ // printf("data layouts %d\n", description.data_layouts_count);
+ for (u32 layout_i = 0; layout_i < description.data_layouts_count; layout_i++) {
+ ShaderDataLayout sdl = description.data_layouts[layout_i].get_layout(NULL);
+ TRACE("Got shader data layout %d's bindings! . found %d", layout_i, sdl.binding_count);
+
+ for (u32 binding_j = 0; binding_j < sdl.binding_count; binding_j++) {
+ u32 binding_id = binding_j;
+ assert(binding_id < MAX_PIPELINE_UNIFORM_BUFFERS);
+ ShaderBinding binding = sdl.bindings[binding_j];
+ // Do I want Buffer vs Bytes?
+ if (binding.kind == BINDING_BUFFER) {
+ static u32 s_binding_point = 0;
+ BufferHandle ubo_handle =
+ GPU_BufferCreate(binding.data.bytes.size, BUFFER_UNIFORM, BUFFER_FLAG_GPU, NULL); // no data right now
+ pipeline->uniform_bindings[ubo_count++] = ubo_handle;
+ GPU_Buffer* ubo_buf = BUFFER_GET(ubo_handle);
+
+ i32 blockIndex = glGetUniformBlockIndex(pipeline->shader_id, binding.label);
+ printf("Block index for %s: %d", binding.label, blockIndex);
+ if (blockIndex < 0) {
+ WARN("Couldn't retrieve block index for uniform block '%s'", binding.label);
+ } else {
+ // DEBUG("Retrived block index %d for %s", blockIndex, binding.label);
+ }
+ u32 blocksize;
+ glGetActiveUniformBlockiv(pipeline->shader_id, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE,
+ &blocksize);
+ printf("\t with size %d bytes\n", blocksize);
+
+ glBindBufferBase(GL_UNIFORM_BUFFER, s_binding_point, ubo_buf->id.ubo);
+ if (blockIndex != GL_INVALID_INDEX) {
+ glUniformBlockBinding(pipeline->shader_id, blockIndex, s_binding_point);
+ }
+ ubo_buf->ubo_binding_point = s_binding_point++;
+ ubo_buf->name = binding.label;
+ assert(s_binding_point < GL_MAX_UNIFORM_BUFFER_BINDINGS);
+ }
+ }
+ }
+ pipeline->uniform_count = ubo_count;
+
+ pipeline->renderpass = description.renderpass;
+ pipeline->wireframe = description.wireframe;
+
+ return pipeline;
+}
+
+void GraphicsPipeline_Destroy(GPU_Pipeline* pipeline) {}
+
+GPU_CmdEncoder GPU_CmdEncoder_Create() {
+ GPU_CmdEncoder encoder = { 0 };
+ return encoder;
+}
+
+
+BufferHandle GPU_BufferCreate(u64 size, GPU_BufferType buf_type, GPU_BufferFlags flags, const void *data) {
+ // "allocating" the cpu-side buffer struct
+ BufferHandle handle;
+ GPU_Buffer* buffer = Buffer_pool_alloc(&context.resource_pools, &handle);
+ buffer->size = size;
+ buffer->vao = 0;
+
+ // Opengl buffer
+ GLuint gl_buffer_id;
+ glGenBuffers(1, &gl_buffer_id);
+
+ GLenum gl_buf_type;
+ GLenum gl_buf_usage = GL_STATIC_DRAW;
+
+ switch (buf_type) {
+ case BUFFER_UNIFORM:
+ DEBUG("Creating Uniform buffer");
+ gl_buf_type = GL_UNIFORM_BUFFER;
+ /* gl_buf_usage = GL_DYNAMIC_DRAW; */
+ buffer->id.ubo = gl_buffer_id;
+ break;
+ case BUFFER_DEFAULT:
+ case BUFFER_VERTEX:
+ DEBUG("Creating Vertex buffer");
+ gl_buf_type = GL_ARRAY_BUFFER;
+ buffer->id.vbo = gl_buffer_id;
+ break;
+ case BUFFER_INDEX:
+ DEBUG("Creating Index buffer");
+ gl_buf_type = GL_ELEMENT_ARRAY_BUFFER;
+ buffer->id.ibo = gl_buffer_id;
+ break;
+ default:
+ WARN("Unimplemented gpu_buffer_type provided %s", buffer_type_names[buf_type]);
+ break;
+ }
+ // bind buffer
+ glBindBuffer(gl_buf_type, gl_buffer_id);
+
+ if (data) {
+ TRACE("Upload data (%d bytes) as part of buffer creation", size);
+ glBufferData(gl_buf_type, buffer->size, data, gl_buf_usage);
+ } else {
+ TRACE("Allocating but not uploading (%d bytes)", size);
+ glBufferData(gl_buf_type, buffer->size, NULL, gl_buf_usage);
+ }
+
+ glBindBuffer(gl_buf_type, 0);
+
+ return handle;
+}
+
+TextureHandle GPU_TextureCreate(TextureDesc desc, bool create_view, const void *data) {
+ // "allocating" the cpu-side struct
+ TextureHandle handle;
+ GPU_Texture* texture = Texture_pool_alloc(&context.resource_pools->textures, &handle);
+ DEBUG("Allocated texture with handle %d", handle.raw);
+
+ GLuint gl_texture_id;
+ glGenTextures(1, &gl_texture_id);
+ texture->id = gl_texture_id;
+
+ glBindTexture(GL_TEXTURE_2D, gl_texture_id);
+
+ GLint internal_format =
+ desc.format == TEXTURE_FORMAT_DEPTH_DEFAULT ? GL_DEPTH_COMPONENT : GL_RGB;
+ GLenum format = desc.format == TEXTURE_FORMAT_DEPTH_DEFAULT ? GL_DEPTH_COMPONENT : GL_RGBA;
+ GLenum data_type = desc.format == TEXTURE_FORMAT_DEPTH_DEFAULT ? GL_FLOAT : GL_UNSIGNED_BYTE;
+
+ if (desc.format == TEXTURE_FORMAT_DEPTH_DEFAULT) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ } else {
+ // set the texture wrapping parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ // set texture filtering parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+
+ if (data) {
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, desc.extents.x, desc.extents.y, 0, format,
+ data_type, data);
+ glGenerateMipmap(GL_TEXTURE_2D);
+ } else {
+ WARN("No image data provided");
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, desc.extents.x, desc.extents.y, 0, format,
+ data_type, NULL);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ return handle;
+}
+
+void GPU_TextureDestroy(TextureHandle handle) {
+ glDeleteTextures(1, &handle.raw);
+}
+
+// TODO: void GPU_TextureUpload(TextureHandle handle, size_t n_bytes, const void* data)
+
+bool GPU_Backend_BeginFrame() {
+ glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void GPU_Backend_EndFrame() {
+ glfwSwapBuffers(context.window);
+}
diff --git a/src/ral/backends/opengl/backend_opengl.h b/src/ral/backends/opengl/backend_opengl.h
index 22162f3..4fbb4bb 100644
--- a/src/ral/backends/opengl/backend_opengl.h
+++ b/src/ral/backends/opengl/backend_opengl.h
@@ -39,11 +39,11 @@ typedef struct GPU_Renderpass {
typedef struct GPU_CmdEncoder {
GPU_Pipeline *pipeline;
-} GPU_CmdEncoder; // Recording
+} GPU_CmdEncoder; // Recording
-typedef struct gpu_cmd_buffer {
+typedef struct GPU_CmdBuffer {
void *pad;
-} gpu_cmd_buffer; // Ready for submission
+} GPU_CmdBuffer; // Ready for submission
typedef struct GPU_Buffer {
union {
diff --git a/src/ral/ral_common.c b/src/ral/ral_common.c
index 89d475b..8ff282e 100644
--- a/src/ral/ral_common.c
+++ b/src/ral/ral_common.c
@@ -1,7 +1,7 @@
#include "ral_common.h"
#include "ral_impl.h"
-void backend_pools_init(arena* a, GPU_BackendPools* backend_pools) {
+void BackendPools_Init(arena* a, GPU_BackendPools* backend_pools) {
PipelineLayout_pool pipeline_layout_pool =
PipelineLayout_pool_create(a, MAX_PIPELINES, sizeof(GPU_PipelineLayout));
backend_pools->pipeline_layouts = pipeline_layout_pool;
@@ -11,9 +11,51 @@ void backend_pools_init(arena* a, GPU_BackendPools* backend_pools) {
backend_pools->renderpasses = rpass_pool;
}
-void resource_pools_init(arena* a, struct ResourcePools* res_pools) {
+void ResourcePools_Init(arena* a, struct ResourcePools* res_pools) {
Buffer_pool buf_pool = Buffer_pool_create(a, MAX_BUFFERS, sizeof(GPU_Buffer));
res_pools->buffers = buf_pool;
Texture_pool tex_pool = Texture_pool_create(a, MAX_TEXTURES, sizeof(GPU_Texture));
res_pools->textures = tex_pool;
}
+
+VertexDescription static_3d_vertex_description() {
+ VertexDescription builder = { .debug_label = "Standard static 3d vertex format" };
+ VertexDesc_AddAttr(&builder, "inPosition", ATTR_F32x3);
+ VertexDesc_AddAttr(&builder, "inNormal", ATTR_F32x3);
+ VertexDesc_AddAttr(&builder, "inTexCoords", ATTR_F32x2);
+ builder.use_full_vertex_size = true;
+ return builder;
+}
+
+void VertexDesc_AddAttr(VertexDescription* builder, const char* name, VertexAttribType type) {
+ u32 i = builder->attributes_count;
+
+ size_t size = VertexAttribSize(type);
+ builder->attributes[i] = type;
+ builder->stride += size;
+ builder->attr_names[i] = name;
+
+ builder->attributes_count++;
+}
+
+size_t VertexAttribSize(VertexAttribType attr) {
+ switch (attr) {
+ case ATTR_F32:
+ case ATTR_U32:
+ case ATTR_I32:
+ return 4;
+ case ATTR_F32x2:
+ case ATTR_U32x2:
+ case ATTR_I32x2:
+ return 8;
+ case ATTR_F32x3:
+ case ATTR_U32x3:
+ case ATTR_I32x3:
+ return 12;
+ case ATTR_F32x4:
+ case ATTR_U32x4:
+ case ATTR_I32x4:
+ return 16;
+ break;
+ }
+}
diff --git a/src/ral/ral_common.h b/src/ral/ral_common.h
index 1088404..0f7c1b7 100644
--- a/src/ral/ral_common.h
+++ b/src/ral/ral_common.h
@@ -1,3 +1,6 @@
+/**
+ * @brief Common functions that don't actually depend on the specific backend
+*/
#pragma once
#include "defines.h"
#include "buf.h"
@@ -5,7 +8,6 @@
#include "ral_types.h"
#include "ral_impl.h"
-
TYPED_POOL(GPU_Buffer, Buffer);
TYPED_POOL(GPU_Texture, Texture);
TYPED_POOL(GPU_PipelineLayout, PipelineLayout);
@@ -17,23 +19,23 @@ TYPED_POOL(GPU_Renderpass, Renderpass);
#define TEXTURE_GET(h) (texture_pool_get(&context.resource_pools->textures, h))
// --- Pools
-typedef struct GPU_BackendPools{
+typedef struct GPU_BackendPools {
Pipeline_pool pipelines;
PipelineLayout_pool pipeline_layouts;
Renderpass_pool renderpasses;
} GPU_BackendPools;
-void backend_pools_init(arena* a, GPU_BackendPools* backend_pools);
+void BackendPools_Init(arena* a, GPU_BackendPools* backend_pools);
struct ResourcePools {
Buffer_pool buffers;
Texture_pool textures;
};
-void resource_pools_init(arena* a, struct ResourcePools* res_pools);
-
+typedef struct ResourcePools ResourcePools;
+void ResourcePools_Init(arena* a, struct ResourcePools* res_pools);
// --- Vertex formats
-bytebuffer vertices_as_bytebuffer(arena* a, VertexFormat format, Vertex_darray* vertices);
-
-void vertex_desc_add(VertexDescription* builder, const char* name, VertexAttribType type);
VertexDescription static_3d_vertex_description();
-size_t vertex_attrib_size(VertexAttribType attr);
+
+void VertexDesc_AddAttr(VertexDescription* builder, const char* name, VertexAttribType type);
+
+size_t VertexAttribSize(VertexAttribType attr);
diff --git a/src/ral/ral_impl.h b/src/ral/ral_impl.h
index 4d1c17a..a896eff 100644
--- a/src/ral/ral_impl.h
+++ b/src/ral/ral_impl.h
@@ -2,7 +2,9 @@
* @brief
*/
#pragma once
+#include "buf.h"
#include "defines.h"
+#include "ral_common.h"
#include "ral_types.h"
struct GLFWwindow;
@@ -18,7 +20,9 @@ typedef struct GPU_CmdBuffer GPU_CmdBuffer; // Ready for submission
typedef struct GPU_Buffer GPU_Buffer;
typedef struct GPU_Texture GPU_Texture;
-bool GPU_Backend_Init(const char* window_name, struct GLFWwindow* window);
+struct ResourcePools;
+
+bool GPU_Backend_Init(const char* window_name, struct GLFWwindow* window, struct ResourcePools* res_pools);
void GPU_Backend_Shutdown();
bool GPU_Device_Create(GPU_Device* out_device);
@@ -27,12 +31,54 @@ void GPU_Device_Destroy(GPU_Device* device);
bool GPU_Swapchain_Create(GPU_Swapchain* out_swapchain);
void GPU_Swapchain_Destroy(GPU_Swapchain* swapchain);
-GPU_Renderpass* GPU_Renderpass_Create(GPU_RenderpassDesc description);
-void GPU_Renderpass_Destroy(GPU_Renderpass* pass);
+PUB GPU_Renderpass* GPU_Renderpass_Create(GPU_RenderpassDesc description);
+PUB void GPU_Renderpass_Destroy(GPU_Renderpass* pass);
+
+PUB GPU_Pipeline* GPU_GraphicsPipeline_Create(GraphicsPipelineDesc description, GPU_Renderpass* renderpass);
+PUB void GraphicsPipeline_Destroy(GPU_Pipeline* pipeline);
+
+// --- Command buffer
+PUB GPU_CmdEncoder GPU_CmdEncoder_Create();
+PUB void GPU_CmdEncoder_Destroy(GPU_CmdEncoder* encoder);
+PUB void GPU_CmdEncoder_BeginRender(GPU_CmdEncoder* encoder, GPU_Renderpass* renderpass);
+PUB void GPU_CmdEncoder_EndRender(GPU_CmdEncoder* encoder);
+PUB GPU_CmdEncoder* GPU_GetDefaultEncoder();
+PUB void GPU_QueueSubmit(GPU_CmdBuffer* cmd_buffer);
+
+// --- Buffers
+PUB BufferHandle GPU_BufferCreate(u64 size, GPU_BufferType buf_type, GPU_BufferFlags flags, const void* data);
+PUB void GPU_BufferDestroy(BufferHandle handle);
+PUB void GPU_BufferUpload(BufferHandle buffer, size_t n_bytes, const void* data);
+
+// --- Textures
+PUB TextureHandle GPU_TextureCreate(TextureDesc desc, bool create_view, const void* data);
+PUB void GPU_TextureDestroy(TextureHandle handle);
+PUB void GPU_TextureUpload(TextureHandle handle, size_t n_bytes, const void* data);
+
+// --- Data copy commands
+// TODO: Rename these to reflect current coding style
+void encode_buffer_copy(GPU_CmdEncoder* encoder, BufferHandle src, u64 src_offset,
+ BufferHandle dst, u64 dst_offset, u64 copy_size);
+void buffer_upload_bytes(BufferHandle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size);
+
+void copy_buffer_to_buffer_oneshot(BufferHandle src, u64 src_offset, BufferHandle dst,
+ u64 dst_offset, u64 copy_size);
+void copy_buffer_to_image_oneshot(BufferHandle src, TextureHandle dst);
+
+// --- Render commands
+PUB void GPU_EncodeBindPipeline(GPU_CmdEncoder* encoder, GPU_Pipeline* pipeline);
+PUB void GPU_EncodeBindShaderData(GPU_CmdEncoder* encoder, u32 group, ShaderData* data);
+void GPU_EncodeSetDefaults(GPU_CmdEncoder* encoder);
+PUB void GPU_EncodeSetVertexBuffer(GPU_CmdEncoder* encoder, BufferHandle buf);
+PUB void GPU_EncodeSetIndexBuffer(GPU_CmdEncoder* encoder, BufferHandle buf);
+PUB void GPU_EncodeDraw(GPU_CmdEncoder* encoder, u64 count);
+PUB void GPU_EncodeDrawIndexed(GPU_CmdEncoder* encoder, u64 index_count);
-GPU_Pipeline* GPU_GraphicsPipeline_Create(GraphicsPipelineDesc description, GPU_Renderpass* renderpass);
-void GraphicsPipeline_Destroy(GPU_Pipeline* pipeline);
+// --- Frame cycle
+PUB bool GPU_Backend_BeginFrame();
+PUB void GPU_Backend_EndFrame();
+// Concrete implementation
#if defined(CEL_REND_BACKEND_OPENGL)
#include "backend_opengl.h"
#endif
diff --git a/src/ral/ral_types.h b/src/ral/ral_types.h
index 0ba7f87..188951a 100644
--- a/src/ral/ral_types.h
+++ b/src/ral/ral_types.h
@@ -176,7 +176,7 @@ typedef struct ShaderDataLayout {
size_t binding_count;
} ShaderDataLayout;
-typedef ShaderDataLayout (*FN_GetBindingLayout)(void);
+typedef ShaderDataLayout (*FN_GetBindingLayout)(void* data);
typedef struct ShaderData {
FN_GetBindingLayout get_layout;
@@ -197,7 +197,6 @@ typedef enum PrimitiveTopology {
typedef enum CullMode { CULL_BACK_FACE, CULL_FRONT_FACE, CULL_COUNT } CullMode;
typedef struct GraphicsPipelineDesc {
- // GPU_Renderpass* renderpass -> takes a renderpass in the create function
const char* debug_name;
VertexDescription vertex_desc;
ShaderDesc vs; /** @brief Vertex shader stage */
@@ -205,7 +204,7 @@ typedef struct GraphicsPipelineDesc {
// Roughly equivalent to a descriptor set layout each. each layout can have multiple bindings
// examples:
- // - uniform buffer reprensenting view projection matrix
+ // - uniform buffer representing view projection matrix
// - texture for shadow map
ShaderData data_layouts[MAX_SHADER_DATA_LAYOUTS];
u32 data_layouts_count;
diff --git a/src/render/backends/opengl/backend_opengl.c b/src/render/backends/opengl/backend_opengl.c
index 70e10d7..43105e2 100644
--- a/src/render/backends/opengl/backend_opengl.c
+++ b/src/render/backends/opengl/backend_opengl.c
@@ -1,7 +1,6 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
-#include "builtin_materials.h"
#include "colours.h"
#include "maths.h"
#include "opengl_helpers.h"
@@ -63,11 +62,6 @@ bool gpu_backend_init(const char* window_name, struct GLFWwindow* window) {
return true;
}
-void gpu_backend_shutdown() {}
-
-bool gpu_device_create(gpu_device* out_device) { /* No-op in OpenGL */ }
-void gpu_device_destroy() { /* No-op in OpenGL */ }
-
// --- Render Pipeline
gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description) {
gpu_pipeline* pipeline = pipeline_pool_alloc(&context.gpu_pools.pipelines, NULL);
@@ -168,10 +162,6 @@ gpu_renderpass* gpu_renderpass_create(const gpu_renderpass_desc* description) {
}
void gpu_renderpass_destroy(gpu_renderpass* pass) { glDeleteFramebuffers(1, &pass->fbo); }
-// --- Swapchain
-bool gpu_swapchain_create(gpu_swapchain* out_swapchain) {}
-void gpu_swapchain_destroy(gpu_swapchain* swapchain) {}
-
// --- Command buffer
gpu_cmd_encoder gpu_cmd_encoder_create() {
gpu_cmd_encoder encoder = { 0 };
@@ -180,12 +170,9 @@ gpu_cmd_encoder gpu_cmd_encoder_create() {
void gpu_cmd_encoder_destroy(gpu_cmd_encoder* encoder) {}
void gpu_cmd_encoder_begin(gpu_cmd_encoder encoder) {}
void gpu_cmd_encoder_begin_render(gpu_cmd_encoder* encoder, gpu_renderpass* renderpass) {
- // glViewport(0, 0, 1000, 1000);
glBindFramebuffer(GL_FRAMEBUFFER, renderpass->fbo);
rgba clear_colour = STONE_800;
glClearColor(clear_colour.r, clear_colour.g, clear_colour.b, 1.0f);
- /* glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); */
- // FIXME: account for both
if (renderpass->description.has_depth_stencil) {
glClear(GL_DEPTH_BUFFER_BIT);
} else {
@@ -353,9 +340,6 @@ buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_f
return handle;
}
-void gpu_buffer_destroy(buffer_handle buffer) {}
-void gpu_buffer_upload(const void* data) {}
-
texture_handle gpu_texture_create(texture_desc desc, bool create_view, const void* data) {
// "allocating" the cpu-side struct
texture_handle handle;
diff --git a/src/render/bind_group_layouts.h b/src/render/bind_group_layouts.h
deleted file mode 100644
index 246d1ef..0000000
--- a/src/render/bind_group_layouts.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * @file bind_group_layouts.h
- * @author your name (you@domain.com)
- * @brief Common bindgroups (descriptor set layouts)
- * @version 0.1
- * @date 2024-04-28
- *
- * @copyright Copyright (c) 2024
- *
- */
-#pragma once
-#include "defines.h"
-#include "maths_types.h"
-
-// Three major sets
-
-// 1. Scene / Global
-typedef struct bg_globals {
- mat4 view;
- mat4 projection;
- f32 total_time;
- f32 delta_time;
-} bg_globals;
-
-// 2. Material (once per object)
-
-// 3. Per draw call
-typedef struct bg_model {
- mat4 model;
-} bg_model;
diff --git a/src/render/immediate.h b/src/render/immediate.h
deleted file mode 100644
index f4b1729..0000000
--- a/src/render/immediate.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "geometry.h"
-#include "maths_types.h"
-
-typedef struct immdraw_system immdraw_system;
-
-bool immdraw_system_init(immdraw_system* state);
-void immdraw_system_shutdown(immdraw_system* state);
-void immdraw_system_render(immdraw_system* state);
-
-// 3. SIMA (simplified immediate mode api) / render.h
-// - dont need to worry about uploading mesh data
-// - very useful for debugging
-void immdraw_plane(vec3 pos, quat rotation, f32 u_scale, f32 v_scale, vec4 colour);
-void immdraw_cuboid(vec3 pos, quat rotation, f32x3 extents, vec4 colour);
-void immdraw_sphere(vec3 pos, f32 radius, vec4 colour);
-
-void immdraw_camera_frustum();
diff --git a/src/render/ral.h b/src/render/ral.h
index 792bb4e..fc3c96c 100644
--- a/src/render/ral.h
+++ b/src/render/ral.h
@@ -109,24 +109,24 @@ typedef struct gpu_renderpass_desc {
} gpu_renderpass_desc;
// --- Lifecycle functions
-bool gpu_backend_init(const char* window_name, struct GLFWwindow* window);
-void gpu_backend_shutdown();
+// bool gpu_backend_init(const char* window_name, struct GLFWwindow* window);
+// void gpu_backend_shutdown();
void resource_pools_init(arena* a, struct resource_pools* res_pools);
-bool gpu_device_create(gpu_device* out_device);
-void gpu_device_destroy();
+// bool gpu_device_create(gpu_device* out_device);
+// void gpu_device_destroy();
-// --- Render Pipeline
-gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description);
-void gpu_pipeline_destroy(gpu_pipeline* pipeline);
+// // --- Render Pipeline
+// gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description);
+// void gpu_pipeline_destroy(gpu_pipeline* pipeline);
-// --- Renderpass
-gpu_renderpass* gpu_renderpass_create(const gpu_renderpass_desc* description);
-void gpu_renderpass_destroy(gpu_renderpass* pass);
+// // --- Renderpass
+// gpu_renderpass* gpu_renderpass_create(const gpu_renderpass_desc* description);
+// void gpu_renderpass_destroy(gpu_renderpass* pass);
-// --- Swapchain
-bool gpu_swapchain_create(gpu_swapchain* out_swapchain);
-void gpu_swapchain_destroy(gpu_swapchain* swapchain);
+// // --- Swapchain
+// bool gpu_swapchain_create(gpu_swapchain* out_swapchain);
+// void gpu_swapchain_destroy(gpu_swapchain* swapchain);
// --- Command buffer
gpu_cmd_encoder gpu_cmd_encoder_create();
@@ -167,20 +167,6 @@ void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count);
void encode_clear_buffer(gpu_cmd_encoder* encoder, buffer_handle buf);
// --- Buffers
-buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_flags flags,
- const void* data);
-void gpu_buffer_destroy(buffer_handle buffer);
-void gpu_buffer_upload(const void* data);
-
-// Textures
-/** @brief Create a new GPU texture resource.
- * @param create_view creates a texture view (with same dimensions) at the same time
- * @param data if not NULL then the data stored at the pointer will be uploaded to the GPU texture
- * @note automatically creates a sampler for you */
-texture_handle gpu_texture_create(texture_desc desc, bool create_view, const void* data);
-void gpu_texture_destroy(texture_handle);
-void gpu_texture_upload(texture_handle texture, const void* data);
-
// --- Vertex formats
bytebuffer vertices_as_bytebuffer(arena* a, vertex_format format, vertex_darray* vertices);
diff --git a/src/render/static_pipeline.h b/src/render/static_pipeline.h
deleted file mode 100644
index bf5bc42..0000000
--- a/src/render/static_pipeline.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-#include "defines.h"
-#include "maths_types.h"
-#include "ral.h"
-#include "ral_types.h"
-#include "render_types.h"
-
-typedef struct mvp_uniforms {
- mat4 model;
- mat4 view;
- mat4 projection;
-} mvp_uniforms;
-typedef struct my_shader_bind_group {
- mvp_uniforms mvp;
-} my_shader_bind_group;
-
-static shader_data_layout mvp_uniforms_layout(void* data) {
- my_shader_bind_group* d = (my_shader_bind_group*)data;
- bool has_data = data != NULL;
-
- shader_binding b1 = { .label = "Matrices",
- .type = SHADER_BINDING_BYTES,
- .stores_data = has_data,
- .data = { .bytes = { .size = sizeof(mvp_uniforms) } } };
-
- if (has_data) {
- b1.data.bytes.data = &d->mvp;
- }
- return (shader_data_layout){ .name = "global_ubo", .bindings = { b1 }, .bindings_count = 1 };
-}
diff --git a/src/resources/gltf.c b/src/resources/gltf.c
index e381954..69ae9e1 100644
--- a/src/resources/gltf.c
+++ b/src/resources/gltf.c
@@ -50,7 +50,7 @@ ModelHandle model_load_gltf(const char *path, bool invert_texture_y) {
const char *file_string = string_from_file(path);
ModelHandle handle;
- // modfl *model = model_pool_alloc(&g_core.models, &handle);
+ // Model *model = model_pool_alloc(&g_core.models, &handle);
// model->name = str8_cstr_view(path);
// model->meshes = mesh_darray_new(1);
// model->materials = material_darray_new(1);
@@ -184,7 +184,8 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, Str8 rel
normal_tex_view.texture->image->uri);
// material our_material =
- // pbr_material_load(albedo_map_path, normal_map_path, true, metal_rough_map_path, NULL, NULL);
+ // pbr_material_load(albedo_map_path, normal_map_path, true, metal_rough_map_path, NULL,
+ // NULL);
// our_material.name = malloc(strlen(gltf_material.name) + 1);
// strcpy(our_material.name, gltf_material.name);
@@ -326,62 +327,61 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, Str8 rel
u32_darray *geo_indices = u32_darray_new(0);
// Store vertices
- // printf("Positions %d Normals %d UVs %d\n", tmp_positions->len, tmp_normals->len, tmp_uvs->len);
- // assert(tmp_positions->len == tmp_normals->len);
- // assert(tmp_normals->len == tmp_uvs->len);
- // for (u32 v_i = 0; v_i < tmp_positions->len; v_i++) {
+ // printf("Positions %d Normals %d UVs %d\n", tmp_positions->len, tmp_normals->len,
+ // tmp_uvs->len); assert(tmp_positions->len == tmp_normals->len); assert(tmp_normals->len ==
+ // tmp_uvs->len); for (u32 v_i = 0; v_i < tmp_positions->len; v_i++) {
// vertex v = { .static_3d = {
// .position = tmp_positions->data[v_i],
// .normal = tmp_normals->data[v_i],
// .tex_coords = tmp_uvs->data[v_i],
// } };
// vertex_darray_push(geo_vertices, v);
- }
+ }
- // Store indices
- // cgltf_accessor *indices = primitive.indices;
- // if (primitive.indices > 0) {
- // WARN("indices! %d", indices->count);
- // has_indices = true;
-
- // // store indices
- // for (cgltf_size i = 0; i < indices->count; ++i) {
- // cgltf_uint ei;
- // cgltf_accessor_read_uint(indices, i, &ei, 1);
- // u32_darray_push(geo_indices, ei);
- // }
-
- // fetch and store vertices for each index
- // for (cgltf_size i = 0; i < indices->count; ++i) {
- // vertex vert;
- // cgltf_uint index = mesh.indices[i];
- // vert.position = tmp_positions->data[index];
- // vert.normal = tmp_normals->data[index];
- // vert.uv = tmp_uvs->data[index];
- // vertex_darray_push(mesh.vertices, vert);
-
- // if (is_skinned) {
- // vertex_bone_data vbd = tmp_vertex_bone_data->data[index]; // create a copy
- // vertex_bone_data_darray_push(mesh.vertex_bone_data, vbd);
- // }
- // // for each vertex do the bone data
- // }
- // } else {
- // has_indices = false;
- // return false; // TODO: handle this
- // }
+ // Store indices
+ // cgltf_accessor *indices = primitive.indices;
+ // if (primitive.indices > 0) {
+ // WARN("indices! %d", indices->count);
+ // has_indices = true;
+
+ // // store indices
+ // for (cgltf_size i = 0; i < indices->count; ++i) {
+ // cgltf_uint ei;
+ // cgltf_accessor_read_uint(indices, i, &ei, 1);
+ // u32_darray_push(geo_indices, ei);
+ // }
+
+ // fetch and store vertices for each index
+ // for (cgltf_size i = 0; i < indices->count; ++i) {
+ // vertex vert;
+ // cgltf_uint index = mesh.indices[i];
+ // vert.position = tmp_positions->data[index];
+ // vert.normal = tmp_normals->data[index];
+ // vert.uv = tmp_uvs->data[index];
+ // vertex_darray_push(mesh.vertices, vert);
+
+ // if (is_skinned) {
+ // vertex_bone_data vbd = tmp_vertex_bone_data->data[index]; // create a copy
+ // vertex_bone_data_darray_push(mesh.vertex_bone_data, vbd);
+ // }
+ // // for each vertex do the bone data
+ // }
+ // } else {
+ // has_indices = false;
+ // return false; // TODO: handle this
+ // }
- // geometry_data *geometry = malloc(sizeof(geometry_data));
- // geometry->format = VERTEX_STATIC_3D;
- // geometry->colour = (rgba){ 1, 1, 1, 1 };
- // geometry->vertices = geo_vertices;
- // geometry->indices = geo_indices;
- // geometry->has_indices = has_indices;
+ // geometry_data *geometry = malloc(sizeof(geometry_data));
+ // geometry->format = VERTEX_STATIC_3D;
+ // geometry->colour = (rgba){ 1, 1, 1, 1 };
+ // geometry->vertices = geo_vertices;
+ // geometry->indices = geo_indices;
+ // geometry->has_indices = has_indices;
- // mesh m = mesh_create(geometry, true);
- // m.material_index = (u32_opt){ .has_value = mat_idx == 9999, .value = mat_idx };
+ // mesh m = mesh_create(geometry, true);
+ // m.material_index = (u32_opt){ .has_value = mat_idx == 9999, .value = mat_idx };
- // mesh_darray_push(out_model->meshes, m);
+ // mesh_darray_push(out_model->meshes, m);
// }
// // clear data for each mesh
diff --git a/src/resources/loaders.h b/src/resources/loaders.h
index d8437b9..3ec41fa 100644
--- a/src/resources/loaders.h
+++ b/src/resources/loaders.h
@@ -5,8 +5,8 @@
#include "str.h"
// --- Public API
-ModelHandle Model_Load_obj(const char *path, bool invert_texture_y);
-ModelHandle Model_Load_gltf(const char *path, bool invert_texture_y);
+PUB ModelHandle Model_Load_obj(const char *path, bool invert_texture_y);
+PUB ModelHandle Model_Load_gltf(const char *path, bool invert_texture_y);
// --- Internal
bool model_load_gltf_str(const char *file_string, const char *filepath, Str8 relative_path,
diff --git a/src/resources/obj.c b/src/resources/obj.c
index 87d3ed6..e5b2fc9 100644
--- a/src/resources/obj.c
+++ b/src/resources/obj.c
@@ -260,7 +260,8 @@ bool model_load_obj_str(const char *file_string, Str8 relative_path, Model *out_
// // }
// // DEBUG("Loaded submesh\n vertices: %zu\n uvs: %zu\n normals: %zu\n faces: %zu",
-// // vec3_darray_len(tmp_positions), vec2_darray_len(tmp_uvs), vec3_darray_len(tmp_normals),
+// // vec3_darray_len(tmp_positions), vec2_darray_len(tmp_uvs),
+// vec3_darray_len(tmp_normals),
// // face_darray_len(tmp_faces));
// // // Clear current object faces
@@ -339,7 +340,8 @@ bool model_load_obj_str(const char *file_string, Str8 relative_path, Model *out_
// // &current_material.ambient_colour.y, &current_material.ambient_colour.z);
// // } else if (strcmp(line_header, "Kd") == 0) {
// // // diffuse
-// // sscanf(pch + offset, "%f %f %f", &current_material.diffuse.x, &current_material.diffuse.y,
+// // sscanf(pch + offset, "%f %f %f", &current_material.diffuse.x,
+// &current_material.diffuse.y,
// // &current_material.diffuse.z);
// // } else if (strcmp(line_header, "Ks") == 0) {
// // // specular
diff --git a/src/std/buf.h b/src/std/buf.h
index de093ec..77fc7b9 100644
--- a/src/std/buf.h
+++ b/src/std/buf.h
@@ -1,12 +1,6 @@
/**
* @file buf.h
- * @author your name (you@domain.com)
* @brief
- * @version 0.1
- * @date 2024-04-28
- *
- * @copyright Copyright (c) 2024
- *
*/
#pragma once
#include "defines.h"
diff --git a/xmake.lua b/xmake.lua
index 960923b..009b9fd 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -159,12 +159,12 @@ end
target("core_shared")
set_kind("shared")
-add_deps("core_config") -- inherit common configurations
+add_deps("core_config") -- inherit common configurations
add_files(core_sources)
-- Link against dynamic CRT
if is_plat("windows") then
- add_links("msvcrt", "legacy_stdio_definitions") -- for release builds
- add_links("msvcrtd", "legacy_stdio_definitions") -- for debug builds
+ add_links("msvcrt", "legacy_stdio_definitions") -- for release builds
+ add_links("msvcrtd", "legacy_stdio_definitions") -- for debug builds
end
-- target("main_loop")