ReflectionProperty

This commit is contained in:
floralrainfall 2023-07-17 19:03:49 -04:00
parent b6f8536d35
commit 73ab158eec
17 changed files with 271 additions and 96 deletions

View File

@ -5,11 +5,13 @@ set(CMAKE_AUTOMOC ON)
list(APPEND SOURCE list(APPEND SOURCE
Source/main.cpp Source/main.cpp
Source/MainWindow.cpp Source/MainWindow.cpp
Source/PropertyViewer.cpp
) )
list(APPEND HEADER list(APPEND HEADER
${CMAKE_BINARY_DIR}/Resource/Studio.hpp ${CMAKE_BINARY_DIR}/Resource/Studio.hpp
Header/MainWindow.hpp Header/MainWindow.hpp
Header/PropertyViewer.hpp
) )
if(WIN32) if(WIN32)

View File

@ -10,6 +10,7 @@
#include <QGridLayout> #include <QGridLayout>
#include <QTreeView> #include <QTreeView>
#include <QVariant> #include <QVariant>
#include <PropertyViewer.hpp>
#include <OGRE/Bites/OgreBitesConfigDialog.h> #include <OGRE/Bites/OgreBitesConfigDialog.h>
#include <OgreWidget.hpp> #include <OgreWidget.hpp>
@ -28,6 +29,7 @@ class MainWindow : public QMainWindow
QTreeWidget* explorer; QTreeWidget* explorer;
QToolBar* toolbar; QToolBar* toolbar;
QMenuBar* menubar; QMenuBar* menubar;
PropertyViewer* properties;
void createToolbar(); void createToolbar();
void updateTree(RNR::Instance* root_instance); void updateTree(RNR::Instance* root_instance);

View File

@ -0,0 +1,16 @@
#pragma once
#include <QTableWidget>
#include <QLabel>
#include <App/Script/ReflectionProperty.hpp>
#include <App/V8/Tree/Instance.hpp>
class PropertyViewer : public QWidget
{
private:
QTableWidget* prop_table;
QLabel* prop_label;
public:
PropertyViewer();
void view(RNR::Instance* instance);
};

View File

