mirror of
https://github.com/minetest/minetest.git
synced 2024-12-29 20:20:29 +01:00
Add model[] formspec element (#10320)
Formspec element to display models, written by @kilbith, rebased and tweaked. Co-authored-by: Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com> Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
parent
e3bd6704a0
commit
3356da0151
@ -2272,6 +2272,18 @@ Elements
|
|||||||
* `frame duration`: Milliseconds between each frame. `0` means the frames don't advance.
|
* `frame duration`: Milliseconds between each frame. `0` means the frames don't advance.
|
||||||
* `frame start` (Optional): The index of the frame to start on. Default `1`.
|
* `frame start` (Optional): The index of the frame to start on. Default `1`.
|
||||||
|
|
||||||
|
### `model[<X>,<Y>;<W>,<H>;<name>;<mesh>;<textures>;<rotation X,Y>;<continuous>;<mouse control>]`
|
||||||
|
|
||||||
|
* Show a mesh model.
|
||||||
|
* `name`: Element name that can be used for styling
|
||||||
|
* `mesh`: The mesh model to use.
|
||||||
|
* `textures`: The mesh textures to use according to the mesh materials.
|
||||||
|
Texture names must be separated by commas.
|
||||||
|
* `rotation {X,Y}` (Optional): Initial rotation of the camera.
|
||||||
|
The axes are euler angles in degrees.
|
||||||
|
* `continuous` (Optional): Whether the rotation is continuous. Default `false`.
|
||||||
|
* `mouse control` (Optional): Whether the model can be controlled with the mouse. Default `true`.
|
||||||
|
|
||||||
### `item_image[<X>,<Y>;<W>,<H>;<item name>]`
|
### `item_image[<X>,<Y>;<W>,<H>;<item name>]`
|
||||||
|
|
||||||
* Show an inventory image of registered item/node
|
* Show an inventory image of registered item/node
|
||||||
@ -2842,6 +2854,10 @@ Some types may inherit styles from parent types.
|
|||||||
* font_size - Sets font size. See button `font_size` property for more information.
|
* font_size - Sets font size. See button `font_size` property for more information.
|
||||||
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
|
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
|
||||||
* textcolor - color. Default white.
|
* textcolor - color. Default white.
|
||||||
|
* model
|
||||||
|
* bgcolor - color, sets background color.
|
||||||
|
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
|
||||||
|
* Default to false in formspec_version version 3 or higher
|
||||||
* image
|
* image
|
||||||
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
|
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
|
||||||
* Default to false in formspec_version version 3 or higher
|
* Default to false in formspec_version version 3 or higher
|
||||||
|
14
games/devtest/mods/testformspec/LICENSE.txt
Normal file
14
games/devtest/mods/testformspec/LICENSE.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
License of media files
|
||||||
|
----------------------
|
||||||
|
Content imported from minetest_game.
|
||||||
|
|
||||||
|
|
||||||
|
BlockMen (CC BY-SA 3.0)
|
||||||
|
default_chest_front.png
|
||||||
|
default_chest_lock.png
|
||||||
|
default_chest_side.png
|
||||||
|
default_chest_top.png
|
||||||
|
|
||||||
|
stujones11 (CC BY-SA 3.0)
|
||||||
|
An0n3m0us (CC BY-SA 3.0)
|
||||||
|
testformspec_character.b3d
|
@ -327,6 +327,10 @@ Number]
|
|||||||
animated_image[3,4.25;1,1;;testformspec_animation.png;4;0;3]
|
animated_image[3,4.25;1,1;;testformspec_animation.png;4;0;3]
|
||||||
animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100]
|
animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100]
|
||||||
animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100]
|
animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100]
|
||||||
|
|
||||||
|
style[m1;bgcolor=black]
|
||||||
|
model[0.5,6;4,4;m1;testformspec_character.b3d;testformspec_character.png]
|
||||||
|
model[5,6;4,4;m2;testformspec_chest.obj;default_chest_top.png,default_chest_top.png,default_chest_side.png,default_chest_side.png,default_chest_front.png,default_chest_inside.png;30,1;true;true]
|
||||||
]],
|
]],
|
||||||
|
|
||||||
-- Scroll containers
|
-- Scroll containers
|
||||||
|
Binary file not shown.
@ -0,0 +1,79 @@
|
|||||||
|
# Blender v2.78 (sub 0) OBJ File: 'chest-open.blend'
|
||||||
|
# www.blender.org
|
||||||
|
o Top_Cube.002_None_Top_Cube.002_None_bottom
|
||||||
|
v -0.500000 0.408471 0.720970
|
||||||
|
v -0.500000 1.115578 0.013863
|
||||||
|
v -0.500000 0.894607 -0.207108
|
||||||
|
v -0.500000 0.187501 0.499999
|
||||||
|
v 0.500000 1.115578 0.013863
|
||||||
|
v 0.500000 0.408471 0.720970
|
||||||
|
v 0.500000 0.187501 0.499999
|
||||||
|
v 0.500000 0.894607 -0.207108
|
||||||
|
v -0.500000 0.187500 -0.500000
|
||||||
|
v -0.500000 -0.500000 -0.500000
|
||||||
|
v -0.500000 -0.500000 0.500000
|
||||||
|
v 0.500000 0.187500 -0.500000
|
||||||
|
v 0.500000 -0.500000 0.500000
|
||||||
|
v 0.500000 -0.500000 -0.500000
|
||||||
|
vt 0.0000 1.0000
|
||||||
|
vt 0.0000 0.0000
|
||||||
|
vt 1.0000 0.0000
|
||||||
|
vt 1.0000 1.0000
|
||||||
|
vt 1.0000 0.0000
|
||||||
|
vt 1.0000 1.0000
|
||||||
|
vt 0.0000 1.0000
|
||||||
|
vt 0.0000 0.0000
|
||||||
|
vt 0.0000 1.0000
|
||||||
|
vt 1.0000 1.0000
|
||||||
|
vt 1.0000 0.6875
|
||||||
|
vt 0.0000 0.6875
|
||||||
|
vt 1.0000 1.0000
|
||||||
|
vt 0.0000 0.6875
|
||||||
|
vt 1.0000 0.6875
|
||||||
|
vt 1.0000 0.6875
|
||||||
|
vt 1.0000 0.0000
|
||||||
|
vt 0.0000 0.0000
|
||||||
|
vt 1.0000 0.6875
|
||||||
|
vt 1.0000 0.0000
|
||||||
|
vt 1.0000 1.0000
|
||||||
|
vt 1.0000 0.6875
|
||||||
|
vt 1.0000 0.0000
|
||||||
|
vt 0.0000 1.0000
|
||||||
|
vt 0.0000 0.6875
|
||||||
|
vt 0.0000 0.6875
|
||||||
|
vt 0.0000 0.0000
|
||||||
|
vt 1.0000 0.5000
|
||||||
|
vt 1.0000 1.0000
|
||||||
|
vt 0.0000 1.0000
|
||||||
|
vt 0.0000 0.5000
|
||||||
|
vt 0.0000 0.0000
|
||||||
|
vt 1.0000 0.0000
|
||||||
|
vn 0.0000 0.7071 0.7071
|
||||||
|
vn -0.0000 -1.0000 -0.0000
|
||||||
|
vn -1.0000 0.0000 0.0000
|
||||||
|
vn 1.0000 0.0000 -0.0000
|
||||||
|
vn 0.0000 -0.7071 0.7071
|
||||||
|
vn 0.0000 0.0000 1.0000
|
||||||
|
vn -0.0000 0.7071 -0.7071
|
||||||
|
vn -0.0000 0.0000 -1.0000
|
||||||
|
vn -0.0000 -0.7071 -0.7071
|
||||||
|
vn -0.0000 1.0000 -0.0000
|
||||||
|
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Top
|
||||||
|
s off
|
||||||
|
f 6/1/1 5/2/1 2/3/1 1/4/1
|
||||||
|
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Bottom
|
||||||
|
f 11/5/2 10/6/2 14/7/2 13/8/2
|
||||||
|
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Right-Left
|
||||||
|
f 1/9/3 2/10/3 3/11/3 4/12/3
|
||||||
|
f 5/13/4 6/1/4 7/14/4 8/15/4
|
||||||
|
f 4/12/3 9/16/3 10/17/3 11/18/3
|
||||||
|
f 12/19/4 7/14/4 13/8/4 14/20/4
|
||||||
|
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Back
|
||||||
|
f 6/21/5 1/9/5 4/12/5 7/22/5
|
||||||
|
f 7/22/6 4/12/6 11/18/6 13/23/6
|
||||||
|
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Front
|
||||||
|
f 2/10/7 5/24/7 8/25/7 3/11/7
|
||||||
|
f 9/16/8 12/26/8 14/27/8 10/17/8
|
||||||
|
g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Inside
|
||||||
|
f 4/28/9 3/29/9 8/30/9 7/31/9
|
||||||
|
f 7/31/10 12/32/10 9/33/10 4/28/10
|
BIN
games/devtest/mods/testformspec/textures/default_chest_front.png
Normal file
BIN
games/devtest/mods/testformspec/textures/default_chest_front.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 423 B |
Binary file not shown.
After Width: | Height: | Size: 102 B |
BIN
games/devtest/mods/testformspec/textures/default_chest_side.png
Normal file
BIN
games/devtest/mods/testformspec/textures/default_chest_side.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 375 B |
BIN
games/devtest/mods/testformspec/textures/default_chest_top.png
Normal file
BIN
games/devtest/mods/testformspec/textures/default_chest_top.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 423 B |
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
@ -15,6 +15,7 @@ set(gui_SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp
|
||||||
|
@ -65,6 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "guiScrollContainer.h"
|
#include "guiScrollContainer.h"
|
||||||
#include "intlGUIEditBox.h"
|
#include "intlGUIEditBox.h"
|
||||||
#include "guiHyperText.h"
|
#include "guiHyperText.h"
|
||||||
|
#include "guiScene.h"
|
||||||
|
|
||||||
#define MY_CHECKPOS(a,b) \
|
#define MY_CHECKPOS(a,b) \
|
||||||
if (v_pos.size() != 2) { \
|
if (v_pos.size() != 2) { \
|
||||||
@ -2695,6 +2696,86 @@ void GUIFormSpecMenu::parseSetFocus(const std::string &element)
|
|||||||
<< "'" << std::endl;
|
<< "'" << 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)
|
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
|
||||||
{
|
{
|
||||||
//some prechecks
|
//some prechecks
|
||||||
@ -2891,6 +2972,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == "model") {
|
||||||
|
parseModel(data, description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore others
|
// Ignore others
|
||||||
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
|
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
@ -38,7 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
class InventoryManager;
|
class InventoryManager;
|
||||||
class ISimpleTextureSource;
|
class ISimpleTextureSource;
|
||||||
class Client;
|
class Client;
|
||||||
class TexturePool;
|
|
||||||
class GUIScrollContainer;
|
class GUIScrollContainer;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -444,6 +443,7 @@ private:
|
|||||||
void parseAnchor(parserData *data, const std::string &element);
|
void parseAnchor(parserData *data, const std::string &element);
|
||||||
bool parseStyle(parserData *data, const std::string &element, bool style_type);
|
bool parseStyle(parserData *data, const std::string &element, bool style_type);
|
||||||
void parseSetFocus(const std::string &element);
|
void parseSetFocus(const std::string &element);
|
||||||
|
void parseModel(parserData *data, const std::string &element);
|
||||||
|
|
||||||
void tryClose();
|
void tryClose();
|
||||||
|
|
||||||
|
257
src/gui/guiScene.cpp
Normal file
257
src/gui/guiScene.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
85
src/gui/guiScene.h
Normal file
85
src/gui/guiScene.h
Normal file
@ -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;
|
||||||
|
};
|
@ -183,6 +183,8 @@ src/gui/guiMainMenu.h
|
|||||||
src/gui/guiPasswordChange.cpp
|
src/gui/guiPasswordChange.cpp
|
||||||
src/gui/guiPathSelectMenu.cpp
|
src/gui/guiPathSelectMenu.cpp
|
||||||
src/gui/guiPathSelectMenu.h
|
src/gui/guiPathSelectMenu.h
|
||||||
|
src/gui/guiScene.cpp
|
||||||
|
src/gui/guiScene.h
|
||||||
src/gui/guiScrollBar.cpp
|
src/gui/guiScrollBar.cpp
|
||||||
src/gui/guiSkin.cpp
|
src/gui/guiSkin.cpp
|
||||||
src/gui/guiSkin.h
|
src/gui/guiSkin.h
|
||||||
|
Loading…
Reference in New Issue
Block a user