]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/guiScene.cpp
Revert "GUIFormSpecMenu: Shift+Click listring workaround for MacOS"
[dragonfireclient.git] / src / gui / guiScene.cpp
1 /*
2 Minetest
3 Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "guiScene.h"
21
22 #include <SViewFrustum.h>
23 #include <IAnimatedMeshSceneNode.h>
24 #include <ILightSceneNode.h>
25 #include "porting.h"
26
27 GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr,
28                    gui::IGUIElement *parent, core::recti rect, s32 id)
29         : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rect)
30 {
31         m_driver = env->getVideoDriver();
32         m_smgr = smgr->createNewSceneManager(false);
33
34         m_cam = m_smgr->addCameraSceneNode(0, v3f(0.f, 0.f, -100.f), v3f(0.f));
35         m_cam->setFOV(30.f * core::DEGTORAD);
36
37         scene::ILightSceneNode *light = m_smgr->addLightSceneNode(m_cam);
38         light->setRadius(1000.f);
39
40         m_smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
41 }
42
43 GUIScene::~GUIScene()
44 {
45         setMesh(nullptr);
46
47         m_smgr->drop();
48 }
49
50 scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh)
51 {
52         if (m_mesh) {
53                 m_mesh->remove();
54                 m_mesh = nullptr;
55         }
56
57         if (!mesh)
58                 return nullptr;
59
60         m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh);
61         m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter());
62         m_mesh->animateJoints();
63         return m_mesh;
64 }
65
66 void GUIScene::setTexture(u32 idx, video::ITexture *texture)
67 {
68         video::SMaterial &material = m_mesh->getMaterial(idx);
69         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
70         material.MaterialTypeParam = 0.5f;
71         material.TextureLayer[0].Texture = texture;
72         material.setFlag(video::EMF_LIGHTING, false);
73         material.setFlag(video::EMF_FOG_ENABLE, true);
74         material.setFlag(video::EMF_BILINEAR_FILTER, false);
75         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
76 }
77
78 void GUIScene::draw()
79 {
80         // Control rotation speed based on time
81         u64 new_time = porting::getTimeMs();
82         u64 dtime_ms = 0;
83         if (m_last_time != 0)
84                 dtime_ms = porting::getDeltaMs(m_last_time, new_time);
85         m_last_time = new_time;
86
87         core::rect<s32> oldViewPort = m_driver->getViewPort();
88         m_driver->setViewPort(getAbsoluteClippingRect());
89         core::recti borderRect = Environment->getRootGUIElement()->getAbsoluteClippingRect();
90
91         if (m_bgcolor != 0) {
92                 Environment->getSkin()->draw3DSunkenPane(
93                         this, m_bgcolor, false, true, borderRect, 0);
94         }
95
96         core::dimension2d<s32> size = getAbsoluteClippingRect().getSize();
97         m_smgr->getActiveCamera()->setAspectRatio((f32)size.Width / (f32)size.Height);
98
99         if (!m_target) {
100                 updateCamera(m_smgr->addEmptySceneNode());
101                 rotateCamera(v3f(0.f));
102                 m_cam->bindTargetAndRotation(true);
103         }
104
105         cameraLoop();
106
107         // Continuous rotation
108         if (m_inf_rot)
109                 rotateCamera(v3f(0.f, -0.03f * (float)dtime_ms, 0.f));
110
111         m_smgr->drawAll();
112
113         if (m_initial_rotation && m_mesh) {
114                 rotateCamera(v3f(m_custom_rot.X, m_custom_rot.Y, 0.f));
115                 calcOptimalDistance();
116
117                 m_initial_rotation = false;
118         }
119
120         m_driver->setViewPort(oldViewPort);
121 }
122
123 bool GUIScene::OnEvent(const SEvent &event)
124 {
125         if (m_mouse_ctrl && event.EventType == EET_MOUSE_INPUT_EVENT) {
126                 if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
127                         m_last_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y);
128                         return true;
129                 } else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) {
130                         if (event.MouseInput.isLeftPressed()) {
131                                 m_curr_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y);
132
133                                 rotateCamera(v3f(
134                                         m_last_pos.Y - m_curr_pos.Y,
135                                         m_curr_pos.X - m_last_pos.X, 0.f));
136
137                                 m_last_pos = m_curr_pos;
138                                 return true;
139                         }
140                 }
141         }
142
143         return gui::IGUIElement::OnEvent(event);
144 }
145
146 void GUIScene::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles)
147 {
148         StyleSpec::State state = StyleSpec::STATE_DEFAULT;
149         StyleSpec style = StyleSpec::getStyleFromStatePropagation(styles, state);
150
151         setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
152         setBackgroundColor(style.getColor(StyleSpec::BGCOLOR, m_bgcolor));
153 }
154
155 /* Camera control functions */
156
157 inline void GUIScene::calcOptimalDistance()
158 {
159         core::aabbox3df box = m_mesh->getBoundingBox();
160         f32 width  = box.MaxEdge.X - box.MinEdge.X;
161         f32 height = box.MaxEdge.Y - box.MinEdge.Y;
162         f32 depth  = box.MaxEdge.Z - box.MinEdge.Z;
163         f32 max_width = width > depth ? width : depth;
164
165         const scene::SViewFrustum *f = m_cam->getViewFrustum();
166         f32 cam_far = m_cam->getFarValue();
167         f32 far_width = core::line3df(f->getFarLeftUp(), f->getFarRightUp()).getLength();
168         f32 far_height = core::line3df(f->getFarLeftUp(), f->getFarLeftDown()).getLength();
169
170         core::recti rect = getAbsolutePosition();
171         f32 zoomX = rect.getWidth() / max_width;
172         f32 zoomY = rect.getHeight() / height;
173         f32 dist;
174
175         if (zoomX < zoomY)
176                 dist = (max_width / (far_width / cam_far)) + (0.5f * max_width);
177         else
178                 dist = (height / (far_height / cam_far)) + (0.5f * max_width);
179
180         m_cam_distance = dist;
181         m_update_cam = true;
182 }
183
184 void GUIScene::updateCamera(scene::ISceneNode *target)
185 {
186         m_target = target;
187         updateTargetPos();
188
189         m_last_target_pos = m_target_pos;
190         updateCameraPos();
191
192         m_update_cam = true;
193 }
194
195 void GUIScene::updateTargetPos()
196 {
197         m_last_target_pos = m_target_pos;
198         m_target->updateAbsolutePosition();
199         m_target_pos = m_target->getAbsolutePosition();
200 }
201
202 void GUIScene::setCameraRotation(v3f rot)
203 {
204         correctBounds(rot);
205
206         core::matrix4 mat;
207         mat.setRotationDegrees(rot);
208
209         m_cam_pos = v3f(0.f, 0.f, m_cam_distance);
210         mat.rotateVect(m_cam_pos);
211
212         m_cam_pos += m_target_pos;
213         m_cam->setPosition(m_cam_pos);
214         m_update_cam = false;
215 }
216
217 bool GUIScene::correctBounds(v3f &rot)
218 {
219         const float ROTATION_MAX_1 = 60.0f;
220         const float ROTATION_MAX_2 = 300.0f;
221
222         // Limit and correct the rotation when needed
223         if (rot.X < 90.f) {
224                 if (rot.X > ROTATION_MAX_1) {
225                         rot.X = ROTATION_MAX_1;
226                         return true;
227                 }
228         } else if (rot.X < ROTATION_MAX_2) {
229                 rot.X = ROTATION_MAX_2;
230                 return true;
231         }
232
233         // Not modified
234         return false;
235 }
236
237 void GUIScene::cameraLoop()
238 {
239         updateCameraPos();
240         updateTargetPos();
241
242         if (m_target_pos != m_last_target_pos)
243                 m_update_cam = true;
244
245         if (m_update_cam) {
246                 m_cam_pos = m_target_pos + (m_cam_pos - m_target_pos).normalize() * m_cam_distance;
247
248                 v3f rot = getCameraRotation();
249                 if (correctBounds(rot))
250                         setCameraRotation(rot);
251
252                 m_cam->setPosition(m_cam_pos);
253                 m_cam->setTarget(m_target_pos);
254
255                 m_update_cam = false;
256         }
257 }