@ -18,20 +18,22 @@ MainWindow::MainWindow()
ogreRoot->initialise(false); ogreRoot->initialise(false);
menubar = new QMenuBar(); menubar = new QMenuBar();
grid->addWidget(menubar, 0, 0, 1, 2);
toolbar = new QToolBar(); toolbar = new QToolBar();
grid->addWidget(toolbar, 1, 0, 1, 2);
createToolbar(); createToolbar();
grid->addWidget(menubar, 0, 0, 1, 3);
grid->addWidget(toolbar, 1, 0, 1, 3);
this->ogreWidget = new RNR::OgreWidget(ogreRoot); this->ogreWidget = new RNR::OgreWidget(ogreRoot);
grid->addWidget(this->ogreWidget, 2, 0, 1, 2); grid->addWidget(this->ogreWidget, 2, 0, 2, 2);
explorer = new QTreeWidget(); explorer = new QTreeWidget();
connect(explorer, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(selectInstance(QTreeWidgetItem*, int))); connect(explorer, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(selectInstance(QTreeWidgetItem*, int)));
grid->addWidget(explorer, 2, 2, 1, 1); grid->addWidget(explorer, 2, 2, 1, 1);
properties = new PropertyViewer();
grid->addWidget(properties, 3, 2, 1, 1);
content_widget->setLayout(grid); content_widget->setLayout(grid);
grid->setContentsMargins(0, 0, 0, 0); grid->setContentsMargins(0, 0, 0, 0);
@ -39,6 +41,7 @@ MainWindow::MainWindow()
setWindowTitle(QString("RNR Studio")); setWindowTitle(QString("RNR Studio"));
setWindowIcon(QIcon(pixmap)); setWindowIcon(QIcon(pixmap));
setCentralWidget(content_widget); setCentralWidget(content_widget);
} }
void MainWindow::widgetItemPrepare(QTreeWidgetItem* item, RNR::Instance* instance) void MainWindow::widgetItemPrepare(QTreeWidgetItem* item, RNR::Instance* instance)
@ -58,10 +61,8 @@ void MainWindow::widgetItemPrepare(QTreeWidgetItem* item, RNR::Instance* instanc
void MainWindow::selectInstance(QTreeWidgetItem *item, int column) void MainWindow::selectInstance(QTreeWidgetItem *item, int column)
{ {
RNR::Instance* instance = item->data(0, Qt::UserRole).value<RNR::Instance*>(); RNR::Instance* instance = item->data(0, Qt::UserRole).value<RNR::Instance*>();
if(dynamic_cast<RNR::ModelInstance*>(instance)) ogreWidget->selectedInstance = instance;
{ properties->view(instance);
ogreWidget->selectedInstance = instance;
}
} }
void MainWindow::recurseTreeAddInstance(QTreeWidgetItem* parent, RNR::Instance* instance) void MainWindow::recurseTreeAddInstance(QTreeWidgetItem* parent, RNR::Instance* instance)
@ -95,7 +96,7 @@ void MainWindow::updateTree(RNR::Instance* root_instance)
void MainWindow::loadDatamodel() void MainWindow::loadDatamodel()
{ {
this->ogreWidget->world->load(QFileDialog::getOpenFileName(this, tr("Open RBXL"), tr(""), tr("RBXLs (*.rbxl)")).toLocal8Bit().data()); this->ogreWidget->world->load(QFileDialog::getOpenFileName(this, tr("Open RBXL"), tr(""), tr("XML RBXLs (*.rbxl *.rbxlx)")).toLocal8Bit().data());
updateTree(ogreWidget->world->getDatamodel()); updateTree(ogreWidget->world->getDatamodel());
} }
@ -117,5 +118,6 @@ void MainWindow::resizeEvent(QResizeEvent* event)
{ {
QSize new_size = event->size(); QSize new_size = event->size();
explorer->setMaximumWidth(new_size.width()/4); explorer->setMaximumWidth(new_size.width()/4);
properties->setMaximumWidth(new_size.width()/4);
} }

View File

@ -0,0 +1,44 @@
#include <PropertyViewer.hpp>
#include <QVBoxLayout>
#include <QHeaderView>
PropertyViewer::PropertyViewer() : QWidget()
{
prop_table = new QTableWidget();
prop_table->verticalHeader()->setVisible(false);
prop_table->horizontalHeader()->setVisible(false);
prop_table->horizontalHeader()->setStretchLastSection(true);
prop_table->setColumnCount(2);
prop_label = new QLabel();
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(prop_label);
layout->addWidget(prop_table);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
prop_label->setText("Properties");
}
void PropertyViewer::view(RNR::Instance* instance)
{
prop_label->setText(QString::asprintf("%s Properties (%s)", instance->getName().c_str(), instance->getClassName().c_str()));
std::vector<RNR::ReflectionProperty> properties = instance->getProperties();
prop_table->setRowCount(properties.size());
int property_count = 0;
for(auto& property : properties)
{
QTableWidgetItem* new_property_item = new QTableWidgetItem(tr("%1").arg(property.name().c_str()));
QTableWidgetItem* new_property_itemval = new QTableWidgetItem(tr("%1").arg(property.toString().c_str()));
new_property_item->setStatusTip(property.description().c_str());
prop_table->setItem(property_count, 0, new_property_item);
prop_table->setItem(property_count, 1, new_property_itemval);
property_count++;
}
}

View File

@ -2,6 +2,16 @@
#include <string> #include <string>
#include <boost/type_index.hpp> #include <boost/type_index.hpp>
#include <functional>
#define REFLECTION_GETTER(c) \
[](const void* object) { c }
#define REFLECTION_NO_GETTER() \
[](const void* object) { return (const void*)NULL; }
#define REFLECTION_SETTER(c) \
[](void* object, const void* value) { c }
#define REFLECTION_NO_SETTER() \
[](void* object, const void* value) { }
namespace RNR namespace RNR
{ {
@ -15,32 +25,51 @@ namespace RNR
ACCESS_AUTHORIZED, ACCESS_AUTHORIZED,
}; };
template <class T> enum ReflectionPropertyOperation
{
OPERATION_READ,
OPERATION_READWRITE,
};
enum ReflectionPropertyType
{
PROPERTY_BOOL,
PROPERTY_STD_STRING,
PROPERTY_INTEGER,
PROPERTY_VECTOR2,
PROPERTY_VECTOR3,
PROPERTY_CFRAME,
PROPERTY_INSTANCE,
};
class ReflectionProperty class ReflectionProperty
{ {
private: private:
const void* m_object;
std::string m_name; std::string m_name;
std::string m_description;
ReflectionPropertyAccess m_access; ReflectionPropertyAccess m_access;
ReflectionPropertyOperation m_op;
ReflectionPropertyType m_type;
std::function<const void* (const void* /* owner */)> m_getter;
std::function<void (void* /* owner */, const void* /* value */)> m_setter;
public: public:
ReflectionProperty(std::string name, ReflectionPropertyAccess access, T(*getter), T(*setter)) // this == m_object
{ // getter returns an address to the value,
this->m_name = name; // setter sets the value to the contents of the address
this->m_access = access; ReflectionProperty(const void* m_object,
this->getter = getter; std::string name,
this->setter = setter; std::string description,
} ReflectionPropertyAccess access,
ReflectionPropertyOperation op,
ReflectionPropertyType m_type,
std::function<const void* (const void* /* owner */)> getter,
std::function<void (void* /* owner */, const void* /* value */)> setter);
bool access(ReflectionPropertyAccess accessor) bool access(ReflectionPropertyAccess accessor) { return this->m_access <= accessor; }
{ std::string name() { return m_name; }
return this->m_access <= accessor; std::string description() { return m_description; }
}
char* class_name() std::string toString();
{
return boost::typeindex::type_id<T>().pretty_name();
}
T (*getter)();
void (*setter)(T val);
}; };
} }

