paint entities on terrain as command = atomic undo/redo

This commit is contained in:
Mikulas Florek 2015-09-27 22:54:02 +02:00
parent 0ea71eefa1
commit b069e36220
8 changed files with 338 additions and 248 deletions

View file

@ -257,13 +257,13 @@ public:
static IEditorCommand* createCreateInstanceCommand(WorldEditor& editor)
{
return editor.getAllocator().newObject<CreateInstanceCommand>(editor);
return LUMIX_NEW(editor.getAllocator(), CreateInstanceCommand)(editor);
}
static IEditorCommand* createCreateTemplateCommand(WorldEditor& editor)
{
return editor.getAllocator().newObject<CreateTemplateCommand>(editor);
return LUMIX_NEW(editor.getAllocator(), CreateTemplateCommand)(editor);
}
@ -339,12 +339,35 @@ public:
}
virtual Entity createInstanceNoCommand(uint32_t name_hash, const Vec3& position) override
{
int instance_index = m_instances.find(name_hash);
ASSERT(instance_index >= 0);
if (instance_index < 0) return INVALID_ENTITY;
Universe* universe = m_editor.getUniverse();
Entity entity = universe->createEntity();
universe->setPosition(entity, position);
float random_angle = Math::degreesToRadians((float)(rand() % 360));
Lumix::Quat rotation(Lumix::Vec3(0, 1, 0), random_angle);
universe->setRotation(entity, rotation);
m_instances.at(instance_index).push(entity);
Entity template_entity = m_instances.at(instance_index)[0];
const auto& template_cmps = m_editor.getComponents(template_entity);
for (const auto& cmp : template_cmps)
{
m_editor.cloneComponent(cmp, entity);
}
return entity;
}
virtual void createTemplateFromEntity(const char* name,
Entity entity) override
{
CreateTemplateCommand* command =
m_editor.getAllocator().newObject<CreateTemplateCommand>(
m_editor, name, entity);
LUMIX_NEW(m_editor.getAllocator(), CreateTemplateCommand)(m_editor, name, entity);
m_editor.executeCommand(command);
}
@ -383,9 +406,8 @@ public:
virtual Entity createInstance(const char* name,
const Vec3& position) override
{
CreateInstanceCommand* command =
m_editor.getAllocator().newObject<CreateInstanceCommand>(
*this, m_editor, name, position);
CreateInstanceCommand* command = LUMIX_NEW(m_editor.getAllocator(), CreateInstanceCommand)(
*this, m_editor, name, position);
m_editor.executeCommand(command);
return command->getEntity();
}
@ -466,7 +488,7 @@ private:
EntityTemplateSystem* EntityTemplateSystem::create(WorldEditor& editor)
{
return editor.getAllocator().newObject<EntityTemplateSystemImpl>(editor);
return LUMIX_NEW(editor.getAllocator(), EntityTemplateSystemImpl)(editor);
}

View file

@ -28,6 +28,7 @@ namespace Lumix
virtual const Array<Entity>& getInstances(uint32_t template_name_hash) = 0;
virtual Array<string>& getTemplateNames() = 0;
virtual Entity createInstance(const char* name, const Vec3& position) = 0;
virtual Entity createInstanceNoCommand(uint32_t name_hash, const Vec3& position) = 0;
virtual DelegateList<void()>& updated() = 0;
};

View file

@ -59,6 +59,8 @@ namespace Lumix
#endif
#endif
#define LUMIX_NEW(allocator, type) new ((allocator).allocate(sizeof(type))) type
#define LUMIX_LIBRARY_EXPORT __declspec(dllexport)
#define LUMIX_LIBRARY_IMPORT __declspec(dllimport)
#define LUMIX_ALIGN_OF(T) __alignof(T)

View file

