terrain editor - WIP
This commit is contained in:
parent
e0e0611d9f
commit
30296d20ae
10 changed files with 193 additions and 111 deletions
|
@ -444,22 +444,33 @@ void PropertyView::on_editScriptClicked()
|
|||
|
||||
void PropertyView::addTerrainCustomProperties()
|
||||
{
|
||||
/*QTreeWidgetItem* tools_item = new QTreeWidgetItem(QStringList() << "Tools");
|
||||
m_ui->propertyList->topLevelItem(0)->insertChild(0, tools_item);
|
||||
QWidget* widget = new QWidget();
|
||||
QHBoxLayout* layout = new QHBoxLayout(widget);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
QPushButton* compile_button = new QPushButton("Play/Pause", widget);
|
||||
QSlider* slider = new QSlider(Qt::Orientation::Horizontal, widget);
|
||||
slider->setObjectName("animation_frame_slider");
|
||||
slider->setMinimum(0);
|
||||
int frame_count = static_cast<Lumix::AnimationSystem*>(cmp.system)->getFrameCount(cmp);
|
||||
slider->setMaximum(frame_count);
|
||||
layout->addWidget(compile_button);
|
||||
layout->addWidget(slider);
|
||||
m_ui->propertyList->setItemWidget(tools_item, 1, widget);
|
||||
connect(compile_button, &QPushButton::clicked, this, &PropertyView::on_animablePlayPause);
|
||||
connect(slider, &QSlider::valueChanged, this, &PropertyView::on_animableTimeSet);*/
|
||||
QSlider* slider = new QSlider(Qt::Orientation::Horizontal);
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem(QStringList() << "Brush size");
|
||||
m_ui->propertyList->topLevelItem(0)->insertChild(0, item);
|
||||
m_ui->propertyList->setItemWidget(item, 1, slider);
|
||||
slider->setMinimum(1);
|
||||
slider->setMaximum(100);
|
||||
connect(slider, &QSlider::valueChanged, this, &PropertyView::on_terrainBrushSizeChanged);
|
||||
|
||||
slider = new QSlider(Qt::Orientation::Horizontal);
|
||||
item = new QTreeWidgetItem(QStringList() << "Brush strength");
|
||||
m_ui->propertyList->topLevelItem(0)->insertChild(1, item);
|
||||
m_ui->propertyList->setItemWidget(item, 1, slider);
|
||||
slider->setMinimum(-100);
|
||||
slider->setMaximum(100);
|
||||
connect(slider, &QSlider::valueChanged, this, &PropertyView::on_terrainBrushStrengthChanged);
|
||||
}
|
||||
|
||||
|
||||
void PropertyView::on_terrainBrushStrengthChanged(int value)
|
||||
{
|
||||
m_server->setTerrainBrushStrength(value / 50000.0f);
|
||||
}
|
||||
|
||||
|
||||
void PropertyView::on_terrainBrushSizeChanged(int value)
|
||||
{
|
||||
m_server->setTerrainBrushSize(value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -596,23 +607,6 @@ void PropertyView::updateValues()
|
|||
Lumix::Component animable = m_selected_entity.getComponent(crc32("animable"));
|
||||
if (animable.isValid())
|
||||
{
|
||||
#if 0
|
||||
QTreeWidgetItem* tools_item = new QTreeWidgetItem(QStringList() << "Tools");
|
||||
m_ui->propertyList->topLevelItem(0)->insertChild(0, tools_item);
|
||||
QWidget* widget = new QWidget();
|
||||
QHBoxLayout* layout = new QHBoxLayout(widget);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
QPushButton* compile_button = new QPushButton("Play/Pause", widget);
|
||||
QSlider* slider = new QSlider(Qt::Orientation::Horizontal, widget);
|
||||
slider->s
|
||||
slider->setMinimum(0);
|
||||
slider->setMaximum(100);
|
||||
layout->addWidget(compile_button);
|
||||
layout->addWidget(slider);
|
||||
m_ui->propertyList->setItemWidget(tools_item, 1, widget);
|
||||
connect(compile_button, &QPushButton::clicked, this, &PropertyView::on_animablePlayPause);
|
||||
connect(slider, &QSlider::valueChanged, this, &PropertyView::on_animableTimeSet);
|
||||
#endif
|
||||
int frame_count = static_cast<Lumix::AnimationSystem*>(animable.system)->getFrameCount(animable);
|
||||
m_ui->propertyList->findChild<QSlider*>("animation_frame_slider")->setMaximum(frame_count);
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ private slots:
|
|||
void on_editScriptClicked();
|
||||
void on_animablePlayPause();
|
||||
void on_animableTimeSet(int value);
|
||||
void on_terrainBrushSizeChanged(int value);
|
||||
void on_terrainBrushStrengthChanged(int value);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TerrainEditor</class>
|
||||
<widget class="QWidget" name="TerrainEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>+</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Brush size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSlider" name="brushSizeSlider">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>-</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -17,6 +17,8 @@
|
|||
#include "core/map.h"
|
||||
#include "core/matrix.h"
|
||||
#include "core/profiler.h"
|
||||
#include "core/resource_manager.h"
|
||||
#include "core/resource_manager_base.h"
|
||||
#include "editor/editor_icon.h"
|
||||
#include "editor/gizmo.h"
|
||||
#include "editor/property_descriptor.h"
|
||||
|
@ -24,9 +26,11 @@
|
|||
#include "engine/iplugin.h"
|
||||
#include "engine/plugin_manager.h"
|
||||
#include "graphics/irender_device.h"
|
||||
#include "graphics/material.h"
|
||||
#include "graphics/model.h"
|
||||
#include "graphics/pipeline.h"
|
||||
#include "graphics/renderer.h"
|
||||
#include "graphics/texture.h"
|
||||
#include "core/input_system.h"
|
||||
#include "core/MT/mutex.h"
|
||||
#include "script/script_system.h"
|
||||
|
@ -185,18 +189,138 @@ struct WorldEditorImpl : public WorldEditor
|
|||
}
|
||||
else if (hit.m_is_hit)
|
||||
{
|
||||
selectEntity(hit.m_component.entity);
|
||||
m_mouse_mode = MouseMode::TRANSFORM;
|
||||
m_gizmo.startTransform(m_camera.getComponent(CAMERA_HASH), x, y, Gizmo::TransformMode::CAMERA_XZ);
|
||||
onEntityMouseDown(hit, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void addTerrainLevel(Component terrain, const RayCastModelHit& hit, float rel_amount, float radius)
|
||||
{
|
||||
string material_path;
|
||||
static_cast<RenderScene*>(terrain.system)->getTerrainMaterial(hit.m_component, material_path);
|
||||
Material* material = static_cast<Material*>(m_engine.getResourceManager().get(ResourceManager::MATERIAL)->get(material_path));
|
||||
Vec3 hit_pos = hit.m_origin + hit.m_dir * hit.m_t;
|
||||
Texture* heightmap = material->getTexture(0);
|
||||
heightmap = heightmap;
|
||||
Matrix entity_mtx = hit.m_component.entity.getMatrix();
|
||||
entity_mtx.fastInverse();
|
||||
Vec3 local_pos = entity_mtx.multiplyPosition(hit_pos);
|
||||
|
||||
int w = heightmap->getWidth();
|
||||
if (heightmap->getBytesPerPixel() == 4)
|
||||
{
|
||||
int from_x = Math::maxValue((int)(local_pos.x - radius), 0);
|
||||
int to_x = Math::minValue((int)(local_pos.x + radius), heightmap->getWidth());
|
||||
int from_z = Math::maxValue((int)(local_pos.z - radius), 0);
|
||||
int to_z = Math::minValue((int)(local_pos.z + radius), heightmap->getHeight());
|
||||
|
||||
float amount = rel_amount * 255;
|
||||
|
||||
for (int i = from_x, end = to_x; i < end; ++i)
|
||||
{
|
||||
for (int j = from_z, end2 = to_z; j < end2; ++j)
|
||||
{
|
||||
float dist = sqrt((local_pos.x - i) * (local_pos.x - i) + (local_pos.z - j) * (local_pos.z - j));
|
||||
float add_rel = 1.0f - Math::minValue(dist / radius, 1.0f);
|
||||
uint8_t add = (uint8_t)(add_rel * amount);
|
||||
add = Math::minValue(add, (uint8_t)(255 - heightmap->getData()[4 * (i + j * w)]));
|
||||
heightmap->getData()[4 * (i + j * w)] += add;
|
||||
heightmap->getData()[4 * (i + j * w) + 1] += add;
|
||||
heightmap->getData()[4 * (i + j * w) + 2] += add;
|
||||
heightmap->getData()[4 * (i + j * w) + 3] += add;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (heightmap->getBytesPerPixel() == 2)
|
||||
{
|
||||
uint16_t* data = reinterpret_cast<uint16_t*>(heightmap->getData());
|
||||
int from_x = Math::maxValue((int)(local_pos.x - radius), 0);
|
||||
int to_x = Math::minValue((int)(local_pos.x + radius), heightmap->getWidth());
|
||||
int from_z = Math::maxValue((int)(local_pos.z - radius), 0);
|
||||
int to_z = Math::minValue((int)(local_pos.z + radius), heightmap->getHeight());
|
||||
|
||||
float amount = rel_amount * (256 * 256 - 1);
|
||||
|
||||
for (int i = from_x, end = to_x; i < end; ++i)
|
||||
{
|
||||
for (int j = from_z, end2 = to_z; j < end2; ++j)
|
||||
{
|
||||
float dist = sqrt((local_pos.x - i) * (local_pos.x - i) + (local_pos.z - j) * (local_pos.z - j));
|
||||
float add_rel = 1.0f - Math::minValue(dist / radius, 1.0f);
|
||||
uint16_t add = (uint16_t)(add_rel * amount);
|
||||
if (rel_amount > 0)
|
||||
{
|
||||
add = Math::minValue(add, (uint16_t)((256 * 256 - 1) - data[i + j * w]));
|
||||
}
|
||||
else if ((uint16_t)(data[i + j * w] + add) > data[i + j * w])
|
||||
{
|
||||
add = (uint16_t)0 - data[i + j * w];
|
||||
}
|
||||
data[i + j * w] = data[i + j * w] + add;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(false);
|
||||
}
|
||||
heightmap->onDataUpdated();
|
||||
}
|
||||
|
||||
|
||||
void onEntityMouseDown(const RayCastModelHit& hit, int x, int y)
|
||||
{
|
||||
Entity entity = hit.m_component.entity;
|
||||
if (m_selected_entity == entity)
|
||||
{
|
||||
Component terrain = entity.getComponent(TERRAIN_HASH);
|
||||
if (terrain.isValid())
|
||||
{
|
||||
Vec3 hit_pos = hit.m_origin + hit.m_dir * hit.m_t;
|
||||
m_mouse_mode = MouseMode::CUSTOM;
|
||||
addTerrainLevel(terrain, hit, m_terrain_brush_strength, (float)m_terrain_brush_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
selectEntity(entity);
|
||||
m_mouse_mode = MouseMode::TRANSFORM;
|
||||
m_gizmo.startTransform(m_camera.getComponent(CAMERA_HASH), x, y, Gizmo::TransformMode::CAMERA_XZ);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void setTerrainBrushStrength(float value) override
|
||||
{
|
||||
m_terrain_brush_strength = value;
|
||||
}
|
||||
|
||||
|
||||
virtual void setTerrainBrushSize(int value) override
|
||||
{
|
||||
m_terrain_brush_size = value;
|
||||
}
|
||||
|
||||
|
||||
virtual void onMouseMove(int x, int y, int relx, int rely, int mouse_flags) override
|
||||
{
|
||||
switch (m_mouse_mode)
|
||||
{
|
||||
case MouseMode::CUSTOM:
|
||||
{
|
||||
Component terrain = m_selected_entity.getComponent(TERRAIN_HASH);
|
||||
Component camera_cmp = m_camera.getComponent(CAMERA_HASH);
|
||||
RenderScene* scene = static_cast<RenderScene*>(camera_cmp.system);
|
||||
Vec3 origin, dir;
|
||||
scene->getRay(camera_cmp, (float)x, (float)y, origin, dir);
|
||||
RayCastModelHit hit = scene->castRay(origin, dir);
|
||||
if (hit.m_is_hit)
|
||||
{
|
||||
addTerrainLevel(terrain, hit, m_terrain_brush_strength, (float)m_terrain_brush_size);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MouseMode::NAVIGATE:
|
||||
rotateCamera(relx, rely);
|
||||
break;
|
||||
|
@ -506,6 +630,8 @@ struct WorldEditorImpl : public WorldEditor
|
|||
m_selected_entity = Entity::INVALID;
|
||||
m_edit_view_render_device = NULL;
|
||||
m_universe_path = "";
|
||||
m_terrain_brush_size = 10;
|
||||
m_terrain_brush_strength = 0.01f;
|
||||
}
|
||||
|
||||
|
||||
|
@ -722,7 +848,8 @@ struct WorldEditorImpl : public WorldEditor
|
|||
NONE,
|
||||
SELECT,
|
||||
NAVIGATE,
|
||||
TRANSFORM
|
||||
TRANSFORM,
|
||||
CUSTOM
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -750,6 +877,8 @@ struct WorldEditorImpl : public WorldEditor
|
|||
bool m_toggle_game_mode_requested;
|
||||
Path m_universe_path;
|
||||
Path m_base_path;
|
||||
int m_terrain_brush_size;
|
||||
float m_terrain_brush_strength;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ namespace Lumix
|
|||
virtual void setWireframe(bool is_wireframe) = 0;
|
||||
virtual void lookAtSelected() = 0;
|
||||
virtual const char* getBasePath() = 0;
|
||||
virtual void setTerrainBrushSize(int value) = 0;
|
||||
virtual void setTerrainBrushStrength(float value) = 0;
|
||||
virtual Entity getSelectedEntity() const = 0;
|
||||
virtual const IPropertyDescriptor& getPropertyDescriptor(uint32_t type, uint32_t name_hash) = 0;
|
||||
virtual DelegateList<void(Entity&)>& entitySelected() = 0;
|
||||
|
|
|
@ -100,7 +100,7 @@ namespace Lumix
|
|||
virtual void getTerrainXZScale(Component cmp, float& scale) = 0;
|
||||
virtual void setTerrainYScale(Component cmp, const float& scale) = 0;
|
||||
virtual void getTerrainYScale(Component cmp, float& scale) = 0;
|
||||
|
||||
|
||||
protected:
|
||||
virtual ~RenderScene() {}
|
||||
};
|
||||
|
|
|
@ -304,12 +304,11 @@ namespace Lumix
|
|||
if (Math::getRayAABBIntersection(rel_origin, rel_dir, m_root->m_min, size, start))
|
||||
{
|
||||
Vec3 p = start;
|
||||
while (p.x >= m_root->m_min.x && p.x <= m_root->m_min.x + m_root->m_size * m_xz_scale
|
||||
&& p.z >= m_root->m_min.z && p.z <= m_root->m_min.z + m_root->m_size * m_xz_scale)
|
||||
int hx = (int)(p.x / m_xz_scale);
|
||||
int hz = (int)(p.z / m_xz_scale);
|
||||
while (hx >= 0 && hz >= 0 && hx < m_width - 1 && hz < m_height - 1)
|
||||
{
|
||||
float t;
|
||||
int hx = (int)(p.x / m_xz_scale);
|
||||
int hz = (int)(p.z / m_xz_scale);
|
||||
float x = hx * m_xz_scale;
|
||||
float z = hz * m_xz_scale;
|
||||
Vec3 p0(x, getHeight(hx, hz), z);
|
||||
|
@ -332,7 +331,9 @@ namespace Lumix
|
|||
hit.m_t = t;
|
||||
return hit;
|
||||
}
|
||||
p += dir;
|
||||
p += rel_dir;
|
||||
hx = (int)(p.x / m_xz_scale);
|
||||
hz = (int)(p.z / m_xz_scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -449,6 +449,24 @@ void Texture::apply(int unit)
|
|||
}
|
||||
|
||||
|
||||
void Texture::onDataUpdated()
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
if (m_BPP == 4)
|
||||
{
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &m_data[0]);
|
||||
}
|
||||
else if (m_BPP == 2)
|
||||
{
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, m_width, m_height, 0, GL_RED, GL_UNSIGNED_SHORT, &m_data[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Texture::loadRaw(FS::IFile& file)
|
||||
{
|
||||
PROFILE_FUNCTION();
|
||||
|
|
|
@ -26,9 +26,11 @@ class LUMIX_ENGINE_API Texture : public Resource
|
|||
int getHeight() const { return m_height; }
|
||||
int getBytesPerPixel() const { return m_BPP; }
|
||||
const uint8_t* getData() const { return m_data.empty() ? NULL : &m_data[0]; }
|
||||
uint8_t* getData() { return m_data.empty() ? NULL : &m_data[0]; }
|
||||
void addDataReference();
|
||||
void removeDataReference();
|
||||
|
||||
void onDataUpdated();
|
||||
|
||||
private:
|
||||
void loaded(FS::IFile* file, bool success, FS::FileSystem& fs);
|
||||
bool loadDDS(FS::IFile& file);
|
||||
|
|
|
@ -582,7 +582,7 @@ struct PhysicsSceneImpl : public PhysicsScene
|
|||
hfDesc.thickness = -1;
|
||||
|
||||
physx::PxHeightField* heightfield = m_system->m_impl->m_physics->createHeightField(hfDesc);
|
||||
float height_scale = bytes_per_pixel == 2 ? 1 / (256 * 256.0f) : 1 / 255.0f;
|
||||
float height_scale = bytes_per_pixel == 2 ? 1 / (256 * 256.0f - 1) : 1 / 255.0f;
|
||||
physx::PxHeightFieldGeometry hfGeom(heightfield, physx::PxMeshGeometryFlags(), height_scale * terrain->m_y_scale, terrain->m_xz_scale, terrain->m_xz_scale);
|
||||
if (terrain->m_actor)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue