improved editing of materials

This commit is contained in:
Mikulas Florek 2015-05-28 23:30:50 +02:00
parent cb28c53a0a
commit 240ed44ed8
11 changed files with 300 additions and 124 deletions

View file

@ -238,6 +238,7 @@
<ClCompile Include="..\..\..\tmp\studio\moc_profilergraph.cpp" />
<ClCompile Include="..\..\..\tmp\studio\moc_profilerui.cpp" />
<ClCompile Include="..\..\..\tmp\studio\moc_property_view.cpp" />
<ClCompile Include="..\..\..\tmp\studio\moc_resource_model.cpp" />
<ClCompile Include="..\..\..\tmp\studio\moc_sceneview.cpp" />
<ClCompile Include="..\..\..\tmp\studio\moc_scriptcompiler.cpp" />
<ClCompile Include="..\..\..\tmp\studio\moc_scriptcompilerwidget.cpp" />
@ -318,7 +319,20 @@
<ClInclude Include="..\..\..\src\studio\file_path_edit.h" />
<ClInclude Include="..\..\..\src\studio\metadata.h" />
<ClInclude Include="..\..\..\src\studio\property_view\dynamic_object_model.h" />
<ClInclude Include="..\..\..\src\studio\property_view\resource_model.h" />
<CustomBuild Include="..\..\..\src\studio\property_view\resource_model.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='AppVeyor|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='AppVeyor|Win32'">Moc%27ing resource_model.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='AppVeyor|Win32'">.\..\..\..\tmp\studio\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='AppVeyor|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\..\..\..\tmp\studio\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_OPENGL_ES_2 -DQT_OPENGL_ES_2_ANGLE -DNDEBUG -DQT_DLL "-I.\..\.." "-I.\..\..\..\src\studio" "-I.\..\..\..\src" "-I.\..\..\..\external\crunch\include" "-I.\..\..\..\external\assimp\include" "-I.\..\..\..\external\glew\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\mkspecs\win32-msvc2013" "-I.\..\..\..\tmp\studio" "-I.\..\..\..\tmp\studio\GeneratedFiles"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing resource_model.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\..\..\..\tmp\studio\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\..\..\..\tmp\studio\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_OPENGL_ES_2 -DQT_OPENGL_ES_2_ANGLE -DQT_DLL "-I.\..\.." "-I.\..\..\..\src\studio" "-I.\..\..\..\src" "-I.\..\..\..\external\crunch\include" "-I.\..\..\..\external\assimp\include" "-I.\..\..\..\external\glew\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2013" "-I.\..\..\..\tmp\studio" "-I.\..\..\..\tmp\studio\GeneratedFiles"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing resource_model.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\..\..\..\tmp\studio\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\..\..\..\tmp\studio\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DQT_NO_DEBUG -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_OPENGL_ES_2 -DQT_OPENGL_ES_2_ANGLE -DNDEBUG -DQT_DLL "-I.\..\.." "-I.\..\..\..\src\studio" "-I.\..\..\..\src" "-I.\..\..\..\external\crunch\include" "-I.\..\..\..\external\assimp\include" "-I.\..\..\..\external\glew\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\mkspecs\win32-msvc2013" "-I.\..\..\..\tmp\studio" "-I.\..\..\..\tmp\studio\GeneratedFiles"</Command>
</CustomBuild>
<ClInclude Include="..\..\..\tmp\studio\GeneratedFiles\ui_assetbrowser.h" />
<ClInclude Include="..\..\..\tmp\studio\GeneratedFiles\ui_entity_list.h" />
<ClInclude Include="..\..\..\tmp\studio\GeneratedFiles\ui_entity_template_list.h" />

View file

