vsg  1.1.0
VulkanSceneGraph library
State.h
1 #pragma once
2 
3 /* <editor-fold desc="MIT License">
4 
5 Copyright(c) 2018 Robert Osfield
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 
9 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12 
13 </editor-fold> */
14 
15 #include <vsg/maths/plane.h>
16 #include <vsg/maths/transform.h>
17 #include <vsg/nodes/MatrixTransform.h>
18 #include <vsg/state/PushConstants.h>
19 #include <vsg/vk/CommandBuffer.h>
20 
21 #include <array>
22 #include <map>
23 #include <stack>
24 
25 namespace vsg
26 {
27 
28 #define POLYTOPE_SIZE 5
29 
31  template<class T>
32  class StateStack
33  {
34  public:
35  StateStack() :
36  dirty(false) {}
37 
38  using Stack = std::stack<ref_ptr<const T>>;
39  Stack stack;
40  bool dirty;
41 
42  template<class R>
43  inline void push(ref_ptr<R> value)
44  {
45  stack.push(value);
46  dirty = true;
47  }
48 
49  template<class R>
50  inline void push(R* value)
51  {
52  stack.push(ref_ptr<const T>(value));
53  dirty = true;
54  }
55 
56  inline void pop()
57  {
58  stack.pop();
59  dirty = !stack.empty();
60  }
61  size_t size() const { return stack.size(); }
62  const T* top() const { return stack.top(); }
63 
64  inline void record(CommandBuffer& commandBuffer)
65  {
66  if (dirty)
67  {
68  stack.top()->record(commandBuffer);
69  dirty = false;
70  }
71  }
72  };
73 
76  {
77  public:
78  MatrixStack(uint32_t in_offset = 0) :
79  offset(in_offset)
80  {
81  // make sure there is an initial matrix
82  matrixStack.emplace(dmat4());
83  dirty = true;
84  }
85 
86  using value_type = double;
87 
88  std::stack<dmat4> matrixStack;
89  uint32_t offset = 0;
90  bool dirty = false;
91 
92  inline void set(const mat4& matrix)
93  {
94  matrixStack = {};
95  matrixStack.emplace(matrix);
96  dirty = true;
97  }
98 
99  inline void set(const dmat4& matrix)
100  {
101  matrixStack = {};
102  matrixStack.emplace(matrix);
103  dirty = true;
104  }
105 
106  inline void push(const mat4& matrix)
107  {
108  matrixStack.emplace(matrix);
109  dirty = true;
110  }
111  inline void push(const dmat4& matrix)
112  {
113  matrixStack.emplace(matrix);
114  dirty = true;
115  }
116  inline void push(const Transform& transform)
117  {
118  matrixStack.emplace(transform.transform(matrixStack.top()));
119  dirty = true;
120  }
121 
122  inline void push(const MatrixTransform& transform)
123  {
124  matrixStack.emplace(matrixStack.top() * transform.matrix);
125  dirty = true;
126  }
127 
128  const dmat4& top() const { return matrixStack.top(); }
129 
130  inline void pop()
131  {
132  matrixStack.pop();
133  dirty = true;
134  }
135 
136  inline void record(CommandBuffer& commandBuffer)
137  {
138  if (dirty)
139  {
140  auto pipeline = commandBuffer.getCurrentPipelineLayout();
141  auto stageFlags = commandBuffer.getCurrentPushConstantStageFlags();
142 
143  // don't attempt to push matrices if no pipeline is current or no stages are enabled for push constants
144  if (pipeline == 0 || stageFlags == 0)
145  {
146  return;
147  }
148 
149  // make sure matrix is a float matrix.
150  mat4 newmatrix(matrixStack.top());
151  vkCmdPushConstants(commandBuffer, pipeline, stageFlags, offset, sizeof(newmatrix), newmatrix.data());
152  dirty = false;
153  }
154  }
155  };
156 
158  struct Frustum
159  {
160  using value_type = MatrixStack::value_type;
161  using Plane = t_plane<value_type>;
162  using Vector = t_vec4<value_type>;
163  Plane face[POLYTOPE_SIZE];
164  Vector lodScale;
165 
166  Frustum()
167  {
168  face[0].set(1.0, 0.0, 0.0, 1.0); // left plane
169  face[1].set(-1.0, 0.0, 0.0, 1.0); // right plane
170  face[2].set(0.0, -1.0, 0.0, 1.0); // bottom plane
171  face[3].set(0.0, 1.0, 0.0, 1.0); // top plane
172  if constexpr (POLYTOPE_SIZE >= 5) face[4].set(0.0, 0.0, 1.0, 0.0); // far plane
173  if constexpr (POLYTOPE_SIZE >= 6) face[5].set(0.0, 0.0, -1.0, 1.0); // near plane
174  }
175 
176  template<class M>
177  Frustum(const Frustum& pt, const M& matrix)
178  {
179  face[0] = pt.face[0] * matrix;
180  face[1] = pt.face[1] * matrix;
181  face[2] = pt.face[2] * matrix;
182  face[3] = pt.face[3] * matrix;
183  if constexpr (POLYTOPE_SIZE >= 5) face[4] = pt.face[4] * matrix;
184  if constexpr (POLYTOPE_SIZE >= 6) face[5] = pt.face[5] * matrix;
185  }
186 
187  template<class M>
188  void set(const Frustum& pt, const M& matrix)
189  {
190  face[0] = pt.face[0] * matrix;
191  face[1] = pt.face[1] * matrix;
192  face[2] = pt.face[2] * matrix;
193  face[3] = pt.face[3] * matrix;
194  if constexpr (POLYTOPE_SIZE >= 5) face[4] = pt.face[4] * matrix;
195  if constexpr (POLYTOPE_SIZE >= 6) face[5] = pt.face[5] * matrix;
196  }
197 
198  template<class M>
199  void computeLodScale(const M& proj, const M& mv)
200  {
201  value_type f = -proj[1][1];
202  value_type sc = f * std::sqrt(square(mv[0][0]) + square(mv[1][0]) + square(mv[2][0]) + square(mv[0][1]) + square(mv[1][1]) + square(mv[2][1])) * 0.5;
203  value_type inv_scale = value_type(1.0) / sc;
204  lodScale.set(mv[0][2] * inv_scale,
205  mv[1][2] * inv_scale,
206  mv[2][2] * inv_scale,
207  mv[3][2] * inv_scale);
208  }
209 
210  template<typename T>
211  bool intersect(const t_sphere<T>& s) const
212  {
213  auto negative_radius = -s.radius;
214  if (distance(face[0], s.center) < negative_radius) return false;
215  if (distance(face[1], s.center) < negative_radius) return false;
216  if (distance(face[2], s.center) < negative_radius) return false;
217  if (distance(face[3], s.center) < negative_radius) return false;
218  if constexpr (POLYTOPE_SIZE >= 5)
219  if (distance(face[4], s.center) < negative_radius) return false;
220  if constexpr (POLYTOPE_SIZE >= 6)
221  if (distance(face[5], s.center) < negative_radius) return false;
222  return true;
223  }
224  };
225 
227  class State : public Inherit<Object, State>
228  {
229  public:
230  explicit State(uint32_t maxSlot) :
231  dirty(false),
232  stateStacks(maxSlot + 1)
233  {
234  }
235 
236  using StateStacks = std::vector<StateStack<StateCommand>>;
237 
238  ref_ptr<CommandBuffer> _commandBuffer;
239 
240  Frustum _frustumUnit;
241  Frustum _frustumProjected;
242 
243  using FrustumStack = std::stack<Frustum>;
244  FrustumStack _frustumStack;
245 
246  bool dirty = true;
247 
248  bool inheritViewForLODScaling = false;
249  dmat4 inheritedProjectionMatrix;
250  dmat4 inheritedViewMatrix;
251  dmat4 inheritedViewTransform;
252 
253  StateStacks stateStacks;
254 
255  MatrixStack projectionMatrixStack{0};
256  MatrixStack modelviewMatrixStack{64};
257 
258  void setInhertiedViewProjectionAndViewMatrix(const dmat4& projMatrix, const dmat4& viewMatrix)
259  {
260  inheritedProjectionMatrix = projMatrix;
261  inheritedViewMatrix = viewMatrix;
262  }
263 
264  void setProjectionAndViewMatrix(const dmat4& projMatrix, const dmat4& viewMatrix)
265  {
266  projectionMatrixStack.set(projMatrix);
267 
268  const auto& proj = projectionMatrixStack.top();
269 
270  _frustumProjected.set(_frustumUnit, proj);
271 
272  modelviewMatrixStack.set(viewMatrix);
273 
274  // clear frustum stack
275  while (!_frustumStack.empty()) _frustumStack.pop();
276 
277  if (inheritViewForLODScaling)
278  {
279  inheritedViewTransform = inheritedViewMatrix * inverse(viewMatrix);
280  }
281 
282  // push frustum in world coords
283  pushFrustum();
284  }
285 
286  inline void record()
287  {
288  if (dirty)
289  {
290  for (auto& stateStack : stateStacks)
291  {
292  stateStack.record(*_commandBuffer);
293  }
294 
295  projectionMatrixStack.record(*_commandBuffer);
296  modelviewMatrixStack.record(*_commandBuffer);
297 
298  dirty = false;
299  }
300  }
301 
302  inline void pushFrustum()
303  {
304  _frustumStack.push(Frustum(_frustumProjected, modelviewMatrixStack.top()));
305  if (inheritViewForLODScaling)
306  _frustumStack.top().computeLodScale(inheritedProjectionMatrix, inheritedViewTransform * modelviewMatrixStack.top());
307  else
308  _frustumStack.top().computeLodScale(projectionMatrixStack.top(), modelviewMatrixStack.top());
309  }
310 
311  inline void applyFrustum()
312  {
313  _frustumStack.top().set(_frustumProjected, modelviewMatrixStack.top());
314  _frustumStack.top().computeLodScale(projectionMatrixStack.top(), modelviewMatrixStack.top());
315  }
316 
317  inline void popFrustum()
318  {
319  _frustumStack.pop();
320  }
321 
322  template<typename T>
323  bool intersect(const t_sphere<T>& s) const
324  {
325  return _frustumStack.top().intersect(s);
326  }
327 
328  template<typename T>
329  T lodDistance(const t_sphere<T>& s) const
330  {
331  const auto& frustum = _frustumStack.top();
332  if (!frustum.intersect(s)) return -1.0;
333 
334  const auto& lodScale = frustum.lodScale;
335  return std::abs(lodScale[0] * s.x + lodScale[1] * s.y + lodScale[2] * s.z + lodScale[3]);
336  }
337  };
338 
339 } // namespace vsg
CommandBuffer encapsulates VkCommandBuffer.
Definition: CommandBuffer.h:27
Definition: Inherit.h:28
MatrixStack used internally by vsg::State to manage stack of projection or modelview matrices.
Definition: State.h:76
Definition: MatrixTransform.h:24
StateStack used internally by vsg::State to manage stack of vsg::StateCommand.
Definition: State.h:33
vsg::State is used by vsg::RecordTraversal to manage state stacks, projection and modelview matrices ...
Definition: State.h:228
Transform node is a pure virtual base class for positioning/scaling/rotating subgraphs.
Definition: Transform.h:22
Definition: ref_ptr.h:22
Frustum used internally by vsg::State to manage view fustum culling during vsg::RecordTraversal.
Definition: State.h:159
template sphere class
Definition: sphere.h:34