Skip to content

Commit 20cc57e

Browse files
author
devsh
committed
Push Mesh Loader example
1 parent 2872604 commit 20cc57e

File tree

7 files changed

+381
-1
lines changed

7 files changed

+381
-1
lines changed

12_MeshLoaders/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
set(NBL_INCLUDE_SERACH_DIRECTORIES
2+
"${CMAKE_CURRENT_SOURCE_DIR}/include"
3+
)
4+
5+
# TODO; Arek I removed `NBL_EXECUTABLE_PROJECT_CREATION_PCH_TARGET` from the last parameter here, doesn't this macro have 4 arguments anyway !?
6+
nbl_create_executable_project("" "" "${NBL_INCLUDE_SERACH_DIRECTORIES}" "" "")
7+
# TODO: Arek temporarily disabled cause I haven't figured out how to make this target yet
8+
# LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} nblExamplesGeometrySpirvBRD)

12_MeshLoaders/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
https://github.com/user-attachments/assets/6f779700-e6d4-4e11-95fb-7a7fddc47255
2+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"enableParallelBuild": true,
3+
"threadsPerBuildProcess" : 2,
4+
"isExecuted": false,
5+
"scriptPath": "",
6+
"cmake": {
7+
"configurations": [ "Release", "Debug", "RelWithDebInfo" ],
8+
"buildModes": [],
9+
"requiredOptions": []
10+
},
11+
"profiles": [
12+
{
13+
"backend": "vulkan",
14+
"platform": "windows",
15+
"buildModes": [],
16+
"runConfiguration": "Release",
17+
"gpuArchitectures": []
18+
}
19+
],
20+
"dependencies": [],
21+
"data": [
22+
{
23+
"dependencies": [],
24+
"command": [""],
25+
"outputs": []
26+
}
27+
]
28+
}

12_MeshLoaders/include/common.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef _NBL_THIS_EXAMPLE_COMMON_H_INCLUDED_
2+
#define _NBL_THIS_EXAMPLE_COMMON_H_INCLUDED_
3+
4+
5+
#include "nbl/examples/examples.hpp"
6+
7+
using namespace nbl;
8+
using namespace core;
9+
using namespace hlsl;
10+
using namespace system;
11+
using namespace asset;
12+
using namespace ui;
13+
using namespace video;
14+
using namespace scene;
15+
using namespace nbl::examples;
16+
17+
18+
#endif // __NBL_THIS_EXAMPLE_COMMON_H_INCLUDED__

