mirror of
https://github.com/minetest/minetest.git
synced 2024-12-23 14:42:24 +01:00
Get the new animation framework properly working
Store start and end frames as v2f Also move bone animations to their own function instead of object properties
This commit is contained in:
parent
ba4d93027f
commit
118285e6ba
@ -1103,6 +1103,8 @@ methods:
|
||||
- get_wielded_item() -> ItemStack
|
||||
- set_wielded_item(item): replaces the wielded item, returns true if successful
|
||||
- set_armor_groups({group1=rating, group2=rating, ...})
|
||||
- set_animations({x=1,y=1}, frame_speed=15, frame_blend=0)
|
||||
- set_bone_posrot("", {x=0,y=0,z=0}, {x=0,y=0,z=0})
|
||||
- set_properties(object property table)
|
||||
LuaEntitySAO-only: (no-op for other objects)
|
||||
- setvelocity({x=num, y=num, z=num})
|
||||
@ -1116,7 +1118,6 @@ LuaEntitySAO-only: (no-op for other objects)
|
||||
- select_horiz_by_yawpitch=false)
|
||||
^ Select sprite from spritesheet with optional animation and DM-style
|
||||
texture selection based on yaw relative to camera
|
||||
- setanimations(frame_start, frame_end, frame_speed, frame_blend)
|
||||
- get_entity_name() (DEPRECATED: Will be removed in a future version)
|
||||
- get_luaentity()
|
||||
Player-only: (no-op for other objects)
|
||||
|
@ -576,6 +576,10 @@ private:
|
||||
v2s16 m_tx_basepos;
|
||||
bool m_initial_tx_basepos_set;
|
||||
bool m_tx_select_horiz_by_yawpitch;
|
||||
v2f m_frames;
|
||||
int m_frame_speed;
|
||||
int m_frame_blend;
|
||||
std::map<std::string, core::vector2d<v3f> > m_bone_posrot;
|
||||
int m_anim_frame;
|
||||
int m_anim_num_frames;
|
||||
float m_anim_framelength;
|
||||
@ -924,6 +928,8 @@ public:
|
||||
m_visuals_expired = false;
|
||||
removeFromScene();
|
||||
addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
|
||||
updateAnimations();
|
||||
updateBonePosRot();
|
||||
}
|
||||
|
||||
if(m_prop.physical){
|
||||
@ -1136,32 +1142,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void updateAnimations(int frame_start, int frame_end, float frame_speed, float frame_blend)
|
||||
void updateAnimations()
|
||||
{
|
||||
if(!m_animated_meshnode)
|
||||
return;
|
||||
|
||||
m_animated_meshnode->setFrameLoop(frame_start, frame_end);
|
||||
m_animated_meshnode->setAnimationSpeed(frame_speed);
|
||||
m_animated_meshnode->setTransitionTime(frame_blend);
|
||||
m_animated_meshnode->setFrameLoop((int)m_frames.X, (int)m_frames.Y);
|
||||
m_animated_meshnode->setAnimationSpeed(m_frame_speed);
|
||||
m_animated_meshnode->setTransitionTime(m_frame_blend);
|
||||
}
|
||||
|
||||
if(m_prop.animation_bone_position.size() > 0)
|
||||
void updateBonePosRot()
|
||||
{
|
||||
if(m_bone_posrot.size() > 0)
|
||||
{
|
||||
for(std::map<std::string, v3f>::const_iterator ii = m_prop.animation_bone_position.begin(); ii != m_prop.animation_bone_position.end(); ++ii){
|
||||
m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
|
||||
m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
|
||||
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_posrot.begin(); ii != m_bone_posrot.end(); ++ii){
|
||||
std::string bone_name = (*ii).first;
|
||||
v3f bone_pos = (*ii).second;
|
||||
v3f bone_pos = (*ii).second.X;
|
||||
v3f bone_rot = (*ii).second.Y;
|
||||
irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
|
||||
bone->setPosition(bone_pos);
|
||||
}
|
||||
}
|
||||
if(m_prop.animation_bone_rotation.size() > 0)
|
||||
{
|
||||
for(std::map<std::string, v3f>::const_iterator ii = m_prop.animation_bone_rotation.begin(); ii != m_prop.animation_bone_rotation.end(); ++ii){
|
||||
m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
|
||||
std::string bone_name = (*ii).first;
|
||||
v3f bone_rot = (*ii).second;
|
||||
irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
|
||||
bone->setRotation(bone_rot);
|
||||
}
|
||||
}
|
||||
@ -1236,12 +1237,22 @@ public:
|
||||
}
|
||||
else if(cmd == GENERIC_CMD_SET_ANIMATIONS)
|
||||
{
|
||||
int frame_start = readU16(is);
|
||||
int frame_end = readU16(is);
|
||||
float frame_speed = readF1000(is);
|
||||
float frame_blend = readF1000(is);
|
||||
m_frames = readV2F1000(is);
|
||||
m_frame_speed = readF1000(is);
|
||||
m_frame_blend = readF1000(is);
|
||||
|
||||
updateAnimations(frame_start, frame_end, frame_speed, frame_blend);
|
||||
updateAnimations();
|
||||
expireVisuals();
|
||||
}
|
||||
else if(cmd == GENERIC_CMD_SET_BONE_POSROT)
|
||||
{
|
||||
std::string bone = deSerializeString(is);
|
||||
v3f position = readV3F1000(is);
|
||||
v3f rotation = readV3F1000(is);
|
||||
m_bone_posrot[bone] = core::vector2d<v3f>(position, rotation);
|
||||
|
||||
updateBonePosRot();
|
||||
expireVisuals();
|
||||
}
|
||||
else if(cmd == GENERIC_CMD_PUNCHED)
|
||||
{
|
||||
|
@ -644,6 +644,22 @@ void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
|
||||
m_armor_groups_sent = false;
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
|
||||
{
|
||||
std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
|
||||
{
|
||||
std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
ObjectProperties* LuaEntitySAO::accessObjectProperties()
|
||||
{
|
||||
return &m_prop;
|
||||
@ -706,14 +722,6 @@ void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
void LuaEntitySAO::setAnimations(int frame_start, int frame_end, float frame_speed, float frame_blend)
|
||||
{
|
||||
std::string str = gob_cmd_set_animations(frame_start, frame_end, frame_speed, frame_blend);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
std::string LuaEntitySAO::getName()
|
||||
{
|
||||
return m_init_name;
|
||||
@ -1085,6 +1093,22 @@ void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
|
||||
m_armor_groups_sent = false;
|
||||
}
|
||||
|
||||
void PlayerSAO::setAnimations(v2f frames, float frame_speed, float frame_blend)
|
||||
{
|
||||
std::string str = gob_cmd_set_animations(frames, frame_speed, frame_blend);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
void PlayerSAO::setBonePosRot(std::string bone, v3f position, v3f rotation)
|
||||
{
|
||||
std::string str = gob_cmd_set_bone_posrot(bone, position, rotation);
|
||||
// create message and add to list
|
||||
ActiveObjectMessage aom(getId(), true, str);
|
||||
m_messages_out.push_back(aom);
|
||||
}
|
||||
|
||||
ObjectProperties* PlayerSAO::accessObjectProperties()
|
||||
{
|
||||
return &m_prop;
|
||||
|
@ -61,6 +61,8 @@ public:
|
||||
void setHP(s16 hp);
|
||||
s16 getHP() const;
|
||||
void setArmorGroups(const ItemGroupList &armor_groups);
|
||||
void setAnimations(v2f frames, float frame_speed, float frame_blend);
|
||||
void setBonePosRot(std::string bone, v3f position, v3f rotation);
|
||||
ObjectProperties* accessObjectProperties();
|
||||
void notifyObjectPropertiesModified();
|
||||
/* LuaEntitySAO-specific */
|
||||
@ -73,7 +75,6 @@ public:
|
||||
void setTextureMod(const std::string &mod);
|
||||
void setSprite(v2s16 p, int num_frames, float framelength,
|
||||
bool select_horiz_by_yawpitch);
|
||||
void setAnimations(int frame_start, int frame_end, float frame_speed, float frame_blend);
|
||||
std::string getName();
|
||||
private:
|
||||
std::string getPropertyPacket();
|
||||
@ -143,6 +144,8 @@ public:
|
||||
void setHP(s16 hp);
|
||||
|
||||
void setArmorGroups(const ItemGroupList &armor_groups);
|
||||
void setAnimations(v2f frames, float frame_speed, float frame_blend);
|
||||
void setBonePosRot(std::string bone, v3f position, v3f rotation);
|
||||
ObjectProperties* accessObjectProperties();
|
||||
void notifyObjectPropertiesModified();
|
||||
|
||||
|
@ -92,19 +92,30 @@ std::string gob_cmd_set_sprite(
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string gob_cmd_set_animations(int frame_start, int frame_end, float frame_speed, float frame_blend)
|
||||
std::string gob_cmd_set_animations(v2f frames, float frame_speed, float frame_blend)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, GENERIC_CMD_SET_ANIMATIONS);
|
||||
// parameters
|
||||
writeU16(os, frame_start);
|
||||
writeU16(os, frame_end);
|
||||
writeV2F1000(os, frames);
|
||||
writeF1000(os, frame_speed);
|
||||
writeF1000(os, frame_blend);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string gob_cmd_set_bone_posrot(std::string bone, v3f position, v3f rotation)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
// command
|
||||
writeU8(os, GENERIC_CMD_SET_BONE_POSROT);
|
||||
// parameters
|
||||
os<<serializeString(bone);
|
||||
writeV3F1000(os, position);
|
||||
writeV3F1000(os, rotation);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string gob_cmd_punched(s16 damage, s16 result_hp)
|
||||
{
|
||||
std::ostringstream os(std::ios::binary);
|
||||
|
@ -29,8 +29,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#define GENERIC_CMD_SET_TEXTURE_MOD 2
|
||||
#define GENERIC_CMD_SET_SPRITE 3
|
||||
#define GENERIC_CMD_SET_ANIMATIONS 4
|
||||
#define GENERIC_CMD_PUNCHED 5
|
||||
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 6
|
||||
#define GENERIC_CMD_SET_BONE_POSROT 5
|
||||
#define GENERIC_CMD_PUNCHED 6
|
||||
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 7
|
||||
|
||||
#include "object_properties.h"
|
||||
std::string gob_cmd_set_properties(const ObjectProperties &prop);
|
||||
@ -55,7 +56,9 @@ std::string gob_cmd_set_sprite(
|
||||
bool select_horiz_by_yawpitch
|
||||
);
|
||||
|
||||
std::string gob_cmd_set_animations(int frame_start, int frame_end, float frame_speed, float frame_blend);
|
||||
std::string gob_cmd_set_animations(v2f frames, float frame_speed, float frame_blend);
|
||||
|
||||
std::string gob_cmd_set_bone_posrot(std::string bone, v3f position, v3f rotation);
|
||||
|
||||
std::string gob_cmd_punched(s16 damage, s16 result_hp);
|
||||
|
||||
|
@ -39,8 +39,6 @@ ObjectProperties::ObjectProperties():
|
||||
makes_footstep_sound(false),
|
||||
automatic_rotate(0)
|
||||
{
|
||||
// Nothing to do for animation_bone_position
|
||||
// Nothing to do for animation_bone_rotation
|
||||
textures.push_back("unknown_object.png");
|
||||
}
|
||||
|
||||
@ -54,22 +52,6 @@ std::string ObjectProperties::dump()
|
||||
os<<", visual="<<visual;
|
||||
os<<", mesh="<<mesh;
|
||||
os<<", visual_size="<<PP2(visual_size);
|
||||
|
||||
os<<", animation_bone_position=[";
|
||||
for(std::map<std::string, v3f>::const_iterator ii = animation_bone_position.begin(); ii != animation_bone_position.end(); ++ii){
|
||||
std::string bone_name = (*ii).first;
|
||||
v3f bone_pos = (*ii).second;
|
||||
os<<bone_name<<" "<<bone_pos.X<<","<<bone_pos.Y<<","<<bone_pos.Z<<"\"";
|
||||
}
|
||||
os<<"]";
|
||||
os<<", animation_bone_rotation=[";
|
||||
for(std::map<std::string, v3f>::const_iterator ii = animation_bone_rotation.begin(); ii != animation_bone_rotation.end(); ++ii){
|
||||
std::string bone_name = (*ii).first;
|
||||
v3f bone_rot = (*ii).second;
|
||||
os<<bone_name<<" "<<bone_rot.X<<","<<bone_rot.Y<<","<<bone_rot.Z<<"\"";
|
||||
}
|
||||
os<<"]";
|
||||
|
||||
os<<", textures=[";
|
||||
for(u32 i=0; i<textures.size(); i++){
|
||||
os<<"\""<<textures[i]<<"\" ";
|
||||
@ -93,24 +75,11 @@ void ObjectProperties::serialize(std::ostream &os) const
|
||||
writeV3F1000(os, collisionbox.MaxEdge);
|
||||
os<<serializeString(visual);
|
||||
os<<serializeString(mesh);
|
||||
|
||||
writeU16(os, animation_bone_position.size());
|
||||
for(std::map<std::string, v3f>::const_iterator ii = animation_bone_position.begin(); ii != animation_bone_position.end(); ++ii){
|
||||
os<<serializeString((*ii).first);
|
||||
writeV3F1000(os, (*ii).second);
|
||||
}
|
||||
writeU16(os, animation_bone_rotation.size());
|
||||
for(std::map<std::string, v3f>::const_iterator ii = animation_bone_rotation.begin(); ii != animation_bone_rotation.end(); ++ii){
|
||||
os<<serializeString((*ii).first);
|
||||
writeV3F1000(os, (*ii).second);
|
||||
}
|
||||
|
||||
writeV2F1000(os, visual_size);
|
||||
writeU16(os, textures.size());
|
||||
for(u32 i=0; i<textures.size(); i++){
|
||||
os<<serializeString(textures[i]);
|
||||
}
|
||||
|
||||
writeV2S16(os, spritediv);
|
||||
writeV2S16(os, initial_sprite_basepos);
|
||||
writeU8(os, is_visible);
|
||||
@ -130,27 +99,12 @@ void ObjectProperties::deSerialize(std::istream &is)
|
||||
collisionbox.MaxEdge = readV3F1000(is);
|
||||
visual = deSerializeString(is);
|
||||
mesh = deSerializeString(is);
|
||||
|
||||
u32 animation_bone_position_count = readU16(is);
|
||||
for(u32 i=0; i<animation_bone_position_count; i++){
|
||||
std::string bone_name = deSerializeString(is);
|
||||
v3f bone_pos = readV3F1000(is);
|
||||
animation_bone_position[bone_name] = bone_pos;
|
||||
}
|
||||
u32 animation_bone_rotation_count = readU16(is);
|
||||
for(u32 i=0; i<animation_bone_rotation_count; i++){
|
||||
std::string bone_name = deSerializeString(is);
|
||||
v3f bone_rot = readV3F1000(is);
|
||||
animation_bone_rotation[bone_name] = bone_rot;
|
||||
}
|
||||
|
||||
visual_size = readV2F1000(is);
|
||||
textures.clear();
|
||||
u32 texture_count = readU16(is);
|
||||
for(u32 i=0; i<texture_count; i++){
|
||||
textures.push_back(deSerializeString(is));
|
||||
}
|
||||
|
||||
spritediv = readV2S16(is);
|
||||
initial_sprite_basepos = readV2S16(is);
|
||||
is_visible = readU8(is);
|
||||
|
@ -34,8 +34,6 @@ struct ObjectProperties
|
||||
core::aabbox3d<f32> collisionbox;
|
||||
std::string visual;
|
||||
std::string mesh;
|
||||
std::map<std::string, v3f> animation_bone_position;
|
||||
std::map<std::string, v3f> animation_bone_rotation;
|
||||
v2f visual_size;
|
||||
core::array<std::string> textures;
|
||||
v2s16 spritediv;
|
||||
|
@ -944,30 +944,6 @@ static void read_object_properties(lua_State *L, int index,
|
||||
prop->visual_size = read_v2f(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "animation_bone_position");
|
||||
if(lua_istable(L, -1))
|
||||
{
|
||||
lua_rawgeti (L, -1, 1);
|
||||
lua_rawgeti (L, -2, 2);
|
||||
std::string bone_name = lua_tostring(L, -2);
|
||||
v3f bone_pos = read_v3f(L, -1);
|
||||
prop->animation_bone_position[bone_name] = bone_pos;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "animation_bone_rotation");
|
||||
if(lua_istable(L, -1))
|
||||
{
|
||||
lua_rawgeti (L, -1, 1);
|
||||
lua_rawgeti (L, -2, 2);
|
||||
std::string bone_name = lua_tostring(L, -2);
|
||||
v3f bone_rot = read_v3f(L, -1);
|
||||
prop->animation_bone_rotation[bone_name] = bone_rot;
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, -1, "textures");
|
||||
if(lua_istable(L, -1)){
|
||||
prop->textures.clear();
|
||||
@ -2723,6 +2699,48 @@ private:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// setanimations(self, frames, frame_speed, frame_blend)
|
||||
static int l_set_animations(lua_State *L)
|
||||
{
|
||||
ObjectRef *ref = checkobject(L, 1);
|
||||
ServerActiveObject *co = getobject(ref);
|
||||
if(co == NULL) return 0;
|
||||
// Do it
|
||||
|
||||
v2f frames = v2f(1, 1);
|
||||
if(!lua_isnil(L, 2))
|
||||
frames = read_v2f(L, 2);
|
||||
float frame_speed = 15;
|
||||
if(!lua_isnil(L, 3))
|
||||
frame_speed = lua_tonumber(L, 3);
|
||||
float frame_blend = 0;
|
||||
if(!lua_isnil(L, 4))
|
||||
frame_blend = lua_tonumber(L, 4);
|
||||
co->setAnimations(frames, frame_speed, frame_blend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// setboneposrot(std::string bone, v3f position, v3f rotation)
|
||||
static int l_set_bone_posrot(lua_State *L)
|
||||
{
|
||||
ObjectRef *ref = checkobject(L, 1);
|
||||
ServerActiveObject *co = getobject(ref);
|
||||
if(co == NULL) return 0;
|
||||
// Do it
|
||||
|
||||
std::string bone = "";
|
||||
if(!lua_isnil(L, 2))
|
||||
bone = lua_tostring(L, 2);
|
||||
v3f position = v3f(0, 0, 0);
|
||||
if(!lua_isnil(L, 3))
|
||||
position = read_v3f(L, 3);
|
||||
v3f rotation = v3f(0, 0, 0);
|
||||
if(!lua_isnil(L, 4))
|
||||
rotation = read_v3f(L, 4);
|
||||
co->setBonePosRot(bone, position, rotation);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// set_properties(self, properties)
|
||||
static int l_set_properties(lua_State *L)
|
||||
{
|
||||
@ -2848,30 +2866,6 @@ private:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// setanimations(self, mod)
|
||||
static int l_setanimations(lua_State *L)
|
||||
{
|
||||
ObjectRef *ref = checkobject(L, 1);
|
||||
LuaEntitySAO *co = getluaobject(ref);
|
||||
if(co == NULL) return 0;
|
||||
// Do it
|
||||
v2s16 p(0,0);
|
||||
int frame_start = 0;
|
||||
if(!lua_isnil(L, 2))
|
||||
frame_start = lua_tonumber(L, 2);
|
||||
int frame_end = 0;
|
||||
if(!lua_isnil(L, 3))
|
||||
frame_end = lua_tonumber(L, 3);
|
||||
float frame_speed = 15;
|
||||
if(!lua_isnil(L, 4))
|
||||
frame_speed = lua_tonumber(L, 4);
|
||||
float frame_blend = 0;
|
||||
if(!lua_isnil(L, 5))
|
||||
frame_blend = lua_tonumber(L, 5);
|
||||
co->setAnimations(frame_start, frame_end, frame_speed, frame_blend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
// get_entity_name(self)
|
||||
static int l_get_entity_name(lua_State *L)
|
||||
@ -3061,6 +3055,8 @@ const luaL_reg ObjectRef::methods[] = {
|
||||
method(ObjectRef, get_wielded_item),
|
||||
method(ObjectRef, set_wielded_item),
|
||||
method(ObjectRef, set_armor_groups),
|
||||
method(ObjectRef, set_animations),
|
||||
method(ObjectRef, set_bone_posrot),
|
||||
method(ObjectRef, set_properties),
|
||||
// LuaEntitySAO-only
|
||||
method(ObjectRef, setvelocity),
|
||||
@ -3071,7 +3067,6 @@ const luaL_reg ObjectRef::methods[] = {
|
||||
method(ObjectRef, getyaw),
|
||||
method(ObjectRef, settexturemod),
|
||||
method(ObjectRef, setsprite),
|
||||
method(ObjectRef, setanimations),
|
||||
method(ObjectRef, get_entity_name),
|
||||
method(ObjectRef, get_luaentity),
|
||||
// Player-only
|
||||
|
@ -152,6 +152,10 @@ public:
|
||||
|
||||
virtual void setArmorGroups(const ItemGroupList &armor_groups)
|
||||
{}
|
||||
virtual void setAnimations(v2f frames, float frame_speed, float frame_blend)
|
||||
{}
|
||||
virtual void setBonePosRot(std::string bone, v3f position, v3f rotation)
|
||||
{}
|
||||
virtual ObjectProperties* accessObjectProperties()
|
||||
{ return NULL; }
|
||||
virtual void notifyObjectPropertiesModified()
|
||||
|
Loading…
Reference in New Issue
Block a user