1 /** 2 Copyright: Copyright (c) 2016-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 module voxelman.edit.tools.filltool; 8 9 import voxelman.container.buffer; 10 import voxelman.log; 11 import voxelman.math; 12 import voxelman.geometry; 13 import voxelman.core.config; 14 import voxelman.core.packets; 15 import voxelman.world.storage; 16 import voxelman.world.block.blockinfo; 17 import voxelman.world.mesh.meshgenerator : SingleBlockMesher; 18 19 import voxelman.client.plugin; 20 import voxelman.worldinteraction.plugin; 21 import voxelman.graphics.plugin; 22 import voxelman.net.plugin; 23 24 import voxelman.edit.tools.itool; 25 import voxelman.edit.plugin; 26 27 final class FillTool : ITool 28 { 29 WorldInteractionPlugin worldInteraction; 30 NetClientPlugin connection; 31 BlockInfoTable blockInfos; 32 33 BlockIdAndMeta currentBlock = BlockIdAndMeta(4); 34 ubyte currentRotation = 0; 35 36 BlockWorldPos startingPos; 37 EditState state; 38 WorldBox selection; 39 bool showCursor = true; 40 Buffer!ColoredVertex cursorBuffer; 41 42 SingleBlockMesher blockMesher; 43 44 enum EditState 45 { 46 none, 47 placing, 48 removing 49 } 50 51 this() { name = "voxelman.edit.fill_tool"; } 52 53 override void onUpdate() { 54 if (currentCursorPos.w != startingPos.w) 55 { 56 startingPos = currentCursorPos; 57 } 58 selection = worldBoxFromCorners(startingPos.vector.xyz, currentCursorPos.vector.xyz, cast(DimensionId)currentCursorPos.w); 59 60 if (state != EditState.placing) 61 updateBlockRotation(); 62 } 63 64 override void onRender(GraphicsPlugin graphics) { 65 import voxelman.graphics.gl; 66 drawSelection(graphics); 67 if (showCursor && !worldInteraction.cameraInSolidBlock) 68 { 69 //worldInteraction.drawCursor(worldInteraction.blockPos, Colors.red); 70 //worldInteraction.drawCursor(worldInteraction.sideBlockPos, Colors.blue); 71 72 // TODO FIX: putting regular vertex into chunk vertex buffer is broken 73 //blockMesher.meshBlock(blockInfos[currentBlock.id], currentBlock.metadata); 74 //graphics.transparentBuffer.putMesh( 75 // blockMesher.geometry.data, 76 // vec3(worldInteraction.sideBlockPos.xyz)); 77 78 //graphics.drawBuffer3d(cursorBuffer.data, GL_TRIANGLES); 79 80 //foreach(ref vert; cursorBuffer.data) 81 // vert.color = Color4ub(0,0,0,255); 82 83 84 glEnable(GL_POLYGON_OFFSET_LINE); 85 glPolygonOffset(-1, 1); 86 //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 87 //graphics.drawBuffer3d(cursorBuffer.data, GL_TRIANGLES); 88 89 //cursorBuffer.clear(); 90 cursorBuffer.putLineBlock(vec3(worldInteraction.blockPos.xyz), vec3(1,1,1), Colors.red); 91 cursorBuffer.putLineBlock(vec3(worldInteraction.sideBlockPos.xyz), vec3(1,1,1), Colors.blue); 92 graphics.drawBuffer3d(cursorBuffer.data, GL_LINES); 93 glPolygonOffset(0,0); 94 95 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 96 glDisable(GL_POLYGON_OFFSET_LINE); 97 98 cursorBuffer.clear(); 99 blockMesher.reset(); 100 } 101 } 102 103 override void onShowDebug() { 104 import voxelman.text.textformatter; 105 auto binfo = blockInfos[currentBlock.id]; 106 //igTextf("Fill block: %s:%s", binfo.name, currentBlock.metadata); 107 } 108 109 BlockWorldPos currentCursorPos() @property { 110 final switch(state) { 111 case EditState.none: return worldInteraction.blockPos; 112 case EditState.placing: 113 return worldInteraction.sideBlockPos; 114 case EditState.removing: 115 return worldInteraction.blockPos; 116 } 117 } 118 119 void drawSelection(GraphicsPlugin graphics) { 120 final switch(state) { 121 case EditState.none: 122 break; 123 case EditState.placing: 124 graphics.debugBatch.putCube(vec3(selection.position) - cursorOffset, 125 vec3(selection.size) + cursorOffset, Colors.blue, false); 126 break; 127 case EditState.removing: 128 graphics.debugBatch.putCube(vec3(selection.position) - cursorOffset, 129 vec3(selection.size) + cursorOffset, Colors.red, false); 130 break; 131 } 132 } 133 134 override void onMainActionPress() { 135 if (state != EditState.none) return; 136 if (!worldInteraction.cursorHit) return; 137 state = EditState.removing; 138 startingPos = currentCursorPos; 139 showCursor = false; 140 } 141 142 override void onMainActionRelease() { 143 if (state != EditState.removing) return; 144 state = EditState.none; 145 showCursor = true; 146 147 if (worldInteraction.cursorHit) 148 { 149 worldInteraction.fillBox(selection, 1); 150 } 151 } 152 153 override void onSecondaryActionPress() { 154 if (state != EditState.none) return; 155 if (!worldInteraction.cursorHit) return; 156 state = EditState.placing; 157 startingPos = currentCursorPos; 158 showCursor = false; 159 } 160 161 override void onSecondaryActionRelease() { 162 if (state != EditState.placing) return; 163 state = EditState.none; 164 showCursor = true; 165 166 if (worldInteraction.cursorHit) 167 { 168 worldInteraction.fillBox(selection, currentBlock.id, currentBlock.metadata); 169 } 170 } 171 172 override void onTertiaryActionRelease() { 173 setCurrentBlock(worldInteraction.pickBlock()); 174 } 175 176 override void onRotateAction() { 177 currentRotation = (currentRotation + 1) % 4; 178 updateBlockRotation(); 179 } 180 181 void updateBlockRotation() { 182 if (auto handler = blockInfos[currentBlock.id].rotationHandler) 183 { 184 CubeSide side = oppSide[sideFromNormal(worldInteraction.hitNormal)]; 185 handler(currentBlock.metadata, currentRotation, side); 186 } 187 } 188 189 void setCurrentBlock(BlockIdAndMeta block) { 190 currentBlock = block; 191 } 192 }