12_MeshLoaders/main.cpp

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O.
2+
// This file is part of the "Nabla Engine".
3+
// For conditions of distribution and use, see copyright notice in nabla.h
4+
#include "common.hpp"
5+
6+
#include "../3rdparty/portable-file-dialogs/portable-file-dialogs.h"
7+
8+
9+
class MeshLoadersApp final : public MonoWindowApplication, public BuiltinResourcesApplication
10+
{
11+
using device_base_t = MonoWindowApplication;
12+
using asset_base_t = BuiltinResourcesApplication;
13+
14+
public:
15+
inline MeshLoadersApp(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD)
16+
: IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD),
17+
device_base_t({1280,720}, EF_UNKNOWN, _localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) {}
18+
19+
inline bool onAppInitialized(smart_refctd_ptr<ISystem>&& system) override
20+
{
21+
if (!asset_base_t::onAppInitialized(smart_refctd_ptr(system)))
22+
return false;
23+
if (!device_base_t::onAppInitialized(smart_refctd_ptr(system)))
24+
return false;
25+
26+
m_semaphore = m_device->createSemaphore(m_realFrameIx);
27+
if (!m_semaphore)
28+
return logFail("Failed to Create a Semaphore!");
29+
30+
auto pool = m_device->createCommandPool(getGraphicsQueue()->getFamilyIndex(),IGPUCommandPool::CREATE_FLAGS::RESET_COMMAND_BUFFER_BIT);
31+
for (auto i=0u; i<MaxFramesInFlight; i++)
32+
{
33+
if (!pool)
34+
return logFail("Couldn't create Command Pool!");
35+
if (!pool->createCommandBuffers(IGPUCommandPool::BUFFER_LEVEL::PRIMARY,{m_cmdBufs.data()+i,1}))
36+
return logFail("Couldn't create Command Buffer!");
37+
}
38+
39+
//! cache results -- speeds up mesh generation on second run
40+
m_qnc->loadCacheFromFile<EF_R8G8B8_SNORM>(m_system.get(),sharedOutputCWD/"../../tmp/normalCache888.sse");
41+
42+
//
43+
if (!reloadModel())
44+
return false;
45+
#if 0
46+
const uint32_t addtionalBufferOwnershipFamilies[] = {getGraphicsQueue()->getFamilyIndex()};
47+
// we want to use the vertex data through UTBs
48+
using usage_f = IGPUBuffer::E_USAGE_FLAGS;
49+
CAssetConverter::patch_t<asset::ICPUPolygonGeometry> patch = {};
50+
patch.positionBufferUsages = usage_f::EUF_UNIFORM_TEXEL_BUFFER_BIT;
51+
patch.indexBufferUsages = usage_f::EUF_INDEX_BUFFER_BIT;
52+
patch.otherBufferUsages = usage_f::EUF_UNIFORM_TEXEL_BUFFER_BIT;
53+
m_scene = CGeometryCreatorScene::create(
54+
{
55+
.transferQueue = getTransferUpQueue(),
56+
.utilities = m_utils.get(),
57+
.logger = m_logger.get(),
58+
.addtionalBufferOwnershipFamilies = addtionalBufferOwnershipFamilies
59+
},patch
60+
);
61+
#endif
62+
63+
auto scRes = static_cast<CDefaultSwapchainFramebuffers*>(m_surface->getSwapchainResources());
64+
m_renderer = CSimpleDebugRenderer::create(m_assetMgr.get(),scRes->getRenderpass(),0,nullptr);
65+
66+
camera.mapKeysToArrows();
67+
68+
onAppInitializedFinish();
69+
return true;
70+
}
71+
72+
inline IQueue::SSubmitInfo::SSemaphoreInfo renderFrame(const std::chrono::microseconds nextPresentationTimestamp) override
73+
{
74+
m_inputSystem->getDefaultMouse(&mouse);
75+
m_inputSystem->getDefaultKeyboard(&keyboard);
76+
77+
//
78+
const auto resourceIx = m_realFrameIx % MaxFramesInFlight;
79+
80+
auto* const cb = m_cmdBufs.data()[resourceIx].get();
81+
cb->reset(IGPUCommandBuffer::RESET_FLAGS::RELEASE_RESOURCES_BIT);
82+
cb->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT);
83+
// clear to black for both things
84+
{
85+
// begin renderpass
86+
{
87+
auto scRes = static_cast<CDefaultSwapchainFramebuffers*>(m_surface->getSwapchainResources());
88+
auto* framebuffer = scRes->getFramebuffer(device_base_t::getCurrentAcquire().imageIndex);
89+
const IGPUCommandBuffer::SClearColorValue clearValue = { .float32 = {1.f,0.f,1.f,1.f} };
90+
const IGPUCommandBuffer::SClearDepthStencilValue depthValue = { .depth = 0.f };
91+
const VkRect2D currentRenderArea =
92+
{
93+
.offset = {0,0},
94+
.extent = {framebuffer->getCreationParameters().width,framebuffer->getCreationParameters().height}
95+
};
96+
const IGPUCommandBuffer::SRenderpassBeginInfo info =
97+
{
98+
.framebuffer = framebuffer,
99+
.colorClearValues = &clearValue,
100+
.depthStencilClearValues = &depthValue,
101+
.renderArea = currentRenderArea
102+
};
103+
cb->beginRenderPass(info,IGPUCommandBuffer::SUBPASS_CONTENTS::INLINE);
104+
105+
const SViewport viewport = {
106+
.x = static_cast<float>(currentRenderArea.offset.x),
107+
.y = static_cast<float>(currentRenderArea.offset.y),
108+
.width = static_cast<float>(currentRenderArea.extent.width),
109+
.height = static_cast<float>(currentRenderArea.extent.height)
110+
};
111+
cb->setViewport(0u,1u,&viewport);
112+
113+
cb->setScissor(0u,1u,&currentRenderArea);
114+
}
115+
// late latch input
116+
{
117+
camera.beginInputProcessing(nextPresentationTimestamp);
118+
mouse.consumeEvents([&](const IMouseEventChannel::range_t& events) -> void { camera.mouseProcess(events); }, m_logger.get());
119+
keyboard.consumeEvents([&](const IKeyboardEventChannel::range_t& events) -> void
120+
{
121+
camera.keyboardProcess(events);
122+
},
123+
m_logger.get()
124+
);
125+
camera.endInputProcessing(nextPresentationTimestamp);
126+
}
127+
// draw scene
128+
{
129+
float32_t3x4 viewMatrix;
130+
float32_t4x4 viewProjMatrix;
131+
// TODO: get rid of legacy matrices
132+
{
133+
memcpy(&viewMatrix,camera.getViewMatrix().pointer(),sizeof(viewMatrix));
134+
memcpy(&viewProjMatrix,camera.getConcatenatedMatrix().pointer(),sizeof(viewProjMatrix));
135+
}
136+
m_renderer->render(cb,CSimpleDebugRenderer::SViewParams(viewMatrix,viewProjMatrix));
137+
}
138+
cb->endRenderPass();
139+
}
140+
cb->end();
141+
142+
//updateGUIDescriptorSet();
143+
144+
IQueue::SSubmitInfo::SSemaphoreInfo retval =
145+
{
146+
.semaphore = m_semaphore.get(),
147+
.value = ++m_realFrameIx,
148+
.stageMask = PIPELINE_STAGE_FLAGS::ALL_GRAPHICS_BITS
149+
};
150+
const IQueue::SSubmitInfo::SCommandBufferInfo commandBuffers[] =
151+
{
152+
{.cmdbuf = cb }
153+
};
154+
const IQueue::SSubmitInfo::SSemaphoreInfo acquired[] = {
155+
{
156+
.semaphore = device_base_t::getCurrentAcquire().semaphore,
157+
.value = device_base_t::getCurrentAcquire().acquireCount,
158+
.stageMask = PIPELINE_STAGE_FLAGS::NONE
159+
}
160+
};
161+
const IQueue::SSubmitInfo infos[] =
162+
{
163+
{
164+
.waitSemaphores = acquired,
165+
.commandBuffers = commandBuffers,
166+
.signalSemaphores = {&retval,1}
167+
}
168+
};
169+
170+
if (getGraphicsQueue()->submit(infos) != IQueue::RESULT::SUCCESS)
171+
{
172+
retval.semaphore = nullptr; // so that we don't wait on semaphore that will never signal
173+
m_realFrameIx--;
174+
}
175+
176+
std::string caption = "[Nabla Engine] Mesh Loaders";
177+
{
178+
caption += ", displaying [";
179+
caption += m_modelPath;
180+
caption += "]";
181+
m_window->setCaption(caption);
182+
}
183+
return retval;
184+
}
185+
186+
protected:
187+
const video::IGPURenderpass::SCreationParams::SSubpassDependency* getDefaultSubpassDependencies() const override
188+
{
189+
// Subsequent submits don't wait for each other, hence its important to have External Dependencies which prevent users of the depth attachment overlapping.
190+
const static IGPURenderpass::SCreationParams::SSubpassDependency dependencies[] = {
191+
// wipe-transition of Color to ATTACHMENT_OPTIMAL and depth
192+
{
193+
.srcSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External,
194+
.dstSubpass = 0,
195+
.memoryBarrier = {
196+
// last place where the depth can get modified in previous frame, `COLOR_ATTACHMENT_OUTPUT_BIT` is implicitly later
197+
.srcStageMask = PIPELINE_STAGE_FLAGS::LATE_FRAGMENT_TESTS_BIT,
198+
// don't want any writes to be available, we'll clear
199+
.srcAccessMask = ACCESS_FLAGS::NONE,
200+
// destination needs to wait as early as possible
201+
// TODO: `COLOR_ATTACHMENT_OUTPUT_BIT` shouldn't be needed, because its a logically later stage, see TODO in `ECommonEnums.h`
202+
.dstStageMask = PIPELINE_STAGE_FLAGS::EARLY_FRAGMENT_TESTS_BIT | PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT,
203+
// because depth and color get cleared first no read mask
204+
.dstAccessMask = ACCESS_FLAGS::DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT
205+
}
206+
// leave view offsets and flags default
207+
},
208+
// color from ATTACHMENT_OPTIMAL to PRESENT_SRC
209+
{
210+
.srcSubpass = 0,
211+
.dstSubpass = IGPURenderpass::SCreationParams::SSubpassDependency::External,
212+
.memoryBarrier = {
213+
// last place where the color can get modified, depth is implicitly earlier
214+
.srcStageMask = PIPELINE_STAGE_FLAGS::COLOR_ATTACHMENT_OUTPUT_BIT,
215+
// only write ops, reads can't be made available
216+
.srcAccessMask = ACCESS_FLAGS::COLOR_ATTACHMENT_WRITE_BIT
217+
// spec says nothing is needed when presentation is the destination
218+
}
219+
// leave view offsets and flags default
220+
},
221+
IGPURenderpass::SCreationParams::DependenciesEnd
222+
};
223+
return dependencies;
224+
}
225+
226+
private:
227+
inline bool reloadModel()
228+
{
229+
pfd::open_file file("Choose a supported Model File", "../../media", { "All Supported Formats", "*.ply *.stl *.serialized *.obj",
230+
"TODO (.ply)", "*.ply",
231+
"TODO (.stl)", "*.stl",
232+
"Mitsuba 0.6 Serialized (.serialized)", "*.serialized",
233+
"Wavefront Object (.obj)", "*.obj"
234+
});
235+
if (file.result().empty())
236+
return false;
237+
m_modelPath = file.result()[0];
238+
239+
// free up
240+
m_assetMgr->clearAllAssetCache();
241+
242+
//! load the geometry
243+
IAssetLoader::SAssetLoadParams params = {};
244+
params.meshManipulatorOverride = nullptr; // TODO
245+
auto bundle = m_assetMgr->getAsset(m_modelPath,params);
246+
if (bundle.getContents().empty())
247+
return false;
248+
//! cache results -- speeds up mesh generation on second run
249+
m_qnc->saveCacheToFile<EF_R8G8B8_SNORM>(m_system.get(),sharedOutputCWD/"../../tmp/normalCache888.sse");
250+
251+
return true;
252+
}
253+
254+
// Maximum frames which can be simultaneously submitted, used to cycle through our per-frame resources like command buffers
255+
constexpr static inline uint32_t MaxFramesInFlight = 3u;
256+
//
257+
smart_refctd_ptr<CQuantNormalCache> m_qnc;
258+
smart_refctd_ptr<CSimpleDebugRenderer> m_renderer;
259+
//
260+
smart_refctd_ptr<ISemaphore> m_semaphore;
261+
uint64_t m_realFrameIx = 0;
262+
std::array<smart_refctd_ptr<IGPUCommandBuffer>,MaxFramesInFlight> m_cmdBufs;
263+
//
264+
InputSystem::ChannelReader<IMouseEventChannel> mouse;
265+
InputSystem::ChannelReader<IKeyboardEventChannel> keyboard;
266+
//
267+
Camera camera = Camera(core::vectorSIMDf(0, 0, 0), core::vectorSIMDf(0, 0, 0), core::matrix4SIMD());
268+
// mutables
269+
std::string m_modelPath;
270+
};
271+
272+
NBL_MAIN_FUNC(MeshLoadersApp)