View File

@ -0,0 +1,9 @@
#pragma once
namespace RNR
{
class Script
{
};
}

View File

@ -7,18 +7,17 @@
namespace RNR namespace RNR
{ {
class PartInstance : public PVInstance, public Ogre::Renderable class PartInstance : public PVInstance
{ {
protected: protected:
int m_brickColor; int m_brickColor;
Ogre::MaterialPtr m_material;
Ogre::Matrix4 m_matrix; Ogre::Matrix4 m_matrix;
Ogre::Vector3 m_position; Ogre::Vector3 m_position;
Ogre::LightList m_nearbyLights;
Ogre::Vector3 m_size; Ogre::Vector3 m_size;
Ogre::Vector4 m_color; Ogre::Vector4 m_color;
static Ogre::MeshPtr m_partMesh;
virtual void deserializeProperty(char* prop_name, pugi::xml_node prop); virtual void deserializeProperty(char* prop_name, pugi::xml_node prop);
virtual void addProperties(std::vector<ReflectionProperty>& properties);
std::string mesh_id;
public: public:
PartInstance(); PartInstance();
@ -31,10 +30,5 @@ namespace RNR
void setBrickColor(int brickcolor) { m_brickColor = brickcolor; } void setBrickColor(int brickcolor) { m_brickColor = brickcolor; }
int getBrickColor() { return m_brickColor; } int getBrickColor() { return m_brickColor; }
virtual const Ogre::MaterialPtr& getMaterial() const;
virtual void getRenderOperation(Ogre::RenderOperation& op);
virtual Ogre::Real getSquaredViewDepth(const Ogre::Camera* cam) const;
virtual const Ogre::LightList& getLights() const;
virtual void getWorldTransforms(Ogre::Matrix4* xform) const;
}; };
} }

View File

