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
Source/main.cpp
Source/MainWindow.cpp
Source/PropertyViewer.cpp
)
list(APPEND HEADER
${CMAKE_BINARY_DIR}/Resource/Studio.hpp
Header/MainWindow.hpp
Header/PropertyViewer.hpp
)
if(WIN32)

View File

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

@ -16,22 +16,24 @@ MainWindow::MainWindow()
ogreRoot->showConfigDialog(config);
ogreRoot->initialise(false);
menubar = new QMenuBar();
grid->addWidget(menubar, 0, 0, 1, 2);
toolbar = new QToolBar();
grid->addWidget(toolbar, 1, 0, 1, 2);
createToolbar();
grid->addWidget(menubar, 0, 0, 1, 3);
grid->addWidget(toolbar, 1, 0, 1, 3);
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();
connect(explorer, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(selectInstance(QTreeWidgetItem*, int)));
grid->addWidget(explorer, 2, 2, 1, 1);
properties = new PropertyViewer();
grid->addWidget(properties, 3, 2, 1, 1);
content_widget->setLayout(grid);
grid->setContentsMargins(0, 0, 0, 0);
@ -39,6 +41,7 @@ MainWindow::MainWindow()
setWindowTitle(QString("RNR Studio"));
setWindowIcon(QIcon(pixmap));
setCentralWidget(content_widget);
}
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)
{
RNR::Instance* instance = item->data(0, Qt::UserRole).value<RNR::Instance*>();
if(dynamic_cast<RNR::ModelInstance*>(instance))
{
ogreWidget->selectedInstance = instance;
}
ogreWidget->selectedInstance = instance;
properties->view(instance);
}
void MainWindow::recurseTreeAddInstance(QTreeWidgetItem* parent, RNR::Instance* instance)
@ -95,7 +96,7 @@ void MainWindow::updateTree(RNR::Instance* root_instance)
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());
}
@ -117,5 +118,6 @@ void MainWindow::resizeEvent(QResizeEvent* event)
{
QSize new_size = event->size();
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 <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
{
@ -15,32 +25,51 @@ namespace RNR
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
{
private:
const void* m_object;
std::string m_name;
std::string m_description;
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:
ReflectionProperty(std::string name, ReflectionPropertyAccess access, T(*getter), T(*setter))
{
this->m_name = name;
this->m_access = access;
this->getter = getter;
this->setter = setter;
}
// this == m_object
// getter returns an address to the value,
// setter sets the value to the contents of the address
ReflectionProperty(const void* m_object,
std::string name,
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)
{
return this->m_access <= accessor;
}
bool access(ReflectionPropertyAccess accessor) { return this->m_access <= accessor; }
std::string name() { return m_name; }
std::string description() { return m_description; }
char* class_name()
{
return boost::typeindex::type_id<T>().pretty_name();
}
T (*getter)();
void (*setter)(T val);
std::string toString();
};
}

View File

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

View File

@ -7,18 +7,17 @@
namespace RNR
{
class PartInstance : public PVInstance, public Ogre::Renderable
class PartInstance : public PVInstance
{
protected:
int m_brickColor;
Ogre::MaterialPtr m_material;
Ogre::Matrix4 m_matrix;
Ogre::Vector3 m_position;
Ogre::LightList m_nearbyLights;
Ogre::Vector3 m_size;
Ogre::Vector4 m_color;
static Ogre::MeshPtr m_partMesh;
virtual void deserializeProperty(char* prop_name, pugi::xml_node prop);
virtual void addProperties(std::vector<ReflectionProperty>& properties);
std::string mesh_id;
public:
PartInstance();
@ -31,10 +30,5 @@ namespace RNR
void setBrickColor(int brickcolor) { m_brickColor = 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 <pugixml.hpp>
#include <App/Script/ReflectionProperty.hpp>
namespace RNR
{
class World;
@ -18,6 +20,7 @@ namespace RNR
protected:
static World* world;
virtual void deserializeProperty(char* prop_name, pugi::xml_node prop) {};
virtual void addProperties(std::vector<ReflectionProperty>& properties) {};
private:
@ -32,6 +35,7 @@ namespace RNR
Instance();
~Instance();
virtual std::vector<ReflectionProperty> getProperties();
void deserializeXmlProperty(pugi::xml_node prop);
bool contains(RNR::Instance* child);

View File

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

View File

@ -1,6 +1,7 @@
#pragma once
#include <string>
#include <memory>
#include <sstream>
#include <random>
#include <ios>
@ -13,5 +14,16 @@ namespace RNR
public:
static uint8_t random_char();
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/V8/Tree/Instance.hpp>
#include <App/CoordinateFrame.hpp>
#include <Helpers/Strings.hpp>
#include <format>
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
{
Ogre::MeshPtr PartInstance::m_partMesh = 0;
PartInstance::PartInstance() : m_matrix(), PVInstance(), Ogre::Renderable(), m_size(2.f, STUD_HEIGHT, 4.f)
PartInstance::PartInstance() : m_matrix(), PVInstance(), m_size(2.f, STUD_HEIGHT, 4.f)
{
setName("Part");
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);
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()
@ -26,46 +18,6 @@ namespace RNR
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)
{
if(prop_name == std::string("size"))
@ -83,4 +35,17 @@ namespace RNR
else
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();
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::Vector2 uvs;
std::string surf_name = std::string(surface->getMaterialName().c_str());
if(surf_name == "TopMaterial")
uvs = Ogre::Vector2(part_size.x, part_size.z);
@ -53,13 +52,12 @@ namespace RNR
else if(surf_name == "FrontMaterial")
uvs = Ogre::Vector2(-part_size.x, part_size.z);
texture->setTextureScale(uvs.x,uvs.y);
}
m_geom->addEntity(m_partEntity,
child_part->getCFrame().getPosition(),
Ogre::Quaternion(child_part->getCFrame().getRotation()),
child_part->getSize());
child_part->getCFrame().getPosition(),
Ogre::Quaternion(child_part->getCFrame().getRotation()),
child_part->getSize());
}
for(auto& child : *instance->getChildren())
buildGeomInstance(child);

View File

@ -17,6 +17,31 @@ namespace RNR
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)
{
pugi::xml_attribute prop_name = prop.attribute("name");

View File

@ -15,4 +15,16 @@ namespace RNR
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)));
}
}