12_MeshLoaders/pipeline.groovy

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import org.DevshGraphicsProgramming.Agent
2+
import org.DevshGraphicsProgramming.BuilderInfo
3+
import org.DevshGraphicsProgramming.IBuilder
4+
5+
class CUIBuilder extends IBuilder
6+
{
7+
public CUIBuilder(Agent _agent, _info)
8+
{
9+
super(_agent, _info)
10+
}
11+
12+
@Override
13+
public boolean prepare(Map axisMapping)
14+
{
15+
return true
16+
}
17+
18+
@Override
19+
public boolean build(Map axisMapping)
20+
{
21+
IBuilder.CONFIGURATION config = axisMapping.get("CONFIGURATION")
22+
IBuilder.BUILD_TYPE buildType = axisMapping.get("BUILD_TYPE")
23+
24+
def nameOfBuildDirectory = getNameOfBuildDirectory(buildType)
25+
def nameOfConfig = getNameOfConfig(config)
26+
27+
agent.execute("cmake --build ${info.rootProjectPath}/${nameOfBuildDirectory}/${info.targetProjectPathRelativeToRoot} --target ${info.targetBaseName} --config ${nameOfConfig} -j12 -v")
28+
29+
return true
30+
}
31+
32+
@Override
33+
public boolean test(Map axisMapping)
34+
{
35+
return true
36+
}
37+
38+
@Override
39+
public boolean install(Map axisMapping)
40+
{
41+
return true
42+
}
43+
}
44+
45+
def create(Agent _agent, _info)
46+
{
47+
return new CUIBuilder(_agent, _info)
48+
}
49+
50+
return this

0 commit comments

Comments
 (0)