vsg  1.0.4
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(mat4());
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(CommandBuffer* commandBuffer, uint32_t maxSlot) :
231  _commandBuffer(commandBuffer),
232  dirty(false),
233  stateStacks(maxSlot + 1)
234  {
235  }
236 
237  using StateStacks = std::vector<StateStack<StateCommand>>;
238 
239  ref_ptr<CommandBuffer> _commandBuffer;
240 
241  Frustum _frustumUnit;
242  Frustum _frustumProjected;
243 
244  using FrustumStack = std::stack<Frustum>;
245  FrustumStack _frustumStack;
246 
247  bool dirty;
248 
249  StateStacks stateStacks;
250 
251  MatrixStack projectionMatrixStack{0};
252  MatrixStack modelviewMatrixStack{64};
253 
254  void setProjectionAndViewMatrix(const dmat4& projMatrix, const dmat4& viewMatrix)
255  {
256  projectionMatrixStack.set(projMatrix);
257 
258  const auto& proj = projectionMatrixStack.top();
259 
260  _frustumProjected.set(_frustumUnit, proj);
261 
262  modelviewMatrixStack.set(viewMatrix);
263 
264  // clear frustum stack
265  while (!_frustumStack.empty()) _frustumStack.pop();
266 
267  // push frustum in world coords
268  pushFrustum();
269  }
270 
271  inline void record()
272  {
273  if (dirty)
274  {
275  for (auto& stateStack : stateStacks)
276  {
277  stateStack.record(*_commandBuffer);
278  }
279 
280  projectionMatrixStack.record(*_commandBuffer);
281  modelviewMatrixStack.record(*_commandBuffer);
282 
283  dirty = false;
284  }
285  }
286 
287  inline void pushFrustum()
288  {
289  _frustumStack.push(Frustum(_frustumProjected, modelviewMatrixStack.top()));
290  _frustumStack.top().computeLodScale(projectionMatrixStack.top(), modelviewMatrixStack.top());
291  }
292 
293  inline void applyFrustum()
294  {
295  _frustumStack.top().set(_frustumProjected, modelviewMatrixStack.top());
296  _frustumStack.top().computeLodScale(projectionMatrixStack.top(), modelviewMatrixStack.top());
297  }
298 
299  inline void popFrustum()
300  {
301  _frustumStack.pop();
302  }
303 
304  template<typename T>
305  bool intersect(const t_sphere<T>& s) const
306  {
307  return _frustumStack.top().intersect(s);
308  }
309 
310  template<typename T>
311  T lodDistance(const t_sphere<T>& s) const
312  {
313  const auto& frustum = _frustumStack.top();
314  if (!frustum.intersect(s)) return -1.0;
315 
316  const auto& lodScale = frustum.lodScale;
317  return std::abs(lodScale[0] * s.x + lodScale[1] * s.y + lodScale[2] * s.z + lodScale[3]);
318  }
319  };
320 
321 } // namespace vsg
CommandBuffer encapsulates VkCommandBuffer.
Definition: CommandBuffer.h:27
Definition: Inherit.h:28
MatrixStack used internally by vsg::State to manage stack of project 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 used by vsg::RecordTraversal to manage state stacks, projection, modelview matrix and frus...
Definition: State.h:228
Transform node is a pure virtual base class for positioning/scaling/rotation 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