mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 12:49:34 +02:00
Merge pull request #2721 from Atlante45/improved_undo
Handle subtrees with undo
This commit is contained in:
commit
b0a561198b
8 changed files with 146 additions and 34 deletions
|
@ -1088,7 +1088,6 @@ function keyPressEvent(event) {
|
||||||
red: colors[whichColor].red,
|
red: colors[whichColor].red,
|
||||||
green: colors[whichColor].green,
|
green: colors[whichColor].green,
|
||||||
blue: colors[whichColor].blue };
|
blue: colors[whichColor].blue };
|
||||||
Voxels.eraseVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s);
|
|
||||||
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
||||||
setAudioPosition();
|
setAudioPosition();
|
||||||
initialVoxelSound.playRandom();
|
initialVoxelSound.playRandom();
|
||||||
|
@ -1394,7 +1393,6 @@ function checkControllers() {
|
||||||
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
|
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
|
||||||
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
|
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
|
||||||
|
|
||||||
Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
|
|
||||||
Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE,
|
Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE,
|
||||||
newColor.red, newColor.green, newColor.blue);
|
newColor.red, newColor.green, newColor.blue);
|
||||||
|
|
||||||
|
|
|
@ -542,6 +542,19 @@ OctreeElement* Octree::getOctreeElementAt(float x, float y, float z, float s) co
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OctreeElement* Octree::getOctreeEnclosingElementAt(float x, float y, float z, float s) const {
|
||||||
|
unsigned char* octalCode = pointToOctalCode(x,y,z,s);
|
||||||
|
OctreeElement* node = nodeForOctalCode(_rootNode, octalCode, NULL);
|
||||||
|
|
||||||
|
delete[] octalCode; // cleanup memory
|
||||||
|
#ifdef HAS_AUDIT_CHILDREN
|
||||||
|
if (node) {
|
||||||
|
node->auditChildren("Octree::getOctreeElementAt()");
|
||||||
|
}
|
||||||
|
#endif // def HAS_AUDIT_CHILDREN
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
OctreeElement* Octree::getOrCreateChildElementAt(float x, float y, float z, float s) {
|
OctreeElement* Octree::getOrCreateChildElementAt(float x, float y, float z, float s) {
|
||||||
return getRoot()->getOrCreateChildElementAt(x, y, z, s);
|
return getRoot()->getOrCreateChildElementAt(x, y, z, s);
|
||||||
|
|
|
@ -210,7 +210,15 @@ public:
|
||||||
void reaverageOctreeElements(OctreeElement* startNode = NULL);
|
void reaverageOctreeElements(OctreeElement* startNode = NULL);
|
||||||
|
|
||||||
void deleteOctreeElementAt(float x, float y, float z, float s);
|
void deleteOctreeElementAt(float x, float y, float z, float s);
|
||||||
|
|
||||||
|
/// Find the voxel at position x,y,z,s
|
||||||
|
/// \return pointer to the OctreeElement or NULL if none at x,y,z,s.
|
||||||
OctreeElement* getOctreeElementAt(float x, float y, float z, float s) const;
|
OctreeElement* getOctreeElementAt(float x, float y, float z, float s) const;
|
||||||
|
|
||||||
|
/// Find the voxel at position x,y,z,s
|
||||||
|
/// \return pointer to the OctreeElement or to the smallest enclosing parent if none at x,y,z,s.
|
||||||
|
OctreeElement* getOctreeEnclosingElementAt(float x, float y, float z, float s) const;
|
||||||
|
|
||||||
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
|
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
|
||||||
|
|
||||||
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL);
|
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL);
|
||||||
|
|
|
@ -42,7 +42,11 @@ void VoxelTree::deleteVoxelAt(float x, float y, float z, float s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelTreeElement* VoxelTree::getVoxelAt(float x, float y, float z, float s) const {
|
VoxelTreeElement* VoxelTree::getVoxelAt(float x, float y, float z, float s) const {
|
||||||
return (VoxelTreeElement*)getOctreeElementAt(x, y, z, s);
|
return static_cast<VoxelTreeElement*>(getOctreeElementAt(x, y, z, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
VoxelTreeElement* VoxelTree::getEnclosingVoxelAt(float x, float y, float z, float s) const {
|
||||||
|
return static_cast<VoxelTreeElement*>(getOctreeEnclosingElementAt(x, y, z, s));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelTree::createVoxel(float x, float y, float z, float s,
|
void VoxelTree::createVoxel(float x, float y, float z, float s,
|
||||||
|
|
|
@ -29,7 +29,15 @@ public:
|
||||||
VoxelTreeElement* getRoot() { return (VoxelTreeElement*)_rootNode; }
|
VoxelTreeElement* getRoot() { return (VoxelTreeElement*)_rootNode; }
|
||||||
|
|
||||||
void deleteVoxelAt(float x, float y, float z, float s);
|
void deleteVoxelAt(float x, float y, float z, float s);
|
||||||
|
|
||||||
|
/// Find the voxel at position x,y,z,s
|
||||||
|
/// \return pointer to the VoxelTreeElement or NULL if none at x,y,z,s.
|
||||||
VoxelTreeElement* getVoxelAt(float x, float y, float z, float s) const;
|
VoxelTreeElement* getVoxelAt(float x, float y, float z, float s) const;
|
||||||
|
|
||||||
|
/// Find the voxel at position x,y,z,s
|
||||||
|
/// \return pointer to the VoxelTreeElement or to the smallest enclosing parent if none at x,y,z,s.
|
||||||
|
VoxelTreeElement* getEnclosingVoxelAt(float x, float y, float z, float s) const;
|
||||||
|
|
||||||
void createVoxel(float x, float y, float z, float s,
|
void createVoxel(float x, float y, float z, float s,
|
||||||
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
|
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,50 @@
|
||||||
|
|
||||||
#include "VoxelTreeCommands.h"
|
#include "VoxelTreeCommands.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct SendVoxelsOperationArgs {
|
||||||
|
const unsigned char* newBaseOctCode;
|
||||||
|
VoxelEditPacketSender* packetSender;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool sendVoxelsOperation(OctreeElement* element, void* extraData) {
|
||||||
|
VoxelTreeElement* voxel = static_cast<VoxelTreeElement*>(element);
|
||||||
|
SendVoxelsOperationArgs* args = static_cast<SendVoxelsOperationArgs*>(extraData);
|
||||||
|
if (voxel->isColored()) {
|
||||||
|
const unsigned char* nodeOctalCode = voxel->getOctalCode();
|
||||||
|
unsigned char* codeColorBuffer = NULL;
|
||||||
|
int codeLength = 0;
|
||||||
|
int bytesInCode = 0;
|
||||||
|
int codeAndColorLength;
|
||||||
|
|
||||||
|
// If the newBase is NULL, then don't rebase
|
||||||
|
if (args->newBaseOctCode) {
|
||||||
|
codeColorBuffer = rebaseOctalCode(nodeOctalCode, args->newBaseOctCode, true);
|
||||||
|
codeLength = numberOfThreeBitSectionsInCode(codeColorBuffer);
|
||||||
|
bytesInCode = bytesRequiredForCodeLength(codeLength);
|
||||||
|
codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA;
|
||||||
|
} else {
|
||||||
|
codeLength = numberOfThreeBitSectionsInCode(nodeOctalCode);
|
||||||
|
bytesInCode = bytesRequiredForCodeLength(codeLength);
|
||||||
|
codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA;
|
||||||
|
codeColorBuffer = new unsigned char[codeAndColorLength];
|
||||||
|
memcpy(codeColorBuffer, nodeOctalCode, bytesInCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the colors over
|
||||||
|
codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX];
|
||||||
|
codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX];
|
||||||
|
codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX];
|
||||||
|
args->packetSender->queueVoxelEditMessage(PacketTypeVoxelSetDestructive,
|
||||||
|
codeColorBuffer, codeAndColorLength);
|
||||||
|
|
||||||
|
delete[] codeColorBuffer;
|
||||||
|
}
|
||||||
|
return true; // keep going
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
AddVoxelCommand::AddVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender, QUndoCommand* parent) :
|
AddVoxelCommand::AddVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender, QUndoCommand* parent) :
|
||||||
QUndoCommand("Add Voxel", parent),
|
QUndoCommand("Add Voxel", parent),
|
||||||
_tree(tree),
|
_tree(tree),
|
||||||
|
@ -43,11 +87,40 @@ DeleteVoxelCommand::DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, Voxe
|
||||||
QUndoCommand("Delete Voxel", parent),
|
QUndoCommand("Delete Voxel", parent),
|
||||||
_tree(tree),
|
_tree(tree),
|
||||||
_packetSender(packetSender),
|
_packetSender(packetSender),
|
||||||
_voxel(voxel)
|
_voxel(voxel),
|
||||||
|
_oldTree(NULL)
|
||||||
{
|
{
|
||||||
|
_tree->lockForRead();
|
||||||
|
VoxelTreeElement* element = _tree->getEnclosingVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||||
|
if (element->getScale() == _voxel.s) {
|
||||||
|
if (!element->hasContent() && !element->isLeaf()) {
|
||||||
|
_oldTree = new VoxelTree();
|
||||||
|
_tree->copySubTreeIntoNewTree(element, _oldTree, false);
|
||||||
|
} else {
|
||||||
|
_voxel.red = element->getColor()[0];
|
||||||
|
_voxel.green = element->getColor()[1];
|
||||||
|
_voxel.blue = element->getColor()[2];
|
||||||
|
}
|
||||||
|
} else if (element->hasContent() && element->isLeaf()) {
|
||||||
|
_voxel.red = element->getColor()[0];
|
||||||
|
_voxel.green = element->getColor()[1];
|
||||||
|
_voxel.blue = element->getColor()[2];
|
||||||
|
} else {
|
||||||
|
_voxel.s = 0.0f;
|
||||||
|
qDebug() << "No element for delete.";
|
||||||
|
}
|
||||||
|
_tree->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteVoxelCommand::~DeleteVoxelCommand() {
|
||||||
|
delete _oldTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeleteVoxelCommand::redo() {
|
void DeleteVoxelCommand::redo() {
|
||||||
|
if (_voxel.s == 0.0f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_tree) {
|
if (_tree) {
|
||||||
_tree->deleteVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
_tree->deleteVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||||
}
|
}
|
||||||
|
@ -57,10 +130,32 @@ void DeleteVoxelCommand::redo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeleteVoxelCommand::undo() {
|
void DeleteVoxelCommand::undo() {
|
||||||
if (_tree) {
|
if (_voxel.s == 0.0f) {
|
||||||
_tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
|
return;
|
||||||
}
|
}
|
||||||
if (_packetSender) {
|
|
||||||
_packetSender->queueVoxelEditMessages(PacketTypeVoxelSet, 1, &_voxel);
|
if (_oldTree) {
|
||||||
|
VoxelTreeElement* element = _oldTree->getVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||||
|
if (element) {
|
||||||
|
if (_tree) {
|
||||||
|
_tree->lockForWrite();
|
||||||
|
_oldTree->copySubTreeIntoNewTree(element, _tree, false);
|
||||||
|
_tree->unlock();
|
||||||
|
}
|
||||||
|
if (_packetSender) {
|
||||||
|
SendVoxelsOperationArgs args;
|
||||||
|
args.newBaseOctCode = NULL;
|
||||||
|
args.packetSender = _packetSender;
|
||||||
|
_oldTree->recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||||
|
_packetSender->releaseQueuedMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_tree) {
|
||||||
|
_tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
|
||||||
|
}
|
||||||
|
if (_packetSender) {
|
||||||
|
_packetSender->queueVoxelEditMessages(PacketTypeVoxelSet, 1, &_voxel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ private:
|
||||||
class DeleteVoxelCommand : public QUndoCommand {
|
class DeleteVoxelCommand : public QUndoCommand {
|
||||||
public:
|
public:
|
||||||
DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender = NULL, QUndoCommand* parent = NULL);
|
DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender = NULL, QUndoCommand* parent = NULL);
|
||||||
|
~DeleteVoxelCommand();
|
||||||
|
|
||||||
virtual void redo();
|
virtual void redo();
|
||||||
virtual void undo();
|
virtual void undo();
|
||||||
|
@ -44,6 +45,7 @@ private:
|
||||||
VoxelTree* _tree;
|
VoxelTree* _tree;
|
||||||
VoxelEditPacketSender* _packetSender;
|
VoxelEditPacketSender* _packetSender;
|
||||||
VoxelDetail _voxel;
|
VoxelDetail _voxel;
|
||||||
|
VoxelTree* _oldTree;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_VoxelTreeCommands_h
|
#endif // hifi_VoxelTreeCommands_h
|
||||||
|
|
|
@ -76,32 +76,16 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
|
||||||
if (_tree) {
|
if (_tree) {
|
||||||
if (_undoStack) {
|
if (_undoStack) {
|
||||||
AddVoxelCommand* addCommand = new AddVoxelCommand(_tree,
|
AddVoxelCommand* addCommand = new AddVoxelCommand(_tree,
|
||||||
addVoxelDetail,
|
addVoxelDetail,
|
||||||
getVoxelPacketSender());
|
getVoxelPacketSender());
|
||||||
|
DeleteVoxelCommand* deleteCommand = new DeleteVoxelCommand(_tree,
|
||||||
VoxelTreeElement* deleteVoxelElement = _tree->getVoxelAt(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s);
|
addVoxelDetail,
|
||||||
if (deleteVoxelElement) {
|
getVoxelPacketSender());
|
||||||
nodeColor color;
|
_undoStack->beginMacro(addCommand->text());
|
||||||
memcpy(&color, &deleteVoxelElement->getColor(), sizeof(nodeColor));
|
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||||
VoxelDetail deleteVoxelDetail = {addVoxelDetail.x,
|
_undoStack->push(deleteCommand);
|
||||||
addVoxelDetail.y,
|
_undoStack->push(addCommand);
|
||||||
addVoxelDetail.z,
|
_undoStack->endMacro();
|
||||||
addVoxelDetail.s,
|
|
||||||
color[0],
|
|
||||||
color[1],
|
|
||||||
color[2]};
|
|
||||||
DeleteVoxelCommand* delCommand = new DeleteVoxelCommand(_tree,
|
|
||||||
deleteVoxelDetail,
|
|
||||||
getVoxelPacketSender());
|
|
||||||
_undoStack->beginMacro(addCommand->text());
|
|
||||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
|
||||||
_undoStack->push(delCommand);
|
|
||||||
_undoStack->push(addCommand);
|
|
||||||
_undoStack->endMacro();
|
|
||||||
} else {
|
|
||||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
|
||||||
_undoStack->push(addCommand);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// queue the destructive add
|
// queue the destructive add
|
||||||
queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail);
|
queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail);
|
||||||
|
|
Loading…
Reference in a new issue