Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : /*
4 : : MIT License
5 : :
6 : : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
7 : :
8 : : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : of this software and associated documentation files (the "Software"), to deal
10 : : in the Software without restriction, including without limitation the rights
11 : : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : copies of the Software, and to permit persons to whom the Software is
13 : : furnished to do so, subject to the following conditions:
14 : :
15 : : The above copyright notice and this permission notice shall be included in all
16 : : copies or substantial portions of the Software.
17 : :
18 : : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : : SOFTWARE.
25 : : */
26 : :
27 : : // ezGL is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
28 : :
29 : : #include <map>
30 : : #include <string>
31 : : #include <memory>
32 : : #include "../ezScreen.hpp"
33 : :
34 : : namespace ez {
35 : : namespace gl {
36 : :
37 : : class Canvas {
38 : : public:
39 : : struct MouseDatas {
40 : : ez::fvec2 displayRect;
41 : : ez::fvec2 mousePos;
42 : : float mouseWheel{};
43 : : bool isPanButtonDown{};
44 : : };
45 : : struct Transform {
46 : : ez::fvec2 origin;
47 : : float scale{1.0f}; // px / unit� monde
48 : : float invScale{1.0f};
49 : : };
50 : : struct UniformTransform {
51 : : ez::fvec2 uScale{1.0f};
52 : : ez::fvec2 uOffset;
53 : : };
54 : :
55 : :
56 : : private:
57 : : bool m_isRenderingActive{true};
58 : : ez::fvec4 m_clearColor;
59 : :
60 : : ez::fvec4 m_displayRect;
61 : : ez::gl::FBOPipeLinePtr mp_fboPipeline;
62 : : ez::fvec2 m_fboSize;
63 : :
64 : : MouseDatas m_mouseDatas;
65 : : Transform m_transform;
66 : :
67 : : // Uniforms NDC pour le shader (uScale/uOffset)
68 : : UniformTransform m_uniformTransform;
69 : :
70 : : // Souris (interne)
71 : : ez::fvec2 m_lastMousePos;
72 : : bool m_isPanning{false};
73 : :
74 : : public:
75 : 0 : bool init(const ez::ivec4& vDisplayRect) {
76 : 0 : bool ret = true;
77 : 0 : m_displayRect = vDisplayRect;
78 : 0 : mp_fboPipeline = ez::gl::FBOPipeLine::create(vDisplayRect.z, vDisplayRect.w, 1, false, false);
79 : 0 : ret &= (mp_fboPipeline != nullptr);
80 : 0 : m_fboSize = m_displayRect.zw();
81 : 0 : return ret;
82 : 0 : }
83 : 0 : void unit() {}
84 : 0 : void startFrame(const MouseDatas& vMouseDatas) {
85 : 0 : if (m_mouseActions(vMouseDatas)) {
86 : 0 : m_computeTransform();
87 : 0 : }
88 : 0 : }
89 : :
90 : 0 : void endFrame() {}
91 : :
92 : 0 : bool resize(const ez::ivec4& vNewDisplayRect) {
93 : 0 : if (mp_fboPipeline->resize(vNewDisplayRect.z, vNewDisplayRect.w)) {
94 : 0 : m_displayRect = vNewDisplayRect;
95 : 0 : if (!m_fboSize.emptyOR()) {
96 : 0 : ez::fvec2 rescale = m_displayRect.zw() / m_fboSize;
97 : 0 : m_transform.origin *= rescale;
98 : 0 : m_transform.scale *= ez::mini(rescale.x, rescale.y);
99 : 0 : }
100 : 0 : m_fboSize = vNewDisplayRect.zw();
101 : 0 : m_computeTransform();
102 : 0 : return true;
103 : 0 : }
104 : 0 : return false;
105 : 0 : }
106 : :
107 : 0 : ez::fvec2 worldToLocal(const ez::fvec2& vWorldPos) const {
108 : 0 : return (vWorldPos - m_transform.origin) * m_transform.invScale;
109 : 0 : }
110 : :
111 : 0 : ez::fvec2 localToWorld(const ez::fvec2& vCanvasPos) const {
112 : 0 : return vCanvasPos * m_transform.scale + m_transform.origin;
113 : 0 : }
114 : :
115 : 0 : bool startOffscreenRender() {
116 : 0 : if (m_isRenderingActive) {
117 : 0 : clearBuffers(m_clearColor);
118 : 0 : if (mp_fboPipeline->bind()) {
119 : 0 : mp_fboPipeline->selectBuffers();
120 : 0 : return true;
121 : 0 : }
122 : 0 : }
123 : 0 : return false;
124 : 0 : }
125 : 0 : void endOffscreenRender() { mp_fboPipeline->unbind(); }
126 : :
127 : 0 : void blitOnScreen() {
128 : 0 : auto fbo_ptr = mp_fboPipeline->getFrontFBO().lock();
129 : 0 : if (fbo_ptr != nullptr) {
130 : 0 : fbo_ptr->blitOnScreen(
131 : 0 : m_displayRect.x, //
132 : 0 : m_displayRect.y, //
133 : 0 : m_displayRect.z, //
134 : 0 : m_displayRect.w, //
135 : 0 : 0,
136 : 0 : GL_COLOR_BUFFER_BIT,
137 : 0 : GL_NEAREST);
138 : 0 : }
139 : 0 : }
140 : 0 : void clearBuffers(const ez::fvec4& vColorPtr) { mp_fboPipeline->clearBuffer({vColorPtr.x, vColorPtr.y, vColorPtr.z, vColorPtr.w}); }
141 : 0 : bool drawUI() {
142 : 0 : #ifdef IMGUI_API
143 : 0 : #ifdef _DEBUG
144 : 0 : if (ImGui::CollapsingHeader("Debug##Renderer")) {
145 : 0 : ImGui::Text("Origin Px : %f,%f", m_transform.origin.x, m_transform.origin.y);
146 : 0 : ImGui::Text("FBO Size : %f,%f", m_fboSize.x, m_fboSize.y);
147 : 0 : ImGui::Text("Scale : %f", m_transform.scale);
148 : 0 : ImGui::Text("Sclae inv : %f", m_transform.invScale);
149 : 0 : ImGui::DragFloat2("Offset", &m_uniformTransform.uOffset.x);
150 : 0 : ImGui::DragFloat2("Scale", &m_uniformTransform.uScale.x);
151 : 0 : }
152 : 0 : #endif
153 : 0 : #endif
154 : 0 : return false;
155 : 0 : }
156 : :
157 : 0 : void updateTransform() { m_computeTransform(); }
158 : :
159 : 0 : void fitToContent(const ez::fAABB& vBoundingBox) {
160 : 0 : m_computeFitToContent( //
161 : 0 : vBoundingBox.lowerBound,
162 : 0 : vBoundingBox.upperBound,
163 : 0 : m_fboSize,
164 : 0 : m_transform.origin,
165 : 0 : m_transform.scale,
166 : 0 : m_transform.invScale);
167 : 0 : m_computeTransform();
168 : 0 : }
169 : :
170 : 0 : ez::fvec4& getBackgroundColorRef() { return m_clearColor; }
171 : 0 : const ez::fvec4& getBackgroundColor() const { return m_clearColor; }
172 : :
173 : 0 : MouseDatas& getMouseDatasRef() { return m_mouseDatas; }
174 : 0 : const MouseDatas& getMouseDatas() const { return m_mouseDatas; }
175 : :
176 : 0 : Transform& getTransfomRef() { return m_transform; }
177 : 0 : const Transform& getTransfom() const { return m_transform; }
178 : :
179 : 0 : UniformTransform& getUniformTransfomRef() { return m_uniformTransform; }
180 : 0 : const UniformTransform& getUniformTransfom() const { return m_uniformTransform; }
181 : :
182 : : private:
183 : 0 : bool m_mouseActions(const MouseDatas& vMouseDatas) {
184 : 0 : bool ret = false;
185 : 0 : const float steps = vMouseDatas.mouseWheel;
186 : 0 : if (fabsf(steps) > 0.0001f) {
187 : 0 : const float factor = powf(1.1f, steps);
188 : 0 : m_applyZoomAtMouse(factor, vMouseDatas.mousePos);
189 : 0 : ret = true;
190 : 0 : }
191 : 0 : if (vMouseDatas.isPanButtonDown) {
192 : 0 : const ez::fvec2 delta = vMouseDatas.mousePos - m_lastMousePos;
193 : 0 : m_isPanning = true;
194 : 0 : m_applyPanDrag(delta);
195 : 0 : ret = true;
196 : 0 : } else {
197 : 0 : m_isPanning = false;
198 : 0 : }
199 : 0 : m_lastMousePos = vMouseDatas.mousePos;
200 : 0 : return ret;
201 : 0 : }
202 : :
203 : 0 : void m_setScale(float v) {
204 : 0 : m_transform.scale = v;
205 : 0 : m_transform.invScale = (v != 0.0f) ? (1.0f / v) : 0.0f;
206 : 0 : }
207 : :
208 : 0 : void m_applyPanDrag(const ez::fvec2& dragPx) {
209 : 0 : m_transform.origin.x += dragPx.x;
210 : 0 : m_transform.origin.y += dragPx.y;
211 : 0 : }
212 : :
213 : 0 : void m_applyZoomAtMouse(float factor, const ez::fvec2& mousePx) {
214 : 0 : if (factor <= 0.0f) {
215 : 0 : return;
216 : 0 : }
217 : 0 : float newScale = m_transform.scale * factor;
218 : 0 : newScale = std::max(1e-6f, std::min(newScale, 1e6f));
219 : 0 : const float applied = (m_transform.scale > 0.0f) ? (newScale / m_transform.scale) : 1.0f;
220 : 0 : if (applied == 1.0f) {
221 : 0 : return;
222 : 0 : }
223 : 0 : m_transform.origin.x = m_transform.origin.x + (1.0f - applied) * (mousePx.x - m_transform.origin.x);
224 : 0 : m_transform.origin.y = m_transform.origin.y + (1.0f - applied) * (mousePx.y - m_transform.origin.y);
225 : 0 : m_setScale(newScale);
226 : 0 : }
227 : :
228 : : // computation of uScale/uOffset (NDC) for
229 : : // gl_Position = vec4(aPos * uScale + uOffset, 0, 1)
230 : 0 : void m_computeTransform() {
231 : 0 : const float VW = m_fboSize.x;
232 : 0 : const float VH = m_fboSize.y;
233 : 0 : if (VW <= 0.0f || VH <= 0.0f) {
234 : 0 : m_transform = {};
235 : 0 : return;
236 : 0 : }
237 : 0 : m_setScale(m_transform.scale);
238 : 0 : m_uniformTransform.uScale.x = (2.0f * m_transform.scale) / VW;
239 : 0 : m_uniformTransform.uOffset.x = (2.0f * m_transform.origin.x) / VW - 1.0f;
240 : 0 : // Y-down (framebuffer in top-left, classic UI)
241 : 0 : m_uniformTransform.uScale.y = -(2.0f * m_transform.scale) / VH;
242 : 0 : m_uniformTransform.uOffset.y = 1.0f - (2.0f * m_transform.origin.y) / VH;
243 : 0 : }
244 : :
245 : : // Fit "contain" d'un AABB monde dans un framebuffer en pixels.
246 : : // Mapping utilis� : screen = world * scale + originPx
247 : : void m_computeFitToContent(
248 : : const ez::fvec2& vWorldMin,
249 : : const ez::fvec2& vWorldMax,
250 : : const ez::fvec2& vFramebufferSizePx,
251 : : ez::fvec2& voOriginPx,
252 : : float& voScale,
253 : 0 : float& voInvScale) {
254 : 0 : const float W = vWorldMax.x - vWorldMin.x;
255 : 0 : const float H = vWorldMax.y - vWorldMin.y;
256 : 0 : const float VW = vFramebufferSizePx.x;
257 : 0 : const float VH = vFramebufferSizePx.y;
258 : 0 :
259 : 0 : if (W <= 0.0f || H <= 0.0f || VW <= 0.0f || VH <= 0.0f) {
260 : 0 : voOriginPx = {0.0f, 0.0f};
261 : 0 : voScale = 1.0f;
262 : 0 : voInvScale = 1.0f;
263 : 0 : return;
264 : 0 : }
265 : 0 :
266 : 0 : const float sx = VW / W;
267 : 0 : const float sy = VH / H;
268 : 0 : const float s = (sx < sy) ? sx : sy;
269 : 0 :
270 : 0 : const float padX = 0.5f * (VW - W * s);
271 : 0 : const float padY = 0.5f * (VH - H * s);
272 : 0 :
273 : 0 : voOriginPx.x = padX - vWorldMin.x * s;
274 : 0 : voOriginPx.y = padY - vWorldMin.y * s;
275 : 0 : voScale = s;
276 : 0 : voInvScale = (s != 0.0f) ? (1.0f / s) : 0.0f;
277 : 0 : }
278 : : };
279 : :
280 : : } // namespace gl
281 : : } // namespace ez
|