@ -213,7 +213,7 @@ Entity Universe::getFirstEntity()
{
for (int i = 0; i < m_id_map.size(); ++i)
{
if (m_id_map[i] != -1)
if (m_id_map[i] >= 0)
{
return i;
}
@ -226,7 +226,7 @@ Entity Universe::getNextEntity(Entity entity)
{
for (int i = entity + 1; i < m_id_map.size(); ++i)
{
if (m_id_map[i] != -1)
if (m_id_map[i] >= 0)
{
return i;
}

View file

@ -12,7 +12,7 @@
#include "renderer/render_scene.h"
#include "renderer/texture.h"
#include "universe/universe.h"
#include "utils.h"
static const uint32_t RENDERABLE_HASH = Lumix::crc32("renderable");
@ -22,103 +22,221 @@ static const char* COLORMAP_UNIFORM = "u_texColormap";
static const char* TEX_COLOR_UNIFORM = "u_texColor";
static bool ColorPicker(const char* label, float col[3])
struct PaintEntitiesCommand : public Lumix::IEditorCommand
{
static const float HUE_PICKER_WIDTH = 20.0f;
static const float CROSSHAIR_SIZE = 7.0f;
static const ImVec2 SV_PICKER_SIZE = ImVec2(200, 200);
ImColor color(col[0], col[1], col[2]);
bool value_changed = false;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 picker_pos = ImGui::GetCursorScreenPos();
ImColor colors[] = { ImColor(255, 0, 0),
ImColor(255, 255, 0),
ImColor(0, 255, 0),
ImColor(0, 255, 255),
ImColor(0, 0, 255),
ImColor(255, 0, 255),
ImColor(255, 0, 0) };
for (int i = 0; i < 6; ++i)
PaintEntitiesCommand(Lumix::WorldEditor& editor,
Lumix::ComponentUID component,
int entity_template,
float brush_strength,
float brush_size,
const Lumix::RayCastModelHit& hit)
: m_world_editor(editor)
, m_component(component)
, m_brush_size(brush_size)
, m_brush_strength(brush_strength)
, m_selected_entity_template(entity_template)
, m_entities(editor.getAllocator())
{
draw_list->AddRectFilledMultiColor(
ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y + i * (SV_PICKER_SIZE.y / 6)),
ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10 + HUE_PICKER_WIDTH,
picker_pos.y + (i + 1) * (SV_PICKER_SIZE.y / 6)),
colors[i],
colors[i],
colors[i + 1],
colors[i + 1]);
m_center = hit.m_origin + hit.m_dir * hit.m_t;
}
float hue, saturation, value;
ImGui::ColorConvertRGBtoHSV(
color.Value.x, color.Value.y, color.Value.z, hue, saturation, value);
auto hue_color = ImColor::HSV(hue, 1, 1);
draw_list->AddLine(
ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 8, picker_pos.y + hue * SV_PICKER_SIZE.y),
ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 12 + HUE_PICKER_WIDTH,
picker_pos.y + hue * SV_PICKER_SIZE.y),
ImColor(255, 255, 255));
draw_list->AddTriangleFilledMultiColor(picker_pos,
ImVec2(picker_pos.x + SV_PICKER_SIZE.x, picker_pos.y + SV_PICKER_SIZE.y),
ImVec2(picker_pos.x, picker_pos.y + SV_PICKER_SIZE.y),
ImColor(0, 0, 0),
hue_color,
ImColor(255, 255, 255));
float x = saturation * value;
ImVec2 p(picker_pos.x + x * SV_PICKER_SIZE.x, picker_pos.y + value * SV_PICKER_SIZE.y);
draw_list->AddLine(ImVec2(p.x - CROSSHAIR_SIZE, p.y), ImVec2(p.x - 2, p.y), ImColor(255, 255, 255));
draw_list->AddLine(ImVec2(p.x + CROSSHAIR_SIZE, p.y), ImVec2(p.x + 2, p.y), ImColor(255, 255, 255));
draw_list->AddLine(ImVec2(p.x, p.y + CROSSHAIR_SIZE), ImVec2(p.x, p.y + 2), ImColor(255, 255, 255));
draw_list->AddLine(ImVec2(p.x, p.y - CROSSHAIR_SIZE), ImVec2(p.x, p.y - 2), ImColor(255, 255, 255));
ImGui::InvisibleButton("saturation_value_selector", SV_PICKER_SIZE);
if (ImGui::IsItemHovered())
virtual void undo()
{
ImVec2 mouse_pos_in_canvas = ImVec2(
ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);
if (ImGui::GetIO().MouseDown[0])
for (auto entity : m_entities)
{
mouse_pos_in_canvas.x =
Lumix::Math::minValue(mouse_pos_in_canvas.x, mouse_pos_in_canvas.y);
const auto& cmps = m_world_editor.getComponents(entity);
for (const auto& cmp : cmps)
{
cmp.scene->destroyComponent(cmp.index, cmp.type);
}
m_world_editor.getUniverse()->destroyEntity(entity);
}
m_entities.clear();
}
value = mouse_pos_in_canvas.y / SV_PICKER_SIZE.y;
saturation = value == 0 ? 0 : (mouse_pos_in_canvas.x / SV_PICKER_SIZE.x) / value;
value_changed = true;
virtual void serialize(Lumix::JsonSerializer& serializer)
{
}
virtual void deserialize(Lumix::JsonSerializer& serializer)
{
}
virtual uint32_t getType()
{
static const uint32_t type = Lumix::crc32("paint_entities_on_terrain");
return type;
}
virtual bool merge(IEditorCommand& command) { return false; }
virtual void execute() override
{
m_entities.clear();
Lumix::RenderScene* scene = static_cast<Lumix::RenderScene*>(m_component.scene);
Lumix::Matrix terrain_matrix = m_world_editor.getUniverse()->getMatrix(m_component.entity);
Lumix::Matrix inv_terrain_matrix = terrain_matrix;
inv_terrain_matrix.inverse();
auto& template_system = m_world_editor.getEntityTemplateSystem();
auto& template_names = template_system.getTemplateNames();
if (m_selected_entity_template < 0 || m_selected_entity_template >= template_names.size())
{
return;
}
const char* template_name = template_names[m_selected_entity_template].c_str();
uint32_t template_name_hash = Lumix::crc32(template_name);
Lumix::Entity tpl = template_system.getInstances(template_name_hash)[0];
if (tpl < 0) return;
Lumix::ComponentUID renderable = m_world_editor.getComponent(tpl, RENDERABLE_HASH);
if (!renderable.isValid()) return;
float w, h;
scene->getTerrainSize(m_component.index, &w, &h);
float scale = 1.0f - Lumix::Math::maxValue(0.01f, m_brush_strength);
Lumix::Model* model = scene->getRenderableModel(renderable.index);
for (int i = 0; i <= m_brush_size * m_brush_size / 1000.0f; ++i)
{
float angle = (float)(rand() % 360);
float dist = (rand() % 100 / 100.0f) * m_brush_size;
Lumix::Vec3 pos(m_center.x + cos(angle) * dist, 0, m_center.z + sin(angle) * dist);
Lumix::Vec3 terrain_pos = inv_terrain_matrix.multiplyPosition(pos);
if (terrain_pos.x >= 0 && terrain_pos.z >= 0 && terrain_pos.x <= w &&
terrain_pos.z <= h)
{
pos.y = scene->getTerrainHeightAt(m_component.index, terrain_pos.x, terrain_pos.z);
Lumix::Matrix mtx = Lumix::Matrix::IDENTITY;
mtx.setTranslation(pos);
if (!isOBBCollision(scene, mtx, model, scale))
{
auto entity = template_system.createInstanceNoCommand(template_name_hash, pos);
m_entities.push(entity);
}
}
}
}
ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y));
ImGui::InvisibleButton("hue_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));
if (ImGui::IsItemHovered())
static void getProjections(const Lumix::Vec3& axis,
const Lumix::Vec3 vertices[8],
float& min,
float& max)
{
if (ImGui::GetIO().MouseDown[0])
min = max = Lumix::dotProduct(vertices[0], axis);
for (int i = 1; i < 8; ++i)
{
hue = ((ImGui::GetIO().MousePos.y - picker_pos.y) / SV_PICKER_SIZE.y);
value_changed = true;
float dot = Lumix::dotProduct(vertices[i], axis);
min = Lumix::Math::minValue(dot, min);
max = Lumix::Math::maxValue(dot, max);
}
}
color = ImColor::HSV(hue, saturation, value);
col[0] = color.Value.x;
col[1] = color.Value.y;
col[2] = color.Value.z;
return value_changed | ImGui::ColorEdit3(label, col);
}
static bool overlaps(float min1, float max1, float min2, float max2)
{
return (min1 <= min2 && min2 <= max1) || (min2 <= min1 && min1 <= max2);
}
class PaintTerrainCommand : public Lumix::IEditorCommand
static bool testOBBCollision(const Lumix::Matrix& matrix_a,
const Lumix::Model* model_a,
const Lumix::Matrix& matrix_b,
const Lumix::Model* model_b,
float scale)
{
Lumix::Vec3 box_a_points[8];
Lumix::Vec3 box_b_points[8];
if (fabs(scale - 1.0) < 0.01f)
{
model_a->getAABB().getCorners(matrix_a, box_a_points);
model_b->getAABB().getCorners(matrix_b, box_b_points);
}
else
{
Lumix::Matrix scale_matrix_a = matrix_a;
scale_matrix_a.multiply3x3(scale);
Lumix::Matrix scale_matrix_b = matrix_b;
scale_matrix_b.multiply3x3(scale);
model_a->getAABB().getCorners(scale_matrix_a, box_a_points);
model_b->getAABB().getCorners(scale_matrix_b, box_b_points);
}
Lumix::Vec3 normals[] = {
matrix_a.getXVector(), matrix_a.getYVector(), matrix_a.getZVector() };
for (int i = 0; i < 3; i++)
{
float box_a_min, box_a_max, box_b_min, box_b_max;
getProjections(normals[i], box_a_points, box_a_min, box_a_max);
getProjections(normals[i], box_b_points, box_b_min, box_b_max);
if (!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
{
return false;
}
}
Lumix::Vec3 normals_b[] = {
matrix_b.getXVector(), matrix_b.getYVector(), matrix_b.getZVector() };
for (int i = 0; i < 3; i++)
{
float box_a_min, box_a_max, box_b_min, box_b_max;
getProjections(normals_b[i], box_a_points, box_a_min, box_a_max);
getProjections(normals_b[i], box_b_points, box_b_min, box_b_max);
if (!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
{
return false;
}
}
return true;
}
bool isOBBCollision(Lumix::RenderScene* scene,
const Lumix::Matrix& matrix,
Lumix::Model* model,
float scale)
{
Lumix::Vec3 pos_a = matrix.getTranslation();
static Lumix::Array<Lumix::RenderableMesh> meshes(m_world_editor.getAllocator());
meshes.clear();
scene->getRenderableMeshes(meshes, ~0);
float radius_a_squared = model->getBoundingRadius();
radius_a_squared = radius_a_squared * radius_a_squared;
for (int i = 0, c = meshes.size(); i < c; ++i)
{
Lumix::Vec3 pos_b = meshes[i].m_matrix->getTranslation();
float radius_b = meshes[i].m_model->getBoundingRadius();
float radius_squared = radius_a_squared + radius_b * radius_b;
if ((pos_a - pos_b).squaredLength() < radius_squared * scale * scale)
{
if (testOBBCollision(matrix, model, *meshes[i].m_matrix, meshes[i].m_model, scale))
{
return true;
}
}
}
return false;
}
Lumix::WorldEditor& m_world_editor;
Lumix::ComponentUID m_component;
Lumix::Array<Lumix::Entity> m_entities;
float m_brush_strength;
float m_brush_size;
int m_selected_entity_template;
Lumix::Vec3 m_center;
};
struct PaintTerrainCommand : public Lumix::IEditorCommand
{
public:
struct Rectangle
{
int m_from_x;
@ -127,7 +245,7 @@ public:
int m_to_y;
};
public:
PaintTerrainCommand(Lumix::WorldEditor& editor,
TerrainEditor::Type type,
int texture_idx,
@ -863,45 +981,14 @@ bool TerrainEditor::onEntityMouseDown(const Lumix::RayCastModelHit& hit,
void TerrainEditor::paintEntities(const Lumix::RayCastModelHit& hit)
{
Lumix::RenderScene* scene = static_cast<Lumix::RenderScene*>(m_component.scene);
Lumix::Vec3 center_pos = hit.m_origin + hit.m_dir * hit.m_t;
Lumix::Matrix terrain_matrix = m_world_editor.getUniverse()->getMatrix(m_component.entity);
Lumix::Matrix inv_terrain_matrix = terrain_matrix;
inv_terrain_matrix.inverse();
auto& template_system = m_world_editor.getEntityTemplateSystem();
auto& template_names = template_system.getTemplateNames();
if (m_selected_entity_template < 0 || m_selected_entity_template >= template_names.size())
{
return;
}
const char* template_name = template_names[m_selected_entity_template].c_str();
Lumix::Entity tpl = template_system.getInstances(Lumix::crc32(template_name))[0];
if (tpl < 0) return;
Lumix::ComponentUID renderable = m_world_editor.getComponent(tpl, RENDERABLE_HASH);
if (!renderable.isValid()) return;
float w, h;
scene->getTerrainSize(m_component.index, &w, &h);
float scale = 1.0f - Lumix::Math::maxValue(0.01f, m_terrain_brush_strength);
Lumix::Model* model = scene->getRenderableModel(renderable.index);
for (int i = 0; i <= m_terrain_brush_size * m_terrain_brush_size / 1000.0f; ++i)
{
float angle = (float)(rand() % 360);
float dist = (rand() % 100 / 100.0f) * m_terrain_brush_size;
Lumix::Vec3 pos(center_pos.x + cos(angle) * dist, 0, center_pos.z + sin(angle) * dist);
Lumix::Vec3 terrain_pos = inv_terrain_matrix.multiplyPosition(pos);
if (terrain_pos.x >= 0 && terrain_pos.z >= 0 && terrain_pos.x <= w && terrain_pos.z <= h)
{
pos.y = scene->getTerrainHeightAt(m_component.index, terrain_pos.x, terrain_pos.z);
Lumix::Matrix mtx = Lumix::Matrix::IDENTITY;
mtx.setTranslation(pos);
if (!isOBBCollision(scene, mtx, model, scale))
{
template_system.createInstance(template_name, pos);
}
}
}
PaintEntitiesCommand* command =
LUMIX_NEW(m_world_editor.getAllocator(), PaintEntitiesCommand)(m_world_editor,
m_component,
m_selected_entity_template,
m_terrain_brush_strength,
m_terrain_brush_size,
hit);
m_world_editor.executeCommand(command);
}
@ -955,115 +1042,6 @@ Lumix::Material* TerrainEditor::getMaterial()
}
void TerrainEditor::getProjections(const Lumix::Vec3& axis,
const Lumix::Vec3 vertices[8],
float& min,
float& max)
{
min = max = Lumix::dotProduct(vertices[0], axis);
for (int i = 1; i < 8; ++i)
{
float dot = Lumix::dotProduct(vertices[i], axis);
min = Lumix::Math::minValue(dot, min);
max = Lumix::Math::maxValue(dot, max);
}
}
bool TerrainEditor::overlaps(float min1, float max1, float min2, float max2)
{
return (min1 <= min2 && min2 <= max1) || (min2 <= min1 && min1 <= max2);
}
bool TerrainEditor::testOBBCollision(const Lumix::Matrix& matrix_a,
const Lumix::Model* model_a,
const Lumix::Matrix& matrix_b,
const Lumix::Model* model_b,
float scale)
{
Lumix::Vec3 box_a_points[8];
Lumix::Vec3 box_b_points[8];
if (fabs(scale - 1.0) < 0.01f)
{
model_a->getAABB().getCorners(matrix_a, box_a_points);
model_b->getAABB().getCorners(matrix_b, box_b_points);
}
else
{
Lumix::Matrix scale_matrix_a = matrix_a;
scale_matrix_a.multiply3x3(scale);
Lumix::Matrix scale_matrix_b = matrix_b;
scale_matrix_b.multiply3x3(scale);
model_a->getAABB().getCorners(scale_matrix_a, box_a_points);
model_b->getAABB().getCorners(scale_matrix_b, box_b_points);
}
Lumix::Vec3 normals[] = {
matrix_a.getXVector(), matrix_a.getYVector(), matrix_a.getZVector()};
for (int i = 0; i < 3; i++)
{
float box_a_min, box_a_max, box_b_min, box_b_max;
getProjections(normals[i], box_a_points, box_a_min, box_a_max);
getProjections(normals[i], box_b_points, box_b_min, box_b_max);
if (!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
{
return false;
}
}
Lumix::Vec3 normals_b[] = {
matrix_b.getXVector(), matrix_b.getYVector(), matrix_b.getZVector()};
for (int i = 0; i < 3; i++)
{
float box_a_min, box_a_max, box_b_min, box_b_max;
getProjections(normals_b[i], box_a_points, box_a_min, box_a_max);
getProjections(normals_b[i], box_b_points, box_b_min, box_b_max);
if (!overlaps(box_a_min, box_a_max, box_b_min, box_b_max))
{
return false;
}
}
return true;
}
bool TerrainEditor::isOBBCollision(Lumix::RenderScene* scene,
const Lumix::Matrix& matrix,
Lumix::Model* model,
float scale)
{
Lumix::Vec3 pos_a = matrix.getTranslation();
static Lumix::Array<Lumix::RenderableMesh> meshes(
m_world_editor.getAllocator());
meshes.clear();
scene->getRenderableMeshes(meshes, ~0);
float radius_a_squared = model->getBoundingRadius();
radius_a_squared = radius_a_squared * radius_a_squared;
for (int i = 0, c = meshes.size(); i < c; ++i)
{
Lumix::Vec3 pos_b = meshes[i].m_matrix->getTranslation();
float radius_b = meshes[i].m_model->getBoundingRadius();
float radius_squared = radius_a_squared + radius_b * radius_b;
if ((pos_a - pos_b).squaredLength() < radius_squared * scale * scale)
{
if (testOBBCollision(matrix,
model,
*meshes[i].m_matrix,
meshes[i].m_model,
scale))
{
return true;
}
}
}
return false;
}
void TerrainEditor::onGui()
{
auto* scene = static_cast<Lumix::RenderScene*>(m_component.scene);

View file

@ -47,16 +47,6 @@ private:
const Lumix::ComponentUID& cmp,
const Lumix::Vec3& center);
Lumix::Material* getMaterial();
bool overlaps(float min1, float max1, float min2, float max2);
bool testOBBCollision(const Lumix::Matrix& matrix_a,
const Lumix::Model* model_a,
const Lumix::Matrix& matrix_b,
const Lumix::Model* model_b,
float scale);
bool isOBBCollision(Lumix::RenderScene* scene,
const Lumix::Matrix& matrix,
Lumix::Model* model,
float scale);
void paint(const Lumix::RayCastModelHit& hit, TerrainEditor::Type type, bool new_stroke);
static void getProjections(const Lumix::Vec3& axis,

View file

@ -1,7 +1,9 @@
#include "utils.h"
#include "core/crc32.h"
#include "core/math_utils.h"
#include "core/path_utils.h"
#include "editor/world_editor.h"
#include "ocornut-imgui/imgui.h"
#include "renderer/render_scene.h"
#include "universe/universe.h"
@ -42,4 +44,98 @@ void getEntityListDisplayName(Lumix::WorldEditor& editor,
{
Lumix::toCString(entity, buf, max_size);
}
}
}
bool ColorPicker(const char* label, float col[3])
{
static const float HUE_PICKER_WIDTH = 20.0f;
static const float CROSSHAIR_SIZE = 7.0f;
static const ImVec2 SV_PICKER_SIZE = ImVec2(200, 200);
ImColor color(col[0], col[1], col[2]);
bool value_changed = false;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 picker_pos = ImGui::GetCursorScreenPos();
ImColor colors[] = { ImColor(255, 0, 0),
ImColor(255, 255, 0),
ImColor(0, 255, 0),
ImColor(0, 255, 255),
ImColor(0, 0, 255),
ImColor(255, 0, 255),
ImColor(255, 0, 0) };
for (int i = 0; i < 6; ++i)
{
draw_list->AddRectFilledMultiColor(
ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y + i * (SV_PICKER_SIZE.y / 6)),
ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10 + HUE_PICKER_WIDTH,
picker_pos.y + (i + 1) * (SV_PICKER_SIZE.y / 6)),
colors[i],
colors[i],
colors[i + 1],
colors[i + 1]);
}
float hue, saturation, value;
ImGui::ColorConvertRGBtoHSV(
color.Value.x, color.Value.y, color.Value.z, hue, saturation, value);
auto hue_color = ImColor::HSV(hue, 1, 1);
draw_list->AddLine(
ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 8, picker_pos.y + hue * SV_PICKER_SIZE.y),
ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 12 + HUE_PICKER_WIDTH,
picker_pos.y + hue * SV_PICKER_SIZE.y),
ImColor(255, 255, 255));
draw_list->AddTriangleFilledMultiColor(picker_pos,
ImVec2(picker_pos.x + SV_PICKER_SIZE.x, picker_pos.y + SV_PICKER_SIZE.y),
ImVec2(picker_pos.x, picker_pos.y + SV_PICKER_SIZE.y),
ImColor(0, 0, 0),
hue_color,
ImColor(255, 255, 255));
float x = saturation * value;
ImVec2 p(picker_pos.x + x * SV_PICKER_SIZE.x, picker_pos.y + value * SV_PICKER_SIZE.y);
draw_list->AddLine(ImVec2(p.x - CROSSHAIR_SIZE, p.y), ImVec2(p.x - 2, p.y), ImColor(255, 255, 255));
draw_list->AddLine(ImVec2(p.x + CROSSHAIR_SIZE, p.y), ImVec2(p.x + 2, p.y), ImColor(255, 255, 255));
draw_list->AddLine(ImVec2(p.x, p.y + CROSSHAIR_SIZE), ImVec2(p.x, p.y + 2), ImColor(255, 255, 255));
draw_list->AddLine(ImVec2(p.x, p.y - CROSSHAIR_SIZE), ImVec2(p.x, p.y - 2), ImColor(255, 255, 255));
ImGui::InvisibleButton("saturation_value_selector", SV_PICKER_SIZE);
if (ImGui::IsItemHovered())
{
ImVec2 mouse_pos_in_canvas = ImVec2(
ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);
if (ImGui::GetIO().MouseDown[0])
{
mouse_pos_in_canvas.x =
Lumix::Math::minValue(mouse_pos_in_canvas.x, mouse_pos_in_canvas.y);
value = mouse_pos_in_canvas.y / SV_PICKER_SIZE.y;
saturation = value == 0 ? 0 : (mouse_pos_in_canvas.x / SV_PICKER_SIZE.x) / value;
value_changed = true;
}
}
ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y));
ImGui::InvisibleButton("hue_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));
if (ImGui::IsItemHovered())
{
if (ImGui::GetIO().MouseDown[0])
{
hue = ((ImGui::GetIO().MousePos.y - picker_pos.y) / SV_PICKER_SIZE.y);
value_changed = true;
}
}
color = ImColor::HSV(hue, saturation, value);
col[0] = color.Value.x;
col[1] = color.Value.y;
col[2] = color.Value.z;
return value_changed | ImGui::ColorEdit3(label, col);
}

View file

@ -87,6 +87,7 @@ namespace Lumix
class WorldEditor;
}
bool ColorPicker(const char* label, float col[3]);
void getEntityListDisplayName(Lumix::WorldEditor& editor,
char* buf,