summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederico Linhares <fred@linhares.blue>2023-10-09 14:46:09 -0300
committerFrederico Linhares <fred@linhares.blue>2023-10-09 15:58:14 -0300
commitba447583c74d44c6b07c5409918207157f921efe (patch)
tree56cd1c36a8e77540136c8871eb3b94f0adf37139
parent8fa221cb60c19638d4ad0833965fee605593eea3 (diff)
feat Render sprite in a 3D position
-rw-r--r--glsl/shader_sprite_3d.frag.glsl42
-rw-r--r--glsl/shader_sprite_3d.vert.glsl73
-rw-r--r--src/core.cpp24
-rw-r--r--src/core.hpp2
-rw-r--r--src/sprite_3d.cpp82
-rw-r--r--src/sprite_3d.hpp25
-rw-r--r--src/vk/device.cpp10
-rw-r--r--src/vk/device.hpp4
-rw-r--r--src/vk/graphics_pipeline_2d_solid.cpp4
-rw-r--r--src/vk/graphics_pipeline_3d.cpp7
-rw-r--r--src/vk/graphics_pipeline_sprite_3d.cpp333
-rw-r--r--src/vk/graphics_pipeline_sprite_3d.hpp44
-rw-r--r--src/vk/renderer.cpp20
-rw-r--r--src/vk/renderer.hpp3
-rw-r--r--src/vk/sprite_3d.cpp181
-rw-r--r--src/vk/sprite_3d.hpp46
-rw-r--r--src/vk/uniform_data_object.hpp7
-rw-r--r--test/src/mode/demo.rb5
18 files changed, 899 insertions, 13 deletions
diff --git a/glsl/shader_sprite_3d.frag.glsl b/glsl/shader_sprite_3d.frag.glsl
new file mode 100644
index 0000000..fe823f8
--- /dev/null
+++ b/glsl/shader_sprite_3d.frag.glsl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+struct DataTransferObject
+{
+ vec4 frag_color;
+ vec2 frag_texture_coord;
+};
+
+layout(location = 0) in DataTransferObject in_dto;
+
+layout(location = 0) out vec4 out_color;
+
+layout(set = 0, binding = 1) uniform UBOWorld
+{
+ vec3 directional_light_direction;
+ vec4 directional_light_color;
+} ubo_world;
+
+layout(set = 2, binding = 1) uniform sampler2D texture_sampler;
+
+void
+main()
+{
+ out_color = texture(texture_sampler, in_dto.frag_texture_coord);
+}
diff --git a/glsl/shader_sprite_3d.vert.glsl b/glsl/shader_sprite_3d.vert.glsl
new file mode 100644
index 0000000..f7c6cf6
--- /dev/null
+++ b/glsl/shader_sprite_3d.vert.glsl
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec2 in_texture_coord;
+
+layout(location = 0) out DataTransferObject
+{
+ vec4 frag_color;
+ vec2 frag_texture_coord;
+} out_dto;
+
+layout(set = 0, binding = 0) uniform UBOWorld
+{
+ vec4 ambient_light_color;
+} ubo_world;
+
+layout(set = 1, binding = 0) uniform UBOView
+{
+ mat4 view;
+ mat4 proj;
+} ubo_view;
+
+layout(set = 2, binding = 0) uniform UBOSprite3D
+{
+ vec3 position;
+ vec2 size;
+} ubo_position;
+
+void
+main()
+{
+ vec4 position = vec4(ubo_position.position, 1.0);
+ position = ubo_view.view * position;
+ switch(gl_VertexIndex)
+ {
+ case 0:
+ position.x -= ubo_position.size.x/2;
+ position.y -= ubo_position.size.y/2;
+ break;
+ case 1:
+ position.x -= ubo_position.size.x/2;
+ position.y += ubo_position.size.y/2;
+ break;
+ case 2:
+ position.x += ubo_position.size.x/2;
+ position.y -= ubo_position.size.y/2;
+ break;
+ case 3:
+ position.x += ubo_position.size.x/2;
+ position.y += ubo_position.size.y/2;
+ break;
+ }
+ gl_Position = ubo_view.proj * position;
+
+ out_dto.frag_color = ubo_world.ambient_light_color;
+ out_dto.frag_texture_coord = in_texture_coord;
+}
diff --git a/src/core.cpp b/src/core.cpp
index 591eaba..e40370e 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -27,6 +27,7 @@
#include "static_mesh.hpp"
#include "sound.hpp"
#include "sprite.hpp"
+#include "sprite_3d.hpp"
#include "texture.hpp"
#include "vector_3d.hpp"
#include "vector_4d.hpp"
@@ -701,6 +702,26 @@ unload_vk_graphics_pipeline_3d_skeletal(void *obj)
}
void
+load_vk_graphics_pipeline_sprite_3d(void *obj)
+{
+ try
+ {
+ cg_core.vk_graphics_pipeline_sprite_3d =
+ std::make_unique<VK::GraphicsPipelineSprite3D>();
+ }
+ catch(const CommandError &e)
+ {
+ throw CommandError{"Failed to create sprite 3d graphics pipeline."};
+ }
+}
+
+void
+unload_vk_graphics_pipeline_sprite_3d(void *obj)
+{
+ cg_core.vk_graphics_pipeline_sprite_3d = nullptr;
+}
+
+void
load_vk_graphics_pipeline_2d_solid(void *obj)
{
try
@@ -778,6 +799,7 @@ load_mruby_interface(void *obj)
cg_static_mesh_init(cg_core.mrb);
cg_sound_init(cg_core.mrb);
cg_sprite_init(cg_core.mrb);
+ cg_sprite_3d_init(cg_core.mrb);
cg_texture_init(cg_core.mrb);
cg_vector_3d_init(cg_core.mrb);
cg_vector_4d_init(cg_core.mrb);
@@ -818,6 +840,8 @@ const CommandChain cg_sCore::loader{
// {&load_vk_graphics_pipeline_3d_skeletal,
// &unload_vk_graphics_pipeline_3d_skeletal},
{&load_vk_graphics_pipeline_3d, &unload_vk_graphics_pipeline_3d},
+ {&load_vk_graphics_pipeline_sprite_3d,
+ &unload_vk_graphics_pipeline_sprite_3d},
{&load_vk_graphics_pipeline_2d_solid, &unload_vk_graphics_pipeline_2d_solid},
{&load_vk_graphics_pipeline_2d_wired, &unload_vk_graphics_pipeline_2d_wired},
diff --git a/src/core.hpp b/src/core.hpp
index 9566a52..7393b33 100644
--- a/src/core.hpp
+++ b/src/core.hpp
@@ -59,6 +59,7 @@
#include "vk/graphics_pipeline_3d_layout.hpp"
#include "vk/graphics_pipeline_3d.hpp"
#include "vk/graphics_pipeline_3d_skeletal.hpp"
+#include "vk/graphics_pipeline_sprite_3d.hpp"
#include "vk/renderer.hpp"
#include "vk/swapchain.hpp"
@@ -131,6 +132,7 @@ struct cg_sCore
std::unique_ptr<VK::GraphicsPipeline3D> vk_graphics_pipeline_3d;
std::unique_ptr<VK::GraphicsPipeline3DSkeletal>
vk_graphics_pipeline_3d_skeletal;
+ std::unique_ptr<VK::GraphicsPipelineSprite3D> vk_graphics_pipeline_sprite_3d;
std::unique_ptr<VK::GraphicsPipeline2DSolid> vk_graphics_pipeline_2d_solid;
std::unique_ptr<VK::GraphicsPipeline2DWired> vk_graphics_pipeline_2d_wired;
diff --git a/src/sprite_3d.cpp b/src/sprite_3d.cpp
new file mode 100644
index 0000000..3e4e29d
--- /dev/null
+++ b/src/sprite_3d.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sprite_3d.hpp"
+
+#include "sprite.hpp"
+#include "vector_3d.hpp"
+
+void
+cg_free_sprite_3d(mrb_state *mrb, void *obj)
+{
+ auto ptr = static_cast<std::shared_ptr<VK::Sprite3D>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_sprite_3d_type = {
+ "CG_Sprite3D", cg_free_sprite_3d };
+
+static mrb_value
+cg_cSprite3D_initialize(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<VK::Sprite> *sprite;
+ std::shared_ptr<glm::vec3> *position;
+ mrb_float w, h;
+ std::shared_ptr<VK::Sprite3D> *ptr;
+
+ mrb_get_args(mrb, "ddff", &sprite, &cg_sprite_type,
+ &position, &cg_vector_3d_type, &w, &h);
+ ptr = (std::shared_ptr<VK::Sprite3D>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<VK::Sprite3D>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<VK::Sprite3D>));
+
+ glm::vec3 size{w, h, 0.0};
+ new(ptr)std::shared_ptr<VK::Sprite3D>(
+ std::make_shared<VK::Sprite3D>(*sprite, *position, size));
+
+ mrb_data_init(self, ptr, &cg_sprite_3d_type);
+ return self;
+}
+
+static mrb_value
+cg_cSprite3D_draw(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<VK::Sprite3D>*)DATA_PTR(self);
+
+ auto &sprites_3d_to_draw = cg_core.vk_renderer->sprites_3d_to_draw[
+ cg_core.vk_swapchain->current_frame];
+ sprites_3d_to_draw.emplace_back(*ptr);
+
+ return self;
+}
+
+void
+cg_sprite_3d_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSprite3D;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSprite3D = mrb_define_class_under(
+ mrb, cg_m, "Sprite3D", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSprite3D, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSprite3D, "initialize", cg_cSprite3D_initialize, MRB_ARGS_REQ(3));
+ mrb_define_method(
+ mrb, cg_cSprite3D, "draw", cg_cSprite3D_draw, MRB_ARGS_NONE());
+}
diff --git a/src/sprite_3d.hpp b/src/sprite_3d.hpp
new file mode 100644
index 0000000..526c23a
--- /dev/null
+++ b/src/sprite_3d.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SPRITE_3D_H
+#define CANDY_GEAR_SPRITE_3D_H 1
+
+#include "core.hpp"
+
+void
+cg_sprite_3d_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_SPRITE_3D_H */
diff --git a/src/vk/device.cpp b/src/vk/device.cpp
index ea943c4..83bdf38 100644
--- a/src/vk/device.cpp
+++ b/src/vk/device.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Frederico de Oliveira Linhares
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -158,6 +158,10 @@ Device::Device(VkPhysicalDevice vk_physical_device, bool with_swapchain)
// Load Shaders
{
+ this->vert_sprite_3d_shader_module = create_shader_module(
+ this->device, DATA_DIR "/glsl/shader_sprite_3d.vert.spv");
+ this->frag_sprite_3d_shader_module = create_shader_module(
+ this->device, DATA_DIR "/glsl/shader_sprite_3d.frag.spv");
this->vert3d_shader_module = create_shader_module(
this->device, DATA_DIR "/glsl/shader_3d.vert.spv");
this->frag3d_shader_module = create_shader_module(
@@ -203,6 +207,10 @@ Device::~Device()
std::free(this->queue_families);
// Destroy shaders
+ vkDestroyShaderModule(
+ this->device, this->vert_sprite_3d_shader_module, nullptr);
+ vkDestroyShaderModule(
+ this->device, this->frag_sprite_3d_shader_module, nullptr);
vkDestroyShaderModule(this->device, this->vert3d_shader_module, nullptr);
vkDestroyShaderModule(this->device, this->frag3d_shader_module, nullptr);
vkDestroyShaderModule(
diff --git a/src/vk/device.hpp b/src/vk/device.hpp
index 9fcec20..36fc08d 100644
--- a/src/vk/device.hpp
+++ b/src/vk/device.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Frederico de Oliveira Linhares
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,8 @@ public:
VkDevice device;
VkPhysicalDevice physical_device;
+ VkShaderModule vert_sprite_3d_shader_module;
+ VkShaderModule frag_sprite_3d_shader_module;
VkShaderModule vert3d_shader_module;
VkShaderModule frag3d_shader_module;
VkShaderModule vert2d_solid_shader_module;
diff --git a/src/vk/graphics_pipeline_2d_solid.cpp b/src/vk/graphics_pipeline_2d_solid.cpp
index d0c1768..00bc26b 100644
--- a/src/vk/graphics_pipeline_2d_solid.cpp
+++ b/src/vk/graphics_pipeline_2d_solid.cpp
@@ -238,8 +238,8 @@ GraphicsPipeline2DSolid::draw(
const size_t current_frame, const size_t next_frame,
const uint32_t image_index)
{
- // Set viewport
- {
+ // TODO set viewport just once per view, not once per pipeline.
+ { // Set viewport
VkViewport vk_viewport{};
vk_viewport.x = view->region.x;
vk_viewport.y = view->region.y;
diff --git a/src/vk/graphics_pipeline_3d.cpp b/src/vk/graphics_pipeline_3d.cpp
index 43ab734..d98a27d 100644
--- a/src/vk/graphics_pipeline_3d.cpp
+++ b/src/vk/graphics_pipeline_3d.cpp
@@ -277,6 +277,10 @@ GraphicsPipeline3D::draw(
vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor);
}
+ vkCmdBindPipeline(
+ draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ this->graphic_pipeline);
+
// Draw models
for(auto& [static_mesh, instances]:
cg_core.vk_renderer->static_models_to_draw[current_frame])
@@ -284,9 +288,6 @@ GraphicsPipeline3D::draw(
VkBuffer vertex_buffers[]{static_mesh->vertex_buffer->buffer};
VkDeviceSize offsets[]{0};
- vkCmdBindPipeline(
- draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
- this->graphic_pipeline);
vkCmdBindVertexBuffers(
draw_command_buffer, 0, 1, vertex_buffers, offsets);
vkCmdBindIndexBuffer(
diff --git a/src/vk/graphics_pipeline_sprite_3d.cpp b/src/vk/graphics_pipeline_sprite_3d.cpp
new file mode 100644
index 0000000..d14cb48
--- /dev/null
+++ b/src/vk/graphics_pipeline_sprite_3d.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "graphics_pipeline_sprite_3d.hpp"
+
+#include <array>
+
+#include "../core.hpp"
+#include "core.hpp"
+#include "sprite.hpp"
+#include "uniform_data_object.hpp"
+
+namespace
+{
+
+struct Sprite3DOrder
+{
+ std::shared_ptr<VK::Sprite3D> sprite_3d;
+ float distance;
+};
+
+bool
+operator<(const Sprite3DOrder &a, const Sprite3DOrder &b)
+{
+ return a.distance < b.distance;
+}
+
+bool
+operator>(const Sprite3DOrder &a, const Sprite3DOrder &b)
+{
+ return a.distance > b.distance;
+}
+
+void
+load_pipeline(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipelineSprite3D*>(obj);
+
+ VkPipelineShaderStageCreateInfo vert_shader_stage_info{};
+ vert_shader_stage_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ vert_shader_stage_info.pNext = nullptr;
+ vert_shader_stage_info.flags = 0;
+ vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
+ vert_shader_stage_info.module =
+ cg_core.vk_device_with_swapchain->vert_sprite_3d_shader_module;
+ vert_shader_stage_info.pName = "main";
+ vert_shader_stage_info.pSpecializationInfo = nullptr;
+
+ VkPipelineShaderStageCreateInfo frag_shader_stage_info{};
+ frag_shader_stage_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ frag_shader_stage_info.pNext = nullptr;
+ frag_shader_stage_info.flags = 0;
+ frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ frag_shader_stage_info.module =
+ cg_core.vk_device_with_swapchain->frag_sprite_3d_shader_module;
+ frag_shader_stage_info.pName = "main";
+ frag_shader_stage_info.pSpecializationInfo = nullptr;
+
+ VkPipelineShaderStageCreateInfo shader_stages[] = {
+ vert_shader_stage_info,
+ frag_shader_stage_info
+ };
+
+ VkVertexInputBindingDescription vertex_input_binding{};
+ vertex_input_binding.binding = 0;
+ vertex_input_binding.stride = sizeof(glm::vec2);
+ vertex_input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ std::array<VkVertexInputAttributeDescription, 1> vertex_attribute{};
+ // Texture coordinate.
+ vertex_attribute[0].location = 0;
+ vertex_attribute[0].binding = 0;
+ vertex_attribute[0].format = VK_FORMAT_R32G32_SFLOAT;
+ vertex_attribute[0].offset = 0;
+
+ VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
+ vertex_input_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertex_input_info.pNext = nullptr;
+ vertex_input_info.flags = 0;
+ vertex_input_info.vertexBindingDescriptionCount = 1;
+ vertex_input_info.pVertexBindingDescriptions = &vertex_input_binding;
+ vertex_input_info.vertexAttributeDescriptionCount =
+ static_cast<uint32_t>(vertex_attribute.size());
+ vertex_input_info.pVertexAttributeDescriptions = vertex_attribute.data();
+
+ VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
+ input_assembly.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ input_assembly.pNext = nullptr;
+ input_assembly.flags = 0;
+ input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ input_assembly.primitiveRestartEnable = VK_FALSE;
+
+ VkViewport viewport = {};
+ viewport.x = 0;
+ viewport.y = 0;
+ viewport.width = cg_core.display_width;
+ viewport.height = cg_core.display_height;
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+
+ VkRect2D scissor = {};
+ scissor.offset = {0, 0};
+ scissor.extent = {cg_core.display_width, cg_core.display_height};
+
+ VkPipelineViewportStateCreateInfo viewport_state = {};
+ viewport_state.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewport_state.pNext = nullptr;
+ viewport_state.flags = 0;
+ viewport_state.viewportCount = 1;
+ viewport_state.pViewports = &viewport;
+ viewport_state.scissorCount = 1;
+ viewport_state.pScissors = &scissor;
+
+ VkPipelineRasterizationStateCreateInfo rasterizer = {};
+ rasterizer.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterizer.pNext = nullptr;
+ rasterizer.flags = 0;
+ rasterizer.depthClampEnable = VK_FALSE;
+ rasterizer.rasterizerDiscardEnable = VK_FALSE;
+ rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizer.cullMode = VK_CULL_MODE_NONE;
+ rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterizer.depthBiasEnable = VK_FALSE;
+ rasterizer.depthBiasConstantFactor = 0.0f;
+ rasterizer.depthBiasClamp = 0.0f;
+ rasterizer.depthBiasSlopeFactor = 0.0f;
+ rasterizer.lineWidth = 1.0f;
+
+ VkPipelineMultisampleStateCreateInfo multisampling = {};
+ multisampling.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ multisampling.sampleShadingEnable = VK_FALSE;
+ multisampling.minSampleShading = 1.0f;
+ multisampling.pSampleMask = nullptr;
+ multisampling.alphaToCoverageEnable = VK_FALSE;
+ multisampling.alphaToOneEnable = VK_FALSE;
+
+ VkPipelineDepthStencilStateCreateInfo depth_stencil = {};
+ depth_stencil.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ depth_stencil.depthTestEnable = VK_TRUE;
+ depth_stencil.depthWriteEnable = VK_TRUE;
+ depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS;
+ depth_stencil.depthBoundsTestEnable = VK_FALSE;
+ depth_stencil.minDepthBounds = 0.0f;
+ depth_stencil.maxDepthBounds = 1.0f;
+ depth_stencil.stencilTestEnable = VK_FALSE;
+ depth_stencil.front = {};
+ depth_stencil.back = {};
+
+ VkPipelineColorBlendAttachmentState color_blend_attachment = {};
+ color_blend_attachment.blendEnable = VK_TRUE;
+ color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ color_blend_attachment.dstColorBlendFactor =
+ VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
+ color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+ color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
+ color_blend_attachment.colorWriteMask =
+ VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+
+ VkPipelineColorBlendStateCreateInfo color_blending = {};
+ color_blending.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ color_blending.pNext = nullptr;
+ color_blending.flags = 0;
+ color_blending.logicOpEnable = VK_FALSE;
+ color_blending.logicOp = VK_LOGIC_OP_COPY;
+ color_blending.attachmentCount = 1;
+ color_blending.pAttachments = &color_blend_attachment;
+ color_blending.blendConstants[0] = 0.0f;
+ color_blending.blendConstants[1] = 0.0f;
+ color_blending.blendConstants[2] = 0.0f;
+ color_blending.blendConstants[3] = 0.0f;
+
+ std::array<VkDynamicState, 3> dynamic_states{
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_LINE_WIDTH,
+ VK_DYNAMIC_STATE_BLEND_CONSTANTS
+ };
+
+ VkPipelineDynamicStateCreateInfo dynamic_state_info = {};
+ dynamic_state_info.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamic_state_info.dynamicStateCount = dynamic_states.size();
+ dynamic_state_info.pDynamicStates = dynamic_states.data();
+
+ VkGraphicsPipelineCreateInfo pipeline_info{};
+ pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipeline_info.pNext = nullptr;
+ pipeline_info.flags = 0;
+ pipeline_info.stageCount = 2;
+ pipeline_info.pStages = shader_stages;
+ pipeline_info.pVertexInputState = &vertex_input_info;
+ pipeline_info.pInputAssemblyState = &input_assembly;
+ pipeline_info.pTessellationState = nullptr;
+ pipeline_info.pViewportState = &viewport_state;
+ pipeline_info.pRasterizationState = &rasterizer;
+ pipeline_info.pMultisampleState = &multisampling;
+ pipeline_info.pDepthStencilState = &depth_stencil;
+ pipeline_info.pColorBlendState = &color_blending;
+ pipeline_info.pDynamicState = &dynamic_state_info;
+ pipeline_info.layout = cg_core.vk_graphics_pipeline_3d_layout->pipeline;
+ pipeline_info.renderPass = cg_core.vk_render_pass->pipeline_3d;
+ pipeline_info.subpass = 0;
+ pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
+ pipeline_info.basePipelineIndex = -1;
+
+ if(vkCreateGraphicsPipelines(
+ cg_core.vk_device_with_swapchain->device, VK_NULL_HANDLE, 1,
+ &pipeline_info, nullptr, &self->graphic_pipeline)
+ != VK_SUCCESS)
+ throw CommandError{"Failed to create graphics pipeline sprite 3d."};
+}
+
+void
+unload_pipeline(void *obj)
+{
+ auto self = static_cast<VK::GraphicsPipelineSprite3D*>(obj);
+
+ vkDestroyPipeline(
+ cg_core.vk_device_with_swapchain->device, self->graphic_pipeline, nullptr);
+}
+
+const CommandChain loader{
+ {&load_pipeline, &unload_pipeline}
+};
+
+}
+
+namespace VK
+{
+
+GraphicsPipelineSprite3D::GraphicsPipelineSprite3D()
+{
+ loader.execute(this);
+}
+
+GraphicsPipelineSprite3D::~GraphicsPipelineSprite3D()
+{
+ loader.revert(this);
+}
+
+void
+GraphicsPipelineSprite3D::draw(
+ std::shared_ptr<View3D> view, const VkCommandBuffer draw_command_buffer,
+ const size_t current_frame, const uint32_t image_index)
+{
+ // TODO set viewport just once per view, not once per pipeline.
+ { // Set viewport
+ VkViewport vk_viewport{};
+ vk_viewport.x = view->region.x;
+ vk_viewport.y = view->region.y;
+ vk_viewport.width = view->region.z;
+ vk_viewport.height = view->region.w;
+ vk_viewport.minDepth = 0.0f;
+ vk_viewport.maxDepth = 1.0f;
+ vkCmdSetViewport(draw_command_buffer, 0, 1, &vk_viewport);
+
+ VkRect2D vk_scissor{};
+ vk_scissor.offset.x = static_cast<int32_t>(view->region.x);
+ vk_scissor.offset.y = static_cast<int32_t>(view->region.y);
+ vk_scissor.extent.width = static_cast<uint32_t>(view->region.z);
+ vk_scissor.extent.height = static_cast<uint32_t>(view->region.w);
+ vkCmdSetScissor(draw_command_buffer, 0, 1, &vk_scissor);
+ }
+
+ vkCmdBindPipeline(
+ draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ this->graphic_pipeline);
+
+ std::vector<Sprite3DOrder> sprite_3d_order;
+ { // Sort sprites 3D
+ sprite_3d_order.reserve(
+ cg_core.vk_renderer->sprites_3d_to_draw[current_frame].size());
+
+ for(std::shared_ptr<VK::Sprite3D> sprite:
+ cg_core.vk_renderer->sprites_3d_to_draw[current_frame])
+ sprite_3d_order.emplace_back(
+ sprite, glm::distance(*view->camera_position, *sprite->position));
+
+ std::sort(sprite_3d_order.rbegin(), sprite_3d_order.rend());
+ }
+
+ // Draw sprites
+ for(auto& sprite: sprite_3d_order)
+ {
+ std::array<VkDescriptorSet, 3> vk_descriptor_sets{
+ cg_core.vk_light->descriptor_sets_world[image_index],
+ view->descriptor_sets_3d[image_index],
+ sprite.sprite_3d->descriptor_sets[image_index]};
+ VkDeviceSize offsets[]{0};
+
+ vkCmdBindVertexBuffers(
+ draw_command_buffer, 0, 1,
+ &sprite.sprite_3d->sprite->vertex_buffer->buffer, offsets);
+ vkCmdBindDescriptorSets(
+ draw_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+ cg_core.vk_graphics_pipeline_3d_layout->pipeline, 0,
+ vk_descriptor_sets.size(), vk_descriptor_sets.data(), 0, nullptr);
+
+ UDOSprite3D ubo_sprite_3d{};
+ ubo_sprite_3d.position = *sprite.sprite_3d->position;
+ ubo_sprite_3d.size = sprite.sprite_3d->size;
+ sprite.sprite_3d->uniform_buffers[image_index].copy_data(&ubo_sprite_3d);
+
+ vkCmdDraw(draw_command_buffer, Sprite::vertex_count, 1, 0, 0);
+ }
+}
+
+}
diff --git a/src/vk/graphics_pipeline_sprite_3d.hpp b/src/vk/graphics_pipeline_sprite_3d.hpp
new file mode 100644
index 0000000..eb7aa34
--- /dev/null
+++ b/src/vk/graphics_pipeline_sprite_3d.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VK_GRAPHICS_PIPELINE_SPRITE_3D_H
+#define CANDY_GEAR_VK_GRAPHICS_PIPELINE_SPRITE_3D_H 1
+
+#include <memory>
+
+#include "core.hpp"
+#include "command_pool.hpp"
+#include "sprite_3d.hpp"
+#include "view_3d.hpp"
+
+namespace VK
+{
+
+struct GraphicsPipelineSprite3D
+{
+ VkPipeline graphic_pipeline;
+
+ GraphicsPipelineSprite3D();
+ ~GraphicsPipelineSprite3D();
+
+ void
+ draw(std::shared_ptr<View3D> view, const VkCommandBuffer draw_command_buffer,
+ const size_t current_frame, const uint32_t image_index);
+};
+
+}
+
+#endif /* CANDY_GEAR_VK_GRAPHICS_PIPELINE_SPRITE_3D_H */
diff --git a/src/vk/renderer.cpp b/src/vk/renderer.cpp
index 766690f..d4e25ba 100644
--- a/src/vk/renderer.cpp
+++ b/src/vk/renderer.cpp
@@ -146,6 +146,7 @@ Renderer::Renderer(std::vector<std::shared_ptr<View2D>> views_2d,
std::vector<std::shared_ptr<View3D>> views_3d):
skeletal_models_to_draw{cg_core.vk_swapchain->images_count},
static_models_to_draw{cg_core.vk_swapchain->images_count},
+ sprites_3d_to_draw{cg_core.vk_swapchain->images_count},
views_2d{views_2d},
views_3d{views_3d}
{
@@ -239,14 +240,19 @@ Renderer::draw()
vkCmdBeginRenderPass(
draw_command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
+ }
- for(auto &view: this->views_3d)
- cg_core.vk_graphics_pipeline_3d->draw(
- view, draw_command_buffer, cg_core.vk_swapchain->current_frame,
- image_index);
+ for(auto &view: this->views_3d)
+ cg_core.vk_graphics_pipeline_3d->draw(
+ view, draw_command_buffer, cg_core.vk_swapchain->current_frame,
+ image_index);
- vkCmdEndRenderPass(draw_command_buffer);
- }
+ for(auto &view: this->views_3d)
+ cg_core.vk_graphics_pipeline_sprite_3d->draw(
+ view, draw_command_buffer, cg_core.vk_swapchain->current_frame,
+ image_index);
+
+ vkCmdEndRenderPass(draw_command_buffer);
{ // 2D render pass
VkRenderPassBeginInfo render_pass_begin{};
@@ -345,6 +351,7 @@ Renderer::draw()
{
this->skeletal_models_to_draw[next_frame].clear();
this->static_models_to_draw[next_frame].clear();
+ this->sprites_3d_to_draw[next_frame].clear();
cg_core.vk_swapchain->current_frame = next_frame;
}
}
@@ -353,6 +360,7 @@ Renderer::draw()
// Clear images for the current frame because we are skipping this frame.
this->skeletal_models_to_draw[cg_core.vk_swapchain->current_frame].clear();
this->static_models_to_draw[cg_core.vk_swapchain->current_frame].clear();
+ this->sprites_3d_to_draw[cg_core.vk_swapchain->current_frame].clear();
for(auto &view: this->views_2d)
view->sprites_to_draw[cg_core.vk_swapchain->current_frame].clear();
for(auto &view: this->views_3d)
diff --git a/src/vk/renderer.hpp b/src/vk/renderer.hpp
index 7c7d111..96d25ba 100644
--- a/src/vk/renderer.hpp
+++ b/src/vk/renderer.hpp
@@ -24,6 +24,7 @@
#include "core.hpp"
#include "skeletal_mesh.hpp"
#include "skeletal_model.hpp"
+#include "sprite_3d.hpp"
#include "static_mesh.hpp"
#include "static_model.hpp"
#include "queue_family.hpp"
@@ -47,6 +48,8 @@ struct Renderer
std::vector<std::shared_ptr<StaticModel>>>>
static_models_to_draw;
+ std::vector<std::vector<std::shared_ptr<Sprite3D>>> sprites_3d_to_draw;
+
VkDescriptorPool descriptor_pool;
std::vector<std::shared_ptr<View2D>> views_2d;
std::vector<std::shared_ptr<View3D>> views_3d;
diff --git a/src/vk/sprite_3d.cpp b/src/vk/sprite_3d.cpp
new file mode 100644
index 0000000..9794285
--- /dev/null
+++ b/src/vk/sprite_3d.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sprite_3d.hpp"
+
+#include "../core.hpp"
+#include "uniform_data_object.hpp"
+
+namespace
+{
+
+void
+load_uniform_buffers(void *obj)
+{
+ auto self = static_cast<VK::Sprite3D*>(obj);
+
+ try
+ {
+ self->uniform_buffers.reserve(cg_core.vk_swapchain->images_count);
+ for(auto i{0}; i < cg_core.vk_swapchain->images_count; i++)
+ self->uniform_buffers.emplace_back(
+ cg_core.vk_device_with_swapchain, sizeof(VK::UDOSprite3D));
+ }
+ catch(const std::exception& e)
+ {
+ throw CommandError{e.what()};
+ }
+}
+
+void
+unload_uniform_buffers(void *obj)
+{
+ auto self = static_cast<VK::Sprite3D*>(obj);
+
+ self->uniform_buffers.clear();
+}
+
+void
+load_descriptor_set_pool(void *obj)
+{
+ auto self = static_cast<VK::Sprite3D*>(obj);
+
+ std::array<VkDescriptorPoolSize, 2> descriptor_pool_sizes{};
+ descriptor_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descriptor_pool_sizes[0].descriptorCount =
+ self->uniform_buffers.size();
+ descriptor_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ descriptor_pool_sizes[1].descriptorCount =
+ cg_core.vk_swapchain->images_count;
+
+ VkDescriptorPoolCreateInfo pool_info{};
+ pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ pool_info.pNext = nullptr;
+ pool_info.flags = 0;
+ pool_info.maxSets = self->uniform_buffers.size();
+ pool_info.poolSizeCount = descriptor_pool_sizes.size();
+ pool_info.pPoolSizes = descriptor_pool_sizes.data();
+
+ if(vkCreateDescriptorPool(
+ self->queue_family->device->device, &pool_info, nullptr,
+ &self->descriptor_pool) != VK_SUCCESS)
+ throw CommandError{"Failed to create a Vulkan descriptor pool."};
+}
+
+void
+unload_descriptor_set_pool(void *obj)
+{
+ auto self = static_cast<VK::Sprite3D*>(obj);
+
+ vkDestroyDescriptorPool(
+ self->queue_family->device->device, self->descriptor_pool, nullptr);
+}
+void
+load_descriptor_sets(void *obj)
+{
+ auto self = static_cast<VK::Sprite3D*>(obj);
+
+ std::vector<VkDescriptorSetLayout> layouts(
+ cg_core.vk_swapchain->images_count,
+ cg_core.vk_descriptor_set_layout->model);
+
+ VkDescriptorSetAllocateInfo alloc_info{};
+ alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ alloc_info.descriptorPool = self->descriptor_pool;
+ alloc_info.descriptorSetCount = layouts.size();
+ alloc_info.pSetLayouts = layouts.data();
+
+ self->descriptor_sets.resize(layouts.size());
+ if(vkAllocateDescriptorSets(
+ self->queue_family->device->device, &alloc_info,
+ self->descriptor_sets.data()) != VK_SUCCESS)
+ CommandError{"Failed to create Vulkan descriptor set."};
+}
+
+void
+load_buffers_to_descriptor_sets(void *obj)
+{
+ auto self = static_cast<VK::Sprite3D*>(obj);
+
+ for(auto i{0}; i < self->uniform_buffers.size(); i++)
+ {
+ VkDescriptorBufferInfo buffer_info{};
+ buffer_info.buffer = self->uniform_buffers[i].buffer;
+ buffer_info.offset = 0;
+ buffer_info.range = sizeof(VK::UDOSprite3D);
+
+ VkDescriptorImageInfo image_info{};
+ image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ image_info.imageView = self->sprite->texture->view;
+ image_info.sampler = self->sprite->texture->sampler;
+
+ std::array<VkWriteDescriptorSet, 2> write_descriptors{};
+ write_descriptors[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write_descriptors[0].dstSet = self->descriptor_sets[i];
+ write_descriptors[0].dstBinding = 0;
+ write_descriptors[0].dstArrayElement = 0;
+ write_descriptors[0].descriptorCount = 1;
+ write_descriptors[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ write_descriptors[0].pBufferInfo = &buffer_info;
+ write_descriptors[0].pImageInfo = nullptr;
+ write_descriptors[0].pTexelBufferView = nullptr;
+ write_descriptors[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write_descriptors[1].dstSet = self->descriptor_sets[i];
+ write_descriptors[1].dstBinding = 1;
+ write_descriptors[1].dstArrayElement = 0;
+ write_descriptors[1].descriptorCount = 1;
+ write_descriptors[1].descriptorType =
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ write_descriptors[1].pBufferInfo = nullptr;
+ write_descriptors[1].pImageInfo = &image_info;
+ write_descriptors[1].pTexelBufferView = nullptr;
+
+ vkUpdateDescriptorSets(
+ cg_core.vk_device_with_swapchain->device, write_descriptors.size(),
+ write_descriptors.data(), 0, nullptr);
+ }
+}
+
+static const CommandChain loader{
+ {&load_uniform_buffers, &unload_uniform_buffers},
+ {&load_descriptor_set_pool, &unload_descriptor_set_pool},
+ {&load_descriptor_sets, nullptr},
+ {&load_buffers_to_descriptor_sets, nullptr}
+};
+
+}
+
+namespace VK
+{
+
+Sprite3D::Sprite3D(
+ std::shared_ptr<VK::Sprite> sprite, std::shared_ptr<glm::vec3> position,
+ glm::vec2 size):
+ sprite{sprite},
+ position{position},
+ size{size}
+{
+ this->queue_family =
+ cg_core.vk_device_with_swapchain->get_queue_family_with_graphics();
+ loader.execute(this);
+}
+
+Sprite3D::~Sprite3D()
+{
+ loader.revert(this);
+}
+
+}
diff --git a/src/vk/sprite_3d.hpp b/src/vk/sprite_3d.hpp
new file mode 100644
index 0000000..2059606
--- /dev/null
+++ b/src/vk/sprite_3d.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VK_SPRITE_3D_H
+#define CANDY_GEAR_VK_SPRITE_3D_H 1
+
+#include "core.hpp"
+#include "sprite.hpp"
+
+namespace VK
+{
+
+struct Sprite3D
+{
+ std::shared_ptr<Sprite> sprite;
+ std::shared_ptr<glm::vec3> position;
+ glm::vec2 size;
+
+ QueueFamily *queue_family;
+
+ std::vector<UniformBuffer> uniform_buffers;
+ VkDescriptorPool descriptor_pool;
+ std::vector<VkDescriptorSet> descriptor_sets;
+
+ Sprite3D(
+ std::shared_ptr<Sprite> sprite, std::shared_ptr<glm::vec3> position,
+ glm::vec2 size);
+ ~Sprite3D();
+};
+
+}
+
+#endif /* CANDY_GEAR_VK_SPRITE_3D_H */
diff --git a/src/vk/uniform_data_object.hpp b/src/vk/uniform_data_object.hpp
index de69655..5cc22cb 100644
--- a/src/vk/uniform_data_object.hpp
+++ b/src/vk/uniform_data_object.hpp
@@ -68,6 +68,13 @@ struct UDOVector3D
glm::vec3 vectors;
};
+struct UDOSprite3D
+{
+ glm::vec3 position;
+ uint32_t padding;
+ glm::vec2 size;
+};
+
}
#endif /* CANDY_GEAR_VK_UNIFORM_DATA_OBJECT_H */
diff --git a/test/src/mode/demo.rb b/test/src/mode/demo.rb
index a9781b6..67fa805 100644
--- a/test/src/mode/demo.rb
+++ b/test/src/mode/demo.rb
@@ -66,6 +66,10 @@ module Mode
mesh, texture, instance_positions[5], @instances_rotation)
]
+ sprite_3d_position = CandyGear::Vector3D.new(0.0, 0.0, 0.0);
+ @sprite_3d = CandyGear::Sprite3D.new(
+ @sprite, sprite_3d_position, 1.0, 1.0);
+
@camera_position = CandyGear::Vector3D.new(0.0, 0.0, 0.0);
@camera_rotation = CandyGear::Rotation3D.new(0.0, 0.0, 0.0);
@@ -125,6 +129,7 @@ module Mode
@instances_rotation.rotate(0.0, BOX_ROTATION_SPEED);
@rectangle.draw_rectangle(@view1, @color);
@instances.each {_1.draw()};
+ @sprite_3d.draw();
end
end
end