Skip to content

Commit 6874617

Browse files
committed
Appendix 02
1 parent ec79cef commit 6874617

File tree

222 files changed

+1154331
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

222 files changed

+1154331
-3
lines changed

bookcontents/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ The book is structured in the following chapters:
2323
- [Chapter 18](chapter-18/chapter-18.md): Buffer Device Address (BDA).
2424
- [Chapter 18](chapter-19/chapter-19.md): Indirect drawing.
2525
- [Appendix 01](appendix-01/appendix-01.md): This chapter provides some insights on how to troubleshoot a loss of device error.
26+
- [Appendix 02](appendix-02/appendix-02.md): This chapter provides a simple 3D game to put everything together
Lines changed: 190 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,192 @@
11
# Appendix 02 - A sample 3D game
22

3-
TBD
3+
In this appendix we will provide a ample 3D game to show how everything fits together. I will not explain the whole set of source code, instead, I will just highlight some changes made to the core source code base and some remarks.
4+
5+
You can find the complete source code for this chapter [here](../../booksamples/appendix-02).
6+
7+
## Overview of the game
8+
9+
The game is inspired by a classic [Sokoban](https://es.wikipedia.org/wiki/Sokoban). In that game you were to push the boxes to its final place in several scenarios acting as puzzles. In this case you will play the rol of a knight which needs to push several boxes to the finish place. You can only push them, pulling is not allowed, therefore making a wrong move can block a maze.
10+
11+
Some remarks:
12+
- The game allows to define multiple levels (using ASCII code to define the contents of each cell) and uses a JSON file to list them (you can make your own puzzles and contribute to the book through a pull request if you want).
13+
- We will need to tweak a little bit the GUI code to use custom fonts. Available fonts will be used defined through a JSON file.
14+
- I think I've gave proper credit for the different models, sounds and fonts used. if there something missing please let me know.
15+
16+
## GUI Modification
17+
18+
In order to use custom fonts we will need to create specific textures for each font family. In order to control that process we will create a new class named `FontsManager` which is defined like this:
19+
20+
```java
21+
package org.vulkanb.boxes;
22+
23+
import com.google.gson.*;
24+
import imgui.*;
25+
import org.tinylog.Logger;
26+
27+
import java.io.IOException;
28+
import java.nio.file.*;
29+
import java.util.*;
30+
31+
public class FontsManager {
32+
33+
private static final String CONFIG_FILE = "resources/fonts/fonts.json";
34+
35+
private final Map<String, ImFont> fontsMap;
36+
private ImFont defaultFont;
37+
38+
public FontsManager() {
39+
fontsMap = new HashMap<>();
40+
Logger.debug("Loading font configuration file {}", CONFIG_FILE);
41+
try {
42+
ImGuiIO imGuiIO = ImGui.getIO();
43+
defaultFont = imGuiIO.getFonts().addFontDefault();
44+
45+
String cfgFileContents = Files.readString(Path.of(CONFIG_FILE));
46+
47+
var gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
48+
List<FontData> fontDataList = new ArrayList<>();
49+
fontDataList.addAll(Arrays.asList(gson.fromJson(cfgFileContents, FontData[].class)));
50+
51+
for (FontData fontData : fontDataList) {
52+
Logger.debug("Loading font [{}]", fontData.ttfFile());
53+
ImFont font = imGuiIO.getFonts().addFontFromFileTTF(fontData.ttfFile(), fontData.size());
54+
fontsMap.put(fontData.id(), font);
55+
}
56+
} catch (IOException excp) {
57+
Logger.error("Error loading configuration file {}", CONFIG_FILE, excp);
58+
}
59+
}
60+
61+
public ImFont getDefaultFont() {
62+
return defaultFont;
63+
}
64+
65+
public ImFont getFont(String fontId) {
66+
ImFont font;
67+
if (fontsMap.containsKey(fontId)) {
68+
font = fontsMap.get(fontId);
69+
} else {
70+
Logger.warn("Requested unknown font {}", fontId);
71+
font = defaultFont;
72+
}
73+
return font;
74+
}
75+
76+
record FontData(String id, String ttfFile, float size) {
77+
}
78+
}```
79+
80+
As you can see, in the constructor we load the JSON file which defines the fonts available. That file may look like this:
81+
82+
```json
83+
[
84+
{
85+
"id": "MAIN",
86+
"ttf_file": "resources/fonts/neuropolitical_rg.otf",
87+
"size": 82.0
88+
}
89+
]
90+
```
91+
92+
A font entry is defined by an identifier, which will be used later to use it, a path to a True Type Font (TTF) file and a size. Once loaded, we use the Imgui `addFontFromFileTTF` function to create an `ImFont` instance. We will store those instances in a map to later use it. We need to change the code of the `GuiRender` class
93+
to be able to generate a font atlas not coming from the default font. We will remove font set up from the `initUI` method and move it to an new one named `loadFontsTexture`:
94+
95+
```java
96+
public class GuiRender {
97+
...
98+
public GuiRender(EngCtx engCtx, VkCtx vkCtx, Queue queue, Attachment dstAttachment) {
99+
attInfoColor = createColorAttachmentInfo(dstAttachment);
100+
renderInfo = createRenderInfo(dstAttachment, attInfoColor);
101+
102+
ShaderModule[] shaderModules = createShaderModules(vkCtx);
103+
textDescSetLayout = new DescSetLayout(vkCtx, new DescSetLayout.LayoutInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
104+
0, 1, VK_SHADER_STAGE_FRAGMENT_BIT));
105+
106+
pipeline = createPipeline(vkCtx, shaderModules, new DescSetLayout[]{textDescSetLayout});
107+
Arrays.asList(shaderModules).forEach(s -> s.cleanup(vkCtx));
108+
109+
buffsVtx = new VkBuffer[VkUtils.MAX_IN_FLIGHT];
110+
buffsIdx = new VkBuffer[VkUtils.MAX_IN_FLIGHT];
111+
112+
initUI(vkCtx);
113+
var textureSamplerInfo = new TextureSamplerInfo(VK_SAMPLER_ADDRESS_MODE_REPEAT,
114+
VK_BORDER_COLOR_INT_OPAQUE_BLACK, 1, true);
115+
fontsTextureSampler = new TextureSampler(vkCtx, textureSamplerInfo);
116+
117+
KeyboardInput ki = engCtx.window().getKeyboardInput();
118+
ki.setCharCallBack(new GuiUtils.CharCallBack());
119+
ki.addKeyCallBack(new GuiUtils.KeyCallback());
120+
121+
guiTexturesMap = new HashMap<>();
122+
}
123+
...
124+
private static void initUI(VkCtx vkCtx) {
125+
ImGui.createContext();
126+
ImGuiIO imGuiIO = ImGui.getIO();
127+
imGuiIO.setIniFilename(null);
128+
VkExtent2D swapChainExtent = vkCtx.getSwapChain().getSwapChainExtent();
129+
imGuiIO.setDisplaySize(swapChainExtent.width(), swapChainExtent.height());
130+
imGuiIO.setDisplayFramebufferScale(1.0f, 1.0f);
131+
}
132+
133+
public void cleanup(VkCtx vkCtx) {
134+
fontsTextureSampler.cleanup(vkCtx);
135+
textDescSetLayout.cleanup(vkCtx);
136+
pipeline.cleanup(vkCtx);
137+
Arrays.stream(buffsVtx).filter(Objects::nonNull).forEach(b -> b.cleanup(vkCtx));
138+
Arrays.stream(buffsIdx).filter(Objects::nonNull).forEach(b -> b.cleanup(vkCtx));
139+
renderInfo.free();
140+
attInfoColor.free();
141+
}
142+
143+
public void loadFontsTexture(VkCtx vkCtx, TextureCache textureCache) {
144+
ImGuiIO imGuiIO = ImGui.getIO();
145+
ImInt texWidth = new ImInt();
146+
ImInt texHeight = new ImInt();
147+
ByteBuffer buf = imGuiIO.getFonts().getTexDataAsRGBA32(texWidth, texHeight);
148+
ImageSrc imageSrc = new ImageSrc(buf, texWidth.get(), texHeight.get(), 4);
149+
Texture fontsTexture = new Texture(vkCtx, "GUI_TEXTURE", imageSrc, VK_FORMAT_R8G8B8A8_SRGB);
150+
textureCache.addTexture(fontsTexture.getId(), fontsTexture);
151+
Device device = vkCtx.getDevice();
152+
DescAllocator descAllocator = vkCtx.getDescAllocator();
153+
DescSet descSet = descAllocator.addDescSets(device, DESC_ID_TEXT, 1, textDescSetLayout)[0];
154+
descSet.setImage(device, fontsTexture.getImageView(), fontsTextureSampler, textDescSetLayout.getLayoutInfo().binding());
155+
}
156+
...
157+
}
158+
```
159+
160+
Some things to notice, we will not need to store the fonts texture in the `GuiRender` class, this is managed now in the `FontsMaTextureCachenager` class. Also ww
161+
need to call the new `loadFontsTexture` method in the `Render` class:
162+
163+
```java
164+
public class Render {
165+
...
166+
public void init(EngCtx engCtx, InitData initData) {
167+
...
168+
guiRender.loadFontsTexture(vkCtx, textureCache);
169+
List<GuiTexture> guiTextures = initData.guiTextures();
170+
if (guiTextures != null) {
171+
initData.guiTextures().forEach(e -> textureCache.addTexture(vkCtx, e.texturePath(), e.texturePath(),
172+
VK_FORMAT_R8G8B8A8_SRGB));
173+
}
174+
...
175+
}
176+
...
177+
}
178+
```
179+
180+
Now, how do we use the fonts in the code? Prior to using a font we just need to call the `pushFont` function in Imgui, like this:
181+
```java
182+
ImGui.pushFont(fontsManager.getFont("MAIN"));
183+
```
184+
185+
Once we have done with the font we just pop the font:
186+
```java
187+
ImGui.popFont();
188+
```
189+
190+
A sample screenshot of the main screen is shown below.
191+
192+
<img src="../appendix-02/rap02-screen-shot.png" title="" alt="Screen Shot" data-align="center">
102 KB
Loading

bookcontents/chapter-08/chapter-08.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ In order to manage materials we will need to create a new class `MaterialsCache`
10341034
package org.vulkanb.eng.graph;
10351035

10361036
import org.lwjgl.system.MemoryUtil;
1037+
import org.tinylog.Logger;
10371038
import org.vulkanb.eng.graph.vk.*;
10381039
import org.vulkanb.eng.model.MaterialData;
10391040

@@ -1070,6 +1071,8 @@ public class MaterialsCache {
10701071
int result = -1;
10711072
if (id != null) {
10721073
result = materialsMap.getIndexOf(id);
1074+
} else {
1075+
Logger.warn("Could not find material with id [{}]", id);
10731076
}
10741077
return result;
10751078
}

bookcontents/vulkanbook.epub

79 Bytes
Binary file not shown.

booksamples/appendix-01/src/main/java/org/vulkanb/eng/graph/MaterialsCache.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.vulkanb.eng.graph;
22

33
import org.lwjgl.system.MemoryUtil;
4+
import org.tinylog.Logger;
45
import org.vulkanb.eng.graph.vk.*;
56
import org.vulkanb.eng.model.MaterialData;
67

@@ -38,10 +39,12 @@ public int getPosition(String id) {
3839
int result = -1;
3940
if (id != null) {
4041
result = materialsMap.getIndexOf(id);
42+
} else {
43+
Logger.warn("Could not find material with id [{}]", id);
4144
}
4245
return result;
4346
}
44-
47+
4548
public void loadMaterials(VkCtx vkCtx, List<MaterialData> materials, TextureCache textureCache, CmdPool cmdPool,
4649
Queue queue) {
4750
int numMaterials = materials.size();

booksamples/appendix-02/pom.xml

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>org.vulkanb</groupId>
8+
<artifactId>book</artifactId>
9+
<version>1.0.0</version>
10+
</parent>
11+
12+
<artifactId>appendix-02</artifactId>
13+
<version>1.0.0</version>
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.tinylog</groupId>
17+
<artifactId>tinylog-api</artifactId>
18+
<version>${tinylog.version}</version>
19+
</dependency>
20+
<dependency>
21+
<groupId>org.tinylog</groupId>
22+
<artifactId>tinylog-impl</artifactId>
23+
<version>${tinylog.version}</version>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.lwjgl</groupId>
27+
<artifactId>lwjgl</artifactId>
28+
<version>${lwjgl.version}</version>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.lwjgl</groupId>
32+
<artifactId>lwjgl-glfw</artifactId>
33+
<version>${lwjgl.version}</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.lwjgl</groupId>
37+
<artifactId>lwjgl-vulkan</artifactId>
38+
<version>${lwjgl.version}</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.lwjgl</groupId>
42+
<artifactId>lwjgl-shaderc</artifactId>
43+
<version>${lwjgl.version}</version>
44+
</dependency>
45+
<dependency>
46+
<groupId>org.lwjgl</groupId>
47+
<artifactId>lwjgl-stb</artifactId>
48+
<version>${lwjgl.version}</version>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.lwjgl</groupId>
52+
<artifactId>lwjgl-assimp</artifactId>
53+
<version>${lwjgl.version}</version>
54+
</dependency>
55+
<dependency>
56+
<groupId>org.lwjgl</groupId>
57+
<artifactId>lwjgl-openal</artifactId>
58+
<version>${lwjgl.version}</version>
59+
</dependency>
60+
<dependency>
61+
<groupId>org.lwjgl</groupId>
62+
<artifactId>lwjgl-vma</artifactId>
63+
<version>${lwjgl.version}</version>
64+
</dependency>
65+
<dependency>
66+
<groupId>org.joml</groupId>
67+
<artifactId>joml</artifactId>
68+
<version>${joml.version}</version>
69+
</dependency>
70+
<dependency>
71+
<groupId>io.github.spair</groupId>
72+
<artifactId>imgui-java-binding</artifactId>
73+
<version>${imgui-java.version}</version>
74+
</dependency>
75+
<dependency>
76+
<groupId>org.jcommander</groupId>
77+
<artifactId>jcommander</artifactId>
78+
<version>${jcommander.version}</version>
79+
</dependency>
80+
<dependency>
81+
<groupId>com.google.code.gson</groupId>
82+
<artifactId>gson</artifactId>
83+
<version>${gson.version}</version>
84+
</dependency>
85+
86+
<!-- Natives -->
87+
<dependency>
88+
<groupId>org.lwjgl</groupId>
89+
<artifactId>lwjgl</artifactId>
90+
<version>${lwjgl.version}</version>
91+
<classifier>${native.target}</classifier>
92+
<scope>runtime</scope>
93+
</dependency>
94+
<dependency>
95+
<groupId>org.lwjgl</groupId>
96+
<artifactId>lwjgl-glfw</artifactId>
97+
<version>${lwjgl.version}</version>
98+
<classifier>${native.target}</classifier>
99+
<scope>runtime</scope>
100+
</dependency>
101+
<dependency>
102+
<groupId>org.lwjgl</groupId>
103+
<artifactId>lwjgl-openal</artifactId>
104+
<version>${lwjgl.version}</version>
105+
<classifier>${native.target}</classifier>
106+
<scope>runtime</scope>
107+
</dependency>
108+
<dependency>
109+
<groupId>org.lwjgl</groupId>
110+
<artifactId>lwjgl-shaderc</artifactId>
111+
<version>${lwjgl.version}</version>
112+
<classifier>${native.target}</classifier>
113+
<scope>runtime</scope>
114+
</dependency>
115+
<dependency>
116+
<groupId>org.lwjgl</groupId>
117+
<artifactId>lwjgl-stb</artifactId>
118+
<version>${lwjgl.version}</version>
119+
<classifier>${native.target}</classifier>
120+
<scope>runtime</scope>
121+
</dependency>
122+
<dependency>
123+
<groupId>org.lwjgl</groupId>
124+
<artifactId>lwjgl-assimp</artifactId>
125+
<version>${lwjgl.version}</version>
126+
<classifier>${native.target}</classifier>
127+
<scope>runtime</scope>
128+
</dependency>
129+
<dependency>
130+
<groupId>org.lwjgl</groupId>
131+
<artifactId>lwjgl-vma</artifactId>
132+
<version>${lwjgl.version}</version>
133+
<classifier>${native.target}</classifier>
134+
<scope>runtime</scope>
135+
</dependency>
136+
<dependency>
137+
<groupId>io.github.spair</groupId>
138+
<artifactId>imgui-java-${native.target}</artifactId>
139+
<version>${imgui-java.version}</version>
140+
<scope>runtime</scope>
141+
</dependency>
142+
</dependencies>
143+
</project>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://www.1001fonts.com/sf-atarian-system-font.h
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"id": "MAIN",
4+
"ttf_file": "resources/fonts/neuropolitical_rg.otf",
5+
"size": 82.0
6+
}
7+
]
Binary file not shown.

0 commit comments

Comments
 (0)