@ -202,6 +202,9 @@
<ClCompile Include="..\..\..\src\studio\metadata.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\tmp\studio\moc_resource_model.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\..\src\studio\assetbrowser.h">
@ -300,6 +303,9 @@
<CustomBuild Include="..\..\..\src\studio\notifications.h">
<Filter>Header Files</Filter>
</CustomBuild>
<CustomBuild Include="..\..\..\src\studio\property_view\resource_model.h">
<Filter>Header Files\property_view</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\studio\file_system_watcher.h">
@ -365,9 +371,6 @@
<ClInclude Include="..\..\..\src\studio\animation_editor\animation_editor_commands.h">
<Filter>Header Files\animation_editor</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\studio\property_view\resource_model.h">
<Filter>Header Files\property_view</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\studio\property_view\dynamic_object_model.h">
<Filter>Header Files\property_view</Filter>
</ClInclude>

View file

@ -97,7 +97,7 @@ namespace Lumix
void Resource::addDependency(Resource& dependent_resource)
{
dependent_resource.m_cb.bind<Resource, &Resource::onStateChanged>(this);
if (!dependent_resource.isReady())
if (!dependent_resource.isReady() && !dependent_resource.isFailure())
{
incrementDepCount();
}
@ -106,7 +106,7 @@ namespace Lumix
void Resource::removeDependency(Resource& dependent_resource)
{
dependent_resource.m_cb.unbind<Resource, &Resource::onStateChanged>(this);
if (!dependent_resource.isReady() && !dependent_resource.isFailure())
if (!dependent_resource.isReady())
{
decrementDepCount();
}

View file

@ -54,7 +54,12 @@ void Material::apply(Renderer& renderer, PipelineInstance& pipeline) const
}
for (int i = 0, c = m_textures.size(); i < c; ++i)
{
m_textures[i].m_texture->apply(i);
const TextureInfo& info = m_textures[i];
info.m_texture->apply(i);
if (info.m_uniform_hash)
{
renderer.setUniform(*m_shader, m_textures[i].m_uniform, info.m_uniform_hash, i);
}
}
renderer.enableAlphaToCoverage(m_is_alpha_to_coverage);
renderer.enableZTest(m_is_z_test);
@ -122,7 +127,7 @@ void Material::doUnload(void)
bool Material::save(JsonSerializer& serializer)
{
serializer.beginObject();
serializer.serialize("shader", m_shader->getPath().c_str());
serializer.serialize("shader", m_shader ? m_shader->getPath().c_str() : "");
for (int i = 0; i < m_textures.size(); ++i)
{
char path[LUMIX_MAX_PATH];
@ -141,7 +146,6 @@ bool Material::save(JsonSerializer& serializer)
{
serializer.beginObject();
serializer.serialize("name", m_uniforms[i].m_name);
serializer.serialize("is_editable", m_uniforms[i].m_is_editable);
switch (m_uniforms[i].m_type)
{
case Uniform::FLOAT:
@ -190,11 +194,7 @@ void Material::deserializeUniforms(JsonSerializer& serializer)
while (!serializer.isObjectEnd())
{
serializer.deserializeLabel(label, 255);
if (strcmp(label, "is_editable") == 0)
{
serializer.deserialize(uniform.m_is_editable, false);
}
else if (strcmp(label, "name") == 0)
if (strcmp(label, "name") == 0)
{
serializer.deserialize(uniform.m_name, Uniform::MAX_NAME_LENGTH, "");
uniform.m_name_hash = crc32(uniform.m_name);
@ -235,6 +235,7 @@ void Material::deserializeUniforms(JsonSerializer& serializer)
serializer.deserializeArrayEnd();
}
void Material::removeTexture(int i)
{
if (m_textures[i].m_texture)
@ -245,6 +246,14 @@ void Material::removeTexture(int i)
m_textures.erase(i);
}
void Material::setTextureUniform(int index, const char* uniform)
{
copyString(m_textures[index].m_uniform, sizeof(m_textures[index].m_uniform), uniform);
m_textures[index].m_uniform_hash = crc32(uniform);
}
Texture* Material::getTextureByUniform(const char* uniform) const
{
for (int i = 0; i < m_textures.size(); ++i)
@ -267,16 +276,17 @@ void Material::setTexturePath(int i, const Path& path)
void Material::setTexture(int i, Texture* texture)
{
if (m_textures[i].m_texture)
{
removeDependency(*m_textures[i].m_texture);
m_resource_manager.get(ResourceManager::TEXTURE)->unload(*m_textures[i].m_texture);
}
Texture* old_texture = m_textures[i].m_texture;
if (texture)
{
addDependency(*texture);
}
m_textures[i].m_texture = texture;
if (old_texture)
{
removeDependency(*old_texture);
m_resource_manager.get(ResourceManager::TEXTURE)->unload(*old_texture);
}
}
void Material::addTexture(Texture* texture)
@ -355,12 +365,8 @@ bool Material::deserializeTexture(JsonSerializer& serializer, const char* materi
}
else if (strcmp("uniform", label) == 0)
{
Uniform& uniform = m_uniforms.pushEmpty();
serializer.deserialize(uniform.m_name, Uniform::MAX_NAME_LENGTH, "");
copyString(info.m_uniform, sizeof(info.m_uniform), uniform.m_name);
uniform.m_name_hash = crc32(uniform.m_name);
uniform.m_type = Uniform::INT;
uniform.m_int = info.m_texture ? m_textures.size() - 1 : m_textures.size();
serializer.deserialize(label, sizeof(label), "");
setTextureUniform(m_textures.size() - 1, label);
}
else if (strcmp("keep_data", label) == 0)
{
@ -394,6 +400,7 @@ void Material::loaded(FS::IFile* file, bool success, FS::FileSystem& fs)
PROFILE_FUNCTION();
if(success)
{
m_uniforms.clear();
JsonSerializer serializer(*file, JsonSerializer::READ, m_path.c_str(), m_allocator);
serializer.deserializeObjectBegin();
char path[LUMIX_MAX_PATH];

View file

@ -33,7 +33,7 @@ public:
struct Uniform
{
Uniform() : m_is_editable(false) {}
Uniform() {}
enum Type
{
@ -54,7 +54,6 @@ public:
float m_float;
float m_matrix[16];
};
bool m_is_editable;
};
public:
@ -79,6 +78,8 @@ public:
int getTextureCount() const { return m_textures.size(); }
Texture* getTexture(int i) const { return m_textures[i].m_texture; }
Texture* getTextureByUniform(const char* uniform) const;
void setTextureUniform(int i, const char* uniform);
const char* getTextureUniform(int i) const { return m_textures[i].m_uniform; }
void addTexture(Texture* texture);
void setTexture(int i, Texture* texture);
void setTexturePath(int i, const Path& path);
@ -121,10 +122,12 @@ private:
m_texture = NULL;
m_keep_data = false;
m_uniform[0] = '\0';
m_uniform_hash = 0;
}
Texture* m_texture;
bool m_keep_data;
uint32_t m_uniform_hash;
char m_uniform[Uniform::MAX_NAME_LENGTH];
};

View file

@ -874,9 +874,12 @@ namespace Lumix
m_allocator.deleteObject(m_root);
m_heightmap = m_material->getTextureByUniform("hm_texture");
m_splatmap = m_material->getTextureByUniform("splat_texture");
m_width = m_heightmap->getWidth();
m_height = m_heightmap->getHeight();
m_root = generateQuadTree((float)m_width);
if (m_heightmap && m_splatmap)
{
m_width = m_heightmap->getWidth();
m_height = m_heightmap->getHeight();
m_root = generateQuadTree((float)m_width);
}
}
}

View file

@ -405,6 +405,7 @@ void AssetBrowser::on_treeView_customContextMenuRequested(const QPoint &pos)
QAction* delete_file_action = new QAction("Delete", menu);
QAction* rename_file_action = new QAction("Rename", menu);
QAction* create_dir_action = new QAction("Create directory", menu);
QAction* create_material_action = new QAction("Create material", menu);
QAction* import_asset_action = new QAction("Import asset", menu);
QAction* reimport_asset_action = new QAction("Reimport asset", menu);
@ -414,6 +415,7 @@ void AssetBrowser::on_treeView_customContextMenuRequested(const QPoint &pos)
{
menu->addAction(import_asset_action);
menu->addAction(create_dir_action);
menu->addAction(create_material_action);
}
char relative_path[LUMIX_MAX_PATH];
@ -468,6 +470,20 @@ void AssetBrowser::on_treeView_customContextMenuRequested(const QPoint &pos)
QDir().mkdir(file_info.absoluteFilePath() + "/" + text);
}
}
else if (selected_action == create_material_action)
{
auto material_name = QInputDialog::getText(nullptr, "Set filename", "Filename", QLineEdit::Normal, ".mat");
auto path = file_info.absoluteFilePath() + "/" + material_name;
QFile file(path);
if (!file.open(QIODevice::WriteOnly))
{
QMessageBox::warning(nullptr, "Error", QString("Could not create file %1").arg(path), QMessageBox::StandardButton::Ok);
}
else
{
file.close();
}
}
}
void AssetBrowser::setExtentionsFilter(const QStringList& filters)

View file

@ -67,6 +67,10 @@ void PropertyView::setSelectedResourceFilename(const char* filename)
{
m_world_editor->selectEntities(NULL, 0);
ResourceModel* model = new ResourceModel(*m_world_editor, Lumix::Path(filename));
connect(model, &ResourceModel::modelReady, [this]()
{
m_ui->treeView->expandToDepth(1);
});
if (model->getResource())
{
setModel(model, new DynamicObjectItemDelegate(this));

View file

@ -77,29 +77,6 @@ class DynamicObjectModel : public QAbstractItemModel
class Object
{
public:
class ArrayProperty
{
public:
ArrayProperty(Node* node, int index)
: m_node(node)
, m_index(index)
{}
template <typename Callback>
void onClick(Callback callback)
{
for (int i = 0; i < m_node->m_children.size(); ++i)
{
Node* node = m_node->m_children[i]->m_children[m_index];
node->onClick = [i, callback](QWidget* widget, QPoint p) { callback(i, widget, p); };
}
}
private:
Node* m_node;
int m_index;
};
template <typename Getter, typename Namer>
class Array
@ -118,20 +95,31 @@ class DynamicObjectModel : public QAbstractItemModel
}
}
template <typename Getter>
typename IsFunctor<Getter, Array>::type property(QString name, Getter getter)
template <typename Functor, typename Adder>
Array<Getter, Namer>& forEach(Functor functor, Adder adder)
{
for (int i = 0; i < m_node->m_children.size(); ++i)
{
Node& node = m_node->m_children[i]->addChild(name);
auto o = (m_parent->*m_getter)(i);
node.m_getter = [getter, o]() -> QVariant { return (getter)(o); };
}
return *this;
auto array_node = m_node;
auto parent = m_parent;
auto getter = m_getter;
m_node->onCreateEditor = [=](QWidget* parent_widget, const QStyleOptionViewItem&) -> QWidget* {
auto button = new QPushButton(" + ", parent_widget);
button->connect(button, &QPushButton::clicked, [=](){
if (adder())
{
int i = array_node->m_children.size();
Node& child = array_node->addChild(QString("%1").arg(i));
auto o = (parent->*getter)(i);
functor(i, o, child);
}
});
return button;
};
m_node->m_setter = [](const QVariant&) {};
return forEach(functor);
}
template <typename Functor>
void forEach(Functor functor)
Array<Getter, Namer>& forEach(Functor functor)
{
for (int i = 0; i < m_node->m_children.size(); ++i)
{
@ -139,24 +127,9 @@ class DynamicObjectModel : public QAbstractItemModel
Node& node = *m_node->m_children[i];
functor(i, o, node);
}
}
template <typename Getter>
typename IsNotFunctor<Getter, Array>::type property(QString name, Getter getter)
{
for (int i = 0; i < m_node->m_children.size(); ++i)
{
Node& node = m_node->m_children[i]->addChild(name);
auto o = (m_parent->*m_getter)(i);
node.m_getter = [getter, o]() -> QVariant { return (o->*getter)(); };
}
return *this;
}
ArrayProperty back() {
return ArrayProperty(m_node, m_node->m_children[0]->m_children.size() - 1);
}
private:
T* m_parent;
Node* m_node;
@ -181,7 +154,7 @@ class DynamicObjectModel : public QAbstractItemModel
}
template <typename Getter, typename Setter>
typename IsFunctor<Getter, Object>::type property(QString name, Getter getter, Setter setter)
Object& property(QString name, Getter getter, Setter setter)
{
Node& node = m_node->addChild(name);
T* inst = m_instance;
@ -191,9 +164,9 @@ class DynamicObjectModel : public QAbstractItemModel
};
return *this;
}
template <typename Getter>
typename IsFunctor<Getter, Object>::type property(QString name, Getter getter)
typename IsFunctor<Getter, Object&>::type property(QString name, Getter getter)
{
Node& node = m_node->addChild(name);
T* inst = m_instance;
@ -202,7 +175,7 @@ class DynamicObjectModel : public QAbstractItemModel
}
template <typename Getter>
typename IsNotFunctor<Getter, Object>::type property(QString name, Getter getter)
typename IsNotFunctor<Getter, Object&>::type property(QString name, Getter getter)
{
Node& node = m_node->addChild(name);
T* inst = m_instance;
@ -215,10 +188,16 @@ class DynamicObjectModel : public QAbstractItemModel
{
Node& node = m_node->addChild(name);
node.m_getter = []() -> QVariant { return ""; };
/*node.onCreateEditor = [adder, this](QWidget* parent, const QStyleOptionViewItem&) -> QWidget* {
auto button = new QPushButton(" + ", parent);
connect(button, &QPushButton::clicked, adder);
return button;
};
node.m_setter = [](const QVariant&) {};*/
return Array<Getter, Namer>(m_instance, count, &node, getter, namer);
}
Node& getNode() { return *m_node; }
private:

View file

@ -12,10 +12,52 @@
#include <qapplication.h>
#include <qfile.h>
#include <qfiledialog.h>
#include <qlayout.h>
#include <qlineedit.h>
#include <qpainter.h>
#include <qpushbutton.h>
FileInput::FileInput(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
m_edit = new QLineEdit(this);
layout->addWidget(m_edit);
QPushButton* browse_button = new QPushButton("...", this);
layout->addWidget(browse_button);
connect(browse_button, &QPushButton::clicked, this, &FileInput::browseClicked);
connect(m_edit, &QLineEdit::editingFinished, this, &FileInput::editingFinished);
}
void FileInput::editingFinished()
{
setValue(m_edit->text());
}
void FileInput::setValue(const QString& path)
{
m_edit->setText(path);
emit valueChanged();
}
QString FileInput::value() const
{
return m_edit->text();
}
void FileInput::browseClicked()
{
auto path = QFileDialog::getOpenFileName(this);
setValue(path);
}
static QSize getPreviewSize(Lumix::Texture* texture)
{
int w = texture->getWidth();
@ -91,12 +133,9 @@ void ResourceModel::fillModelInfo()
.property("Bone count", &Lumix::Model::getBoneCount)
.property("Bounding radius", &Lumix::Model::getBoundingRadius);
auto meshes = object.array("Meshes", model->getMeshCount(), &Lumix::Model::getMeshPtr, [](const Lumix::Mesh* mesh) -> const char* { return mesh->getName(); });
meshes.property("Triangles", &Lumix::Mesh::getTriangleCount);
/*meshes.property("Material", [](const Lumix::Mesh* mesh) -> const char* { return mesh->getMaterial()->getPath().c_str(); });
meshes.back().onClick([this, model](int index, QWidget*, QPoint) {
setResource(model->getMesh(index).getMaterial()->getPath());
});*/
meshes.forEach([this](int, const Lumix::Mesh* mesh, Node& node){
Object<Lumix::Mesh>((Lumix::Mesh*)mesh, &node)
.property("Triangles", &Lumix::Mesh::getTriangleCount);
fillMaterialInfo(mesh->getMaterial(), node.addChild("material"));
});
}
@ -140,7 +179,7 @@ void ResourceModel::saveMaterial(Lumix::Material* material)
}
void ResourceModel::showFileDialog(DynamicObjectModel::Node* node, QString filter)
void ResourceModel::showFileDialog(const DynamicObjectModel::Node* node, QString filter)
{
auto fileName = QFileDialog::getOpenFileName(NULL, "Select file", "", filter);
if (!fileName.isEmpty())
@ -159,6 +198,52 @@ void ResourceModel::setMaterialShader(Lumix::Material* material, QString value)
}
class BaseEditorProperty
{
public:
BaseEditorProperty();
virtual ~BaseEditorProperty();
virtual int childCount() const { return m_children.size(); }
virtual void addChild(BaseEditorProperty* child);
virtual void removeChild(int index);
virtual QVariant getValue() const = 0;
virtual BaseEditorProperty* getParent() const { return m_parent; }
private:
BaseEditorProperty* m_parent;
QVector<BaseEditorProperty*> m_children;
};
BaseEditorProperty::BaseEditorProperty()
: m_parent(nullptr)
{}
BaseEditorProperty::~BaseEditorProperty()
{
for (auto child : m_children)
{
delete child;
}
}
void BaseEditorProperty::addChild(BaseEditorProperty* child)
{
child->m_parent = this;
m_children.append(child);
}
void BaseEditorProperty::removeChild(int index)
{
delete m_children[index];
m_children.removeAt(index);
}
void ResourceModel::fillMaterialInfo(Lumix::Material* material, Node& node)
{
auto object = Object<Lumix::Material>(material, &node);
@ -181,7 +266,7 @@ void ResourceModel::fillMaterialInfo(Lumix::Material* material, Node& node)
.property("Shadow receiver", &Lumix::Material::isShadowReceiver, &Lumix::Material::enableShadowReceiving)
.property("Z test", &Lumix::Material::isZTest, &Lumix::Material::enableZTest)
.property("Shader",
[](Lumix::Material* material) -> QVariant { return material->getShader()->getPath().c_str(); },
[](Lumix::Material* material) -> QVariant { return material->getShader() ? material->getShader()->getPath().c_str() : ""; },
[this](Lumix::Material* material, QVariant value) { setMaterialShader(material, value.toString()); }
);
auto shader_node = object.getNode().m_children.back();
@ -190,43 +275,69 @@ void ResourceModel::fillMaterialInfo(Lumix::Material* material, Node& node)
for (int i = 0; i < material->getUniformCount(); ++i)
{
auto& uniform = material->getUniform(i);
if (uniform.m_is_editable)
{
QString name = uniform.m_name;
object.property(uniform.m_name
, [name](Lumix::Material* material) -> QVariant {
Lumix::Material::Uniform* uniform = getMaterialUniform(material, name);
if (uniform)
QString name = uniform.m_name;
object.property(uniform.m_name
, [name](Lumix::Material* material) -> QVariant {
Lumix::Material::Uniform* uniform = getMaterialUniform(material, name);
if (uniform)
{
switch (uniform->m_type)
{
switch (uniform->m_type)
{
case Lumix::Material::Uniform::FLOAT:
return uniform->m_float;
}
}
return QVariant();
}
, [name](Lumix::Material* material, const QVariant& value) {
Lumix::Material::Uniform* uniform = getMaterialUniform(material, name);
if (uniform)
{
switch (uniform->m_type)
{
case Lumix::Material::Uniform::FLOAT:
uniform->m_float = value.toFloat();
break;
}
case Lumix::Material::Uniform::FLOAT:
return uniform->m_float;
}
}
);
}
return QVariant();
}
, [name](Lumix::Material* material, const QVariant& value) {
Lumix::Material::Uniform* uniform = getMaterialUniform(material, name);
if (uniform)
{
switch (uniform->m_type)
{
case Lumix::Material::Uniform::FLOAT:
uniform->m_float = value.toFloat();
break;
}
}
}
);
}
object
.array("Textures", material->getTextureCount(), &Lumix::Material::getTexture, [](Lumix::Texture* texture) -> const char* { return texture->getPath().c_str(); })
.forEach([this](int i, Lumix::Texture* texture, Node& node) {
fillTextureInfo(texture, node);
node.m_name = QString("Texture %1").arg(i);
});
.array("Textures", material->getTextureCount(), &Lumix::Material::getTexture,
[](Lumix::Texture* texture) -> const char* { return texture->getPath().c_str(); }
)
.forEach([this, material](int i, Lumix::Texture* texture, Node& node) {
fillTextureInfo(texture, node);
Object<Lumix::Texture>(texture, &node)
.property("uniform"
, [material, i](Lumix::Texture*) -> QVariant { return material->getTextureUniform(i); }
, [material, i](Lumix::Texture*, QVariant value) { material->setTextureUniform(i, value.toString().toLatin1().data()); }
);
node.m_name = QString("Texture %1").arg(i);
node.onCreateEditor = [&node, i, texture, material](QWidget* parent, const QStyleOptionViewItem&) -> QWidget* {
auto input = new FileInput(parent);
input->setValue(texture->getPath().c_str());
input->connect(input, &FileInput::valueChanged, [&node, input]() {
node.m_setter(input->value());
});
return input;
};
node.m_setter = [material, i](const QVariant& value) {
if (value.isValid())
{
material->setTexturePath(i, Lumix::Path(value.toString().toLatin1().data()));
}
};
//node.onClick = [node, this](QWidget*, QPoint) { showFileDialog(&node, "Textures (*.dds|*.tga)"); };
}
, [material]() {
auto texture = static_cast<Lumix::Texture*>(material->getResourceManager().get(Lumix::ResourceManager::TEXTURE)->load(Lumix::Path("texture.dds")));
material->addTexture(texture);
return false;
}
);
}
@ -253,13 +364,20 @@ void ResourceModel::fillTextureInfo(Lumix::Texture* texture, Node& node)
void ResourceModel::onResourceLoaded(Lumix::Resource::State, Lumix::Resource::State new_state)
{
beginResetModel();
/*
endResetModel closes any opened property editor, if this is done in the end of this method
it can result in a crash, since the editor can access some destroyed node
*/
endResetModel();
beginResetModel();
for (int i = 0; i < getRoot().m_children.size(); ++i)
{
delete getRoot().m_children[i];
}
getRoot().m_children.clear();
if (new_state == Lumix::Resource::State::READY)
this->getRoot().m_getter = [new_state]() -> QVariant { return new_state == Lumix::Resource::State::LOADING ? "Loading..." : "Ready"; };
if (new_state == Lumix::Resource::State::READY || new_state == Lumix::Resource::State::FAILURE)
{
if (dynamic_cast<Lumix::Model*>(m_resource))
{
@ -279,4 +397,8 @@ void ResourceModel::onResourceLoaded(Lumix::Resource::State, Lumix::Resource::St
}
}
endResetModel();
if (new_state == Lumix::Resource::State::READY || new_state == Lumix::Resource::State::FAILURE)
{
emit modelReady();
}
}

View file

@ -14,8 +14,30 @@ namespace Lumix
}
class FileInput : public QWidget
{
Q_OBJECT
public:
FileInput(QWidget* parent);
void setValue(const QString& path);
QString value() const;
signals:
void valueChanged();
private:
void editingFinished();
void browseClicked();
private:
QLineEdit* m_edit;
};
class ResourceModel : public DynamicObjectModel
{
Q_OBJECT
public:
ResourceModel(Lumix::WorldEditor& editor, const Lumix::Path& path);
~ResourceModel();
@ -23,13 +45,16 @@ class ResourceModel : public DynamicObjectModel
Lumix::Resource* getResource() { return m_resource; }
void setResource(const Lumix::Path& path);
signals:
void modelReady();
private:
void onResourceLoaded(Lumix::Resource::State, Lumix::Resource::State new_state);
void fillModelInfo();
void fillMaterialInfo(Lumix::Material* material, Node& node);
void fillTextureInfo(Lumix::Texture*, Node& node);
void saveMaterial(Lumix::Material* material);
void showFileDialog(DynamicObjectModel::Node* node, QString filter);
void showFileDialog(const DynamicObjectModel::Node* node, QString filter);
void setMaterialShader(Lumix::Material* material, QString value);
private: