Gamp v0.0.7-54-gccdc599
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
Scene.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com> (C++, Java) and Rami Santina (Java)
3 * Copyright Gothel Software e.K. and the authors
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11#ifndef JAU_GAMP_GRAPH_UI_SCENE_HPP_
12#define JAU_GAMP_GRAPH_UI_SCENE_HPP_
13
14#include <jau/basic_types.hpp>
15#include <jau/darray.hpp>
16#include <jau/float_math.hpp>
17#include <jau/float_types.hpp>
18#include <jau/fraction_type.hpp>
21#include <jau/math/vec2i.hpp>
22#include <jau/math/vec3f.hpp>
23#include <jau/math/vec4f.hpp>
24#include <jau/math/vec4f.hpp>
26
27#include <gamp/Gamp.hpp>
33
34namespace gamp::graph::ui {
35
36 using namespace jau::math;
37 using namespace jau::math::util;
38 using namespace jau::math::geom;
39
40 using namespace gamp::wt;
41 using namespace gamp::wt::event;
42
43 using namespace gamp::graph;
44
45 /** @defgroup Gamp_GraphUI Gamp Graph UI
46 * Graph UI Framework
47 *
48 * @{
49 */
50
51 class Scene {
52 private:
53 ShapeRef m_activeShape;
54
55 public:
57 { }
58
59 private:
60 /**
61 * General {@link Shape} visitor
62 * @param s the {@link Shape} to process
63 * @param pmv the {@link PMVMatrix4f} setup from the {@link Scene} down to the {@link Shape}
64 * @return the picked shape signaling end of traversal, otherwise null to continue
65 */
66 typedef jau::function<ShapeRef(ShapeRef& s, PMVMat4f& pmv)> PickVisitorFunc;
67
68 static ShapeRef pickForAllRenderedDesc(Container& cont, PMVMat4f& pmv, const PickVisitorFunc& visitor) {
69 ShapeRef picked = nullptr;
70 ShapeList& shapes = cont.renderedShapes();
71 // synchronized( shapes ) { // tripple-buffering is just almost enough FIXME
72 for(size_t i=shapes.size(); !picked && i-- > 0; ) {
73 ShapeRef& s = shapes[i];
74 pmv.pushMv();
75 s->applyMatToMv(pmv);
76 picked = visitor(s, pmv);
77 if( s->asContainer() ) {
78 ShapeRef childPick = pickForAllRenderedDesc(*s->asContainer(), pmv, visitor);
79 if( childPick ) {
80 picked = childPick; // child picked overrides group parent!
81 }
82 }
83 pmv.popMv();
84 }
85 // }
86 return picked;
87 }
88
89 /**
90 * Attempt to pick a {@link Shape} using the window coordinates and contained {@ling Shape}'s {@link AABBox} {@link Shape#getBounds() bounds}
91 * using a ray-intersection algorithm in Z-axis descending order.
92 * <p>
93 * If {@link Shape} was found the given action is performed.
94 * </p>
95 * <p>
96 * Method performs on current thread and returns after either a shaper is determined to be picked/active or
97 * probing every {@link Shape} w/o result.
98 * </p>
99 * @param pmv a new {@link PMVMatrix4f} which will {@link Scene.PMVMatrixSetup#set(PMVMatrix4f, Recti) be setup},
100 * {@link Shape#applyMatToMv(PMVMatrix4f) shape-transformed} and can be reused by the caller and runnable.
101 * @param ray temporary {@link Ray} storage, passed for reusage
102 * @param glWinX window X coordinate, bottom-left origin
103 * @param glWinY window Y coordinate, bottom-left origin
104 * @param objPos storage for found object position in model-space if found {@link Shape}
105 * @param runnable the action to perform if {@link Shape} was found
106 * @return last picked (inner) Shape if any or null
107 */
108 ShapeRef pickShape(PMVMat4f& pmv, const Vec2i& winPos, Vec3f& objPos, final Shape.Visitor1 visitor) {
109 setupMatrix(pmv);
110
111 final float winZ0 = 0f;
112 final float winZ1 = 0.3f;
113 /**
114 final FloatBuffer winZRB = Buffers.newDirectFloatBuffer(1);
115 gl.glReadPixels( x, y, 1, 1, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_FLOAT, winZRB);
116 winZ1 = winZRB.get(0); // dir
117 */
118 final Recti viewport = getViewport();
119 final int[] shapeIdx = { -1 };
120 return pickForAllRenderedDesc(this, pmv, (final Shape s, final PMVMatrix4f pmv2) -> {
121 shapeIdx[0]++;
122 if( pmv.mapWinToRay(glWinX, glWinY, winZ0, winZ1, viewport, ray) ) {
123 final AABBox sbox = s.getBounds();
124 if( sbox.intersectsRay(ray) ) {
125 if( null == sbox.getRayIntersection(objPos, ray, FloatUtil.EPSILON, true) ) {
126 throw new InternalError("Ray "+ray+", box "+sbox);
127 }
128 if( visitor.visit(s) ) {
129 return s;
130 }
131 }
132 }
133 return null;
134 });
135 }
136 /**
137 * Pick the shape using the event coordinates
138 * @param e original Newt {@link MouseEvent}
139 * @param glWinX in GL window coordinates, origin bottom-left
140 * @param glWinY in GL window coordinates, origin bottom-left
141 */
142 private final Shape dispatchMouseEventPickShape(final MouseEvent e, final int glWinX, final int glWinY) {
143 final Shape shape = pickShape(dispMEPSPMv, dispMEPSRay, glWinX, glWinY, dispMEPSObjPos, (final Shape s) -> {
144 // return s.isInteractive() && ( s.dispatchMouseEvent(e, glWinX, glWinY, dispMEPSObjPos) || true );
145 if( !s.isInteractive() ) {
146 if( DEBUG_PICKING ) {
147 System.err.printf("Pick.X.0: shape %s/%s, [%d, %d]%n", s.getClass().getSimpleName(), s.getName(), glWinX, glWinY);
148 }
149 return false;
150 }
151 if( !s.dispatchMouseEvent(e, glWinX, glWinY, dispMEPSObjPos) ) {
152 if( DEBUG_PICKING ) {
153 System.err.printf("Pick.X.1: shape %s/%s, [%d, %d], %s%n", s.getClass().getSimpleName(), s.getName(), glWinX, glWinY, e);
154 }
155 return false;
156 }
157 if( DEBUG_PICKING ) {
158 System.err.printf("Pick.X.S: shape %s/%s, [%d, %d], %s%n", s.getClass().getSimpleName(), s.getName(), glWinX, glWinY, e);
159 }
160 return true;
161 });
162 if( null != shape ) {
163 if( DEBUG_PICKING ) {
164 System.err.printf("Pick.X: shape %s/%s%n%n", shape.getClass().getSimpleName(), shape.getName());
165 }
166 setActiveShape(shape);
167 return shape;
168 } else {
169 if( DEBUG_PICKING ) {
170 System.err.printf("Pick.X: shape null%n%n");
171 }
172 releaseActiveShape();
173 return null;
174 }
175 }
176 private final PMVMatrix4f dispMEPSPMv = new PMVMatrix4f();
177 private final Ray dispMEPSRay = new Ray();
178 private final Vec3f dispMEPSObjPos = new Vec3f();
179
180 /**
181 * Dispatch event to shape
182 * @param shape target active shape of event
183 * @param e original Newt {@link MouseEvent}
184 * @param glWinX in GL window coordinates, origin bottom-left
185 * @param glWinY in GL window coordinates, origin bottom-left
186 */
187 private final void dispatchMouseEventForShape(final Shape shape, final MouseEvent e, final int glWinX, final int glWinY) {
188 final PMVMatrix4f pmv = new PMVMatrix4f();
189 final Vec3f objPos = new Vec3f();
190 winToShapeCoord(shape, glWinX, glWinY, pmv, objPos, () -> { shape.dispatchMouseEvent(e, glWinX, glWinY, objPos); });
191 if( DEBUG_PICKING ) {
192 System.err.printf("ForShape: shape %s/%s%n%n", shape.getClass().getSimpleName(), shape.getName());
193 }
194 }
195
196 class SBCPointerListener : public PointerListener {
197 private:
198 Scene& scene;
199 Vec2i lastPos;
200 int lastId;
201 bool mouseOver;
202
203 void clear() {
204 lastPos.x = -1; lastPos.y = -1; lastId = -1; mouseOver = false;
205 }
206 const ShapeRef& dispatchPickShape(const PointerEvent& e) {
207 const ShapeRef s = dispatchMouseEventPickShape(e);
208 if( !s ) {
209 clear();
210 }
211 return s;
212 }
213 public:
214 SBCPointerListener(Scene& p): scene(p) { clear(); }
215
216 void pointerPressed(PointerEvent& e) override {
217 if( -1 == lastId || e.pointerId() == lastId ) {
218 lastPos = e.position();
219 lastId = e.pointerId();
220 }
221 // Can't use selected activeShape via mouseOver,
222 // since mouseMove and mousePressed/Drag or mouseClicked may-shall select a different shape
223 // based on draggable. mouseMove simply activates any shape.
224 dispatchPickShape(e);
225 }
226
227 void pointerReleased(PointerEvent& e) override {
228 if( mouseOver && scene.m_activeShape &&
229 scene.m_activeShape.isInteractive() &&
230 // !pinchToZoomGesture.isWithinGesture() &&
231 e.pointerId() == lastId )
232 {
233 scene.dispatchMouseEventForShape(scene.m_activeShape, e);
234 } else {
235 dispatchPickShape(e);
236 }
237 if( !mouseOver && 1 == e.pointerCount() ) {
238 // Release active shape: last pointer has been lifted!
239 scene.releaseActiveShape();
240 clear();
241 }
242 }
243
244 void pointerClicked(PointerEvent& e) override {
245 // Can't use selected activeShape via mouseOver,
246 // since mouseMove and mousePressed/Drag or mouseClicked may-shall select a different shape
247 // based on draggable. mouseMove simply activates any shape.
248 dispatchPickShape(e);
249 }
250
251 void pointerDragged(PointerEvent& e) override {
252 scene.clearToolTip();
253 // drag activeShape, if no gesture-activity, only on 1st pointer
254 if( scene.m_activeShape &&
255 scene.m_activeShape.isInteractive() &&
256 // !pinchToZoomGesture.isWithinGesture() &&
257 e.pointerId() == lastId )
258 {
259 lastPos = e.position();
260 scene.dispatchMouseEventForShape(scene.m_activeShape, e);
261 }
262 }
263
264 void pointerWheelMoved(PointerEvent&) override {
265 scene.clearToolTip();
266 if( mouseOver && scene.m_activeShape &&
267 scene.m_activeShape.isInteractive() &&
268 // !pinchToZoomGesture.isWithinGesture() &&
269 e.pointerId() == lastId ) {
270 scene.dispatchMouseEventForShape(scene.m_activeShape, e);
271 } else {
272 dispatchPickShape(e);
273 }
274 }
275
276 void pointerMoved(PointerEvent&) override {
277 if( -1 == lastId || e.pointerId() == lastId ) {
278 lastPos = e.position();
279 lastId = e.getPointerId(0);
280 }
281 scene.clearToolTip();
282 const ShapeRef& s = dispatchPickShape(e);
283 if( s ) {
284 mouseOver = true;
285 // synchronized( toolTipActive ) {
286 // FIXME toolTipActive.set( s.startToolTip(true /* lookupParents */) );
287 // }
288 }
289 }
290 void pointerExited(PointerEvent&) override {
291 scene.clearToolTip();
292 scene.releaseActiveShape();
293 clear();
294 }
295 };
296
297 class SBCKeyListener : public KeyListener {
298 private:
299 Scene& m_parent;
300
301 public:
302 SBCKeyListener(Scene& p): m_parent(p) { }
303
304 void keyPressed(KeyEvent& e, const KeyboardTracker& kt) override {
305 if( m_parent.m_activeShape && m_parent.activeShape.isInteractive() ) {
306 m_parent.activeShape.dispatchKeyEvent(e, kt);
307 }
308 }
309 void keyReleased(KeyEvent& e, const KeyboardTracker& kt) override {
310 if( m_parent.m_activeShape && m_parent.activeShape.isInteractive() ) {
311 m_parent.activeShape.dispatchKeyEvent(e, kt);
312 }
313 }
314 };
315 typedef std::shared_ptr<SBCKeyListener> SBCKeyListenerRef;
316
317 };
318
319 /**@}*/
320
321} // namespace gamp::graph
322
323#endif /* JAU_GAMP_GRAPH_UI_SCENE_HPP_ */
Container interface of UI Shapes.
Definition Container.hpp:58
virtual ShapeList & renderedShapes() const noexcept=0
Returns added shapes which are rendered and sorted by z-axis in ascending order toward z-near.
constexpr int pointerId(size_t index=0) const noexcept
Return the pointer id for the given index or -1 if index not available.
constexpr const jau::math::Vec2i & position(size_t index=0) const noexcept
Returns position of given pointer-index in pixel units.
constexpr size_t pointerCount() const noexcept
See details for multiple-pointer events.
Class template jau::function is a general-purpose static-polymorphic function wrapper.
value_type x
Definition vec2i.hpp:63
constexpr_cxx20 PMVMatrix4 & popMv() noexcept
Pop the modelview matrix from its stack.
Definition pmvmat4.hpp:795
constexpr_cxx20 PMVMatrix4 & pushMv() noexcept
Push the modelview matrix to its stack, while preserving its values.
Definition pmvmat4.hpp:813
@ null
Denotes a func::null_target_t.
std::vector< ShapeRef > ShapeList
Definition Container.hpp:49
std::shared_ptr< Shape > ShapeRef
Definition Container.hpp:48
Vector2I< int > Vec2i
Definition vec2i.hpp:321
RectI< int > Recti
Definition recti.hpp:146
Vector3F< float > Vec3f
Definition vec3f.hpp:422
PMVMatrix4< float > PMVMat4f
Definition pmvmat4.hpp:1463
static std::string f(uint32_t v)