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 }