Cogs.Core
EditorCommand.cpp
1#include "EditorCommand.h"
2
3#include "Context.h"
4#include "EntityStore.h"
5
6#include "Serialization/EntityReader.h"
7#include "Serialization/EntityWriter.h"
8#include "Serialization/SceneReader.h"
9#include "Serialization/EntityCreator.h"
10
11#include "Bridge/SceneFunctions.h"
12
13#include "Components/Core/TransformComponent.h"
14
15#include "Foundation/Geometry/BoundingBox.hpp"
16#include "Foundation/Logging/Logger.h"
17#include "Foundation/Reflection/TypeDatabase.h"
18
19#include <algorithm>
20
21using namespace Cogs::Geometry;
22
23
24Cogs::Core::PostCommand::~PostCommand() = default;
25
26// --------
27
28Cogs::Core::CreateEntityCommand::CreateEntityCommand(EditorState* state, const StringView& type, const StringView& name, bool openInRoot)
29 : EditorCommand(state, state->context), type(type.to_string()), name(name.to_string()),
30 openInRoot(openInRoot)
31{
32}
33
35{
36 EntityPtr entity;
37 if (!openInRoot && state->getSelected()) {
38 ComponentModel::Entity * parent = state->getSelected();
39 entity = context->store->createChildEntity(type, parent, name);
40 parentId = parent->getId();
41 }
42 else {
43 entity = context->store->createEntity(name, type);
44 }
45
46 entityId = entity->getId();
47
48 previousSelection = state->selected;
49
50 state->setSelection(entity->getId());
51}
52
53void Cogs::Core::CreateEntityCommand::undo()
54{
55 if (entityId == NoEntity) return;
56
57 if (parentId == NoEntity) {
58 context->store->destroyEntity(entityId);
59 }
60 else {
61 ComponentModel::Entity* parent = context->store->getEntityPtr(parentId);
62 ComponentModel::Entity* entity = context->store->getEntityPtr(entityId);
63 if (entity && parent) {
64 context->store->removeChild(parent, entity);
65 }
66 }
67
68 state->setSelection(previousSelection);
69}
70
71// --------
72
74{
75 ComponentModel::Entity * parent = state->getSelected();
76
77 previousSelection = state->selected;
78
79 Value object;
80 object.CopyFrom(state->copied, state->copied.GetAllocator());
81
82 // The following is based on Cogs::Core::readScene() from SceneReader.cpp
83
84 AssetDefinition asset;
85 SceneDefinition& scene = asset.scene;
86 readSceneDefinition(context, object, AssetLoadFlags::None, asset);
87 std::vector<ComponentModel::Entity*> entities(scene.entities.size());
88 EntityIds ids;
89 ids.reserve(scene.entities.size());
90
91 for (SceneEntityDefinition& definition : scene.entities) {
92 ComponentModel::Entity* parentEntity = definition.isParentedByName() ? nullptr :
93 (definition.parentIndex == SceneEntityDefinition::NoIndex ? parent : entities[definition.parentIndex]);
94
95 entities[definition.index] = createEntity(context, scene, definition, parentEntity);
96
97 ids.push_back(entities[definition.index]->getId());
98 }
99
100 RemoveEntitiesWithAncestors(context, ids, entityIds);
101 state->setSelection(entityIds);
102}
103
104void Cogs::Core::PasteEntityCommand::undo()
105{
106 for (const EntityId entityId : entityIds) {
107 const ComponentModel::Entity* entity = context->store->getEntityPtr(entityId);
108 ComponentModel::Entity* parent = entity ? context->store->getEntityParent(entity) : nullptr;
109 if (parent && entity) {
110 context->store->removeChild(parent, entity);
111 }
112 else {
113 context->store->destroyEntity(entityId);
114 }
115 }
116
117 state->setSelection(previousSelection);
118}
119
120// --------
121
123{
124 previousSelection = state->selected;
125 state->scrolled = false;
126
127 if (selectedIds.size() && selectedIds[0] == NoEntity) {
128 selectedIds.clear();
129 }
130
131 if (mode == SelectMode::Exclusive) {
132 state->pickId = pickId;
133 state->setSelection(selectedIds);
134 }
135 else if (mode == SelectMode::Add) {
136 EntityIds newSelection = state->selected;
137 for (const EntityId id : selectedIds) {
138 const bool found = std::find(previousSelection.begin(), previousSelection.end(), id) != previousSelection.end();
139 if (!found) {
140 newSelection.push_back(id);
141 }
142 }
143 state->pickId = pickId;
144 state->setSelection(newSelection);
145 }
146 else if (mode == SelectMode::Toggle) {
147 EntityIds newSelection = state->selected;
148 for (const EntityId id : selectedIds) {
149 const bool found = std::find(previousSelection.begin(), previousSelection.end(), id) != previousSelection.end();
150 if (!found) {
151 newSelection.push_back(id);
152 if (selectedIds.size() == 1) {
153 state->pickId = pickId;
154 }
155 }
156 else {
157 auto end = std::remove(newSelection.begin(), newSelection.end(), id);
158 if (end != newSelection.end()) {
159 newSelection.erase(end, newSelection.end());
160 }
161 }
162 }
163
164 state->setSelection(newSelection);
165 }
166}
167
168void Cogs::Core::SelectCommand::undo()
169{
170 state->setSelection(previousSelection);
171}
172
173// --------
174
176{
177 ComponentModel::Entity* entity = context->store->getEntityPtr(entityId);
178
179 context->store->addComponent(entity, type);
180}
181
182void Cogs::Core::AddComponentCommand::undo()
183{
184 ComponentModel::Entity* entity = context->store->getEntityPtr(entityId);
185
186 const Reflection::Type & componentType = Reflection::TypeDatabase::getType(type);
187
188 ComponentModel::ComponentHandle handle = entity->getComponentHandle(componentType);
189
190 context->store->removeComponent(entity, handle);
191}
192
193// --------
194
196{
197 previousMode = state->mode;
198 state->mode = newMode;
199}
200
201void Cogs::Core::ChangeEditingModeCommand::undo()
202{
203 state->mode = previousMode;
204}
205
206// --------
207
209{
210 for (size_t i = 0; i < state->selected.size(); ++i) {
211 auto entity = context->store->getEntityPtr(entityIds[i]);
212 auto transformComponent = entity->getComponent<TransformComponent>();
213
214 transformComponent->position = state->startTranslations[i] + translation;
215 transformComponent->setChanged();
216 }
217
218 previousTranslations = state->startTranslations;
219}
220
221void Cogs::Core::TranslateCommand::undo()
222{
223 for (size_t i = 0; i < state->selected.size(); ++i) {
224 auto entity = context->store->getEntityPtr(entityIds[i]);
225 auto transformComponent = entity->getComponent<TransformComponent>();
226
227 transformComponent->position = previousTranslations[i];
228 transformComponent->setChanged();
229 }
230}
231
232// --------
233
235{
236 const TranslateCommand* translateCommand = dynamic_cast<const TranslateCommand *>(command);
237
238 if (!translateCommand) return false;
239 if (entityIds != translateCommand->entityIds) return false;
240
241 translation = translateCommand->translation;
242
243 return true;
244}
245
246// --------
247
249{
250 for (size_t i = 0; i < state->selected.size(); ++i) {
251 auto entity = context->store->getEntityPtr(entityIds[i]);
252 auto transformComponent = entity->getComponent<TransformComponent>();
253
254 transformComponent->rotation = rotation * state->startRotations[i];
255 transformComponent->setChanged();
256 }
257
258 previousRotations = state->startRotations;
259}
260
261void Cogs::Core::RotateCommand::undo()
262{
263 for (size_t i = 0; i < state->selected.size(); ++i) {
264 auto entity = context->store->getEntityPtr(entityIds[i]);
265 auto transformComponent = entity->getComponent<TransformComponent>();
266
267 transformComponent->rotation = previousRotations[i];
268 transformComponent->setChanged();
269 }
270}
271
273{
274 const RotateCommand* rotateCommand = dynamic_cast<const RotateCommand *>(command);
275
276 if (!rotateCommand) return false;
277 if (entityIds != rotateCommand->entityIds) return false;
278
279 rotation = rotateCommand->rotation;
280 return true;
281}
282
283// --------
284
286{
287 for (size_t i = 0; i < state->selected.size(); ++i) {
288 auto entity = context->store->getEntityPtr(entityIds[i]);
289 auto transformComponent = entity->getComponent<TransformComponent>();
290
291 transformComponent->scale = state->startScales[i] + scale;
292 transformComponent->setChanged();
293 }
294
295 previousScales = state->startScales;
296}
297
298void Cogs::Core::ScaleCommand::undo()
299{
300 for (size_t i = 0; i < state->selected.size(); ++i) {
301 auto entity = context->store->getEntityPtr(entityIds[i]);
302 auto transformComponent = entity->getComponent<TransformComponent>();
303
304 transformComponent->scale = previousScales[i];
305 transformComponent->setChanged();
306 }
307}
308
310{
311 const ScaleCommand* scaleCommand = dynamic_cast<const ScaleCommand*>(command);
312
313 if (!scaleCommand) return false;
314 if (entityIds != scaleCommand->entityIds) return false;
315
316 scale = scaleCommand->scale;
317 return true;
318}
319
320// --------
321
323{
324 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("ScaleToUnitCubeCommand");
325
326 Geometry::DBoundingBox bb;
327 ::calculateBoundingBoxWorld(context, entityId, bb.data());
328 LOG_DEBUG(logger, "Original bounds: [%.2f %.2f %.2f] x [%.2f %.2f %.2f]",
329 bb.min.x, bb.min.y, bb.min.z,
330 bb.max.x, bb.max.y, bb.max.z);
331
332 double scale = 1.0;
333 if (!isEmpty(bb) && bb.min.x < bb.max.x) {
334 const glm::dvec3 extent = bb.max - bb.min;
335 scale = 2.0 / std::max(std::max(extent.x, extent.y), extent.z);
336 }
337 LOG_DEBUG(logger, "Scale: %e", scale);
338
339 if (ComponentModel::Entity* entity = context->store->getEntityPtr(entityId)) {
340 if (TransformComponent* trComp = entity->getComponent<TransformComponent>()) {
341 previousScale = trComp->scale;
342 trComp->scale = glm::vec3(float(scale)) * trComp->scale;
343
344 previousPosition = trComp->position;
345 trComp->position = trComp->position - glm::vec3(scale * bb.min) - glm::vec3(1.f);
346
347 trComp->setChanged();
348 }
349 }
350}
351
352void Cogs::Core::ScaleToUnitCubeCommand::undo()
353{
354 if (ComponentModel::Entity* entity = context->store->getEntityPtr(entityId)) {
355 if (TransformComponent* trComp = entity->getComponent<TransformComponent>()) {
356 trComp->scale = previousScale;
357 trComp->position = previousPosition;
358 trComp->setChanged();
359 }
360 }
361}
362
363// --------
364
366{
367 if (allowUndo) {
368 Document d;
369 d.SetArray();
371
372 for (const EntityId s : entityIds) {
373 Value entityValue;
374 entityValue.SetObject();
375
376 writeEntity(context, context->store->getEntityPtr(s), entityValue, d, nullptr, writeFlags);
377
378 d.PushBack(entityValue, d.GetAllocator());
379 }
380
381 copied = std::move(d);
382 }
383
384 if (allowUndo) {
385 parentIds.reserve(entityIds.size());
386 }
387 for (const EntityId entityId : entityIds) {
388 EntityId parentId = NoEntity;
389 const ComponentModel::Entity* entity = context->store->getEntityPtr(entityId);
390 ComponentModel::Entity* parent = entity ? context->store->getEntityParent(entity) : nullptr;
391 if (parent && entity) {
392 context->store->removeChild(parent, entity);
393 parentId = parent->getId();
394 }
395 else {
396 context->store->destroyEntity(entityId);
397 }
398
399 if (allowUndo) {
400 parentIds.emplace_back(parentId);
401 }
402 }
403
404 state->clearSelection();
405 state->mode = EditingMode::Select;
406}
407
408void Cogs::Core::DestroyCommand::undo()
409{
410 if (!allowUndo) {
411 return;
412 }
413
414 // This is similar to PasteEntityCommand::apply()
415 // But here we have to pay attention to parent Entities from the deleted list
416
417 Value object;
418 object.CopyFrom(copied, copied.GetAllocator());
419
420 AssetDefinition asset;
421 SceneDefinition& scene = asset.scene;
422 readSceneDefinition(context, object, AssetLoadFlags::None, asset);
423 std::vector<ComponentModel::Entity*> entities(scene.entities.size());
424 EntityIds ids;
425 ids.reserve(scene.entities.size());
426
427 size_t entityNo = 0; // count Entities from the original deletion list
428 for (SceneEntityDefinition& definition : scene.entities) {
429 ComponentModel::Entity* parentEntity = nullptr;
430 if (!definition.isParentedByName()) {
431 if (definition.parentIndex == SceneEntityDefinition::NoIndex) {
432 // Entities without parentIndex are from the original delete list
433 assert(entityNo < parentIds.size());
434 parentEntity = (entityNo < parentIds.size() && parentIds[entityNo] != NoEntity) ?
435 context->store->getEntityPtr(parentIds[entityNo]) : nullptr;
436 entityNo++;
437 }
438 else {
439 parentEntity = entities[definition.parentIndex];
440 }
441 }
442
443 entities[definition.index] = createEntity(context, scene, definition, parentEntity);
444
445 ids.push_back(entities[definition.index]->getId());
446 }
447
448 RemoveEntitiesWithAncestors(context, ids, ids);
449 state->setSelection(ids);
450}
451
453{
454 EntityPtr group = context->store->createEntity("Group", "Group");
455
456 groupId = group->getId();
457 ComponentModel::Entity* groupEntity = context->store->getEntityPtr(groupId);
458
459 for (const EntityId childId : entityIds) {
460 EntityPtr childEntity = context->store->getEntity(childId);
461 if(childEntity)
462 context->store->addChild(groupEntity, childEntity);
463 }
464
465 state->setSelection(groupId);
466}
467
468void Cogs::Core::GroupCommand::undo()
469{
470 for (const EntityId id : entityIds) {
471 context->store->removeChild(context->store->getEntityPtr(groupId), context->store->getEntityPtr(id));
472 }
473
474 context->store->destroyEntity(groupId);
475
476 state->setSelection(entityIds);
477}
478
479
480void Cogs::Core::RemoveEntitiesWithAncestors(Context* context, const EntityIds& entityIds, EntityIds& output)
481{
482 EntityIds ids;
483 ids.reserve(entityIds.size());
484 for (const EntityId id : entityIds) {
485 if (const ComponentModel::Entity* entity = context->store->getEntityPtr(id); entity) {
486 while ((entity = context->store->getEntityParent(entity)) != nullptr) {
487 EntityId parentId = entity->getId();
488 if (std::find(entityIds.begin(), entityIds.end(), parentId) != entityIds.end()) {
489 break;
490 }
491 }
492 if (!entity) {
493 ids.push_back(id);
494 }
495 }
496 }
497
498 output = std::move(ids);
499}
Container for components, providing composition of dynamic entities.
Definition: Entity.h:18
T * getComponent() const
Get a pointer to the first component implementing the given type in the entity.
Definition: Entity.h:35
constexpr size_t getId() const noexcept
Get the unique identifier of this entity.
Definition: Entity.h:113
void addComponent(ComponentHandle component)
Definition: Entity.cpp:18
ComponentHandle getComponentHandle() const
Get a component handle to the first component implementing the given type.
Definition: Entity.h:90
class EntityStore * store
Entity store.
Definition: Context.h:231
EntityPtr createChildEntity(const StringView &type, ComponentModel::Entity *parent, const StringView &name=StringView())
Create a new Entity, parenting it to the given parent.
EntityPtr createEntity(const StringView &name, const StringView &type, bool storeOwnership=true)
Create a new Entity.
void apply() override
Run the command.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
glm::vec3 scale
Scale factor to apply to each of the axes.
glm::quat rotation
Rotation given as a quaternion.
glm::vec3 position
Local position relative to the global coordinates, or the parent coordinate system if the parent fiel...
Log implementation class.
Definition: LogManager.h:139
static const Type & getType()
Get the Type of the given template argument.
Definition: TypeDatabase.h:168
Represents a discrete type definition, describing a native type class.
Definition: Type.h:89
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
Definition: EntityPtr.h:12
void COGSCORE_DLL_API readSceneDefinition(class Context *context, const Value &jsonScene, AssetLoadFlags flags, AssetDefinition &asset)
Parse JSON description of Entities in Cogs Scene. Store contents in asset param.
AssetWriteFlags
Flags that control serialization of assets.
@ Hierarchy
Save all children, not only children in EntityStore.
@ Geometry
Store entity vector fields (vector<vec3>, vector<vec2>, vector<int>, vector<float>).
void COGSCORE_DLL_API writeEntity(Context *context, ComponentModel::Entity *entity, rapidjson::Value &object, rapidjson::Document &d, SerializationContext *sc=nullptr, const AssetWriteFlags flags=AssetWriteFlags::None)
Serialize entity adding entity to the given parent 'object'.
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Handle to a Component instance.
Definition: Component.h:67
void apply() override
Run the command.
void apply() override
Run the command.
void apply() override
Run the command.
void apply() override
Run the command.
Base class for Cogs Editor commands.
Definition: EditorCommand.h:19
ComponentModel::Entity * getSelected() const
Gets entity Pointer to the single selected entity. Nullptr if not one selected or not found.
Definition: EditorState.h:133
void setSelection(const EntityIds &ids)
Set new selected entities.
Definition: EditorState.h:100
void apply() override
Run the command.
void apply() override
Run the command.
void apply() override
Run the command.
bool mergeWith(const EditorCommand *command) override
bool mergeWith(const EditorCommand *command) override
void apply() override
Run the command.
void apply() override
Run the command.
bool mergeWith(const EditorCommand *command) override
void apply() override
Run the command.