aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui/CMakeLists.txt1
-rw-r--r--src/gui/guiFormSpecMenu.cpp86
-rw-r--r--src/gui/guiFormSpecMenu.h2
-rw-r--r--src/gui/guiScene.cpp257
-rw-r--r--src/gui/guiScene.h85
5 files changed, 430 insertions, 1 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 147f445f4..5305e7ad3 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -15,6 +15,7 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index 7e3ad3b15..039b28e79 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -65,6 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiScrollContainer.h"
#include "intlGUIEditBox.h"
#include "guiHyperText.h"
+#include "guiScene.h"
#define MY_CHECKPOS(a,b) \
if (v_pos.size() != 2) { \
@@ -2695,6 +2696,86 @@ void GUIFormSpecMenu::parseSetFocus(const std::string &element)
<< "'" << std::endl;
}
+void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() < 5 || (parts.size() > 8 &&
+ m_formspec_version <= FORMSPEC_API_VERSION)) {
+ errorstream << "Invalid model element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+ return;
+ }
+
+ // Avoid length checks by resizing
+ if (parts.size() < 8)
+ parts.resize(8);
+
+ std::vector<std::string> v_pos = split(parts[0], ',');
+ std::vector<std::string> v_geom = split(parts[1], ',');
+ std::string name = unescape_string(parts[2]);
+ std::string meshstr = unescape_string(parts[3]);
+ std::vector<std::string> textures = split(parts[4], ',');
+ std::vector<std::string> vec_rot = split(parts[5], ',');
+ bool inf_rotation = is_yes(parts[6]);
+ bool mousectrl = is_yes(parts[7]) || parts[7].empty(); // default true
+
+ MY_CHECKPOS("model", 0);
+ MY_CHECKGEOM("model", 1);
+
+ v2s32 pos;
+ v2s32 geom;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(&v_pos);
+ geom.X = stof(v_geom[0]) * (float)imgsize.X;
+ geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+ }
+
+ if (!data->explicit_size)
+ warningstream << "invalid use of model without a size[] element" << std::endl;
+
+ scene::IAnimatedMesh *mesh = m_client->getMesh(meshstr);
+
+ if (!mesh) {
+ errorstream << "Invalid model element: Unable to load mesh:"
+ << std::endl << "\t" << meshstr << std::endl;
+ return;
+ }
+
+ FieldSpec spec(
+ name,
+ L"",
+ L"",
+ 258 + m_fields.size()
+ );
+
+ core::rect<s32> rect(pos, pos + geom);
+
+ GUIScene *e = new GUIScene(Environment, RenderingEngine::get_scene_manager(),
+ data->current_parent, rect, spec.fid);
+
+ auto meshnode = e->setMesh(mesh);
+
+ for (u32 i = 0; i < textures.size() && i < meshnode->getMaterialCount(); ++i)
+ e->setTexture(i, m_tsrc->getTexture(textures[i]));
+
+ if (vec_rot.size() >= 2)
+ e->setRotation(v2f(stof(vec_rot[0]), stof(vec_rot[1])));
+
+ e->enableContinuousRotation(inf_rotation);
+ e->enableMouseControl(mousectrl);
+
+ auto style = getStyleForElement("model", spec.fname);
+ e->setStyles(style);
+ e->drop();
+
+ m_fields.push_back(spec);
+}
+
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
{
//some prechecks
@@ -2891,6 +2972,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
+ if (type == "model") {
+ parseModel(data, description);
+ return;
+ }
+
// Ignore others
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
<< std::endl;
diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h
index 613acaa04..c5d662a69 100644
--- a/src/gui/guiFormSpecMenu.h
+++ b/src/gui/guiFormSpecMenu.h
@@ -38,7 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class InventoryManager;
class ISimpleTextureSource;
class Client;
-class TexturePool;
class GUIScrollContainer;
typedef enum {
@@ -444,6 +443,7 @@ private:
void parseAnchor(parserData *data, const std::string &element);
bool parseStyle(parserData *data, const std::string &element, bool style_type);
void parseSetFocus(const std::string &element);
+ void parseModel(parserData *data, const std::string &element);
void tryClose();
diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp
new file mode 100644
index 000000000..08f119e07
--- /dev/null
+++ b/src/gui/guiScene.cpp
@@ -0,0 +1,257 @@
+/*
+Minetest
+Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "guiScene.h"
+
+#include <SViewFrustum.h>
+#include <IAnimatedMeshSceneNode.h>
+#include <ILightSceneNode.h>
+#include "porting.h"
+
+GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr,
+ gui::IGUIElement *parent, core::recti rect, s32 id)
+ : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rect)
+{
+ m_driver = env->getVideoDriver();
+ m_smgr = smgr->createNewSceneManager(false);
+
+ m_cam = m_smgr->addCameraSceneNode(0, v3f(0.f, 0.f, -100.f), v3f(0.f));
+ m_cam->setFOV(30.f * core::DEGTORAD);
+
+ scene::ILightSceneNode *light = m_smgr->addLightSceneNode(m_cam);
+ light->setRadius(1000.f);
+
+ m_smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
+}
+
+GUIScene::~GUIScene()
+{
+ setMesh(nullptr);
+
+ m_smgr->drop();
+}
+
+scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh)
+{
+ if (m_mesh) {
+ m_mesh->remove();
+ m_mesh = nullptr;
+ }
+
+ if (!mesh)
+ return nullptr;
+
+ m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh);
+ m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter());
+ m_mesh->animateJoints();
+ return m_mesh;
+}
+
+void GUIScene::setTexture(u32 idx, video::ITexture *texture)
+{
+ video::SMaterial &material = m_mesh->getMaterial(idx);
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ material.MaterialTypeParam = 0.5f;
+ material.TextureLayer[0].Texture = texture;
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.setFlag(video::EMF_FOG_ENABLE, true);
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, false);
+}
+
+void GUIScene::draw()
+{
+ // Control rotation speed based on time
+ u64 new_time = porting::getTimeMs();
+ u64 dtime_ms = 0;
+ if (m_last_time != 0)
+ dtime_ms = porting::getDeltaMs(m_last_time, new_time);
+ m_last_time = new_time;
+
+ core::rect<s32> oldViewPort = m_driver->getViewPort();
+ m_driver->setViewPort(getAbsoluteClippingRect());
+ core::recti borderRect = Environment->getRootGUIElement()->getAbsoluteClippingRect();
+
+ if (m_bgcolor != 0) {
+ Environment->getSkin()->draw3DSunkenPane(
+ this, m_bgcolor, false, true, borderRect, 0);
+ }
+
+ core::dimension2d<s32> size = getAbsoluteClippingRect().getSize();
+ m_smgr->getActiveCamera()->setAspectRatio((f32)size.Width / (f32)size.Height);
+
+ if (!m_target) {
+ updateCamera(m_smgr->addEmptySceneNode());
+ rotateCamera(v3f(0.f));
+ m_cam->bindTargetAndRotation(true);
+ }
+
+ cameraLoop();
+
+ // Continuous rotation
+ if (m_inf_rot)
+ rotateCamera(v3f(0.f, -0.03f * (float)dtime_ms, 0.f));
+
+ m_smgr->drawAll();
+
+ if (m_initial_rotation && m_mesh) {
+ rotateCamera(v3f(m_custom_rot.X, m_custom_rot.Y, 0.f));
+ calcOptimalDistance();
+
+ m_initial_rotation = false;
+ }
+
+ m_driver->setViewPort(oldViewPort);
+}
+
+bool GUIScene::OnEvent(const SEvent &event)
+{
+ if (m_mouse_ctrl && event.EventType == EET_MOUSE_INPUT_EVENT) {
+ if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+ m_last_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y);
+ return true;
+ } else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) {
+ if (event.MouseInput.isLeftPressed()) {
+ m_curr_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y);
+
+ rotateCamera(v3f(
+ m_last_pos.Y - m_curr_pos.Y,
+ m_curr_pos.X - m_last_pos.X, 0.f));
+
+ m_last_pos = m_curr_pos;
+ return true;
+ }
+ }
+ }
+
+ return gui::IGUIElement::OnEvent(event);
+}
+
+void GUIScene::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles)
+{
+ StyleSpec::State state = StyleSpec::STATE_DEFAULT;
+ StyleSpec style = StyleSpec::getStyleFromStatePropagation(styles, state);
+
+ setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ setBackgroundColor(style.getColor(StyleSpec::BGCOLOR, m_bgcolor));
+}
+
+/* Camera control functions */
+
+inline void GUIScene::calcOptimalDistance()
+{
+ core::aabbox3df box = m_mesh->getBoundingBox();
+ f32 width = box.MaxEdge.X - box.MinEdge.X;
+ f32 height = box.MaxEdge.Y - box.MinEdge.Y;
+ f32 depth = box.MaxEdge.Z - box.MinEdge.Z;
+ f32 max_width = width > depth ? width : depth;
+
+ const scene::SViewFrustum *f = m_cam->getViewFrustum();
+ f32 cam_far = m_cam->getFarValue();
+ f32 far_width = core::line3df(f->getFarLeftUp(), f->getFarRightUp()).getLength();
+ f32 far_height = core::line3df(f->getFarLeftUp(), f->getFarLeftDown()).getLength();
+
+ core::recti rect = getAbsolutePosition();
+ f32 zoomX = rect.getWidth() / max_width;
+ f32 zoomY = rect.getHeight() / height;
+ f32 dist;
+
+ if (zoomX < zoomY)
+ dist = (max_width / (far_width / cam_far)) + (0.5f * max_width);
+ else
+ dist = (height / (far_height / cam_far)) + (0.5f * max_width);
+
+ m_cam_distance = dist;
+ m_update_cam = true;
+}
+
+void GUIScene::updateCamera(scene::ISceneNode *target)
+{
+ m_target = target;
+ updateTargetPos();
+
+ m_last_target_pos = m_target_pos;
+ updateCameraPos();
+
+ m_update_cam = true;
+}
+
+void GUIScene::updateTargetPos()
+{
+ m_last_target_pos = m_target_pos;
+ m_target->updateAbsolutePosition();
+ m_target_pos = m_target->getAbsolutePosition();
+}
+
+void GUIScene::setCameraRotation(v3f rot)
+{
+ correctBounds(rot);
+
+ core::matrix4 mat;
+ mat.setRotationDegrees(rot);
+
+ m_cam_pos = v3f(0.f, 0.f, m_cam_distance);
+ mat.rotateVect(m_cam_pos);
+
+ m_cam_pos += m_target_pos;
+ m_cam->setPosition(m_cam_pos);
+ m_update_cam = false;
+}
+
+bool GUIScene::correctBounds(v3f &rot)
+{
+ const float ROTATION_MAX_1 = 60.0f;
+ const float ROTATION_MAX_2 = 300.0f;
+
+ // Limit and correct the rotation when needed
+ if (rot.X < 90.f) {
+ if (rot.X > ROTATION_MAX_1) {
+ rot.X = ROTATION_MAX_1;
+ return true;
+ }
+ } else if (rot.X < ROTATION_MAX_2) {
+ rot.X = ROTATION_MAX_2;
+ return true;
+ }
+
+ // Not modified
+ return false;
+}
+
+void GUIScene::cameraLoop()
+{
+ updateCameraPos();
+ updateTargetPos();
+
+ if (m_target_pos != m_last_target_pos)
+ m_update_cam = true;
+
+ if (m_update_cam) {
+ m_cam_pos = m_target_pos + (m_cam_pos - m_target_pos).normalize() * m_cam_distance;
+
+ v3f rot = getCameraRotation();
+ if (correctBounds(rot))
+ setCameraRotation(rot);
+
+ m_cam->setPosition(m_cam_pos);
+ m_cam->setTarget(m_target_pos);
+
+ m_update_cam = false;
+ }
+}
diff --git a/src/gui/guiScene.h b/src/gui/guiScene.h
new file mode 100644
index 000000000..707e6f66a
--- /dev/null
+++ b/src/gui/guiScene.h
@@ -0,0 +1,85 @@
+/*
+Minetest
+Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "ICameraSceneNode.h"
+#include "StyleSpec.h"
+
+using namespace irr;
+
+class GUIScene : public gui::IGUIElement
+{
+public:
+ GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr,
+ gui::IGUIElement *parent, core::recti rect, s32 id = -1);
+
+ ~GUIScene();
+
+ scene::IAnimatedMeshSceneNode *setMesh(scene::IAnimatedMesh *mesh = nullptr);
+ void setTexture(u32 idx, video::ITexture *texture);
+ void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; };
+ void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; };
+ void setRotation(v2f rot) noexcept { m_custom_rot = rot; };
+ void enableContinuousRotation(bool enable) noexcept { m_inf_rot = enable; };
+ void setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles);
+
+ virtual void draw();
+ virtual bool OnEvent(const SEvent &event);
+
+private:
+ void calcOptimalDistance();
+ void updateTargetPos();
+ void updateCamera(scene::ISceneNode *target);
+ void setCameraRotation(v3f rot);
+ /// @return true indicates that the rotation was corrected
+ bool correctBounds(v3f &rot);
+ void cameraLoop();
+
+ void updateCameraPos() { m_cam_pos = m_cam->getPosition(); };
+ v3f getCameraRotation() const { return (m_cam_pos - m_target_pos).getHorizontalAngle(); };
+ void rotateCamera(const v3f &delta) { setCameraRotation(getCameraRotation() + delta); };
+
+ scene::ISceneManager *m_smgr;
+ video::IVideoDriver *m_driver;
+ scene::ICameraSceneNode *m_cam;
+ scene::ISceneNode *m_target = nullptr;
+ scene::IAnimatedMeshSceneNode *m_mesh = nullptr;
+
+ f32 m_cam_distance = 50.f;
+
+ u64 m_last_time = 0;
+
+ v3f m_cam_pos;
+ v3f m_target_pos;
+ v3f m_last_target_pos;
+ // Cursor positions
+ v2f m_curr_pos;
+ v2f m_last_pos;
+ // Initial rotation
+ v2f m_custom_rot;
+
+ bool m_mouse_ctrl = true;
+ bool m_update_cam = false;
+ bool m_inf_rot = false;
+ bool m_initial_rotation = true;
+
+ video::SColor m_bgcolor = 0;
+};