@ -9,6 +9,8 @@
#include <Helpers/Name.hpp> #include <Helpers/Name.hpp>
#include <pugixml.hpp> #include <pugixml.hpp>
#include <App/Script/ReflectionProperty.hpp>
namespace RNR namespace RNR
{ {
class World; class World;
@ -18,6 +20,7 @@ namespace RNR
protected: protected:
static World* world; static World* world;
virtual void deserializeProperty(char* prop_name, pugi::xml_node prop) {}; virtual void deserializeProperty(char* prop_name, pugi::xml_node prop) {};
virtual void addProperties(std::vector<ReflectionProperty>& properties) {};
private: private:
@ -32,6 +35,7 @@ namespace RNR
Instance(); Instance();
~Instance(); ~Instance();
virtual std::vector<ReflectionProperty> getProperties();
void deserializeXmlProperty(pugi::xml_node prop); void deserializeXmlProperty(pugi::xml_node prop);
bool contains(RNR::Instance* child); bool contains(RNR::Instance* child);

View File

@ -9,6 +9,7 @@ namespace RNR
protected: protected:
CoordinateFrame m_cframe; CoordinateFrame m_cframe;
virtual void deserializeProperty(char* prop_name, pugi::xml_node prop); virtual void deserializeProperty(char* prop_name, pugi::xml_node prop);
virtual void addProperties(std::vector<ReflectionProperty>& properties);
public: public:
PVInstance(); PVInstance();

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <memory>
#include <sstream> #include <sstream>
#include <random> #include <random>
#include <ios> #include <ios>
@ -13,5 +14,16 @@ namespace RNR
public: public:
static uint8_t random_char(); static uint8_t random_char();
static std::string random_hex(const uint64_t length); static std::string random_hex(const uint64_t length);
template<typename ... Args>
static std::string string_format( const std::string& format, Args ... args )
{
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
auto size = static_cast<size_t>( size_s );
std::unique_ptr<char[]> buf( new char[ size ] );
std::snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
}; };
} }

View File

@ -1,6 +1,66 @@
#include <App/Script/ReflectionProperty.hpp> #include <App/Script/ReflectionProperty.hpp>
#include <App/V8/Tree/Instance.hpp>
#include <App/CoordinateFrame.hpp>
#include <Helpers/Strings.hpp>
#include <format>
namespace RNR namespace RNR
{ {
ReflectionProperty::ReflectionProperty(const void* object,
std::string name,
std::string description,
ReflectionPropertyAccess access,
ReflectionPropertyOperation op,
ReflectionPropertyType type,
std::function<const void* (const void* /* owner */)> getter,
std::function<void (void* /* owner */, const void* /* value */)> setter)
{
this->m_object = object;
this->m_name = name;
this->m_access = access;
this->m_op = op;
this->m_type = type;
this->m_getter = getter;
this->m_setter = setter;
}
std::string ReflectionProperty::toString()
{
switch(m_type)
{
case PROPERTY_STD_STRING:
return *(std::string*)m_getter(m_object);
case PROPERTY_BOOL:
return std::string((*(bool*)m_getter(m_object))?"true":"false");
case PROPERTY_INSTANCE:
{
Instance* instance = (Instance*)m_getter(m_object);
return instance->getName();
}
case PROPERTY_VECTOR3:
{
Ogre::Vector3 vector = *(Ogre::Vector3*)m_getter(m_object);
return Strings::string_format("(%f,%f,%f)",
vector.x,
vector.y,
vector.z);
}
case PROPERTY_CFRAME:
{
CoordinateFrame cframe = *(CoordinateFrame*)m_getter(m_object);
Ogre::Vector3 cframe_position = cframe.getPosition();
Ogre::Quaternion cframe_rotation = Ogre::Quaternion(cframe.getRotation());
return Strings::string_format("(%f,%f,%f)), (%f,%f,%f,%f)",
cframe_position.x,
cframe_position.y,
cframe_position.z,
cframe_rotation.x,
cframe_rotation.y,
cframe_rotation.z,
cframe_rotation.w);
}
default:
return std::string("???");
}
}
} }

