From 5a395365a85fa8b1b6cfa399c00ce736028bbf1f Mon Sep 17 00:00:00 2001 From: David Date: Fri, 25 Oct 2024 14:12:35 +0200 Subject: [PATCH 1/5] Rudimentary text intersection support --- include/vsg/utils/Intersector.h | 2 ++ src/vsg/utils/Intersector.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/vsg/utils/Intersector.h b/include/vsg/utils/Intersector.h index d1fdf2255..1e10efa8c 100644 --- a/include/vsg/utils/Intersector.h +++ b/include/vsg/utils/Intersector.h @@ -53,6 +53,8 @@ namespace vsg void apply(const ushortArray& array) override; void apply(const uintArray& array) override; + void apply(const TextTechnique& technique) override; + // // provide virtual functions for concrete Intersector implementations to provide handling of intersection with mesh geometries // diff --git a/src/vsg/utils/Intersector.cpp b/src/vsg/utils/Intersector.cpp index 93bfb850c..c227d096a 100644 --- a/src/vsg/utils/Intersector.cpp +++ b/src/vsg/utils/Intersector.cpp @@ -28,6 +28,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include +#include using namespace vsg; @@ -204,6 +206,16 @@ void Intersector::apply(const uintArray& array) uint_indices = &array; } +void Intersector::apply(const TextTechnique& technique) +{ + // TODO: Implement TextLayout support + + if (auto cpuTechnique = technique.cast()) + cpuTechnique->scenegraph->accept(*this); + if (auto gpuTechnique = technique.cast()) + gpuTechnique->scenegraph->accept(*this); +} + void Intersector::apply(const Draw& draw) { PushPopNode ppn(_nodePath, &draw); From 4280dac19c665dd0c47a651b2b38ce6bede12253 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 30 Oct 2024 16:35:12 +0100 Subject: [PATCH 2/5] CpuLayoutTechnique intersect billboard support --- include/vsg/maths/transform.h | 19 ++++++++ src/vsg/state/ArrayState.cpp | 17 +------ src/vsg/text/CpuLayoutTechnique.cpp | 71 +++++++++++++++++++++++++++++ src/vsg/utils/Intersector.cpp | 2 - 4 files changed, 92 insertions(+), 17 deletions(-) diff --git a/include/vsg/maths/transform.h b/include/vsg/maths/transform.h index 9e5fe03d8..2ede4463e 100644 --- a/include/vsg/maths/transform.h +++ b/include/vsg/maths/transform.h @@ -176,6 +176,25 @@ namespace vsg vsg::translate(-eye.x, -eye.y, -eye.z); } + template + constexpr t_mat4 computeBillboardMatrix(const t_vec3& centerEye, T autoscaleDistance) + { + auto distance = -centerEye.z; + + auto scale = (distance < autoscaleDistance) ? distance / autoscaleDistance : 1.0; + t_mat4 mS(scale, 0.0, 0.0, 0.0, + 0.0, scale, 0.0, 0.0, + 0.0, 0.0, scale, 0.0, + 0.0, 0.0, 0.0, 1.0); + + t_mat4 mT(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + centerEye.x, centerEye.y, centerEye.z, 1.0); + + return mT * mS; + } + /// Hint on axis, using Collada conventions, all Right Hand enum class CoordinateConvention { diff --git a/src/vsg/state/ArrayState.cpp b/src/vsg/state/ArrayState.cpp index ae256a283..a3d0f05f1 100644 --- a/src/vsg/state/ArrayState.cpp +++ b/src/vsg/state/ArrayState.cpp @@ -491,27 +491,14 @@ ref_ptr BillboardArrayState::vertexArray(uint32_t instanceIndex dvec3 position(gv.value.xyz); double autoDistanceScale = gv.value.w; + dmat4 billboard_to_local; if (!localToWorldStack.empty() && !worldToLocalStack.empty()) { const auto& mv = localToWorldStack.back(); const auto& inverse_mv = worldToLocalStack.back(); - auto center_eye = mv * position; - double distance = -center_eye.z; - - double scale = (distance < autoDistanceScale) ? distance / autoDistanceScale : 1.0; - dmat4 S(scale, 0.0, 0.0, 0.0, - 0.0, scale, 0.0, 0.0, - 0.0, 0.0, scale, 0.0, - 0.0, 0.0, 0.0, 1.0); - - dmat4 T(1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - center_eye.x, center_eye.y, center_eye.z, 1.0); - - dmat4 billboard_mv = T * S; + auto billboard_mv = computeBillboardMatrix(center_eye, autoDistanceScale); billboard_to_local = inverse_mv * billboard_mv; } else diff --git a/src/vsg/text/CpuLayoutTechnique.cpp b/src/vsg/text/CpuLayoutTechnique.cpp index 1aff2d9ac..305d5d2a7 100644 --- a/src/vsg/text/CpuLayoutTechnique.cpp +++ b/src/vsg/text/CpuLayoutTechnique.cpp @@ -34,6 +34,73 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; +class VSG_DECLSPEC CpuLayoutTechniqueArrayState : public Inherit +{ +public: + CpuLayoutTechniqueArrayState(const CpuLayoutTechnique& in_technique) : + technique(in_technique) + { + } + + CpuLayoutTechniqueArrayState(const CpuLayoutTechniqueArrayState& rhs) : + Inherit(rhs), + technique(rhs.technique) + { + } + + ref_ptr cloneArrayState() override + { + return CpuLayoutTechniqueArrayState::create(*this); + } + + ref_ptr cloneArrayState(ref_ptr arrayState) override + { + auto clone = CpuLayoutTechniqueArrayState::create(*this); + clone->localToWorldStack = arrayState->localToWorldStack; + clone->worldToLocalStack = arrayState->worldToLocalStack; + return clone; + } + + ref_ptr vertexArray(uint32_t instanceIndex) override + { + auto new_vertices = vsg::vec3Array::create(static_cast(vertices->size())); + auto src_vertex_itr = vertices->begin(); + size_t v_index = 0; + for (auto& v : *new_vertices) + { + const auto& sv = *(src_vertex_itr++); + + dvec4 centerAndAutoScaleDistance; + if (technique.centerAndAutoScaleDistances->size() == 1) + centerAndAutoScaleDistance = technique.centerAndAutoScaleDistances->at(0); + else + centerAndAutoScaleDistance = technique.centerAndAutoScaleDistances->at(v_index++); + + dmat4 billboard_to_local; + if (!localToWorldStack.empty() && !worldToLocalStack.empty()) + { + const auto& mv = localToWorldStack.back(); + const auto& inverse_mv = worldToLocalStack.back(); + dvec3 center_eye = mv * centerAndAutoScaleDistance.xyz; + dmat4 billboard_mv = computeBillboardMatrix(center_eye, centerAndAutoScaleDistance.w); + billboard_to_local = inverse_mv * billboard_mv; + } + else + { + billboard_to_local = vsg::translate(centerAndAutoScaleDistance.xyz); + } + + v = vec3(billboard_to_local * dvec3(sv)); + } + + return new_vertices; + } + + using ArrayState::apply; + + const CpuLayoutTechnique& technique; +}; + void CpuLayoutTechnique::setup(Text* text, uint32_t minimumAllocation, ref_ptr options) { if (!text || !(text->text) || !text->font || !text->layout) return; @@ -291,6 +358,10 @@ ref_ptr CpuLayoutTechnique::createRenderingSubgraph(ref_ptr sha drawCommands->addChild(bindIndexBuffer); drawCommands->addChild(drawIndexed); + // Assign ArrayState for CPU mapping of billboarding + if (billboard) + stategroup->prototypeArrayState = CpuLayoutTechniqueArrayState::create(*this); + stategroup->addChild(drawCommands); } else diff --git a/src/vsg/utils/Intersector.cpp b/src/vsg/utils/Intersector.cpp index c227d096a..10309aeb2 100644 --- a/src/vsg/utils/Intersector.cpp +++ b/src/vsg/utils/Intersector.cpp @@ -208,8 +208,6 @@ void Intersector::apply(const uintArray& array) void Intersector::apply(const TextTechnique& technique) { - // TODO: Implement TextLayout support - if (auto cpuTechnique = technique.cast()) cpuTechnique->scenegraph->accept(*this); if (auto gpuTechnique = technique.cast()) From ce710e34876e7d9df4446cf76466845d78cc0717 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 31 Oct 2024 17:27:31 +0100 Subject: [PATCH 3/5] GpuLayoutTechnique intersect support --- src/vsg/state/ArrayState.cpp | 1 - src/vsg/text/CpuLayoutTechnique.cpp | 35 +++++---- src/vsg/text/GpuLayoutTechnique.cpp | 118 +++++++++++++++++++++++++--- 3 files changed, 126 insertions(+), 28 deletions(-) diff --git a/src/vsg/state/ArrayState.cpp b/src/vsg/state/ArrayState.cpp index a3d0f05f1..3bbe12cc1 100644 --- a/src/vsg/state/ArrayState.cpp +++ b/src/vsg/state/ArrayState.cpp @@ -491,7 +491,6 @@ ref_ptr BillboardArrayState::vertexArray(uint32_t instanceIndex dvec3 position(gv.value.xyz); double autoDistanceScale = gv.value.w; - dmat4 billboard_to_local; if (!localToWorldStack.empty() && !worldToLocalStack.empty()) { diff --git a/src/vsg/text/CpuLayoutTechnique.cpp b/src/vsg/text/CpuLayoutTechnique.cpp index 305d5d2a7..d33fa5b01 100644 --- a/src/vsg/text/CpuLayoutTechnique.cpp +++ b/src/vsg/text/CpuLayoutTechnique.cpp @@ -37,7 +37,7 @@ using namespace vsg; class VSG_DECLSPEC CpuLayoutTechniqueArrayState : public Inherit { public: - CpuLayoutTechniqueArrayState(const CpuLayoutTechnique& in_technique) : + CpuLayoutTechniqueArrayState(const CpuLayoutTechnique* in_technique) : technique(in_technique) { } @@ -48,6 +48,11 @@ class VSG_DECLSPEC CpuLayoutTechniqueArrayState : public Inherit cloneArrayState() override { return CpuLayoutTechniqueArrayState::create(*this); @@ -55,9 +60,8 @@ class VSG_DECLSPEC CpuLayoutTechniqueArrayState : public Inherit cloneArrayState(ref_ptr arrayState) override { - auto clone = CpuLayoutTechniqueArrayState::create(*this); - clone->localToWorldStack = arrayState->localToWorldStack; - clone->worldToLocalStack = arrayState->worldToLocalStack; + auto clone = CpuLayoutTechniqueArrayState::create(*arrayState); + clone->technique = technique; return clone; } @@ -70,17 +74,19 @@ class VSG_DECLSPEC CpuLayoutTechniqueArrayState : public Inheritsize() == 1) - centerAndAutoScaleDistance = technique.centerAndAutoScaleDistances->at(0); + if (technique->centerAndAutoScaleDistances->size() == 1) + centerAndAutoScaleDistance = technique->centerAndAutoScaleDistances->at(0); else - centerAndAutoScaleDistance = technique.centerAndAutoScaleDistances->at(v_index++); + centerAndAutoScaleDistance = technique->centerAndAutoScaleDistances->at(v_index++); + // billboard effect dmat4 billboard_to_local; if (!localToWorldStack.empty() && !worldToLocalStack.empty()) { - const auto& mv = localToWorldStack.back(); - const auto& inverse_mv = worldToLocalStack.back(); + const dmat4& mv = localToWorldStack.back(); + const dmat4& inverse_mv = worldToLocalStack.back(); dvec3 center_eye = mv * centerAndAutoScaleDistance.xyz; dmat4 billboard_mv = computeBillboardMatrix(center_eye, centerAndAutoScaleDistance.w); billboard_to_local = inverse_mv * billboard_mv; @@ -96,9 +102,7 @@ class VSG_DECLSPEC CpuLayoutTechniqueArrayState : public Inherit options) @@ -357,12 +361,11 @@ ref_ptr CpuLayoutTechnique::createRenderingSubgraph(ref_ptr sha drawCommands->addChild(bindVertexBuffers); drawCommands->addChild(bindIndexBuffer); drawCommands->addChild(drawIndexed); + stategroup->addChild(drawCommands); - // Assign ArrayState for CPU mapping of billboarding + // Assign ArrayState for CPU mapping of vertices for billboarding if (billboard) - stategroup->prototypeArrayState = CpuLayoutTechniqueArrayState::create(*this); - - stategroup->addChild(drawCommands); + stategroup->prototypeArrayState = CpuLayoutTechniqueArrayState::create(this); } else { diff --git a/src/vsg/text/GpuLayoutTechnique.cpp b/src/vsg/text/GpuLayoutTechnique.cpp index 2c5742d8f..ae458c08b 100644 --- a/src/vsg/text/GpuLayoutTechnique.cpp +++ b/src/vsg/text/GpuLayoutTechnique.cpp @@ -34,6 +34,104 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; +class VSG_DECLSPEC GpuLayoutTechniqueArrayState : public Inherit +{ +public: + GpuLayoutTechniqueArrayState(const GpuLayoutTechnique* in_technique, const Text* in_text, bool in_billboard) : + technique(in_technique), + text(in_text), + billboard(in_billboard) + { + } + + GpuLayoutTechniqueArrayState(const GpuLayoutTechniqueArrayState& rhs) : + Inherit(rhs), + technique(rhs.technique), + text(rhs.text), + billboard(rhs.billboard) + { + } + + explicit GpuLayoutTechniqueArrayState(const ArrayState& rhs) : + Inherit(rhs) + { + } + + ref_ptr cloneArrayState() override + { + return GpuLayoutTechniqueArrayState::create(*this); + } + + ref_ptr cloneArrayState(ref_ptr arrayState) override + { + auto clone = GpuLayoutTechniqueArrayState::create(*arrayState); + clone->technique = technique; + clone->text = text; + clone->billboard = billboard; + return clone; + } + + ref_ptr vertexArray(uint32_t instanceIndex) override + { + // compute the position of the glyph + float horiAdvance = 0.0; + float vertAdvance = 0.0; + for (uint32_t i = 0; i < instanceIndex; ++i) + { + uint32_t glyph_index = technique->textArray->at(i); + if (glyph_index == 0) + { + // treat as a newlline + vertAdvance -= 1.0; + horiAdvance = 0.0; + } + else + { + const GlyphMetrics& glyph_metrics = text->font->glyphMetrics->at(glyph_index); + horiAdvance += glyph_metrics.horiAdvance; + } + } + + uint32_t glyph_index = technique->textArray->at(instanceIndex); + const GlyphMetrics& glyph_metrics = text->font->glyphMetrics->at(glyph_index); + + // billboard effect + auto textLayout = technique->layoutValue->value(); + dmat4 transform_to_local; + if (billboard && !localToWorldStack.empty() && !worldToLocalStack.empty()) + { + const dmat4& mv = localToWorldStack.back(); + const dmat4& inverse_mv = worldToLocalStack.back(); + dvec3 center_eye = mv * dvec3(textLayout.position); + dmat4 billboard_mv = computeBillboardMatrix(center_eye, (double)textLayout.billboardAutoScaleDistance); + transform_to_local = inverse_mv * billboard_mv; + } + else + { + transform_to_local = vsg::translate(textLayout.position); + } + + auto new_vertices = vsg::vec3Array::create(6); + auto src_vertex_itr = vertices->begin(); + for (auto& v : *new_vertices) + { + const auto& sv = *(src_vertex_itr++); + + // compute the position of vertex + vec3 pos = textLayout.horizontal * (horiAdvance + textLayout.horizontalAlignment + glyph_metrics.horiBearingX + sv.x * glyph_metrics.width) + + textLayout.vertical * (vertAdvance + textLayout.verticalAlignment + glyph_metrics.horiBearingY + (sv.y - 1.f) * glyph_metrics.height); + + v = vec3(transform_to_local * dvec3(pos)); + } + + return new_vertices; + } + + const GpuLayoutTechnique* technique = nullptr; + const Text* text = nullptr; + bool billboard = false; +}; + template void assignValue(T& dest, const T& src, bool& updated) { @@ -176,18 +274,21 @@ void GpuLayoutTechnique::setup(Text* text, uint32_t minimumAllocation, ref_ptrset(0, vec3(0.0f, 1.0f, 2.0f * leadingEdgeGradient)); vertices->set(1, vec3(0.0f, 0.0f, leadingEdgeGradient)); vertices->set(2, vec3(1.0f, 1.0f, leadingEdgeGradient)); - vertices->set(3, vec3(1.0f, 0.0f, 0.0f)); + + vertices->set(3, vec3(0.0f, 0.0f, leadingEdgeGradient)); + vertices->set(4, vec3(1.0f, 0.0f, 0.0f)); + vertices->set(5, vec3(1.0f, 1.0f, leadingEdgeGradient)); } if (!draw) - draw = Draw::create(4, num_quads, 0, 0); + draw = Draw::create(6, num_quads, 0, 0); else draw->instanceCount = num_quads; @@ -223,14 +324,6 @@ void GpuLayoutTechnique::setup(Text* text, uint32_t minimumAllocation, ref_ptrassignDescriptor("textLayout", layoutValue); config->assignDescriptor("text", textArray); - // Set the InputAssemblyState.topology - struct SetPipelineStates : public Visitor - { - void apply(Object& object) override { object.traverse(*this); } - void apply(InputAssemblyState& ias) override { ias.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; } - }; - vsg::visit(config); - if (sharedObjects) sharedObjects->share(config, [](auto gpc) { gpc->init(); }); else @@ -245,6 +338,9 @@ void GpuLayoutTechnique::setup(Text* text, uint32_t minimumAllocation, ref_ptraddChild(bindVertexBuffers); drawCommands->addChild(draw); stateGroup->addChild(drawCommands); + + // Assign ArrayState for CPU mapping of vertices + stateGroup->prototypeArrayState = GpuLayoutTechniqueArrayState::create(this, text, billboard); } else { From 8fd6cd0725735f9a174211750313ce022dcf9462 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 31 Oct 2024 17:34:17 +0100 Subject: [PATCH 4/5] PolytopeIntersector push matrices --- src/vsg/utils/PolytopeIntersector.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vsg/utils/PolytopeIntersector.cpp b/src/vsg/utils/PolytopeIntersector.cpp index c61b7464b..84c963a07 100644 --- a/src/vsg/utils/PolytopeIntersector.cpp +++ b/src/vsg/utils/PolytopeIntersector.cpp @@ -236,6 +236,10 @@ PolytopeIntersector::PolytopeIntersector(const Camera& camera, double xMin, doub } _polytopeStack.push_back(worldspace); + + dmat4 eyeToWorld = inverse(viewMatrix); + localToWorldStack().push_back(viewMatrix); + worldToLocalStack().push_back(eyeToWorld); } PolytopeIntersector::Intersection::Intersection(const dvec3& in_localIntersection, const dvec3& in_worldIntersection, const dmat4& in_localToWorld, const NodePath& in_nodePath, const DataList& in_arrays, const std::vector& in_indices, uint32_t in_instanceIndex) : From 679dabab131c3bee3a4e2a2d55c46f33eaca5400 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 1 Nov 2024 11:45:03 +0100 Subject: [PATCH 5/5] Remove index check from intersectDraw() --- src/vsg/utils/PolytopeIntersector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vsg/utils/PolytopeIntersector.cpp b/src/vsg/utils/PolytopeIntersector.cpp index 84c963a07..644896d98 100644 --- a/src/vsg/utils/PolytopeIntersector.cpp +++ b/src/vsg/utils/PolytopeIntersector.cpp @@ -309,7 +309,7 @@ bool PolytopeIntersector::intersectDraw(uint32_t firstVertex, uint32_t vertexCou auto& arrayState = *arrayStateStack.back(); vsg::PrimitiveFunctor printPrimitives(*this, arrayState, _polytopeStack.back()); - if (ushort_indices) printPrimitives.draw(arrayState.topology, firstVertex, vertexCount, firstInstance, instanceCount); + printPrimitives.draw(arrayState.topology, firstVertex, vertexCount, firstInstance, instanceCount); return intersections.size() != previous_size; }