Skip to content
Permalink
Browse files

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>
  • Loading branch information
3 people committed Nov 4, 2020
1 parent e3bd670 commit 3356da01513860d899cde503408436f7e1918f63
@@ -2272,6 +2272,18 @@ Elements
* `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`.

### `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>]`

* 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.
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* 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
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* Default to false in formspec_version version 3 or higher
@@ -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[5.5,0.5;5,2;;testformspec_animation.png;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
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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
@@ -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;
@@ -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 @@ class GUIFormSpecMenu : public GUIModalMenu
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();

0 comments on commit 3356da0

Please sign in to comment.
You can’t perform that action at this time.