View File

@ -4,20 +4,12 @@
namespace RNR namespace RNR
{ {
Ogre::MeshPtr PartInstance::m_partMesh = 0; PartInstance::PartInstance() : m_matrix(), PVInstance(), m_size(2.f, STUD_HEIGHT, 4.f)
PartInstance::PartInstance() : m_matrix(), PVInstance(), Ogre::Renderable(), m_size(2.f, STUD_HEIGHT, 4.f)
{ {
setName("Part"); setName("Part");
updateMatrix(); updateMatrix();
m_nearbyLights = Ogre::LightList();
m_nearbyLights.insert(m_nearbyLights.begin(), world->getOgreSceneManager()->getLight("SunLight"));
m_color = Ogre::Vector4(0.63, 0.64, 0.63, 1.0); m_color = Ogre::Vector4(0.63, 0.64, 0.63, 1.0);
if(m_partMesh == 0)
m_partMesh = Ogre::Root::getSingletonPtr()->getMeshManager()->load("fonts/Cube.mesh", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
m_material = m_partMesh->getSubMesh(0)->getMaterial();
} }
void PartInstance::updateMatrix() void PartInstance::updateMatrix()
@ -26,46 +18,6 @@ namespace RNR
m_position = m_cframe.getPosition(); m_position = m_cframe.getPosition();
} }
const Ogre::MaterialPtr& PartInstance::getMaterial() const
{
return m_material;
}
void PartInstance::getRenderOperation(Ogre::RenderOperation& op)
{
Ogre::SubMesh* submesh = m_partMesh->getSubMesh(0);
if(submesh)
{
op.operationType = op.OT_TRIANGLE_LIST;
if(submesh->useSharedVertices == false)
op.vertexData = submesh->vertexData;
else
op.vertexData = m_partMesh->sharedVertexData;
op.indexData = submesh->indexData;
op.numberOfInstances = 1;
op.srcRenderable = this;
op.useIndexes = true;
}
else
printf("BasePart::getRenderOperation: couldnt get submesh\n");
}
Ogre::Real PartInstance::getSquaredViewDepth(const Ogre::Camera* cam) const
{
Ogre::Vector3 diff = m_position - cam->getDerivedPosition();
return diff.squaredLength();
}
const Ogre::LightList& PartInstance::getLights() const
{
return m_nearbyLights;
}
void PartInstance::getWorldTransforms(Ogre::Matrix4* xform) const
{
*xform = m_matrix;
}
void PartInstance::deserializeProperty(char* prop_name, pugi::xml_node node) void PartInstance::deserializeProperty(char* prop_name, pugi::xml_node node)
{ {
if(prop_name == std::string("size")) if(prop_name == std::string("size"))
@ -83,4 +35,17 @@ namespace RNR
else else
PVInstance::deserializeProperty(prop_name, node); PVInstance::deserializeProperty(prop_name, node);
} }
void PartInstance::addProperties(std::vector<ReflectionProperty>& properties)
{
ReflectionProperty _properties[] = {
{ this, std::string("Size"), std::string(""),
ACCESS_NONE, OPERATION_READWRITE, PROPERTY_VECTOR3,
REFLECTION_GETTER(PartInstance* instance = (PartInstance*)object; return &instance->m_size; ),
REFLECTION_SETTER(PartInstance* instance = (PartInstance*)object; instance->setSize(*(Ogre::Vector3*)value); ) },
};
PVInstance::addProperties(properties);
properties.insert(properties.end(), _properties, _properties+(sizeof(_properties)/sizeof(ReflectionProperty)));
}
} }

View File

