Skip to content

Commit 9fa88d5

Browse files
Merge pull request #352 from rhabacker/vsglod
Add example for vsg::LOD nodes
2 parents 3acf34f + 515ca12 commit 9fa88d5

File tree

3 files changed

+350
-0
lines changed

3 files changed

+350
-0
lines changed

examples/nodes/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_subdirectory(vsgannotation)
22
add_subdirectory(vsggroups)
3+
add_subdirectory(vsglod)
34
add_subdirectory(vsgtransform)
45
add_subdirectory(vsgtextureprojection)
56
add_subdirectory(vsglayers)

examples/nodes/vsglod/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
set(SOURCES
2+
vsglod.cpp
3+
)
4+
5+
add_executable(vsglod ${SOURCES})
6+
7+
target_link_libraries(vsglod vsg::vsg)
8+
9+
if (Tracy_FOUND)
10+
target_compile_definitions(vsglod PRIVATE Tracy_FOUND)
11+
target_link_libraries(vsglod Tracy::TracyClient)
12+
endif()
13+
14+
install(TARGETS vsglod RUNTIME DESTINATION bin)

examples/nodes/vsglod/vsglod.cpp

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
#include <vsg/all.h>
2+
3+
#ifdef Tracy_FOUND
4+
# include <vsg/utils/TracyInstrumentation.h>
5+
#endif
6+
7+
#include <iostream>
8+
9+
struct ModelSettings
10+
{
11+
vsg::ref_ptr<vsg::Options> options;
12+
double lodMinScreenRatio = 0.01;
13+
};
14+
15+
vsg::ref_ptr<vsg::Node> createTestScene(const ModelSettings& settings)
16+
{
17+
auto builder = vsg::Builder::create();
18+
builder->options = settings.options;
19+
20+
auto scene = vsg::Group::create();
21+
22+
vsg::GeometryInfo geomInfo;
23+
geomInfo.color.set(1.0f, 1.0f, 0.5f, 1.0f);
24+
vsg::StateInfo stateInfo;
25+
26+
auto node = builder->createCone(geomInfo, stateInfo);
27+
auto node2 = builder->createCylinder(geomInfo, stateInfo);
28+
29+
vsg::ComputeBounds cb;
30+
node->accept(cb);
31+
node2->accept(cb);
32+
auto bb = cb.bounds;
33+
34+
// shows empty, then node2, then node when approaching
35+
auto lod = vsg::LOD::create();
36+
lod->bound = vsg::dsphere((bb.min + bb.max) * 0.5, vsg::length(bb.max - bb.min));
37+
lod->addChild(vsg::LOD::Child{settings.lodMinScreenRatio+1, node});
38+
lod->addChild(vsg::LOD::Child{settings.lodMinScreenRatio, node2});
39+
scene->addChild(lod);
40+
41+
geomInfo.position += geomInfo.dx * 1.5f;
42+
geomInfo.color.set(1.0f, 0.5f, 1.0f, 1.0f);
43+
auto node3 = builder->createCone(geomInfo, stateInfo);
44+
auto node4 = builder->createSphere(geomInfo, stateInfo);
45+
46+
vsg::ComputeBounds cb2;
47+
node3->accept(cb2);
48+
node4->accept(cb2);
49+
auto bb2 = cb2.bounds;
50+
51+
// should show node4, then node3 when approaching
52+
auto lod2 = vsg::LOD::create();
53+
lod2->bound = vsg::dsphere((bb2.min + bb2.max) * 0.5, vsg::length(bb2.max - bb2.min));
54+
lod2->addChild(vsg::LOD::Child{settings.lodMinScreenRatio, node3});
55+
lod2->addChild(vsg::LOD::Child{0.0, node4});
56+
scene->addChild(lod2);
57+
58+
// should show empty, then node5 when approaching
59+
geomInfo.position += geomInfo.dx * 1.5f;
60+
geomInfo.color.set(0.5f, 1.0f, 1.0f, 1.0f);
61+
auto node5 = builder->createCone(geomInfo, stateInfo);
62+
vsg::ComputeBounds cb3;
63+
node5->accept(cb3);
64+
auto bb3 = cb3.bounds;
65+
66+
auto lod3 = vsg::LOD::create();
67+
lod3->bound = vsg::dsphere((bb3.min + bb3.max) * 0.5, vsg::length(bb3.max - bb3.min));
68+
lod3->addChild(vsg::LOD::Child{settings.lodMinScreenRatio+0.5, node5});
69+
scene->addChild(lod3);
70+
71+
// should show for node7, then empty when approaching
72+
geomInfo.position += geomInfo.dx * 1.5f;
73+
geomInfo.color.set(0.5f, 0.5f, 1.0f, 1.0f);
74+
auto node6 = vsg::Group::create();
75+
auto node7 = builder->createSphere(geomInfo, stateInfo);
76+
vsg::ComputeBounds cb4;
77+
node6->accept(cb4);
78+
node7->accept(cb4);
79+
auto bb4 = cb4.bounds;
80+
81+
auto lod4 = vsg::LOD::create();
82+
lod4->bound = vsg::dsphere((bb4.min + bb4.max) * 0.5, vsg::length(bb4.max - bb4.min));
83+
lod4->addChild(vsg::LOD::Child{settings.lodMinScreenRatio+1, node6});
84+
lod4->addChild(vsg::LOD::Child{settings.lodMinScreenRatio, node7});
85+
scene->addChild(lod4);
86+
87+
auto bounds = vsg::visit<vsg::ComputeBounds>(scene).bounds;
88+
vsg::info("createTestScene() extents = ", bounds);
89+
90+
return scene;
91+
}
92+
93+
int main(int argc, char** argv)
94+
{
95+
// set up defaults and read command line arguments to override them
96+
auto options = vsg::Options::create();
97+
options->paths = vsg::getEnvPaths("VSG_FILE_PATH");
98+
options->sharedObjects = vsg::SharedObjects::create();
99+
100+
auto windowTraits = vsg::WindowTraits::create();
101+
windowTraits->windowTitle = "vsglod";
102+
103+
// set up defaults and read command line arguments to override them
104+
vsg::CommandLine arguments(&argc, argv);
105+
windowTraits->debugLayer = arguments.read({"--debug", "-d"});
106+
windowTraits->apiDumpLayer = arguments.read({"--api", "-a"});
107+
windowTraits->synchronizationLayer = arguments.read("--sync");
108+
109+
bool reportAverageFrameRate = arguments.read("--fps");
110+
arguments.read("--screen", windowTraits->screenNum);
111+
arguments.read("--display", windowTraits->display);
112+
auto numFrames = arguments.value(-1, "-f");
113+
if (arguments.read({"--fullscreen", "--fs"})) windowTraits->fullscreen = true;
114+
if (arguments.read({"--window", "-w"}, windowTraits->width, windowTraits->height)) { windowTraits->fullscreen = false; }
115+
if (arguments.read("--IMMEDIATE")) windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
116+
if (arguments.read("--d32")) windowTraits->depthFormat = VK_FORMAT_D32_SFLOAT;
117+
arguments.read("--samples", windowTraits->samples);
118+
if (arguments.read({"-t", "--test"}))
119+
{
120+
windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
121+
windowTraits->fullscreen = true;
122+
reportAverageFrameRate = true;
123+
}
124+
if (arguments.read("--st"))
125+
{
126+
windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
127+
windowTraits->width = 192, windowTraits->height = 108;
128+
windowTraits->decoration = false;
129+
reportAverageFrameRate = true;
130+
}
131+
132+
const double invalid_time = std::numeric_limits<double>::max();
133+
auto duration = arguments.value(invalid_time, "--duration");
134+
135+
vsg::ref_ptr<vsg::Instrumentation> instrumentation;
136+
if (arguments.read({"--gpu-annotation", "--ga"}) && vsg::isExtensionSupported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
137+
{
138+
windowTraits->debugUtils = true;
139+
140+
auto gpu_instrumentation = vsg::GpuAnnotation::create();
141+
if (arguments.read("--func")) gpu_instrumentation->labelType = vsg::GpuAnnotation::SourceLocation_function;
142+
143+
instrumentation = gpu_instrumentation;
144+
}
145+
else if (arguments.read({"--profiler", "--pr"}))
146+
{
147+
// set Profiler options
148+
auto settings = vsg::Profiler::Settings::create();
149+
arguments.read("--cpu", settings->cpu_instrumentation_level);
150+
arguments.read("--gpu", settings->gpu_instrumentation_level);
151+
arguments.read("--log-size", settings->log_size);
152+
arguments.read("--gpu-size", settings->gpu_timestamp_size);
153+
154+
// create the profiler
155+
instrumentation = vsg::Profiler::create(settings);
156+
}
157+
#ifdef Tracy_FOUND
158+
else if (arguments.read("--tracy"))
159+
{
160+
windowTraits->deviceExtensionNames.push_back(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME);
161+
162+
auto tracy_instrumentation = vsg::TracyInstrumentation::create();
163+
arguments.read("--cpu", tracy_instrumentation->settings->cpu_instrumentation_level);
164+
arguments.read("--gpu", tracy_instrumentation->settings->gpu_instrumentation_level);
165+
instrumentation = tracy_instrumentation;
166+
}
167+
#endif
168+
169+
ModelSettings settings;
170+
settings.options = options;
171+
172+
vsg::ref_ptr<vsg::ResourceHints> resourceHints;
173+
if (auto resourceHintsFilename = arguments.value<vsg::Path>("", "--rh"))
174+
{
175+
resourceHints = vsg::read_cast<vsg::ResourceHints>(resourceHintsFilename, options);
176+
}
177+
178+
if (!resourceHints) resourceHints = vsg::ResourceHints::create();
179+
180+
if (auto outputResourceHintsFilename = arguments.value<vsg::Path>("", "--orh"))
181+
{
182+
if (!resourceHints) resourceHints = vsg::ResourceHints::create();
183+
vsg::write(resourceHints, outputResourceHintsFilename, options);
184+
return 0;
185+
}
186+
187+
auto shaderHints = vsg::ShaderCompileSettings::create();
188+
vsg::ref_ptr<vsg::ShadowSettings> shadowSettings;
189+
190+
auto pathFilename = arguments.value<vsg::Path>("", "-p");
191+
auto outputFilename = arguments.value<vsg::Path>("", "-o");
192+
193+
auto inherit = arguments.read("--inherit");
194+
auto direction = arguments.value(vsg::dvec3(0.0, 0.0, -1.0), "--direction");
195+
auto angleSubtended = arguments.value<float>(0.0090f, "--angleSubtended");
196+
197+
vsg::ref_ptr<vsg::StateGroup> stateGroup;
198+
if (inherit)
199+
{
200+
auto shaderSet = vsg::createPhongShaderSet(options);
201+
auto layout = shaderSet->createPipelineLayout({}, {0, 1});
202+
203+
stateGroup = vsg::StateGroup::create();
204+
205+
uint32_t vds_set = 0;
206+
stateGroup->add(vsg::BindViewDescriptorSets::create(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, vds_set));
207+
208+
vsg::info("Added state to inherit ");
209+
options->inheritedState = stateGroup->stateCommands;
210+
}
211+
212+
vsg::ref_ptr<vsg::Node> scene;
213+
scene = createTestScene(settings);
214+
215+
if (stateGroup)
216+
{
217+
// if setup place the StateGroup at the root of the scene graph
218+
stateGroup->addChild(scene);
219+
scene = stateGroup;
220+
}
221+
222+
// compute the bounds of the scene graph to help position camera
223+
auto bounds = vsg::visit<vsg::ComputeBounds>(scene).bounds;
224+
auto viewingDistance = vsg::length(bounds.max - bounds.min) * 2.0;
225+
226+
vsg::ref_ptr<vsg::LookAt> lookAt;
227+
228+
{
229+
vsg::dvec3 centre = (bounds.min + bounds.max) * 0.5;
230+
231+
// set up the camera
232+
lookAt = vsg::LookAt::create(centre + vsg::dvec3(0.0, -viewingDistance, 0.0), centre, vsg::dvec3(0.0, 0.0, 1.0));
233+
}
234+
235+
//auto span = vsg::length(bounds.max - bounds.min);
236+
auto group = vsg::Group::create();
237+
group->addChild(scene);
238+
239+
vsg::ref_ptr<vsg::DirectionalLight> directionalLight;
240+
directionalLight = vsg::DirectionalLight::create();
241+
directionalLight->name = "directional";
242+
directionalLight->color.set(1.0f, 1.0f, 1.0f);
243+
directionalLight->intensity = 0.95f;
244+
directionalLight->direction = direction;
245+
directionalLight->angleSubtended = angleSubtended;
246+
directionalLight->shadowSettings = shadowSettings;
247+
248+
group->addChild(directionalLight);
249+
250+
scene = group;
251+
252+
// write out scene if required
253+
if (!outputFilename.empty())
254+
{
255+
vsg::write(scene, outputFilename, options);
256+
return 0;
257+
}
258+
259+
// create the viewer and assign window(s) to it
260+
auto viewer = vsg::Viewer::create();
261+
262+
auto window = vsg::Window::create(windowTraits);
263+
if (!window)
264+
{
265+
std::cout << "Could not create windows." << std::endl;
266+
return 1;
267+
}
268+
269+
viewer->addWindow(window);
270+
271+
vsg::ref_ptr<vsg::ProjectionMatrix> perspective;
272+
perspective = vsg::Perspective::create(30, 1.0, 1.0, 1000);
273+
274+
auto camera = vsg::Camera::create(perspective, lookAt, vsg::ViewportState::create(window->extent2D()));
275+
276+
// add the camera and scene graph to View
277+
auto view = vsg::View::create();
278+
view->camera = camera;
279+
view->addChild(scene);
280+
281+
// add close handler to respond the close window button and pressing escape
282+
viewer->addEventHandler(vsg::CloseHandler::create(viewer));
283+
284+
auto cameraAnimation = vsg::CameraAnimationHandler::create(camera, pathFilename, options);
285+
viewer->addEventHandler(cameraAnimation);
286+
if (cameraAnimation->animation)
287+
{
288+
cameraAnimation->play();
289+
if (reportAverageFrameRate && duration == invalid_time) duration = cameraAnimation->animation->maxTime();
290+
}
291+
292+
viewer->addEventHandler(vsg::Trackball::create(camera));
293+
294+
auto renderGraph = vsg::RenderGraph::create(window, view);
295+
auto commandGraph = vsg::CommandGraph::create(window, renderGraph);
296+
viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph});
297+
298+
if (instrumentation) viewer->assignInstrumentation(instrumentation);
299+
300+
viewer->compile(resourceHints);
301+
302+
auto startTime = vsg::clock::now();
303+
double numFramesCompleted = 0.0;
304+
305+
// rendering main loop
306+
while (viewer->advanceToNextFrame() && (numFrames < 0 || (numFrames--) > 0) && (viewer->getFrameStamp()->simulationTime < duration))
307+
{
308+
// pass any events into EventHandlers assigned to the Viewer
309+
viewer->handleEvents();
310+
viewer->update();
311+
312+
viewer->recordAndSubmit();
313+
314+
viewer->present();
315+
316+
numFramesCompleted += 1.0;
317+
}
318+
319+
if (reportAverageFrameRate)
320+
{
321+
auto elapesedTime = std::chrono::duration<double, std::chrono::seconds::period>(vsg::clock::now() - startTime).count();
322+
if (numFramesCompleted > 0.0)
323+
{
324+
std::cout << "Average frame rate = " << (numFramesCompleted / elapesedTime) << std::endl;
325+
}
326+
}
327+
328+
if (auto profiler = instrumentation.cast<vsg::Profiler>())
329+
{
330+
instrumentation->finish();
331+
profiler->log->report(std::cout);
332+
}
333+
334+
return 0;
335+
}

0 commit comments

Comments
 (0)