mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 03:53:52 +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,
|
||||
green: colors[whichColor].green,
|
||||
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);
|
||||
setAudioPosition();
|
||||
initialVoxelSound.playRandom();
|
||||
|
@ -1394,7 +1393,6 @@ function checkControllers() {
|
|||
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
|
||||
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,
|
||||
newColor.red, newColor.green, newColor.blue);
|
||||
|
||||
|
|
|
@ -542,6 +542,19 @@ OctreeElement* Octree::getOctreeElementAt(float x, float y, float z, float s) co
|
|||
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) {
|
||||
return getRoot()->getOrCreateChildElementAt(x, y, z, s);
|
||||
|
|
|
@ -210,7 +210,15 @@ public:
|
|||
void reaverageOctreeElements(OctreeElement* startNode = NULL);
|
||||
|
||||
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;
|
||||
|
||||
/// 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);
|
||||
|
||||
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 {
|
||||
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,
|
||||
|
|
|
@ -29,7 +29,15 @@ public:
|
|||
VoxelTreeElement* getRoot() { return (VoxelTreeElement*)_rootNode; }
|
||||
|
||||
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;
|
||||
|
||||
/// 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,
|
||||
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
|
||||
|
||||
|
|
|
@ -13,6 +13,50 @@
|
|||
|
||||
#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) :
|
||||
QUndoCommand("Add Voxel", parent),
|
||||
_tree(tree),
|
||||
|
@ -43,11 +87,40 @@ DeleteVoxelCommand::DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, Voxe
|
|||
QUndoCommand("Delete Voxel", parent),
|
||||
_tree(tree),
|
||||
_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() {
|
||||
if (_voxel.s == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tree) {
|
||||
_tree->deleteVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||
}
|
||||
|
@ -57,10 +130,32 @@ void DeleteVoxelCommand::redo() {
|
|||
}
|
||||
|
||||
void DeleteVoxelCommand::undo() {
|
||||
if (_tree) {
|
||||
_tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
|
||||
if (_voxel.s == 0.0f) {
|
||||
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 {
|
||||
public:
|
||||
DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender = NULL, QUndoCommand* parent = NULL);
|
||||
~DeleteVoxelCommand();
|
||||
|
||||
virtual void redo();
|
||||
virtual void undo();
|
||||
|
@ -44,6 +45,7 @@ private:
|
|||
VoxelTree* _tree;
|
||||
VoxelEditPacketSender* _packetSender;
|
||||
VoxelDetail _voxel;
|
||||
VoxelTree* _oldTree;
|
||||
};
|
||||
|
||||
#endif // hifi_VoxelTreeCommands_h
|
||||
|
|
|
@ -76,32 +76,16 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
|
|||
if (_tree) {
|
||||
if (_undoStack) {
|
||||
AddVoxelCommand* addCommand = new AddVoxelCommand(_tree,
|
||||
addVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
|
||||
VoxelTreeElement* deleteVoxelElement = _tree->getVoxelAt(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s);
|
||||
if (deleteVoxelElement) {
|
||||
nodeColor color;
|
||||
memcpy(&color, &deleteVoxelElement->getColor(), sizeof(nodeColor));
|
||||
VoxelDetail deleteVoxelDetail = {addVoxelDetail.x,
|
||||
addVoxelDetail.y,
|
||||
addVoxelDetail.z,
|
||||
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);
|
||||
}
|
||||
addVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
DeleteVoxelCommand* deleteCommand = new DeleteVoxelCommand(_tree,
|
||||
addVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
_undoStack->beginMacro(addCommand->text());
|
||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||
_undoStack->push(deleteCommand);
|
||||
_undoStack->push(addCommand);
|
||||
_undoStack->endMacro();
|
||||
} else {
|
||||
// queue the destructive add
|
||||
queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail);
|
||||
|
|
Loading…
Reference in a new issue