@ -34,11 +34,10 @@ namespace RNR
Ogre::Vector3 part_size = child_part->getSize(); Ogre::Vector3 part_size = child_part->getSize();
for(int i = 0; i < m_partEntity->getNumSubEntities(); i++) for(int i = 0; i < m_partEntity->getNumSubEntities(); i++)
{ {
Ogre::SubEntity* surface = m_partEntity->getSubEntity(i); Ogre::SubMesh* surface = m_partEntity->getMesh()->getSubMesh(i);
Ogre::TextureUnitState* texture = surface->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0); Ogre::TextureUnitState* texture = surface->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0);
Ogre::Vector2 uvs; Ogre::Vector2 uvs;
std::string surf_name = std::string(surface->getMaterialName().c_str()); std::string surf_name = std::string(surface->getMaterialName().c_str());
if(surf_name == "TopMaterial") if(surf_name == "TopMaterial")
uvs = Ogre::Vector2(part_size.x, part_size.z); uvs = Ogre::Vector2(part_size.x, part_size.z);
@ -53,13 +52,12 @@ namespace RNR
else if(surf_name == "FrontMaterial") else if(surf_name == "FrontMaterial")
uvs = Ogre::Vector2(-part_size.x, part_size.z); uvs = Ogre::Vector2(-part_size.x, part_size.z);
texture->setTextureScale(uvs.x,uvs.y);
} }
m_geom->addEntity(m_partEntity, m_geom->addEntity(m_partEntity,
child_part->getCFrame().getPosition(), child_part->getCFrame().getPosition(),
Ogre::Quaternion(child_part->getCFrame().getRotation()), Ogre::Quaternion(child_part->getCFrame().getRotation()),
child_part->getSize()); child_part->getSize());
} }
for(auto& child : *instance->getChildren()) for(auto& child : *instance->getChildren())
buildGeomInstance(child); buildGeomInstance(child);

View File

@ -17,6 +17,31 @@ namespace RNR
setParent(NULL); setParent(NULL);
} }
std::vector<ReflectionProperty> Instance::getProperties()
{
ReflectionProperty properties[] = {
{ this, std::string("Name"), std::string(""),
ACCESS_NONE, OPERATION_READWRITE, PROPERTY_STD_STRING,
REFLECTION_GETTER(Instance* instance = (Instance*)object; return &instance->m_name; ),
REFLECTION_SETTER(Instance* instance = (Instance*)object; instance->setName(*(std::string*)value); ) },
{ this, std::string("Parent"), std::string(""),
ACCESS_NONE, OPERATION_READ, PROPERTY_INSTANCE,
REFLECTION_GETTER(Instance* instance = (Instance*)object; return instance->m_parent; ),
REFLECTION_NO_SETTER() },
{ this, std::string("Archivable"), std::string(""),
ACCESS_NONE, OPERATION_READWRITE, PROPERTY_BOOL,
REFLECTION_GETTER(Instance* instance = (Instance*)object; return &instance->m_archivable; ),
REFLECTION_SETTER(Instance* instance = (Instance*)object; instance->m_archivable = *(bool*)value; ) },
};
std::vector<ReflectionProperty> _properties(properties, properties+(sizeof(properties)/sizeof(ReflectionProperty)));
addProperties(_properties);
return _properties;
}
void Instance::deserializeXmlProperty(pugi::xml_node prop) void Instance::deserializeXmlProperty(pugi::xml_node prop)
{ {
pugi::xml_attribute prop_name = prop.attribute("name"); pugi::xml_attribute prop_name = prop.attribute("name");

View File

@ -15,4 +15,16 @@ namespace RNR
setCFrame(XML::getCFrame(node)); setCFrame(XML::getCFrame(node));
} }
} }
void PVInstance::addProperties(std::vector<ReflectionProperty>& properties)
{
ReflectionProperty _properties[] = {
{ this, std::string("CFrame"), std::string(""),
ACCESS_NONE, OPERATION_READWRITE, PROPERTY_CFRAME,
REFLECTION_GETTER(PVInstance* instance = (PVInstance*)object; return &instance->m_cframe; ),
REFLECTION_SETTER(PVInstance* instance = (PVInstance*)object; instance->setCFrame(*(CoordinateFrame*)value); ) },
};
properties.insert(properties.end(), _properties, _properties+(sizeof(_properties)/sizeof(ReflectionProperty)));
}
} }