ПЕРЕДУМОВИ
Переконайтеся, що ви спочатку прочитали концепції рендера. Ця сторінка базується на цих концепціях і обговорює, як рендеряться об’єкти у світі.
На цій сторінці розглядаються деякі більш сучасні концепції рендера. Ви дізнаєтеся більше про дві розділені фази рендера: «вилучення» (або «підготовка») і «малювання» (або «рендер»). У цьому посібнику ми будемо називати фазу «вилучення/підготовки» фазою «вилучення», а фазу «малювання/рендера» — фазою «малювання».
Щоб рендерити власні об'єкти у світі, у вас є два варіанти. Ви можете вставити в наявний стандартний рендер та додати свій код, але це обмежує вас наявними стандартними конвеєрами рендера. Якщо наявні стандартні конвеєри рендера не відповідають вашим потребам, вам потрібен власний конвеєр рендера.
Перш ніж перейти до власних конвеєрів рендера, подивімося на стандартний рендер.
Фази вилучення та малювання
Як згадувалося в концепціях рендера, останні оновлення Minecraft працюють над розділенням рендера на дві фази: «вилучення» та «малювання».
Усі дані, необхідні для рендера, збираються під час фази «вилучення». Це включає, наприклад, запис у буферизований конструктор. Запис вершин у буферизований конструктор через buffer.addVertex є частиною фази «вилучення». Зауважте, що попри те, що багато методів мають префікс draw або render, їх слід викликати під час фази «вилучення». Ви повинні додати всі елементи, які ви хочете рендерити на цьому етапі.
Після завершення фази «вилучення» починається фаза «малювання», і створено буферизований конструктор. Під час цієї фази буферизований конструктор промальовування на екрані. Кінцева мета цього розділення «вилучення» та «малювання» полягає в тому, щоб дозволити малювати попередній кадр паралельно з вилученням наступного кадру, покращуючи продуктивність.
Тепер, пам’ятаючи про ці дві фази, подивімось, як створити власний конвеєр рендера.
Власні конвеєри рендера
Скажімо, ми хочемо рендерити маршрутні точки, які мають з’являтися крізь стіни. Найближчим стандартним конвеєром для цього буде RenderPipelines#DEBUG_FILLED_BOX, але він не буде рендеритися через стіни, тому нам знадобиться спеціальний конвеєр рендера.
Визначення власного конвеєра рендера
Ми визначаємо власний конвеєр рендера в класі:
java
private static final RenderPipeline FILLED_THROUGH_WALLS = RenderPipelines.register(RenderPipeline.builder(RenderPipelines.DEBUG_FILLED_SNIPPET)
.withLocation(Identifier.fromNamespaceAndPath(ExampleMod.MOD_ID, "pipeline/debug_filled_box_through_walls"))
.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
.build()
);1
2
3
4
5
2
3
4
5
Фаза вилучення
Спочатку ми реалізуємо фазу «вилучення». Ми можемо викликати цей метод під час фази «вилучення», щоб додати маршрутну точку для рендера.
java
private static final ByteBufferBuilder allocator = new ByteBufferBuilder(RenderType.SMALL_BUFFER_SIZE);
private BufferBuilder buffer;
private void renderWaypoint(WorldRenderContext context) {
PoseStack matrices = context.matrices();
Vec3 camera = context.worldState().cameraRenderState.pos;
matrices.pushPose();
matrices.translate(-camera.x, -camera.y, -camera.z);
if (buffer == null) {
buffer = new BufferBuilder(allocator, FILLED_THROUGH_WALLS.getVertexFormatMode(), FILLED_THROUGH_WALLS.getVertexFormat());
}
renderFilledBox(matrices.last().pose(), buffer, 0f, 100f, 0f, 1f, 101f, 1f, 0f, 1f, 0f, 0.5f);
matrices.popPose();
}
private void renderFilledBox(Matrix4fc positionMatrix, BufferBuilder buffer, float minX, float minY, float minZ, float maxX, float maxY, float maxZ, float red, float green, float blue, float alpha) {
// Front Face
buffer.addVertex(positionMatrix, minX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, maxZ).setColor(red, green, blue, alpha);
// Back face
buffer.addVertex(positionMatrix, maxX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, minZ).setColor(red, green, blue, alpha);
// Left face
buffer.addVertex(positionMatrix, minX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, minZ).setColor(red, green, blue, alpha);
// Right face
buffer.addVertex(positionMatrix, maxX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha);
// Top face
buffer.addVertex(positionMatrix, minX, maxY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, minZ).setColor(red, green, blue, alpha);
// Bottom face
buffer.addVertex(positionMatrix, minX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, minY, maxZ).setColor(red, green, blue, alpha);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Зауважте, що розмір, який використовується в конструкторі BufferAllocator, залежить від конвеєра рендера, який ви використовуєте. У нашому випадку це RenderType.SMALL_BUFFER_SIZE.
Якщо ви хочете рендерити кілька маршрутних точок, викликайте цей метод кілька разів. Переконайтеся, що ви робите це під час фази «вилучення», ПЕРЕД початком фази «малювання», на якій будується конструктор буферів.
Стани рендера
Зауважте, що у наведеному вище коді ми зберігаємо BufferBuilder у полі. Це тому, що нам це потрібно на етапі «малювання». У цьому випадку BufferBuilder — це наш «стан рендера» або «вилучені дані». Якщо вам потрібні додаткові дані під час фази «малювання», вам слід створити спеціальний клас стану рендера для зберігання BufferedBuilder і будь-яких додаткових даних рендера, які вам потрібні.
Фаза малювання
Тепер ми реалізуємо етап «малювання». Це слід викликати після того, як усі маршрутні точки, які ви хочете рендерити, були додані до BufferBuilder під час фази «вилучення».
java
private static final Vector4f COLOR_MODULATOR = new Vector4f(1f, 1f, 1f, 1f);
private static final Vector3f MODEL_OFFSET = new Vector3f();
private static final Matrix4f TEXTURE_MATRIX = new Matrix4f();
private MappableRingBuffer vertexBuffer;
private void drawFilledThroughWalls(Minecraft client, @SuppressWarnings("SameParameterValue") RenderPipeline pipeline) {
// Build the buffer
MeshData builtBuffer = buffer.buildOrThrow();
MeshData.DrawState drawParameters = builtBuffer.drawState();
VertexFormat format = drawParameters.format();
GpuBuffer vertices = upload(drawParameters, format, builtBuffer);
draw(client, pipeline, builtBuffer, drawParameters, vertices, format);
// Rotate the vertex buffer so we are less likely to use buffers that the GPU is using
vertexBuffer.rotate();
buffer = null;
}
private GpuBuffer upload(MeshData.DrawState drawParameters, VertexFormat format, MeshData builtBuffer) {
// Calculate the size needed for the vertex buffer
int vertexBufferSize = drawParameters.vertexCount() * format.getVertexSize();
// Initialize or resize the vertex buffer as needed
if (vertexBuffer == null || vertexBuffer.size() < vertexBufferSize) {
if (vertexBuffer != null) {
vertexBuffer.close();
}
vertexBuffer = new MappableRingBuffer(() -> ExampleMod.MOD_ID + " example render pipeline", GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_MAP_WRITE, vertexBufferSize);
}
// Copy vertex data into the vertex buffer
CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(vertexBuffer.currentBuffer().slice(0, builtBuffer.vertexBuffer().remaining()), false, true)) {
MemoryUtil.memCopy(builtBuffer.vertexBuffer(), mappedView.data());
}
return vertexBuffer.currentBuffer();
}
private static void draw(Minecraft client, RenderPipeline pipeline, MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBuffer vertices, VertexFormat format) {
GpuBuffer indices;
VertexFormat.IndexType indexType;
if (pipeline.getVertexFormatMode() == VertexFormat.Mode.QUADS) {
// Sort the quads if there is translucency
builtBuffer.sortQuads(allocator, RenderSystem.getProjectionType().vertexSorting());
// Upload the index buffer
indices = pipeline.getVertexFormat().uploadImmediateIndexBuffer(builtBuffer.indexBuffer());
indexType = builtBuffer.drawState().indexType();
} else {
// Use the general shape index buffer for non-quad draw modes
RenderSystem.AutoStorageIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(pipeline.getVertexFormatMode());
indices = shapeIndexBuffer.getBuffer(drawParameters.indexCount());
indexType = shapeIndexBuffer.type();
}
// Actually execute the draw
GpuBufferSlice dynamicTransforms = RenderSystem.getDynamicUniforms()
.writeTransform(RenderSystem.getModelViewMatrix(), COLOR_MODULATOR, MODEL_OFFSET, TEXTURE_MATRIX);
try (RenderPass renderPass = RenderSystem.getDevice()
.createCommandEncoder()
.createRenderPass(() -> ExampleMod.MOD_ID + " example render pipeline rendering", client.getMainRenderTarget().getColorTextureView(), OptionalInt.empty(), client.getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) {
renderPass.setPipeline(pipeline);
RenderSystem.bindDefaultUniforms(renderPass);
renderPass.setUniform("DynamicTransforms", dynamicTransforms);
// Bind texture if applicable:
// Sampler0 is used for texture inputs in vertices
// renderPass.bindTexture("Sampler0", textureSetup.texure0(), textureSetup.sampler0());
renderPass.setVertexBuffer(0, vertices);
renderPass.setIndexBuffer(indices, indexType);
// The base vertex is the starting index when we copied the data into the vertex buffer divided by vertex size
//noinspection ConstantValue
renderPass.drawIndexed(0 / format.getVertexSize(), 0, drawParameters.indexCount(), 1);
}
builtBuffer.close();
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Очищення
Нарешті, нам потрібно очистити ресурси, коли ігровий рендер закінчено. GameRenderer#close має викликати цей метод, і для цього вам наразі потрібно вставити в GameRenderer#close за допомогою міксина.
java
public void close() {
allocator.close();
if (vertexBuffer != null) {
vertexBuffer.close();
vertexBuffer = null;
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
java
package com.example.docs.mixin.client;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.renderer.GameRenderer;
import com.example.docs.rendering.CustomRenderPipeline;
@Mixin(GameRenderer.class)
public class GameRendererMixin {
@Inject(method = "close", at = @At("RETURN"))
private void onGameRendererClose(CallbackInfo ci) {
CustomRenderPipeline.getInstance().close();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Остаточний код
Об'єднавши всі описані вище кроки, ми отримаємо простий клас, який рендерить маршрутну точку на (0, 100, 0) через стіни.
java
package com.example.docs.rendering;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.DepthTestFunction;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.ByteBufferBuilder;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.lwjgl.system.MemoryUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MappableRingBuffer;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.client.renderer.rendertype.RenderType;
import net.minecraft.resources.Identifier;
import net.minecraft.world.phys.Vec3;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents;
import com.example.docs.ExampleMod;
public class CustomRenderPipeline implements ClientModInitializer {
private static CustomRenderPipeline instance;
// :::custom-pipelines:define-pipeline
private static final RenderPipeline FILLED_THROUGH_WALLS = RenderPipelines.register(RenderPipeline.builder(RenderPipelines.DEBUG_FILLED_SNIPPET)
.withLocation(Identifier.fromNamespaceAndPath(ExampleMod.MOD_ID, "pipeline/debug_filled_box_through_walls"))
.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
.build()
);
// :::custom-pipelines:define-pipeline
// :::custom-pipelines:extraction-phase
private static final ByteBufferBuilder allocator = new ByteBufferBuilder(RenderType.SMALL_BUFFER_SIZE);
private BufferBuilder buffer;
// :::custom-pipelines:extraction-phase
// :::custom-pipelines:drawing-phase
private static final Vector4f COLOR_MODULATOR = new Vector4f(1f, 1f, 1f, 1f);
private static final Vector3f MODEL_OFFSET = new Vector3f();
private static final Matrix4f TEXTURE_MATRIX = new Matrix4f();
private MappableRingBuffer vertexBuffer;
// :::custom-pipelines:drawing-phase
public static CustomRenderPipeline getInstance() {
return instance;
}
@Override
public void onInitializeClient() {
instance = this;
WorldRenderEvents.BEFORE_TRANSLUCENT.register(this::extractAndDrawWaypoint);
}
private void extractAndDrawWaypoint(WorldRenderContext context) {
renderWaypoint(context);
drawFilledThroughWalls(Minecraft.getInstance(), FILLED_THROUGH_WALLS);
}
// :::custom-pipelines:extraction-phase
private void renderWaypoint(WorldRenderContext context) {
PoseStack matrices = context.matrices();
Vec3 camera = context.worldState().cameraRenderState.pos;
matrices.pushPose();
matrices.translate(-camera.x, -camera.y, -camera.z);
if (buffer == null) {
buffer = new BufferBuilder(allocator, FILLED_THROUGH_WALLS.getVertexFormatMode(), FILLED_THROUGH_WALLS.getVertexFormat());
}
renderFilledBox(matrices.last().pose(), buffer, 0f, 100f, 0f, 1f, 101f, 1f, 0f, 1f, 0f, 0.5f);
matrices.popPose();
}
private void renderFilledBox(Matrix4fc positionMatrix, BufferBuilder buffer, float minX, float minY, float minZ, float maxX, float maxY, float maxZ, float red, float green, float blue, float alpha) {
// Front Face
buffer.addVertex(positionMatrix, minX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, maxZ).setColor(red, green, blue, alpha);
// Back face
buffer.addVertex(positionMatrix, maxX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, minZ).setColor(red, green, blue, alpha);
// Left face
buffer.addVertex(positionMatrix, minX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, minZ).setColor(red, green, blue, alpha);
// Right face
buffer.addVertex(positionMatrix, maxX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha);
// Top face
buffer.addVertex(positionMatrix, minX, maxY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, maxY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, maxY, minZ).setColor(red, green, blue, alpha);
// Bottom face
buffer.addVertex(positionMatrix, minX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, minY, minZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, maxX, minY, maxZ).setColor(red, green, blue, alpha);
buffer.addVertex(positionMatrix, minX, minY, maxZ).setColor(red, green, blue, alpha);
}
// :::custom-pipelines:extraction-phase
// :::custom-pipelines:drawing-phase
private void drawFilledThroughWalls(Minecraft client, @SuppressWarnings("SameParameterValue") RenderPipeline pipeline) {
// Build the buffer
MeshData builtBuffer = buffer.buildOrThrow();
MeshData.DrawState drawParameters = builtBuffer.drawState();
VertexFormat format = drawParameters.format();
GpuBuffer vertices = upload(drawParameters, format, builtBuffer);
draw(client, pipeline, builtBuffer, drawParameters, vertices, format);
// Rotate the vertex buffer so we are less likely to use buffers that the GPU is using
vertexBuffer.rotate();
buffer = null;
}
private GpuBuffer upload(MeshData.DrawState drawParameters, VertexFormat format, MeshData builtBuffer) {
// Calculate the size needed for the vertex buffer
int vertexBufferSize = drawParameters.vertexCount() * format.getVertexSize();
// Initialize or resize the vertex buffer as needed
if (vertexBuffer == null || vertexBuffer.size() < vertexBufferSize) {
if (vertexBuffer != null) {
vertexBuffer.close();
}
vertexBuffer = new MappableRingBuffer(() -> ExampleMod.MOD_ID + " example render pipeline", GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_MAP_WRITE, vertexBufferSize);
}
// Copy vertex data into the vertex buffer
CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(vertexBuffer.currentBuffer().slice(0, builtBuffer.vertexBuffer().remaining()), false, true)) {
MemoryUtil.memCopy(builtBuffer.vertexBuffer(), mappedView.data());
}
return vertexBuffer.currentBuffer();
}
private static void draw(Minecraft client, RenderPipeline pipeline, MeshData builtBuffer, MeshData.DrawState drawParameters, GpuBuffer vertices, VertexFormat format) {
GpuBuffer indices;
VertexFormat.IndexType indexType;
if (pipeline.getVertexFormatMode() == VertexFormat.Mode.QUADS) {
// Sort the quads if there is translucency
builtBuffer.sortQuads(allocator, RenderSystem.getProjectionType().vertexSorting());
// Upload the index buffer
indices = pipeline.getVertexFormat().uploadImmediateIndexBuffer(builtBuffer.indexBuffer());
indexType = builtBuffer.drawState().indexType();
} else {
// Use the general shape index buffer for non-quad draw modes
RenderSystem.AutoStorageIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(pipeline.getVertexFormatMode());
indices = shapeIndexBuffer.getBuffer(drawParameters.indexCount());
indexType = shapeIndexBuffer.type();
}
// Actually execute the draw
GpuBufferSlice dynamicTransforms = RenderSystem.getDynamicUniforms()
.writeTransform(RenderSystem.getModelViewMatrix(), COLOR_MODULATOR, MODEL_OFFSET, TEXTURE_MATRIX);
try (RenderPass renderPass = RenderSystem.getDevice()
.createCommandEncoder()
.createRenderPass(() -> ExampleMod.MOD_ID + " example render pipeline rendering", client.getMainRenderTarget().getColorTextureView(), OptionalInt.empty(), client.getMainRenderTarget().getDepthTextureView(), OptionalDouble.empty())) {
renderPass.setPipeline(pipeline);
RenderSystem.bindDefaultUniforms(renderPass);
renderPass.setUniform("DynamicTransforms", dynamicTransforms);
// Bind texture if applicable:
// Sampler0 is used for texture inputs in vertices
// renderPass.bindTexture("Sampler0", textureSetup.texure0(), textureSetup.sampler0());
renderPass.setVertexBuffer(0, vertices);
renderPass.setIndexBuffer(indices, indexType);
// The base vertex is the starting index when we copied the data into the vertex buffer divided by vertex size
//noinspection ConstantValue
renderPass.drawIndexed(0 / format.getVertexSize(), 0, drawParameters.indexCount(), 1);
}
builtBuffer.close();
}
// :::custom-pipelines:drawing-phase
// :::custom-pipelines:clean-up
public void close() {
allocator.close();
if (vertexBuffer != null) {
vertexBuffer.close();
vertexBuffer = null;
}
}
// :::custom-pipelines:clean-up
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
Не забудьте також про GameRendererMixin! Ось результат:



