Merge remote-tracking branch 'origin/master' into crashreports
This commit is contained in:
		
							
								
								
									
										25
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								build.gradle
									
									
									
									
									
								
							| @@ -18,10 +18,12 @@ dependencies { | |||||||
|     implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1' |     implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1' | ||||||
|  |  | ||||||
| 	// log4j | 	// log4j | ||||||
| 	compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3' | 	implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3' | ||||||
| 	compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3' | 	implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3' | ||||||
|  |  | ||||||
| 	testImplementation 'junit:junit:4.12' | 	testImplementation 'junit:junit:4.12' | ||||||
|  | 	 | ||||||
|  | 	// See also LWJGL dependencies below | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -77,3 +79,22 @@ dependencies { | |||||||
| } | } | ||||||
|  |  | ||||||
| // LWJGL END | // LWJGL END | ||||||
|  |  | ||||||
|  | jar { | ||||||
|  |     manifest { | ||||||
|  |         attributes( | ||||||
|  |         	"Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain", | ||||||
|  |         	"Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() }.join(' ') | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Copies runtime dependencies to a prespecified location so they can be packaged properly. | ||||||
|  |  */ | ||||||
|  | task copyLibs(type: Copy) { | ||||||
|  |     into "${libsDir}/lib" | ||||||
|  |     from configurations.runtimeClasspath | ||||||
|  | } | ||||||
|  |  | ||||||
|  | build.dependsOn(copyLibs) | ||||||
|   | |||||||
| @@ -26,6 +26,7 @@ import java.util.ArrayList; | |||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Iterator; | import java.util.Iterator; | ||||||
|  | import java.util.Objects; | ||||||
| import java.util.function.IntFunction; | import java.util.function.IntFunction; | ||||||
|  |  | ||||||
| public class StringUtil { | public class StringUtil { | ||||||
| @@ -775,4 +776,35 @@ public class StringUtil { | |||||||
| 		else             return (char) ('A' - 0xA + value); | 		else             return (char) ('A' - 0xA + value); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	public static String replaceAll(String source, String substring, String replacement) { | ||||||
|  | 		Objects.requireNonNull(source, "source"); | ||||||
|  | 		Objects.requireNonNull(substring, "substring"); | ||||||
|  | 		 | ||||||
|  | 		if (substring.isEmpty()) { | ||||||
|  | 			throw new IllegalArgumentException("substring is empty"); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (!source.contains(substring)) { // also passes if source is empty | ||||||
|  | 			return source; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if (substring.equals(replacement)) { // null-safe | ||||||
|  | 			return source; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		StringBuilder sb = new StringBuilder(2 * source.length()); | ||||||
|  | 		 | ||||||
|  | 		for (int i = 0; i < source.length() - substring.length() + 1; ++i) { | ||||||
|  | 			if (source.startsWith(substring, i)) { | ||||||
|  | 				if (replacement != null) { | ||||||
|  | 					sb.append(replacement); | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				sb.append(source.charAt(i)); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		return sb.toString(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,10 +3,10 @@ package ru.windcorp.progressia.client; | |||||||
| import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; | import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; | ||||||
| import ru.windcorp.progressia.client.graphics.GUI; | import ru.windcorp.progressia.client.graphics.GUI; | ||||||
| import ru.windcorp.progressia.client.graphics.flat.LayerTestUI; | import ru.windcorp.progressia.client.graphics.flat.LayerTestUI; | ||||||
| import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI; |  | ||||||
| import ru.windcorp.progressia.client.graphics.world.LayerWorld; | import ru.windcorp.progressia.client.graphics.world.LayerWorld; | ||||||
| import ru.windcorp.progressia.common.world.WorldData; | import ru.windcorp.progressia.common.world.WorldData; | ||||||
| import ru.windcorp.progressia.server.ServerState; | import ru.windcorp.progressia.server.ServerState; | ||||||
|  | import ru.windcorp.progressia.test.LayerTestGUI; | ||||||
|  |  | ||||||
| public class ClientState { | public class ClientState { | ||||||
| 	 | 	 | ||||||
|   | |||||||
| @@ -1,125 +0,0 @@ | |||||||
| /******************************************************************************* |  | ||||||
|  * Progressia |  | ||||||
|  * Copyright (C) 2020  Wind Corporation |  | ||||||
|  * |  | ||||||
|  * This program is free software: you can redistribute it and/or modify |  | ||||||
|  * it under the terms of the GNU General Public License as published by |  | ||||||
|  * the Free Software Foundation, either version 3 of the License, or |  | ||||||
|  * (at your option) any later version. |  | ||||||
|  * |  | ||||||
|  * This program is distributed in the hope that it will be useful, |  | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|  * GNU General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU General Public License |  | ||||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>. |  | ||||||
|  *******************************************************************************/ |  | ||||||
| package ru.windcorp.progressia.client.graphics.gui; |  | ||||||
|  |  | ||||||
| import com.google.common.eventbus.Subscribe; |  | ||||||
| import glm.vec._2.i.Vec2i; |  | ||||||
| import org.lwjgl.glfw.GLFW; |  | ||||||
| import ru.windcorp.progressia.client.graphics.Colors; |  | ||||||
| import ru.windcorp.progressia.client.graphics.flat.RenderTarget; |  | ||||||
| import ru.windcorp.progressia.client.graphics.font.Font; |  | ||||||
| import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent; |  | ||||||
| import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; |  | ||||||
| import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical; |  | ||||||
| import ru.windcorp.progressia.client.graphics.input.KeyEvent; |  | ||||||
| import ru.windcorp.progressia.client.localization.Localizer; |  | ||||||
| import ru.windcorp.progressia.client.localization.MutableString; |  | ||||||
| import ru.windcorp.progressia.client.localization.MutableStringLocalized; |  | ||||||
|  |  | ||||||
| public class LayerTestGUI extends GUILayer { |  | ||||||
| 	 |  | ||||||
| 	private static class DebugComponent extends Component { |  | ||||||
| 		private final int color; |  | ||||||
| 		 |  | ||||||
| 		public DebugComponent(String name, Vec2i size, int color) { |  | ||||||
| 			super(name); |  | ||||||
| 			this.color = color; |  | ||||||
| 			 |  | ||||||
| 			setPreferredSize(size); |  | ||||||
| 			 |  | ||||||
| 			addListener(new Object() { |  | ||||||
| 				@Subscribe |  | ||||||
| 				public void onHoverChanged(HoverEvent e) { |  | ||||||
| 					requestReassembly(); |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
| 			 |  | ||||||
| 			addListener(KeyEvent.class, this::onClicked); |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		private boolean onClicked(KeyEvent event) { |  | ||||||
| 			if (!event.isMouse()) { |  | ||||||
| 				return false; |  | ||||||
| 			} else if (event.isPress() && event.isLeftMouseButton()) { |  | ||||||
| 				System.out.println("You pressed a Component!"); |  | ||||||
| 			} |  | ||||||
| 			return true; |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		@Override |  | ||||||
| 		protected void assembleSelf(RenderTarget target) { |  | ||||||
| 			target.fill(getX(), getY(), getWidth(), getHeight(), Colors.BLACK); |  | ||||||
| 			 |  | ||||||
| 			target.fill( |  | ||||||
| 					getX() + 2, getY() + 2, |  | ||||||
| 					getWidth() - 4, getHeight() - 4, |  | ||||||
| 					isHovered() ? Colors.DEBUG_YELLOW : color |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public LayerTestGUI() { |  | ||||||
| 		super("LayerTestGui", new LayoutAlign(1, 0.75, 5)); |  | ||||||
| 		 |  | ||||||
| 		Panel panel = new Panel("Alex", new LayoutVertical(5)); |  | ||||||
| 		 |  | ||||||
| 		panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44)); |  | ||||||
| 		 |  | ||||||
| 		Component charlie = new DebugComponent("Charlie", null, 0x222222); |  | ||||||
| 		charlie.setLayout(new LayoutVertical(5)); |  | ||||||
|  |  | ||||||
| 		//Debug |  | ||||||
| 		Localizer.getInstance().setLanguage("ru-RU"); |  | ||||||
| 		MutableString epsilon = new MutableStringLocalized("Epsilon") |  | ||||||
| 				.addListener(() -> ((Label)charlie.getChild(0)).update()).format(34, "thirty-four"); |  | ||||||
| 		// These two are swapped in code due to a bug in layouts, fixing ATM |  | ||||||
| 		charlie.addChild( |  | ||||||
| 				new Label( |  | ||||||
| 						"Delta", |  | ||||||
| 						new Font().withColor(0xCCBB44).deriveShadow().deriveBold(), |  | ||||||
| 						"Пре-альфа!" |  | ||||||
| 				) |  | ||||||
| 		); |  | ||||||
| 		charlie.addChild( |  | ||||||
| 				new Label( |  | ||||||
| 						"Epsilon", |  | ||||||
| 						new Font().withColor(0x4444BB).deriveItalic(), |  | ||||||
| 						() -> epsilon.get().concat("\u269b") |  | ||||||
| 				) |  | ||||||
| 		); |  | ||||||
| 		panel.addChild(charlie); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 		charlie.addListener(KeyEvent.class, e -> { |  | ||||||
| 			if(e.isPress() && e.getKey() == GLFW.GLFW_KEY_L) { |  | ||||||
| 				Localizer localizer = Localizer.getInstance(); |  | ||||||
| 				if (localizer.getLanguage().equals("ru-RU")) { |  | ||||||
| 					localizer.setLanguage("en-US"); |  | ||||||
| 				} else { |  | ||||||
| 					localizer.setLanguage("ru-RU"); |  | ||||||
| 				} |  | ||||||
| 				return true; |  | ||||||
| 			} return false; |  | ||||||
| 		}); |  | ||||||
| 		charlie.setFocusable(true); |  | ||||||
| 		charlie.takeFocus(); |  | ||||||
|  |  | ||||||
| 		getRoot().addChild(panel); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -122,4 +122,13 @@ public class LambdaModel extends DynamicModel { | |||||||
| 		 | 		 | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	public static LambdaModel animate(Renderable model, TransformGetter transform) { | ||||||
|  | 		return new LambdaModel( | ||||||
|  | 				new Renderable[]      { model }, | ||||||
|  | 				new Mat4[]            { new Mat4() }, | ||||||
|  | 				new boolean[]         { true }, | ||||||
|  | 				new TransformGetter[] { transform } | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,10 +19,10 @@ public class ComplexTexture { | |||||||
| 		this.primitive = primitive; | 		this.primitive = primitive; | ||||||
| 		 | 		 | ||||||
| 		this.assumedWidth = abstractWidth | 		this.assumedWidth = abstractWidth | ||||||
| 				* primitive.getWidth() / (float) primitive.getBufferWidth(); | 				/ (float) primitive.getWidth() * primitive.getBufferWidth(); | ||||||
| 		 | 		 | ||||||
| 		this.assumedHeight = abstractHeight | 		this.assumedHeight = abstractHeight | ||||||
| 				* primitive.getHeight() / (float) primitive.getBufferHeight(); | 				/ (float) primitive.getHeight() * primitive.getBufferHeight(); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	public Texture get(int x, int y, int width, int height) { | 	public Texture get(int x, int y, int width, int height) { | ||||||
|   | |||||||
| @@ -272,6 +272,10 @@ public class Camera { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	public int getCurrentModeIndex() { | ||||||
|  | 		return currentModeIndex; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	public float getLastAnchorYaw() { | 	public float getLastAnchorYaw() { | ||||||
| 		return lastAnchorYaw; | 		return lastAnchorYaw; | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -20,45 +20,27 @@ package ru.windcorp.progressia.client.graphics.world; | |||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import org.lwjgl.glfw.GLFW; |  | ||||||
|  |  | ||||||
| import glm.Glm; |  | ||||||
| import glm.mat._3.Mat3; |  | ||||||
| import glm.vec._2.Vec2; |  | ||||||
| import glm.vec._3.Vec3; |  | ||||||
| import ru.windcorp.progressia.client.Client; | import ru.windcorp.progressia.client.Client; | ||||||
|  | import ru.windcorp.progressia.client.ClientState; | ||||||
| import ru.windcorp.progressia.client.comms.controls.InputBasedControls; | import ru.windcorp.progressia.client.comms.controls.InputBasedControls; | ||||||
| import ru.windcorp.progressia.client.graphics.Layer; | import ru.windcorp.progressia.client.graphics.Layer; | ||||||
| import ru.windcorp.progressia.client.graphics.backend.FaceCulling; | import ru.windcorp.progressia.client.graphics.backend.FaceCulling; | ||||||
| import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend; |  | ||||||
| import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; | import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; | ||||||
| import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; |  | ||||||
| import ru.windcorp.progressia.client.graphics.input.InputEvent; |  | ||||||
| import ru.windcorp.progressia.client.graphics.input.KeyEvent; |  | ||||||
| import ru.windcorp.progressia.client.graphics.input.bus.Input; | import ru.windcorp.progressia.client.graphics.input.bus.Input; | ||||||
| import ru.windcorp.progressia.common.collision.AABB; | import ru.windcorp.progressia.common.Units; | ||||||
| import ru.windcorp.progressia.common.collision.Collideable; | import ru.windcorp.progressia.common.collision.Collideable; | ||||||
| import ru.windcorp.progressia.common.collision.CollisionClock; |  | ||||||
| import ru.windcorp.progressia.common.collision.CollisionModel; |  | ||||||
| import ru.windcorp.progressia.common.collision.CompoundCollisionModel; |  | ||||||
| import ru.windcorp.progressia.common.collision.colliders.Collider; | import ru.windcorp.progressia.common.collision.colliders.Collider; | ||||||
| import ru.windcorp.progressia.common.util.FloatMathUtils; |  | ||||||
| import ru.windcorp.progressia.common.util.Vectors; |  | ||||||
| import ru.windcorp.progressia.common.world.entity.EntityData; | import ru.windcorp.progressia.common.world.entity.EntityData; | ||||||
| import ru.windcorp.progressia.test.AABBRenderer; | import ru.windcorp.progressia.test.CollisionModelRenderer; | ||||||
|  | import ru.windcorp.progressia.test.TestPlayerControls; | ||||||
|  |  | ||||||
| public class LayerWorld extends Layer { | public class LayerWorld extends Layer { | ||||||
| 	 | 	 | ||||||
| 	private final Mat3 angMat = new Mat3(); |  | ||||||
|  |  | ||||||
| 	private int movementForward = 0; |  | ||||||
| 	private int movementRight = 0; |  | ||||||
| 	private int movementUp = 0; |  | ||||||
| 	 |  | ||||||
| 	private final WorldRenderHelper helper = new WorldRenderHelper(); | 	private final WorldRenderHelper helper = new WorldRenderHelper(); | ||||||
| 	 | 	 | ||||||
| 	private final Client client; | 	private final Client client; | ||||||
| 	private final InputBasedControls inputBasedControls; | 	private final InputBasedControls inputBasedControls; | ||||||
|  | 	private final TestPlayerControls tmp_testControls = TestPlayerControls.getInstance(); | ||||||
|  |  | ||||||
| 	public LayerWorld(Client client) { | 	public LayerWorld(Client client) { | ||||||
| 		super("World"); | 		super("World"); | ||||||
| @@ -79,40 +61,12 @@ public class LayerWorld extends Layer { | |||||||
| 	 | 	 | ||||||
| 	@Override | 	@Override | ||||||
| 	protected void doRender() { | 	protected void doRender() { | ||||||
| 		if (client.getLocalPlayer() != null) { |  | ||||||
| 			tmp_handleControls(); |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		Camera camera = client.getCamera(); | 		Camera camera = client.getCamera(); | ||||||
| 		if (camera.hasAnchor()) { | 		if (camera.hasAnchor()) { | ||||||
| 			renderWorld(); | 			renderWorld(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private void tmp_handleControls() { |  | ||||||
| 		EntityData player = client.getLocalPlayer(); |  | ||||||
| 		 |  | ||||||
| 		angMat.identity().rotateZ(player.getYaw()); |  | ||||||
| 		 |  | ||||||
| 		Vec3 movement = Vectors.grab3(); |  | ||||||
| 		 |  | ||||||
| 		// Horizontal and vertical max control speed |  | ||||||
| 		final float movementSpeed = 0.1f * 60.0f; |  | ||||||
| 		// (0; 1], 1 is instant change, 0 is no control authority |  | ||||||
| 		final float controlAuthority = 0.1f; |  | ||||||
| 		 |  | ||||||
| 		movement.set(movementForward, -movementRight, 0); |  | ||||||
| 		if (movementForward != 0 && movementRight != 0) movement.normalize(); |  | ||||||
| 		angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped |  | ||||||
| 		movement.z = movementUp; |  | ||||||
| 		movement.mul(movementSpeed); |  | ||||||
| 		movement.sub(player.getVelocity()); |  | ||||||
| 		movement.mul(controlAuthority); |  | ||||||
| 		player.getVelocity().add(movement); |  | ||||||
| 		 |  | ||||||
| 		Vectors.release(movement); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	private void renderWorld() { | 	private void renderWorld() { | ||||||
| 		client.getCamera().apply(helper); | 		client.getCamera().apply(helper); | ||||||
| 		FaceCulling.push(true); | 		FaceCulling.push(true); | ||||||
| @@ -128,150 +82,75 @@ public class LayerWorld extends Layer { | |||||||
| 	private final Collider.ColliderWorkspace tmp_colliderWorkspace = new Collider.ColliderWorkspace(); | 	private final Collider.ColliderWorkspace tmp_colliderWorkspace = new Collider.ColliderWorkspace(); | ||||||
| 	private final List<Collideable> tmp_collideableList = new ArrayList<>(); | 	private final List<Collideable> tmp_collideableList = new ArrayList<>(); | ||||||
| 	 | 	 | ||||||
| 	private static final boolean RENDER_AABBS = true; | 	private static final boolean RENDER_COLLISION_MODELS = false; | ||||||
| 	 | 	 | ||||||
| 	private void tmp_doEveryFrame() { | 	private void tmp_doEveryFrame() { | ||||||
|  | 		float tickLength = (float) GraphicsInterface.getFrameLength(); | ||||||
|  | 		 | ||||||
| 		try { | 		try { | ||||||
| 			if (RENDER_AABBS) { | 			tmp_performCollisions(tickLength); | ||||||
| 				for (EntityData data : this.client.getWorld().getData().getEntities()) { |  | ||||||
| 					CollisionModel model = data.getCollisionModel(); |  | ||||||
| 					if (model instanceof AABB) { |  | ||||||
| 						AABBRenderer.renderAABB((AABB) model, helper); |  | ||||||
| 					} else if (model instanceof CompoundCollisionModel) { |  | ||||||
| 						AABBRenderer.renderAABBsInCompound((CompoundCollisionModel) model, helper); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			 | 			 | ||||||
| 			tmp_collideableList.clear(); | 			tmp_testControls.applyPlayerControls(); | ||||||
| 			tmp_collideableList.addAll(this.client.getWorld().getData().getEntities()); |  | ||||||
| 			 |  | ||||||
| 			Collider.performCollisions( |  | ||||||
| 					tmp_collideableList, |  | ||||||
| 					new CollisionClock() { |  | ||||||
| 						private float t = 0; |  | ||||||
| 						@Override |  | ||||||
| 						public float getTime() { |  | ||||||
| 							return t; |  | ||||||
| 						} |  | ||||||
| 						 |  | ||||||
| 						@Override |  | ||||||
| 						public void advanceTime(float change) { |  | ||||||
| 							t += change; |  | ||||||
| 						} |  | ||||||
| 					}, |  | ||||||
| 					(float) GraphicsInterface.getFrameLength(), |  | ||||||
| 					tmp_colliderWorkspace |  | ||||||
| 			); |  | ||||||
| 			 |  | ||||||
| 			final float frictionCoeff = 1 - 1e-2f; |  | ||||||
| 			 | 			 | ||||||
| 			for (EntityData data : this.client.getWorld().getData().getEntities()) { | 			for (EntityData data : this.client.getWorld().getData().getEntities()) { | ||||||
| 				data.getVelocity().mul(frictionCoeff); | 				tmp_applyFriction(data); | ||||||
|  | 				tmp_applyGravity(data, tickLength); | ||||||
|  | 				tmp_renderCollisionModel(data); | ||||||
| 			} | 			} | ||||||
| 		} catch (Exception e) { | 		} catch (Throwable e) { | ||||||
|  | 			e.printStackTrace(); | ||||||
|  | 			System.err.println("OLEGSHA is to blame. Tell him he vry stupiDD!!"); | ||||||
| 			System.exit(31337); | 			System.exit(31337); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	private void tmp_renderCollisionModel(EntityData entity) { | ||||||
|  | 		if (RENDER_COLLISION_MODELS) { | ||||||
|  | 			CollisionModelRenderer.renderCollisionModel(entity.getCollisionModel(), helper); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void tmp_performCollisions(float tickLength) { | ||||||
|  | 		tmp_collideableList.clear(); | ||||||
|  | 		tmp_collideableList.addAll(this.client.getWorld().getData().getEntities()); | ||||||
|  | 		 | ||||||
|  | 		Collider.performCollisions( | ||||||
|  | 				tmp_collideableList, | ||||||
|  | 				this.client.getWorld().getData(), | ||||||
|  | 				tickLength, | ||||||
|  | 				tmp_colliderWorkspace | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void tmp_applyFriction(EntityData entity) { | ||||||
|  | 		final float frictionCoeff = 1 - 1e-5f; | ||||||
|  | 		entity.getVelocity().mul(frictionCoeff); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private void tmp_applyGravity(EntityData entity, float tickLength) { | ||||||
|  | 		if (ClientState.getInstance().getLocalPlayer() == entity && tmp_testControls.isFlying()) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		final float gravitationalAcceleration; | ||||||
|  | 		 | ||||||
|  | 		if (tmp_testControls.useMinecraftGravity()) { | ||||||
|  | 			gravitationalAcceleration = 32f * Units.METERS_PER_SECOND_SQUARED; // plz dont sue me M$ | ||||||
|  | 		} else { | ||||||
|  | 			gravitationalAcceleration = 9.81f * Units.METERS_PER_SECOND_SQUARED; | ||||||
|  | 		} | ||||||
|  | 		entity.getVelocity().add(0, 0, -gravitationalAcceleration * tickLength); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	protected void handleInput(Input input) { | 	protected void handleInput(Input input) { | ||||||
| 		if (input.isConsumed()) return; | 		if (input.isConsumed()) return; | ||||||
| 		 | 		 | ||||||
| 		InputEvent event = input.getEvent(); | 		tmp_testControls.handleInput(input); | ||||||
| 		 |  | ||||||
| 		if (event instanceof KeyEvent) { |  | ||||||
| 			if (onKeyEvent((KeyEvent) event)) { |  | ||||||
| 				input.consume(); |  | ||||||
| 			} |  | ||||||
| 		} else if (event instanceof CursorMoveEvent) { |  | ||||||
| 			onMouseMoved((CursorMoveEvent) event); |  | ||||||
| 			input.consume(); |  | ||||||
| 		} |  | ||||||
| 		 | 		 | ||||||
| 		if (!input.isConsumed()) { | 		if (!input.isConsumed()) { | ||||||
| 			inputBasedControls.handleInput(input); | 			inputBasedControls.handleInput(input); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private boolean flag = true; |  | ||||||
| 	 |  | ||||||
| 	private boolean onKeyEvent(KeyEvent event) { |  | ||||||
| 		if (event.isRepeat()) return false; |  | ||||||
| 		 |  | ||||||
| 		int multiplier = event.isPress() ? 1 : -1; |  | ||||||
| 		 |  | ||||||
| 		switch (event.getKey()) { |  | ||||||
| 		case GLFW.GLFW_KEY_W: |  | ||||||
| 			movementForward += +1 * multiplier; |  | ||||||
| 			break; |  | ||||||
| 		case GLFW.GLFW_KEY_S: |  | ||||||
| 			movementForward += -1 * multiplier; |  | ||||||
| 			break; |  | ||||||
| 		case GLFW.GLFW_KEY_A: |  | ||||||
| 			movementRight += -1 * multiplier; |  | ||||||
| 			break; |  | ||||||
| 		case GLFW.GLFW_KEY_D: |  | ||||||
| 			movementRight += +1 * multiplier; |  | ||||||
| 			break; |  | ||||||
| 		case GLFW.GLFW_KEY_SPACE: |  | ||||||
| 			movementUp += +1 * multiplier; |  | ||||||
| 			break; |  | ||||||
| 		case GLFW.GLFW_KEY_LEFT_SHIFT: |  | ||||||
| 			movementUp += -1 * multiplier; |  | ||||||
| 			break; |  | ||||||
| 			 |  | ||||||
| 		case GLFW.GLFW_KEY_ESCAPE: |  | ||||||
| 			if (!event.isPress()) return false; |  | ||||||
| 			 |  | ||||||
| 			if (flag) { |  | ||||||
| 				GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); |  | ||||||
| 			} else { |  | ||||||
| 				GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); |  | ||||||
| 			} |  | ||||||
| 			 |  | ||||||
| 			flag = !flag; |  | ||||||
| 			break; |  | ||||||
| 			 |  | ||||||
| 		case GLFW.GLFW_KEY_F5: |  | ||||||
| 			if (!event.isPress()) return false; |  | ||||||
| 			 |  | ||||||
| 			if (client.getCamera().hasAnchor()) { |  | ||||||
| 				client.getCamera().selectNextMode(); |  | ||||||
| 			} |  | ||||||
| 			break; |  | ||||||
| 			 |  | ||||||
| 		default: |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	private void onMouseMoved(CursorMoveEvent event) { |  | ||||||
| 		if (!flag) return; |  | ||||||
| 		 |  | ||||||
| 		final float yawScale = -0.002f; |  | ||||||
| 		final float pitchScale = yawScale; |  | ||||||
|  |  | ||||||
| 		EntityData player = client.getLocalPlayer(); |  | ||||||
| 		 |  | ||||||
| 		if (player != null) { |  | ||||||
| 			normalizeAngles(player.getDirection().add( |  | ||||||
| 					(float) (event.getChangeX() * yawScale), |  | ||||||
| 					(float) (event.getChangeY() * pitchScale) |  | ||||||
| 			)); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	private void normalizeAngles(Vec2 dir) { |  | ||||||
| 		// Normalize yaw |  | ||||||
| 		dir.x = FloatMathUtils.normalizeAngle(dir.x); |  | ||||||
| 		 |  | ||||||
| 		// Clamp pitch |  | ||||||
| 		dir.y = Glm.clamp( |  | ||||||
| 				dir.y, -FloatMathUtils.PI_F/2, +FloatMathUtils.PI_F/2 |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,116 @@ | |||||||
|  | package ru.windcorp.progressia.client.world.entity; | ||||||
|  |  | ||||||
|  | import static java.lang.Math.*; | ||||||
|  | import static ru.windcorp.progressia.common.util.FloatMathUtils.*; | ||||||
|  |  | ||||||
|  | import glm.mat._4.Mat4; | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.Renderable; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; | ||||||
|  | import ru.windcorp.progressia.common.world.entity.EntityData; | ||||||
|  |  | ||||||
|  | public class HumanoidModel extends NPedModel { | ||||||
|  | 	 | ||||||
|  | 	protected static abstract class Limb extends BodyPart { | ||||||
|  | 		private final float animationOffset; | ||||||
|  | 		 | ||||||
|  | 		public Limb( | ||||||
|  | 				Renderable renderable, Vec3 joint, | ||||||
|  | 				float animationOffset | ||||||
|  | 		) { | ||||||
|  | 			super(renderable, joint); | ||||||
|  | 			this.animationOffset = animationOffset; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		protected void applyTransform(Mat4 mat, NPedModel model) { | ||||||
|  | 			float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset; | ||||||
|  | 			float value = sin(phase); | ||||||
|  | 			float amplitude = getSwingAmplitude((HumanoidModel) model) * model.getVelocityParameter(); | ||||||
|  | 			mat.rotateY(value * amplitude); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		protected abstract float getSwingAmplitude(HumanoidModel model); | ||||||
|  | 		 | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static class Leg extends Limb { | ||||||
|  | 		public Leg( | ||||||
|  | 				Renderable renderable, Vec3 joint, | ||||||
|  | 				float animationOffset | ||||||
|  | 		) { | ||||||
|  | 			super(renderable, joint, animationOffset); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		protected float getSwingAmplitude(HumanoidModel model) { | ||||||
|  | 			return model.walkingLegSwing; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static class Arm extends Limb { | ||||||
|  | 		public Arm( | ||||||
|  | 				Renderable renderable, Vec3 joint, | ||||||
|  | 				float animationOffset | ||||||
|  | 		) { | ||||||
|  | 			super(renderable, joint, animationOffset); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		protected float getSwingAmplitude(HumanoidModel model) { | ||||||
|  | 			return model.walkingArmSwing; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private final Arm leftArm; | ||||||
|  | 	private final Arm rightArm; | ||||||
|  | 	private final Leg leftLeg; | ||||||
|  | 	private final Leg rightLeg; | ||||||
|  | 	 | ||||||
|  | 	private float walkingLegSwing; | ||||||
|  | 	private float walkingArmSwing; | ||||||
|  | 	 | ||||||
|  | 	public HumanoidModel( | ||||||
|  | 			EntityData entity, | ||||||
|  | 			 | ||||||
|  | 			Body body, Head head, | ||||||
|  | 			Arm leftArm, Arm rightArm, | ||||||
|  | 			Leg leftLeg, Leg rightLeg, | ||||||
|  | 			 | ||||||
|  | 			float scale | ||||||
|  | 	) { | ||||||
|  | 		super(entity, body, head, scale); | ||||||
|  | 		this.leftArm = leftArm; | ||||||
|  | 		this.rightArm = rightArm; | ||||||
|  | 		this.leftLeg = leftLeg; | ||||||
|  | 		this.rightLeg = rightLeg; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	@Override | ||||||
|  | 	protected void renderBodyParts(ShapeRenderHelper renderer) { | ||||||
|  | 		super.renderBodyParts(renderer); | ||||||
|  | 		leftArm.render(renderer, this); | ||||||
|  | 		rightArm.render(renderer, this); | ||||||
|  | 		leftLeg.render(renderer, this); | ||||||
|  | 		rightLeg.render(renderer, this); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public float getWalkingArmSwing() { | ||||||
|  | 		return walkingArmSwing; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public float getWalkingLegSwing() { | ||||||
|  | 		return walkingLegSwing; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public HumanoidModel setWalkingLegSwing(float walkingLegSwing) { | ||||||
|  | 		this.walkingLegSwing = walkingLegSwing; | ||||||
|  | 		return this; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public HumanoidModel setWalkingArmSwing(float walkingArmSwing) { | ||||||
|  | 		this.walkingArmSwing = walkingArmSwing; | ||||||
|  | 		return this; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,284 @@ | |||||||
|  | package ru.windcorp.progressia.client.world.entity; | ||||||
|  |  | ||||||
|  | import static java.lang.Math.atan2; | ||||||
|  | import static java.lang.Math.min; | ||||||
|  | import static java.lang.Math.pow; | ||||||
|  | import static java.lang.Math.toRadians; | ||||||
|  | import static ru.windcorp.progressia.common.util.FloatMathUtils.normalizeAngle; | ||||||
|  |  | ||||||
|  | import glm.Glm; | ||||||
|  | import glm.mat._4.Mat4; | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import glm.vec._4.Vec4; | ||||||
|  | import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.Renderable; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; | ||||||
|  | import ru.windcorp.progressia.common.Units; | ||||||
|  | import ru.windcorp.progressia.common.util.Matrices; | ||||||
|  | import ru.windcorp.progressia.common.util.Vectors; | ||||||
|  | import ru.windcorp.progressia.common.world.entity.EntityData; | ||||||
|  |  | ||||||
|  | public abstract class NPedModel extends EntityRenderable { | ||||||
|  |  | ||||||
|  | 	protected static abstract class BodyPart { | ||||||
|  | 			private final Renderable renderable; | ||||||
|  | 			private final Vec3 translation = new Vec3(); | ||||||
|  | 	 | ||||||
|  | 			public BodyPart(Renderable renderable, Vec3 joint) { | ||||||
|  | 				this.renderable = renderable; | ||||||
|  | 				if (joint != null) { | ||||||
|  | 					this.translation.set(joint); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			 | ||||||
|  | 			protected void render( | ||||||
|  | 					ShapeRenderHelper renderer, NPedModel model | ||||||
|  | 			) { | ||||||
|  | 				renderer.pushTransform().translate(translation); | ||||||
|  | 				applyTransform(renderer.pushTransform(), model); | ||||||
|  | 				renderable.render(renderer); | ||||||
|  | 				renderer.popTransform(); | ||||||
|  | 				renderer.popTransform(); | ||||||
|  | 			} | ||||||
|  | 	 | ||||||
|  | 			protected abstract void applyTransform(Mat4 mat, NPedModel model); | ||||||
|  | 			 | ||||||
|  | 			public Vec3 getTranslation() { | ||||||
|  | 				return translation; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	public static class Body extends BodyPart { | ||||||
|  | 		public Body(Renderable renderable) { | ||||||
|  | 			super(renderable, null); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		protected void applyTransform(Mat4 mat, NPedModel model) { | ||||||
|  | 			// Do nothing | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static class Head extends BodyPart { | ||||||
|  | 		private final float maxYaw; | ||||||
|  | 		private final float maxPitch; | ||||||
|  | 		 | ||||||
|  | 		private final Vec3 viewPoint; | ||||||
|  | 		 | ||||||
|  | 		public Head( | ||||||
|  | 				Renderable renderable, Vec3 joint, | ||||||
|  | 				double maxYawDegrees, double maxPitchDegrees, | ||||||
|  | 				Vec3 viewPoint | ||||||
|  | 		) { | ||||||
|  | 			super(renderable, joint); | ||||||
|  | 			this.maxYaw = (float) toRadians(maxYawDegrees); | ||||||
|  | 			this.maxPitch = (float) toRadians(maxPitchDegrees); | ||||||
|  | 			this.viewPoint = viewPoint; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		protected void applyTransform(Mat4 mat, NPedModel model) { | ||||||
|  | 			mat.rotateZ(model.getHeadYaw()).rotateY(model.getHeadPitch()); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		public Vec3 getViewPoint() { | ||||||
|  | 			return viewPoint; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	protected final Body body; | ||||||
|  | 	protected final Head head; | ||||||
|  | 	 | ||||||
|  | 	private float walkingParameter = 0; | ||||||
|  | 	private float velocityParameter = 0; | ||||||
|  | 	private float velocity = 0; | ||||||
|  | 	 | ||||||
|  | 	/** | ||||||
|  | 	 * If {@link #velocity} is greater than this value, {@link #velocityParameter} is 1.0. | ||||||
|  | 	 */ | ||||||
|  | 	private float maxEffectiveVelocity = 5 * Units.METERS_PER_SECOND; | ||||||
|  | 	 | ||||||
|  | 	/** | ||||||
|  | 	 * If {@link #velocity} is less than {@link #maxEffectiveVelocity}, then | ||||||
|  | 	 * {@code velocityCoeff = exp(velocity / maxEffectiveVelocity, velocityCoeffPower)}. | ||||||
|  | 	 */ | ||||||
|  | 	private float velocityCoeffPower = 1; | ||||||
|  | 	 | ||||||
|  | 	private final float scale; | ||||||
|  | 	 | ||||||
|  | 	private float walkingFrequency; | ||||||
|  | 	 | ||||||
|  | 	private float bodyYaw = Float.NaN; | ||||||
|  | 	private float headYaw; | ||||||
|  | 	private float headPitch; | ||||||
|  |  | ||||||
|  | 	public NPedModel(EntityData data, Body body, Head head, float scale) { | ||||||
|  | 		super(data); | ||||||
|  | 		this.body = body; | ||||||
|  | 		this.head = head; | ||||||
|  | 		this.scale = scale; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void render(ShapeRenderHelper renderer) { | ||||||
|  | 		renderer.pushTransform().scale(scale).rotateZ(bodyYaw); | ||||||
|  | 		renderBodyParts(renderer); | ||||||
|  | 		renderer.popTransform(); | ||||||
|  | 		 | ||||||
|  | 		accountForVelocity(); | ||||||
|  | 		evaluateAngles(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	protected void renderBodyParts(ShapeRenderHelper renderer) { | ||||||
|  | 		body.render(renderer, this); | ||||||
|  | 		head.render(renderer, this); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void evaluateAngles() { | ||||||
|  | 		float globalYaw = normalizeAngle(getData().getYaw()); | ||||||
|  | 		 | ||||||
|  | 		if (Float.isNaN(bodyYaw)) { | ||||||
|  | 			bodyYaw = globalYaw; | ||||||
|  | 			headYaw = 0; | ||||||
|  | 		} else { | ||||||
|  | 			headYaw = normalizeAngle(globalYaw - bodyYaw); | ||||||
|  | 			 | ||||||
|  | 			if (headYaw > +head.maxYaw) { | ||||||
|  | 				bodyYaw += headYaw - +head.maxYaw; | ||||||
|  | 				headYaw = +head.maxYaw; | ||||||
|  | 			} else if (headYaw < -head.maxYaw) { | ||||||
|  | 				bodyYaw += headYaw - -head.maxYaw; | ||||||
|  | 				headYaw = -head.maxYaw; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		bodyYaw = normalizeAngle(bodyYaw); | ||||||
|  | 		 | ||||||
|  | 		headPitch = Glm.clamp( | ||||||
|  | 				getData().getPitch(), | ||||||
|  | 				-head.maxPitch, head.maxPitch | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void accountForVelocity() { | ||||||
|  | 		Vec3 horizontal = Vectors.grab3(); | ||||||
|  | 		horizontal.set(getData().getVelocity()); | ||||||
|  | 		horizontal.z = 0; | ||||||
|  | 		 | ||||||
|  | 		velocity = horizontal.length(); | ||||||
|  | 		 | ||||||
|  | 		evaluateVelocityCoeff(); | ||||||
|  | 		 | ||||||
|  | 		// TODO switch to world time | ||||||
|  | 		walkingParameter += velocity * GraphicsInterface.getFrameLength() * 1000; | ||||||
|  | 		 | ||||||
|  | 		bodyYaw += velocityParameter * normalizeAngle( | ||||||
|  | 				(float) (atan2(horizontal.y, horizontal.x) - bodyYaw) | ||||||
|  | 		) * min(1, GraphicsInterface.getFrameLength() * 10); | ||||||
|  | 		Vectors.release(horizontal); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void evaluateVelocityCoeff() { | ||||||
|  | 		if (velocity > maxEffectiveVelocity) { | ||||||
|  | 			velocityParameter = 1; | ||||||
|  | 		} else { | ||||||
|  | 			velocityParameter = (float) pow(velocity / maxEffectiveVelocity, velocityCoeffPower); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void getViewPoint(Vec3 output) { | ||||||
|  | 		Mat4 m = Matrices.grab4(); | ||||||
|  | 		Vec4 v = Vectors.grab4(); | ||||||
|  | 		 | ||||||
|  | 		m.identity() | ||||||
|  | 		.scale(scale) | ||||||
|  | 		.rotateZ(bodyYaw) | ||||||
|  | 		.translate(head.getTranslation()) | ||||||
|  | 		.rotateZ(headYaw) | ||||||
|  | 		.rotateY(headPitch); | ||||||
|  | 		 | ||||||
|  | 		v.set(head.getViewPoint(), 1); | ||||||
|  | 		m.mul(v); | ||||||
|  | 		 | ||||||
|  | 		output.set(v.x, v.y, v.z); | ||||||
|  | 		 | ||||||
|  | 		Vectors.release(v); | ||||||
|  | 		Matrices.release(m); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public Body getBody() { | ||||||
|  | 		return body; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public Head getHead() { | ||||||
|  | 		return head; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public float getBodyYaw() { | ||||||
|  | 		return bodyYaw; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public float getHeadYaw() { | ||||||
|  | 		return headYaw; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public float getHeadPitch() { | ||||||
|  | 		return headPitch; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	/** | ||||||
|  | 	 * Returns a number in the range [0; 1] that can be used to scale animation effects that depend on speed. | ||||||
|  | 	 * This parameter is 0 when the entity is not moving and 1 when it's moving "fast". | ||||||
|  | 	 * @return velocity parameter | ||||||
|  | 	 */ | ||||||
|  | 	protected float getVelocityParameter() { | ||||||
|  | 		return velocityParameter; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	/** | ||||||
|  | 	 * Returns a number that can be used to parameterize animation effects that depend on walking. | ||||||
|  | 	 * This parameter increases when the entity moves (e.g. this can be total traveled distance). | ||||||
|  | 	 * @return walking parameter | ||||||
|  | 	 */ | ||||||
|  | 	protected float getWalkingParameter() { | ||||||
|  | 		return walkingParameter; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	protected float getVelocity() { | ||||||
|  | 		return velocity; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public float getScale() { | ||||||
|  | 		return scale; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	protected float getWalkingFrequency() { | ||||||
|  | 		return walkingFrequency; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public NPedModel setWalkingFrequency(float walkingFrequency) { | ||||||
|  | 		this.walkingFrequency = walkingFrequency; | ||||||
|  | 		return this; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public float getMaxEffectiveVelocity() { | ||||||
|  | 		return maxEffectiveVelocity; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public float getVelocityCoeffPower() { | ||||||
|  | 		return velocityCoeffPower; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public NPedModel setMaxEffectiveVelocity(float maxEffectiveVelocity) { | ||||||
|  | 		this.maxEffectiveVelocity = maxEffectiveVelocity; | ||||||
|  | 		return this; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public NPedModel setVelocityCoeffPower(float velocityCoeffPower) { | ||||||
|  | 		this.velocityCoeffPower = velocityCoeffPower; | ||||||
|  | 		return this; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -2,86 +2,15 @@ package ru.windcorp.progressia.client.world.entity; | |||||||
|  |  | ||||||
| import static java.lang.Math.*; | import static java.lang.Math.*; | ||||||
| import static ru.windcorp.progressia.common.util.FloatMathUtils.*; | import static ru.windcorp.progressia.common.util.FloatMathUtils.*; | ||||||
|  | import static ru.windcorp.progressia.common.util.FloatMathUtils.sin; | ||||||
|  |  | ||||||
| import glm.Glm; |  | ||||||
| import glm.mat._4.Mat4; | import glm.mat._4.Mat4; | ||||||
| import glm.vec._3.Vec3; | import glm.vec._3.Vec3; | ||||||
| import glm.vec._4.Vec4; |  | ||||||
| import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; |  | ||||||
| import ru.windcorp.progressia.client.graphics.model.Renderable; | import ru.windcorp.progressia.client.graphics.model.Renderable; | ||||||
| import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; | import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; | ||||||
| import ru.windcorp.progressia.common.util.Matrices; |  | ||||||
| import ru.windcorp.progressia.common.util.Vectors; |  | ||||||
| import ru.windcorp.progressia.common.world.entity.EntityData; | import ru.windcorp.progressia.common.world.entity.EntityData; | ||||||
|  |  | ||||||
| public class QuadripedModel extends EntityRenderable { | public class QuadripedModel extends NPedModel { | ||||||
| 	 |  | ||||||
| 	private static abstract class BodyPart { |  | ||||||
| 		private final Renderable renderable; |  | ||||||
| 		private final Vec3 translation = new Vec3(); |  | ||||||
|  |  | ||||||
| 		public BodyPart(Renderable renderable, Vec3 joint) { |  | ||||||
| 			this.renderable = renderable; |  | ||||||
| 			if (joint != null) { |  | ||||||
| 				this.translation.set(joint); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		 |  | ||||||
| 		protected void render( |  | ||||||
| 				ShapeRenderHelper renderer, QuadripedModel model |  | ||||||
| 		) { |  | ||||||
| 			renderer.pushTransform().translate(translation); |  | ||||||
| 			applyTransform(renderer.pushTransform(), model); |  | ||||||
| 			renderable.render(renderer); |  | ||||||
| 			renderer.popTransform(); |  | ||||||
| 			renderer.popTransform(); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		protected abstract void applyTransform(Mat4 mat, QuadripedModel model); |  | ||||||
| 		 |  | ||||||
| 		public Vec3 getTranslation() { |  | ||||||
| 			return translation; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static class Body extends BodyPart { |  | ||||||
| 		public Body(Renderable renderable) { |  | ||||||
| 			super(renderable, null); |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		@Override |  | ||||||
| 		protected void applyTransform(Mat4 mat, QuadripedModel model) { |  | ||||||
| 			// Do nothing |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static class Head extends BodyPart { |  | ||||||
| 		private final float maxYaw; |  | ||||||
| 		private final float maxPitch; |  | ||||||
| 		 |  | ||||||
| 		private final Vec3 viewPoint; |  | ||||||
| 		 |  | ||||||
| 		public Head( |  | ||||||
| 				Renderable renderable, Vec3 joint, |  | ||||||
| 				double maxYawDegrees, double maxPitchDegrees, |  | ||||||
| 				Vec3 viewPoint |  | ||||||
| 		) { |  | ||||||
| 			super(renderable, joint); |  | ||||||
| 			this.maxYaw = (float) toRadians(maxYawDegrees); |  | ||||||
| 			this.maxPitch = (float) toRadians(maxPitchDegrees); |  | ||||||
| 			this.viewPoint = viewPoint; |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		@Override |  | ||||||
| 		protected void applyTransform(Mat4 mat, QuadripedModel model) { |  | ||||||
| 			mat.rotateZ(model.headYaw).rotateY(model.headPitch); |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		public Vec3 getViewPoint() { |  | ||||||
| 			return viewPoint; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 | 	 | ||||||
| 	public static class Leg extends BodyPart { | 	public static class Leg extends BodyPart { | ||||||
| 		private final float animationOffset; | 		private final float animationOffset; | ||||||
| @@ -95,34 +24,20 @@ public class QuadripedModel extends EntityRenderable { | |||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		@Override | 		@Override | ||||||
| 		protected void applyTransform(Mat4 mat, QuadripedModel model) { | 		protected void applyTransform(Mat4 mat, NPedModel model) { | ||||||
| 			mat.rotateY(sin(model.walkingFrequency * model.walkingAnimationParameter + animationOffset) * model.walkingSwing * model.velocityCoeff); | 			float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset; | ||||||
|  | 			float value = sin(phase); | ||||||
|  | 			float amplitude = ((QuadripedModel) model).getWalkingSwing() * model.getVelocityParameter(); | ||||||
|  | 			mat.rotateY(value * amplitude); | ||||||
| 		} | 		} | ||||||
|  | 		 | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	private final Body body; |  | ||||||
| 	private final Head head; |  | ||||||
| 	private final Leg leftForeLeg, rightForeLeg; | 	private final Leg leftForeLeg, rightForeLeg; | ||||||
| 	private final Leg leftHindLeg, rightHindLeg; | 	private final Leg leftHindLeg, rightHindLeg; | ||||||
| 	 | 	 | ||||||
| 	private final float scale; |  | ||||||
| 	 |  | ||||||
| 	private float walkingAnimationParameter = 0; |  | ||||||
| 	private float velocityCoeff = 0; |  | ||||||
| 	private float velocity = 0; |  | ||||||
| 	 |  | ||||||
| 	/** |  | ||||||
| 	 * Controls how quickly velocityCoeff approaches 1 |  | ||||||
| 	 */ |  | ||||||
| 	private float velocityCutoff = 10; |  | ||||||
| 	 |  | ||||||
| 	private float walkingFrequency = 0.15f / 60.0f; |  | ||||||
| 	private float walkingSwing = (float) toRadians(30); | 	private float walkingSwing = (float) toRadians(30); | ||||||
|  |  | ||||||
| 	private float bodyYaw = Float.NaN; |  | ||||||
| 	private float headYaw; |  | ||||||
| 	private float headPitch; |  | ||||||
|  |  | ||||||
| 	public QuadripedModel( | 	public QuadripedModel( | ||||||
| 			EntityData entity, | 			EntityData entity, | ||||||
| 			 | 			 | ||||||
| @@ -132,105 +47,30 @@ public class QuadripedModel extends EntityRenderable { | |||||||
| 			 | 			 | ||||||
| 			float scale | 			float scale | ||||||
| 	) { | 	) { | ||||||
| 		super(entity); | 		super(entity, body, head, scale); | ||||||
| 		 | 		 | ||||||
| 		this.body = body; |  | ||||||
| 		this.head = head; |  | ||||||
| 		this.leftForeLeg = leftForeLeg; | 		this.leftForeLeg = leftForeLeg; | ||||||
| 		this.rightForeLeg = rightForeLeg; | 		this.rightForeLeg = rightForeLeg; | ||||||
| 		this.leftHindLeg = leftHindLeg; | 		this.leftHindLeg = leftHindLeg; | ||||||
| 		this.rightHindLeg = rightHindLeg; | 		this.rightHindLeg = rightHindLeg; | ||||||
| 		 |  | ||||||
| 		this.scale = scale; |  | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	@Override | 	@Override | ||||||
| 	public void render(ShapeRenderHelper renderer) { | 	protected void renderBodyParts(ShapeRenderHelper renderer) { | ||||||
| 		renderer.pushTransform().scale(scale).rotateZ(bodyYaw); | 		super.renderBodyParts(renderer); | ||||||
| 		body.render(renderer, this); | 		this.leftForeLeg.render(renderer, this); | ||||||
| 		head.render(renderer, this); | 		this.rightForeLeg.render(renderer, this); | ||||||
| 		leftForeLeg.render(renderer, this); | 		this.leftHindLeg.render(renderer, this); | ||||||
| 		rightForeLeg.render(renderer, this); | 		this.rightHindLeg.render(renderer, this); | ||||||
| 		leftHindLeg.render(renderer, this); |  | ||||||
| 		rightHindLeg.render(renderer, this); |  | ||||||
| 		renderer.popTransform(); |  | ||||||
| 		 |  | ||||||
| 		accountForVelocity(); |  | ||||||
| 		evaluateAngles(); |  | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	private void evaluateAngles() { | 	public float getWalkingSwing() { | ||||||
| 		float globalYaw = normalizeAngle(getData().getYaw()); | 		return walkingSwing; | ||||||
| 		 |  | ||||||
| 		if (Float.isNaN(bodyYaw)) { |  | ||||||
| 			bodyYaw = globalYaw; |  | ||||||
| 			headYaw = 0; |  | ||||||
| 		} else { |  | ||||||
| 			headYaw = normalizeAngle(globalYaw - bodyYaw); |  | ||||||
| 			 |  | ||||||
| 			if (headYaw > +head.maxYaw) { |  | ||||||
| 				bodyYaw += headYaw - +head.maxYaw; |  | ||||||
| 				headYaw = +head.maxYaw; |  | ||||||
| 			} else if (headYaw < -head.maxYaw) { |  | ||||||
| 				bodyYaw += headYaw - -head.maxYaw; |  | ||||||
| 				headYaw = -head.maxYaw; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		bodyYaw = normalizeAngle(bodyYaw); |  | ||||||
| 		 |  | ||||||
| 		headPitch = Glm.clamp( |  | ||||||
| 				getData().getPitch(), |  | ||||||
| 				-head.maxPitch, head.maxPitch |  | ||||||
| 		); |  | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	private void accountForVelocity() { | 	public QuadripedModel setWalkingSwing(float walkingSwing) { | ||||||
| 		Vec3 horizontal = Vectors.grab3(); | 		this.walkingSwing = walkingSwing; | ||||||
| 		horizontal.set(getData().getVelocity()); | 		return this; | ||||||
| 		horizontal.z = 0; |  | ||||||
| 		 |  | ||||||
| 		velocity = horizontal.length(); |  | ||||||
| 		 |  | ||||||
| 		evaluateVelocityCoeff(); |  | ||||||
| 		 |  | ||||||
| 		// TODO switch to world time |  | ||||||
| 		walkingAnimationParameter += velocity * GraphicsInterface.getFrameLength() * 1000; |  | ||||||
| 		 |  | ||||||
| 		bodyYaw += velocityCoeff * normalizeAngle( |  | ||||||
| 				(float) (atan2(horizontal.y, horizontal.x) - bodyYaw) |  | ||||||
| 		) * min(1, GraphicsInterface.getFrameLength() * 10); |  | ||||||
| 		Vectors.release(horizontal); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	private void evaluateVelocityCoeff() { |  | ||||||
| 		if (velocity * velocityCutoff > 1) { |  | ||||||
| 			velocityCoeff = 1; |  | ||||||
| 		} else { |  | ||||||
| 			velocityCoeff = velocity * velocityCutoff; |  | ||||||
| 			velocityCoeff *= velocityCoeff; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	@Override |  | ||||||
| 	public void getViewPoint(Vec3 output) { |  | ||||||
| 		Mat4 m = Matrices.grab4(); |  | ||||||
| 		Vec4 v = Vectors.grab4(); |  | ||||||
| 		 |  | ||||||
| 		m.identity() |  | ||||||
| 		.scale(scale) |  | ||||||
| 		.rotateZ(bodyYaw) |  | ||||||
| 		.translate(head.getTranslation()) |  | ||||||
| 		.rotateZ(headYaw) |  | ||||||
| 		.rotateY(headPitch); |  | ||||||
| 		 |  | ||||||
| 		v.set(head.getViewPoint(), 1); |  | ||||||
| 		m.mul(v); |  | ||||||
| 		 |  | ||||||
| 		output.set(v.x, v.y, v.z); |  | ||||||
| 		 |  | ||||||
| 		Vectors.release(v); |  | ||||||
| 		Matrices.release(m); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										56
									
								
								src/main/java/ru/windcorp/progressia/common/Units.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/main/java/ru/windcorp/progressia/common/Units.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | package ru.windcorp.progressia.common; | ||||||
|  |  | ||||||
|  | public class Units { | ||||||
|  | 	 | ||||||
|  | 	// Base units | ||||||
|  | 	// We're SI. | ||||||
|  | 	public static final float METERS                     = 1; | ||||||
|  | 	public static final float KILOGRAMS                  = 1; | ||||||
|  | 	public static final float SECONDS                    = 1; | ||||||
|  |  | ||||||
|  | 	// Length                                             | ||||||
|  | 	public static final float CENTIMETERS                = METERS / 100;  | ||||||
|  | 	public static final float MILLIMETERS                = METERS / 1000; | ||||||
|  | 	public static final float KILOMETERS                 = METERS * 1000; | ||||||
|  | 	 | ||||||
|  | 	// Surface | ||||||
|  | 	public static final float SQUARE_CENTIMETERS         = CENTIMETERS * CENTIMETERS; | ||||||
|  | 	public static final float SQUARE_METERS              = METERS * METERS; | ||||||
|  | 	public static final float SQUARE_MILLIMETERS         = MILLIMETERS * MILLIMETERS; | ||||||
|  | 	public static final float SQUARE_KILOMETERS          = KILOMETERS * KILOMETERS; | ||||||
|  | 	 | ||||||
|  | 	// Volume | ||||||
|  | 	public static final float CUBIC_CENTIMETERS          = CENTIMETERS * CENTIMETERS * CENTIMETERS; | ||||||
|  | 	public static final float CUBIC_METERS               = METERS * METERS * METERS; | ||||||
|  | 	public static final float CUBIC_MILLIMETERS          = MILLIMETERS * MILLIMETERS * MILLIMETERS; | ||||||
|  | 	public static final float CUBIC_KILOMETERS           = KILOMETERS * KILOMETERS * KILOMETERS; | ||||||
|  |  | ||||||
|  | 	// Mass                                               | ||||||
|  | 	public static final float GRAMS                      = KILOGRAMS / 1000; | ||||||
|  | 	public static final float TONNES                     = KILOGRAMS * 1000; | ||||||
|  | 	 | ||||||
|  | 	// Density | ||||||
|  | 	public static final float KILOGRAMS_PER_CUBIC_METER  = KILOGRAMS / CUBIC_METERS; | ||||||
|  | 	public static final float GRAMS_PER_CUBIC_CENTIMETER = GRAMS / CUBIC_CENTIMETERS; | ||||||
|  |  | ||||||
|  | 	// Time                                               | ||||||
|  | 	public static final float MILLISECONDS               = SECONDS / 1000; | ||||||
|  | 	public static final float MINUTES                    = SECONDS * 60; | ||||||
|  | 	public static final float HOURS                      = MINUTES * 60; | ||||||
|  | 	public static final float DAYS                       = HOURS * 24; | ||||||
|  | 	 | ||||||
|  | 	// Frequency | ||||||
|  | 	public static final float HERTZ                      = 1 / SECONDS; | ||||||
|  | 	public static final float KILOHERTZ                  = HERTZ * 1000; | ||||||
|  |  | ||||||
|  | 	// Velocity                                           | ||||||
|  | 	public static final float METERS_PER_SECOND          = METERS / SECONDS; | ||||||
|  | 	public static final float KILOMETERS_PER_HOUR        = KILOMETERS / HOURS; | ||||||
|  |  | ||||||
|  | 	// Acceleration                                       | ||||||
|  | 	public static final float METERS_PER_SECOND_SQUARED  = METERS_PER_SECOND / SECONDS; | ||||||
|  |  | ||||||
|  | 	// Force                                              | ||||||
|  | 	public static final float NEWTONS                    = METERS_PER_SECOND_SQUARED * KILOGRAMS; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,53 +1,71 @@ | |||||||
| package ru.windcorp.progressia.common.collision; | package ru.windcorp.progressia.common.collision; | ||||||
|  |  | ||||||
| import java.util.Collection; |  | ||||||
| import java.util.Map; |  | ||||||
|  |  | ||||||
| import glm.vec._3.Vec3; | import glm.vec._3.Vec3; | ||||||
| import ru.windcorp.progressia.common.world.block.BlockFace; |  | ||||||
|  |  | ||||||
| public class AABB implements CollisionModel { | /** | ||||||
|  |  * An implementation of an | ||||||
|  |  * <a href="https://en.wikipedia.org/wiki/Minimum_bounding_box#Axis-aligned_minimum_bounding_box">Axis-Aligned Bounding Box</a>. | ||||||
|  |  * @author javapony | ||||||
|  |  */ | ||||||
|  | public class AABB implements AABBoid { | ||||||
| 	 | 	 | ||||||
| 	private final Map<BlockFace, CollisionWall> faces = BlockFace.mapToFaces( | 	private class AABBWallImpl implements Wall { | ||||||
| 			new CollisionWall(-0.5f, -0.5f, -0.5f,   +1,  0,  0,    0,  0, +1), |  | ||||||
| 			new CollisionWall(+0.5f, -0.5f, -0.5f,    0, +1,  0,    0,  0, +1), |  | ||||||
| 			new CollisionWall(+0.5f, +0.5f, -0.5f,   -1,  0,  0,    0,  0, +1), |  | ||||||
| 			new CollisionWall(-0.5f, +0.5f, -0.5f,    0, -1,  0,    0,  0, +1), |  | ||||||
| 		 | 		 | ||||||
| 			new CollisionWall(-0.5f, -0.5f, +0.5f,   +1,  0,  0,    0, +1,  0), | 		private final Vec3 originOffset = new Vec3(); | ||||||
| 			new CollisionWall(-0.5f, -0.5f, -0.5f,    0, +1,  0,   +1,  0,  0) | 		private final Vec3 widthSelector = new Vec3(); | ||||||
| 	); | 		private final Vec3 heightSelector = new Vec3(); | ||||||
|  |  | ||||||
|  | 		public AABBWallImpl( | ||||||
|  | 			float ox, float oy, float oz, | ||||||
|  | 			float wx, float wy, float wz, | ||||||
|  | 			float hx, float hy, float hz | ||||||
|  | 		) { | ||||||
|  | 			this.originOffset.set(ox, oy, oz); | ||||||
|  | 			this.widthSelector.set(wx, wy, wz); | ||||||
|  | 			this.heightSelector.set(hx, hy, hz); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		@Override | ||||||
|  | 		public void getOrigin(Vec3 output) { | ||||||
|  | 			output.set(originOffset).mul(AABB.this.getSize()).add(AABB.this.getOrigin()); | ||||||
|  | 		} | ||||||
|  | 	 | ||||||
|  | 		@Override | ||||||
|  | 		public void getWidth(Vec3 output) { | ||||||
|  | 			output.set(AABB.this.getSize()).mul(widthSelector); | ||||||
|  | 		} | ||||||
|  | 	 | ||||||
|  | 		@Override | ||||||
|  | 		public void getHeight(Vec3 output) { | ||||||
|  | 			output.set(AABB.this.getSize()).mul(heightSelector); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static final AABB UNIT_CUBE = new AABB(0, 0, 0, 1, 1, 1); | ||||||
|  | 	 | ||||||
|  | 	private final Wall[] walls = new Wall[] { | ||||||
|  | 			new AABBWallImpl(-0.5f, -0.5f, +0.5f,   +1,  0,  0,    0, +1,  0), // Top | ||||||
|  | 			new AABBWallImpl(-0.5f, -0.5f, -0.5f,    0, +1,  0,   +1,  0,  0), // Bottom | ||||||
|  | 			new AABBWallImpl(+0.5f, -0.5f, -0.5f,    0, +1,  0,    0,  0, +1), // North | ||||||
|  | 			new AABBWallImpl(-0.5f, +0.5f, -0.5f,    0, -1,  0,    0,  0, +1), // South | ||||||
|  | 			new AABBWallImpl(+0.5f, +0.5f, -0.5f,   -1,  0,  0,    0,  0, +1), // West | ||||||
|  | 			new AABBWallImpl(-0.5f, -0.5f, -0.5f,   +1,  0,  0,    0,  0, +1)  // East | ||||||
|  | 	}; | ||||||
| 	 | 	 | ||||||
| 	private final Vec3 origin = new Vec3(); | 	private final Vec3 origin = new Vec3(); | ||||||
| 	private final Vec3 size = new Vec3(); | 	private final Vec3 size = new Vec3(); | ||||||
| 	 | 	 | ||||||
| 	public AABB(Vec3 origin, Vec3 size) { | 	public AABB(Vec3 origin, Vec3 size) { | ||||||
| 		this.origin.set(origin); | 		this(origin.x, origin.y, origin.z, size.x, size.y, size.z); | ||||||
| 		this.size.set(size); |  | ||||||
| 		 |  | ||||||
| 		for (CollisionWall wall : getFaces()) { |  | ||||||
| 			wall.moveOrigin(origin); |  | ||||||
| 			wall.getWidth().mul(size); |  | ||||||
| 			wall.getHeight().mul(size); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	public AABB( | 	public AABB( | ||||||
| 			float ox, float oy, float oz, | 			float ox,    float oy,    float oz, | ||||||
| 			float xSize, float ySize, float zSize | 			float xSize, float ySize, float zSize | ||||||
| 	) { | 	) { | ||||||
| 		this.origin.set(ox, oy, oz); | 		this.origin.set(ox, oy, oz); | ||||||
| 		this.size.set(xSize, ySize, zSize); | 		this.size.set(xSize, ySize, zSize); | ||||||
| 		 |  | ||||||
| 		for (CollisionWall wall : getFaces()) { |  | ||||||
| 			wall.moveOrigin(ox, oy, oz); |  | ||||||
| 			wall.getWidth().mul(xSize, ySize, zSize); |  | ||||||
| 			wall.getHeight().mul(xSize, ySize, zSize); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Collection<CollisionWall> getFaces() { |  | ||||||
| 		return faces.values(); |  | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	public Vec3 getOrigin() { | 	public Vec3 getOrigin() { | ||||||
| @@ -55,20 +73,17 @@ public class AABB implements CollisionModel { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void setOrigin(Vec3 origin) { | 	public void getOrigin(Vec3 output) { | ||||||
| 		for (CollisionWall wall : getFaces()) { | 		output.set(origin); | ||||||
| 			wall.getOrigin().sub(this.origin).add(origin); | 	} | ||||||
| 		} |  | ||||||
| 	 | 	 | ||||||
|  | 	@Override | ||||||
|  | 	public void setOrigin(Vec3 origin) { | ||||||
| 		this.origin.set(origin); | 		this.origin.set(origin); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	@Override | 	@Override | ||||||
| 	public void moveOrigin(Vec3 displacement) { | 	public void moveOrigin(Vec3 displacement) { | ||||||
| 		for (CollisionWall wall : getFaces()) { |  | ||||||
| 			wall.getOrigin().add(displacement); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		this.origin.add(displacement); | 		this.origin.add(displacement); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| @@ -76,18 +91,23 @@ public class AABB implements CollisionModel { | |||||||
| 		return size; | 		return size; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void getSize(Vec3 output) { | ||||||
|  | 		output.set(size); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
| 	public void setSize(Vec3 size) { | 	public void setSize(Vec3 size) { | ||||||
| 		setSize(size.x, size.y, size.z); | 		setSize(size.x, size.y, size.z); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	public void setSize(float xSize, float ySize, float zSize) { | 	public void setSize(float xSize, float ySize, float zSize) { | ||||||
| 		for (CollisionWall wall : getFaces()) { |  | ||||||
| 			wall.getWidth().div(this.size).mul(xSize, ySize, zSize); |  | ||||||
| 			wall.getHeight().div(this.size).mul(xSize, ySize, zSize); |  | ||||||
| 			wall.getOrigin().sub(getOrigin()).div(this.size).mul(xSize, ySize, zSize).add(getOrigin()); |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		this.size.set(xSize, ySize, zSize); | 		this.size.set(xSize, ySize, zSize); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public Wall getWall(int faceId) { | ||||||
|  | 		// No, we don't support Apple. | ||||||
|  | 		return walls[faceId]; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | package ru.windcorp.progressia.common.collision; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import ru.windcorp.progressia.common.world.block.BlockFace; | ||||||
|  |  | ||||||
|  | public interface AABBoid extends CollisionModel { | ||||||
|  | 	 | ||||||
|  | 	void getOrigin(Vec3 output); | ||||||
|  | 	void getSize(Vec3 output); | ||||||
|  | 	 | ||||||
|  | 	default Wall getWall(BlockFace face) { | ||||||
|  | 		return getWall(face.getId()); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	Wall getWall(int faceId); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| package ru.windcorp.progressia.common.collision; |  | ||||||
|  |  | ||||||
| public interface CollisionClock { |  | ||||||
| 	 |  | ||||||
| 	float getTime(); |  | ||||||
| 	void advanceTime(float change); |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | package ru.windcorp.progressia.common.collision; | ||||||
|  |  | ||||||
|  | import java.util.function.Consumer; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import glm.vec._3.i.Vec3i; | ||||||
|  | import ru.windcorp.progressia.common.util.Vectors; | ||||||
|  |  | ||||||
|  | import static java.lang.Math.*; | ||||||
|  |  | ||||||
|  | public class CollisionPathComputer { | ||||||
|  | 	 | ||||||
|  | 	public static void forEveryBlockInCollisionPath( | ||||||
|  | 			Collideable coll, | ||||||
|  | 			float maxTime, | ||||||
|  | 			Consumer<Vec3i> action | ||||||
|  | 	) { | ||||||
|  | 		Vec3 displacement = Vectors.grab3(); | ||||||
|  | 		coll.getCollideableVelocity(displacement); | ||||||
|  | 		displacement.mul(maxTime); | ||||||
|  | 		 | ||||||
|  | 		handleModel(coll.getCollisionModel(), displacement, action); | ||||||
|  | 		 | ||||||
|  | 		Vectors.release(displacement); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private static void handleModel( | ||||||
|  | 			CollisionModel model, | ||||||
|  | 			Vec3 displacement, | ||||||
|  | 			Consumer<Vec3i> action | ||||||
|  | 	) { | ||||||
|  | 		if (model instanceof CompoundCollisionModel) { | ||||||
|  | 			for (CollisionModel subModel : ((CompoundCollisionModel) model).getModels()) { | ||||||
|  | 				handleModel(subModel, displacement, action); | ||||||
|  | 			} | ||||||
|  | 		} else if (model instanceof AABBoid) { | ||||||
|  | 			handleAABBoid((AABBoid) model, displacement, action); | ||||||
|  | 		} else { | ||||||
|  | 			throw new RuntimeException("not supported"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private static void handleAABBoid(AABBoid model, Vec3 displacement, Consumer<Vec3i> action) { | ||||||
|  | 		Vec3 size = Vectors.grab3(); | ||||||
|  | 		Vec3 origin = Vectors.grab3(); | ||||||
|  | 		 | ||||||
|  | 		model.getOrigin(origin); | ||||||
|  | 		model.getSize(size); | ||||||
|  | 		 | ||||||
|  | 		origin.mul(2).sub(size).div(2); // Subtract 0.5*size | ||||||
|  | 		 | ||||||
|  | 		Vec3i pos = Vectors.grab3i(); | ||||||
|  | 		 | ||||||
|  | 		for ( | ||||||
|  | 				pos.x =  (int) floor(origin.x + min(0, size.x) + min(0, displacement.x)); | ||||||
|  | 				pos.x <= (int)  ceil(origin.x + max(0, size.x) + max(0, displacement.x)); | ||||||
|  | 				pos.x += 1 | ||||||
|  | 		) { | ||||||
|  | 			for ( | ||||||
|  | 					pos.y =  (int) floor(origin.y + min(0, size.y) + min(0, displacement.y)); | ||||||
|  | 					pos.y <= (int)  ceil(origin.y + max(0, size.y) + max(0, displacement.y)); | ||||||
|  | 					pos.y += 1 | ||||||
|  | 			) { | ||||||
|  | 				for ( | ||||||
|  | 						pos.z =  (int) floor(origin.z + min(0, size.z) + min(0, displacement.z)); | ||||||
|  | 						pos.z <= (int)  ceil(origin.z + max(0, size.z) + max(0, displacement.z)); | ||||||
|  | 						pos.z += 1 | ||||||
|  | 				) { | ||||||
|  | 					action.accept(pos); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		Vectors.release(origin); | ||||||
|  | 		Vectors.release(size); | ||||||
|  | 		Vectors.release(pos); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,55 +0,0 @@ | |||||||
| package ru.windcorp.progressia.common.collision; |  | ||||||
|  |  | ||||||
| import glm.vec._3.Vec3; |  | ||||||
|  |  | ||||||
| public class CollisionWall { |  | ||||||
| 	 |  | ||||||
| 	private final Vec3 origin = new Vec3(); |  | ||||||
| 	private final Vec3 width = new Vec3(); |  | ||||||
| 	private final Vec3 height = new Vec3(); |  | ||||||
| 	 |  | ||||||
| 	public CollisionWall(Vec3 origin, Vec3 width, Vec3 height) { |  | ||||||
| 		this.origin.set(origin); |  | ||||||
| 		this.width.set(width); |  | ||||||
| 		this.height.set(height); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public CollisionWall( |  | ||||||
| 		float ox, float oy, float oz, |  | ||||||
| 		float wx, float wy, float wz, |  | ||||||
| 		float hx, float hy, float hz |  | ||||||
| 	) { |  | ||||||
| 		this.origin.set(ox, oy, oz); |  | ||||||
| 		this.width.set(wx, wy, wz); |  | ||||||
| 		this.height.set(hx, hy, hz); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Vec3 getOrigin() { |  | ||||||
| 		return origin; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Vec3 getWidth() { |  | ||||||
| 		return width; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public Vec3 getHeight() { |  | ||||||
| 		return height; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void setOrigin(Vec3 origin) { |  | ||||||
| 		setOrigin(origin.x, origin.y, origin.z); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void setOrigin(float x, float y, float z) { |  | ||||||
| 		this.origin.set(x, y, z); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void moveOrigin(Vec3 displacement) { |  | ||||||
| 		moveOrigin(displacement.x, displacement.y, displacement.z); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public void moveOrigin(float dx, float dy, float dz) { |  | ||||||
| 		this.origin.add(dx, dy, dz); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -8,9 +8,9 @@ import glm.vec._3.Vec3; | |||||||
|  |  | ||||||
| public class CompoundCollisionModel implements CollisionModel { | public class CompoundCollisionModel implements CollisionModel { | ||||||
| 	 | 	 | ||||||
| 	private final Collection<CollisionModel> models; | 	private final Collection<? extends CollisionModel> models; | ||||||
|  |  | ||||||
| 	public CompoundCollisionModel(Collection<CollisionModel> models) { | 	public CompoundCollisionModel(Collection<? extends CollisionModel> models) { | ||||||
| 		this.models = models; | 		this.models = models; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| @@ -18,7 +18,7 @@ public class CompoundCollisionModel implements CollisionModel { | |||||||
| 		this(ImmutableList.copyOf(models)); | 		this(ImmutableList.copyOf(models)); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	public Collection<CollisionModel> getModels() { | 	public Collection<? extends CollisionModel> getModels() { | ||||||
| 		return models; | 		return models; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,105 @@ | |||||||
|  | package ru.windcorp.progressia.common.collision; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import ru.windcorp.progressia.common.util.Vectors; | ||||||
|  | import ru.windcorp.progressia.common.world.block.BlockFace; | ||||||
|  |  | ||||||
|  | public class TranslatedAABB implements AABBoid { | ||||||
|  | 	 | ||||||
|  | 	private class TranslatedAABBWall implements Wall { | ||||||
|  | 		private final int id; | ||||||
|  | 		 | ||||||
|  | 		public TranslatedAABBWall(int id) { | ||||||
|  | 			this.id = id; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		@Override | ||||||
|  | 		public void getOrigin(Vec3 output) { | ||||||
|  | 			parent.getWall(id).getOrigin(output); | ||||||
|  | 			output.add(translation); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		public void getWidth(Vec3 output) { | ||||||
|  | 			parent.getWall(id).getWidth(output); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		public void getHeight(Vec3 output) { | ||||||
|  | 			parent.getWall(id).getHeight(output); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private AABBoid parent; | ||||||
|  | 	private final Vec3 translation = new Vec3(); | ||||||
|  | 	 | ||||||
|  | 	private final TranslatedAABBWall[] walls = new TranslatedAABBWall[BlockFace.BLOCK_FACE_COUNT]; | ||||||
|  | 	 | ||||||
|  | 	{ | ||||||
|  | 		for (int id = 0; id < walls.length; ++id) { | ||||||
|  | 			walls[id] = new TranslatedAABBWall(id); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public TranslatedAABB(AABBoid parent, float tx, float ty, float tz) { | ||||||
|  | 		setParent(parent); | ||||||
|  | 		setTranslation(tx, ty, tz); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public TranslatedAABB(AABBoid parent, Vec3 translation) { | ||||||
|  | 		this(parent, translation.x, translation.y, translation.z); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public TranslatedAABB() { | ||||||
|  | 		this(null, 0, 0, 0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void setOrigin(Vec3 origin) { | ||||||
|  | 		Vec3 v = Vectors.grab3().set(origin).sub(translation); | ||||||
|  | 		parent.setOrigin(v); | ||||||
|  | 		Vectors.release(v); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void moveOrigin(Vec3 displacement) { | ||||||
|  | 		parent.moveOrigin(displacement); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void getOrigin(Vec3 output) { | ||||||
|  | 		parent.getOrigin(output); | ||||||
|  | 		output.add(translation); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public void getSize(Vec3 output) { | ||||||
|  | 		parent.getSize(output); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public Wall getWall(int faceId) { | ||||||
|  | 		return walls[faceId]; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public AABBoid getParent() { | ||||||
|  | 		return parent; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public void setParent(AABBoid parent) { | ||||||
|  | 		this.parent = parent; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public Vec3 getTranslation() { | ||||||
|  | 		return translation; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public void setTranslation(Vec3 translation) { | ||||||
|  | 		setTranslation(translation.x, translation.y, translation.z); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public void setTranslation(float tx, float ty, float tz) { | ||||||
|  | 		this.translation.set(tx, ty, tz); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | package ru.windcorp.progressia.common.collision; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  |  | ||||||
|  | public interface Wall { | ||||||
|  | 	 | ||||||
|  | 	void getOrigin(Vec3 output); | ||||||
|  | 	 | ||||||
|  | 	void getWidth(Vec3 output); | ||||||
|  | 	void getHeight(Vec3 output); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,94 @@ | |||||||
|  | package ru.windcorp.progressia.common.collision; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Collection; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import glm.vec._3.i.Vec3i; | ||||||
|  | import ru.windcorp.progressia.common.util.LowOverheadCache; | ||||||
|  | import ru.windcorp.progressia.common.world.WorldData; | ||||||
|  |  | ||||||
|  | public class WorldCollisionHelper { | ||||||
|  | 	 | ||||||
|  | 	private final Collideable collideable = new Collideable() { | ||||||
|  | 		@Override | ||||||
|  | 		public boolean onCollision(Collideable other) { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		public void moveAsCollideable(Vec3 displacement) { | ||||||
|  | 			// Ignore | ||||||
|  | 			assert displacement.length() < 1e-3f; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		public CollisionModel getCollisionModel() { | ||||||
|  | 			return WorldCollisionHelper.this.model; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		public float getCollisionMass() { | ||||||
|  | 			return Float.POSITIVE_INFINITY; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		public void getCollideableVelocity(Vec3 output) { | ||||||
|  | 			output.set(0); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		@Override | ||||||
|  | 		public void changeVelocityOnCollision(Vec3 velocityChange) { | ||||||
|  | 			// Ignore | ||||||
|  | 			assert velocityChange.length() < 1e-3f; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 	 | ||||||
|  | 	private final Collection<TranslatedAABB> activeBlockModels = new ArrayList<>(); | ||||||
|  | 	private final CollisionModel model = new CompoundCollisionModel(activeBlockModels); | ||||||
|  | 	private final LowOverheadCache<TranslatedAABB> blockModelCache = new LowOverheadCache<>(TranslatedAABB::new); | ||||||
|  | 	 | ||||||
|  | 	/** | ||||||
|  | 	 * Changes the state of this helper's {@link #getCollideable()} so it is ready to adequately handle | ||||||
|  | 	 * collisions with the {@code collideable} that might happen in the next {@code maxTime} seconds. | ||||||
|  | 	 * This helper is only valid for checking collisions with the given Collideable and only within | ||||||
|  | 	 * the given time limit. | ||||||
|  | 	 * @param collideable the {@link Collideable} that collisions will be checked against | ||||||
|  | 	 * @param maxTime maximum collision time | ||||||
|  | 	 */ | ||||||
|  | 	public void tuneToCollideable(WorldData world, Collideable collideable, float maxTime) { | ||||||
|  | 		activeBlockModels.forEach(blockModelCache::release); | ||||||
|  | 		activeBlockModels.clear(); | ||||||
|  | 		CollisionPathComputer.forEveryBlockInCollisionPath( | ||||||
|  | 				collideable, | ||||||
|  | 				maxTime, | ||||||
|  | 				v -> addModel(world.getCollisionModelOfBlock(v), v) | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private void addModel(CollisionModel model, Vec3i pos) { | ||||||
|  | 		if (model == null) { | ||||||
|  | 			// Ignore | ||||||
|  | 		} else if (model instanceof AABBoid) { | ||||||
|  | 			addAABBoidModel((AABBoid) model, pos); | ||||||
|  | 		} else if (model instanceof CompoundCollisionModel) { | ||||||
|  | 			for (CollisionModel subModel : ((CompoundCollisionModel) model).getModels()) { | ||||||
|  | 				addModel(subModel, pos); | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			throw new RuntimeException("not supported"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private void addAABBoidModel(AABBoid model, Vec3i pos) { | ||||||
|  | 		TranslatedAABB translator = blockModelCache.grab(); | ||||||
|  | 		translator.setParent(model); | ||||||
|  | 		translator.setTranslation(pos.x, pos.y, pos.z); | ||||||
|  | 		activeBlockModels.add(translator); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public Collideable getCollideable() { | ||||||
|  | 		return collideable; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -2,26 +2,25 @@ package ru.windcorp.progressia.common.collision.colliders; | |||||||
| 
 | 
 | ||||||
| import glm.mat._3.Mat3; | import glm.mat._3.Mat3; | ||||||
| import glm.vec._3.Vec3; | import glm.vec._3.Vec3; | ||||||
| import ru.windcorp.progressia.common.collision.AABB; | import ru.windcorp.progressia.common.collision.*; | ||||||
| import ru.windcorp.progressia.common.collision.Collideable; |  | ||||||
| import ru.windcorp.progressia.common.collision.CollisionWall; |  | ||||||
| import ru.windcorp.progressia.common.collision.colliders.Collider.ColliderWorkspace; | import ru.windcorp.progressia.common.collision.colliders.Collider.ColliderWorkspace; | ||||||
| import ru.windcorp.progressia.common.collision.colliders.Collider.Collision; | import ru.windcorp.progressia.common.collision.colliders.Collider.Collision; | ||||||
| import ru.windcorp.progressia.common.util.Matrices; | import ru.windcorp.progressia.common.util.Matrices; | ||||||
| import ru.windcorp.progressia.common.util.Vectors; | import ru.windcorp.progressia.common.util.Vectors; | ||||||
|  | import ru.windcorp.progressia.common.world.block.BlockFace; | ||||||
| 
 | 
 | ||||||
| class AABBWithAABBCollider { | class AABBoidCollider { | ||||||
| 	 | 	 | ||||||
| 	static Collider.Collision computeModelCollision( | 	static Collider.Collision computeModelCollision( | ||||||
| 			Collideable aBody, Collideable bBody, | 			Collideable aBody, Collideable bBody, | ||||||
| 			AABB aModel, AABB bModel, | 			AABBoid aModel, AABBoid bModel, | ||||||
| 			float tickLength, | 			float tickLength, | ||||||
| 			ColliderWorkspace workspace | 			ColliderWorkspace workspace | ||||||
| 	) { | 	) { | ||||||
| 		Collideable obstacleBody = bBody; | 		Collideable obstacleBody = bBody; | ||||||
| 		Collideable colliderBody = aBody; | 		Collideable colliderBody = aBody; | ||||||
| 		AABB obstacleModel = bModel; | 		AABBoid obstacleModel = bModel; | ||||||
| 		AABB colliderModel = aModel; | 		AABBoid colliderModel = aModel; | ||||||
| 		 | 		 | ||||||
| 		Collision result = null; | 		Collision result = null; | ||||||
| 		 | 		 | ||||||
| @@ -32,7 +31,8 @@ class AABBWithAABBCollider { | |||||||
| 		computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody); | 		computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody); | ||||||
| 		 | 		 | ||||||
| 		// For every wall of collision space | 		// For every wall of collision space | ||||||
| 		for (CollisionWall wall : originCollisionSpace.getFaces()) { | 		for (int i = 0; i < BlockFace.BLOCK_FACE_COUNT; ++i) { | ||||||
|  | 			Wall wall = originCollisionSpace.getWall(i); | ||||||
| 				 | 				 | ||||||
| 			Collision collision = computeWallCollision( | 			Collision collision = computeWallCollision( | ||||||
| 					wall, colliderModel, | 					wall, colliderModel, | ||||||
| @@ -80,12 +80,21 @@ class AABBWithAABBCollider { | |||||||
| 		Vectors.release(colliderVelocity); | 		Vectors.release(colliderVelocity); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private static AABB createOriginCollisionSpace(AABB obstacle, AABB collider, AABB output) { | 	private static AABB createOriginCollisionSpace(AABBoid obstacle, AABBoid collider, AABB output) { | ||||||
| 		output.setOrigin(obstacle.getOrigin()); | 		Vec3 obstacleOrigin = Vectors.grab3(); | ||||||
|  | 		Vec3 obstacleSize = Vectors.grab3(); | ||||||
|  | 		Vec3 colliderSize = Vectors.grab3(); | ||||||
| 		 | 		 | ||||||
| 		Vec3 size = Vectors.grab3().set(obstacle.getSize()).add(collider.getSize()); | 		obstacle.getOrigin(obstacleOrigin); | ||||||
| 		output.setSize(size); | 		output.setOrigin(obstacleOrigin); | ||||||
| 		Vectors.release(size); | 		 | ||||||
|  | 		obstacle.getSize(obstacleSize); | ||||||
|  | 		collider.getSize(colliderSize); | ||||||
|  | 		output.setSize(obstacleSize.add(colliderSize)); | ||||||
|  | 		 | ||||||
|  | 		Vectors.release(obstacleOrigin); | ||||||
|  | 		Vectors.release(obstacleSize); | ||||||
|  | 		Vectors.release(colliderSize); | ||||||
| 		 | 		 | ||||||
| 		return output; | 		return output; | ||||||
| 	} | 	} | ||||||
| @@ -134,27 +143,34 @@ class AABBWithAABBCollider { | |||||||
| 	 * If all conditions are satisfied, then the moment of impact is t0 + t. | 	 * If all conditions are satisfied, then the moment of impact is t0 + t. | ||||||
| 	 */ | 	 */ | ||||||
| 	private static Collision computeWallCollision( | 	private static Collision computeWallCollision( | ||||||
| 			CollisionWall obstacleWall, | 			Wall obstacleWall, | ||||||
| 			AABB colliderModel, | 			AABBoid colliderModel, | ||||||
| 			Vec3 collisionVelocity, | 			Vec3 collisionVelocity, | ||||||
| 			float tickLength, ColliderWorkspace workspace, | 			float tickLength, ColliderWorkspace workspace, | ||||||
| 			Collideable aBody, Collideable bBody | 			Collideable aBody, Collideable bBody | ||||||
| 	) { | 	) { | ||||||
| 		Vec3 w = obstacleWall.getWidth(); | 		Vec3 w = Vectors.grab3(); | ||||||
| 		Vec3 h = obstacleWall.getHeight(); | 		Vec3 h = Vectors.grab3(); | ||||||
| 		Vec3 v = Vectors.grab3(); | 		Vec3 v = Vectors.grab3(); | ||||||
| 		Mat3 m = Matrices.grab3(); // The matrix [w h -v] | 		Mat3 m = Matrices.grab3(); // The matrix [w h -v] | ||||||
| 		Vec3 r = Vectors.grab3(); | 		Vec3 r = Vectors.grab3(); | ||||||
|  | 		Vec3 r_line = Vectors.grab3(); | ||||||
|  | 		Vec3 r_wall = Vectors.grab3(); | ||||||
| 		Vec3 xyt = Vectors.grab3(); | 		Vec3 xyt = Vectors.grab3(); | ||||||
| 		 | 		 | ||||||
| 		try { | 		try { | ||||||
|  | 			obstacleWall.getWidth(w); | ||||||
|  | 			obstacleWall.getHeight(h); | ||||||
|  | 			 | ||||||
| 			v.set(collisionVelocity); | 			v.set(collisionVelocity); | ||||||
| 			 | 			 | ||||||
| 			if (isExiting(v, w, h)) { | 			if (isExiting(v, w, h)) { | ||||||
| 				return null; | 				return null; | ||||||
| 			} | 			} | ||||||
| 			 | 			 | ||||||
| 			r.set(colliderModel.getOrigin()).sub(obstacleWall.getOrigin()); | 			obstacleWall.getOrigin(r_wall); | ||||||
|  | 			colliderModel.getOrigin(r_line); | ||||||
|  | 			r.set(r_line).sub(r_wall); | ||||||
| 			m.c0(w).c1(h).c2(v.negate()); | 			m.c0(w).c1(h).c2(v.negate()); | ||||||
| 			 | 			 | ||||||
| 			if (Math.abs(m.det()) < 1e-6) { | 			if (Math.abs(m.det()) < 1e-6) { | ||||||
| @@ -179,9 +195,13 @@ class AABBWithAABBCollider { | |||||||
| 			 | 			 | ||||||
| 			return workspace.grab().set(aBody, bBody, obstacleWall, t); | 			return workspace.grab().set(aBody, bBody, obstacleWall, t); | ||||||
| 		} finally { | 		} finally { | ||||||
|  | 			Vectors.release(w); | ||||||
|  | 			Vectors.release(h); | ||||||
| 			Vectors.release(v); | 			Vectors.release(v); | ||||||
| 			Vectors.release(r); |  | ||||||
| 			Matrices.release(m); | 			Matrices.release(m); | ||||||
|  | 			Vectors.release(r); | ||||||
|  | 			Vectors.release(r_line); | ||||||
|  | 			Vectors.release(r_wall); | ||||||
| 			Vectors.release(xyt); | 			Vectors.release(xyt); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -193,6 +213,6 @@ class AABBWithAABBCollider { | |||||||
| 		return result; | 		return result; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private AABBWithAABBCollider() {} | 	private AABBoidCollider() {} | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| @@ -6,14 +6,10 @@ import java.util.List; | |||||||
| import org.apache.logging.log4j.LogManager; | import org.apache.logging.log4j.LogManager; | ||||||
|  |  | ||||||
| import glm.vec._3.Vec3; | import glm.vec._3.Vec3; | ||||||
| import ru.windcorp.progressia.common.collision.AABB; | import ru.windcorp.progressia.common.collision.*; | ||||||
| import ru.windcorp.progressia.common.collision.Collideable; |  | ||||||
| import ru.windcorp.progressia.common.collision.CollisionClock; |  | ||||||
| import ru.windcorp.progressia.common.collision.CollisionModel; |  | ||||||
| import ru.windcorp.progressia.common.collision.CollisionWall; |  | ||||||
| import ru.windcorp.progressia.common.collision.CompoundCollisionModel; |  | ||||||
| import ru.windcorp.progressia.common.util.LowOverheadCache; | import ru.windcorp.progressia.common.util.LowOverheadCache; | ||||||
| import ru.windcorp.progressia.common.util.Vectors; | import ru.windcorp.progressia.common.util.Vectors; | ||||||
|  | import ru.windcorp.progressia.common.world.WorldData; | ||||||
|  |  | ||||||
| public class Collider { | public class Collider { | ||||||
| 	 | 	 | ||||||
| @@ -21,7 +17,7 @@ public class Collider { | |||||||
| 	 | 	 | ||||||
| 	public static void performCollisions( | 	public static void performCollisions( | ||||||
| 			List<? extends Collideable> colls, | 			List<? extends Collideable> colls, | ||||||
| 			CollisionClock clock, | 			WorldData world, | ||||||
| 			float tickLength, | 			float tickLength, | ||||||
| 			ColliderWorkspace workspace | 			ColliderWorkspace workspace | ||||||
| 	) { | 	) { | ||||||
| @@ -37,12 +33,12 @@ public class Collider { | |||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 			 | 			 | ||||||
| 			Collision firstCollision = getFirstCollision(colls, tickLength, workspace); | 			Collision firstCollision = getFirstCollision(colls, tickLength, world, workspace); | ||||||
| 			 | 			 | ||||||
| 			if (firstCollision == null) { | 			if (firstCollision == null) { | ||||||
| 				break; | 				break; | ||||||
| 			} else { | 			} else { | ||||||
| 				collide(firstCollision, colls, clock, tickLength, workspace); | 				collide(firstCollision, colls, world, tickLength, workspace); | ||||||
| 				workspace.release(firstCollision); | 				workspace.release(firstCollision); | ||||||
| 				collisionCount++; | 				collisionCount++; | ||||||
| 				 | 				 | ||||||
| @@ -50,45 +46,49 @@ public class Collider { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		advanceTime(colls, clock, tickLength); | 		advanceTime(colls, world, tickLength); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private static Collision getFirstCollision( | 	private static Collision getFirstCollision( | ||||||
| 			List<? extends Collideable> colls, | 			List<? extends Collideable> colls, | ||||||
| 			float tickLength, | 			float tickLength, | ||||||
|  | 			WorldData world, | ||||||
| 			ColliderWorkspace workspace | 			ColliderWorkspace workspace | ||||||
| 	) { | 	) { | ||||||
| 		Collision result = null; | 		Collision result = null; | ||||||
|  | 		Collideable worldColl = workspace.worldCollisionHelper.getCollideable(); | ||||||
| 		 | 		 | ||||||
| 		// For every pair of colls | 		// For every pair of colls | ||||||
| 		for (int i = 0; i < colls.size(); ++i) { | 		for (int i = 0; i < colls.size(); ++i) { | ||||||
| 			Collideable a = colls.get(i); | 			Collideable a = colls.get(i); | ||||||
| 			 | 			 | ||||||
|  | 			tuneWorldCollisionHelper(a, tickLength, world, workspace); | ||||||
|  | 			 | ||||||
|  | 			result = workspace.updateLatestCollision( | ||||||
|  | 					result, | ||||||
|  | 					getCollision(a, worldColl, tickLength, workspace) | ||||||
|  | 			); | ||||||
|  | 			 | ||||||
| 			for (int j = i + 1; j < colls.size(); ++j) { | 			for (int j = i + 1; j < colls.size(); ++j) { | ||||||
| 				Collideable b = colls.get(j); | 				Collideable b = colls.get(j); | ||||||
| 				 |  | ||||||
| 				Collision collision = getCollision(a, b, tickLength, workspace); | 				Collision collision = getCollision(a, b, tickLength, workspace); | ||||||
| 				 | 				result = workspace.updateLatestCollision(result, collision); | ||||||
| 				// Update result |  | ||||||
| 				if (collision != null) { |  | ||||||
| 					Collision second; |  | ||||||
| 					 |  | ||||||
| 					if (result == null || collision.time < result.time) { |  | ||||||
| 						second = result; |  | ||||||
| 						result = collision; |  | ||||||
| 					} else { |  | ||||||
| 						second = collision; |  | ||||||
| 					} |  | ||||||
| 					 |  | ||||||
| 					// Release Collision that is no longer used |  | ||||||
| 					if (second != null) workspace.release(second); |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		return result; | 		return result; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	private static void tuneWorldCollisionHelper( | ||||||
|  | 			Collideable coll, | ||||||
|  | 			float tickLength, | ||||||
|  | 			WorldData world, | ||||||
|  | 			ColliderWorkspace workspace | ||||||
|  | 	) { | ||||||
|  | 		WorldCollisionHelper wch = workspace.worldCollisionHelper; | ||||||
|  | 		wch.tuneToCollideable(world, coll, tickLength); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	static Collision getCollision( | 	static Collision getCollision( | ||||||
| 			Collideable a, | 			Collideable a, | ||||||
| 			Collideable b, | 			Collideable b, | ||||||
| @@ -108,10 +108,10 @@ public class Collider { | |||||||
| 			float tickLength, | 			float tickLength, | ||||||
| 			ColliderWorkspace workspace | 			ColliderWorkspace workspace | ||||||
| 	) { | 	) { | ||||||
| 		if (aModel instanceof AABB && bModel instanceof AABB) { | 		if (aModel instanceof AABBoid && bModel instanceof AABBoid) { | ||||||
| 			return AABBWithAABBCollider.computeModelCollision( | 			return AABBoidCollider.computeModelCollision( | ||||||
| 					aBody, bBody, | 					aBody, bBody, | ||||||
| 					(AABB) aModel, (AABB) bModel, | 					(AABBoid) aModel, (AABBoid) bModel, | ||||||
| 					tickLength, | 					tickLength, | ||||||
| 					workspace | 					workspace | ||||||
| 			); | 			); | ||||||
| @@ -144,11 +144,11 @@ public class Collider { | |||||||
| 			Collision collision, | 			Collision collision, | ||||||
| 			 | 			 | ||||||
| 			Collection<? extends Collideable> colls, | 			Collection<? extends Collideable> colls, | ||||||
| 			CollisionClock clock, | 			WorldData world, | ||||||
| 			float tickLength, | 			float tickLength, | ||||||
| 			ColliderWorkspace workspace | 			ColliderWorkspace workspace | ||||||
| 	) { | 	) { | ||||||
| 		advanceTime(colls, clock, collision.time); | 		advanceTime(colls, world, collision.time); | ||||||
| 		 | 		 | ||||||
| 		boolean doNotHandle = false; | 		boolean doNotHandle = false; | ||||||
| 		 | 		 | ||||||
| @@ -237,7 +237,7 @@ public class Collider { | |||||||
| 		Vec3 du_a = Vectors.grab3(); | 		Vec3 du_a = Vectors.grab3(); | ||||||
| 		Vec3 du_b = Vectors.grab3(); | 		Vec3 du_b = Vectors.grab3(); | ||||||
| 		 | 		 | ||||||
| 		n.set(collision.wall.getWidth()).cross(collision.wall.getHeight()).normalize(); | 		n.set(collision.wallWidth).cross(collision.wallHeight).normalize(); | ||||||
| 		collision.a.getCollideableVelocity(v_a); | 		collision.a.getCollideableVelocity(v_a); | ||||||
| 		collision.b.getCollideableVelocity(v_b); | 		collision.b.getCollideableVelocity(v_b); | ||||||
| 		 | 		 | ||||||
| @@ -306,10 +306,10 @@ public class Collider { | |||||||
|  |  | ||||||
| 	private static void advanceTime( | 	private static void advanceTime( | ||||||
| 			Collection<? extends Collideable> colls, | 			Collection<? extends Collideable> colls, | ||||||
| 			CollisionClock clock, | 			WorldData world, | ||||||
| 			float step | 			float step | ||||||
| 	) { | 	) { | ||||||
| 		clock.advanceTime(step); | 		world.advanceTime(step); | ||||||
| 		 | 		 | ||||||
| 		Vec3 tmp = Vectors.grab3(); | 		Vec3 tmp = Vectors.grab3(); | ||||||
| 		 | 		 | ||||||
| @@ -329,6 +329,8 @@ public class Collider { | |||||||
| 		 | 		 | ||||||
| 		AABB dummyAABB = new AABB(0, 0, 0, 1, 1, 1); | 		AABB dummyAABB = new AABB(0, 0, 0, 1, 1, 1); | ||||||
| 		 | 		 | ||||||
|  | 		WorldCollisionHelper worldCollisionHelper = new WorldCollisionHelper(); | ||||||
|  |  | ||||||
| 		Collision grab() { | 		Collision grab() { | ||||||
| 			return collisionCache.grab(); | 			return collisionCache.grab(); | ||||||
| 		} | 		} | ||||||
| @@ -337,12 +339,35 @@ public class Collider { | |||||||
| 			collisionCache.release(object); | 			collisionCache.release(object); | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
|  | 		Collision updateLatestCollision(Collision a, Collision b) { | ||||||
|  | 			if (a == null) { | ||||||
|  | 				return b; // may be null | ||||||
|  | 			} else if (b == null) { | ||||||
|  | 				return a; | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			Collision first, second; | ||||||
|  | 			 | ||||||
|  | 			if (a.time > b.time) { | ||||||
|  | 				first = b; | ||||||
|  | 				second = a; | ||||||
|  | 			} else { | ||||||
|  | 				first = a; | ||||||
|  | 				second = b; | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			release(second); | ||||||
|  | 			return first; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	static class Collision { | 	static class Collision { | ||||||
| 		public Collideable a; | 		public Collideable a; | ||||||
| 		public Collideable b; | 		public Collideable b; | ||||||
| 		public final CollisionWall wall = new CollisionWall(0, 0, 0, 0, 0, 0, 0, 0, 0); | 		 | ||||||
|  | 		public final Vec3 wallWidth = new Vec3(); | ||||||
|  | 		public final Vec3 wallHeight = new Vec3(); | ||||||
| 		 | 		 | ||||||
| 		/** | 		/** | ||||||
| 		 * Time offset from the start of the tick. | 		 * Time offset from the start of the tick. | ||||||
| @@ -350,12 +375,15 @@ public class Collider { | |||||||
| 		 */ | 		 */ | ||||||
| 		public float time; | 		public float time; | ||||||
| 		 | 		 | ||||||
| 		public Collision set(Collideable a, Collideable b, CollisionWall wall, float time) { | 		public Collision set( | ||||||
|  | 				Collideable a, Collideable b, | ||||||
|  | 				Wall wall, | ||||||
|  | 				float time | ||||||
|  | 		) { | ||||||
| 			this.a = a; | 			this.a = a; | ||||||
| 			this.b = b; | 			this.b = b; | ||||||
| 			this.wall.getOrigin().set(wall.getOrigin()); | 			wall.getWidth(wallWidth); | ||||||
| 			this.wall.getWidth().set(wall.getWidth()); | 			wall.getHeight(wallHeight); | ||||||
| 			this.wall.getHeight().set(wall.getHeight()); |  | ||||||
| 			this.time = time; | 			this.time = time; | ||||||
| 			 | 			 | ||||||
| 			return this; | 			return this; | ||||||
|   | |||||||
| @@ -30,6 +30,19 @@ import glm.vec._4.i.Vec4i; | |||||||
|  */ |  */ | ||||||
| public class Vectors { | public class Vectors { | ||||||
| 	 | 	 | ||||||
|  | 	public static final Vec2  ZERO_2  = new Vec2 (0, 0); | ||||||
|  | 	public static final Vec2i ZERO_2i = new Vec2i(0, 0); | ||||||
|  | 	public static final Vec3  ZERO_3  = new Vec3 (0, 0, 0); | ||||||
|  | 	public static final Vec3i ZERO_3i = new Vec3i(0, 0, 0); | ||||||
|  | 	public static final Vec4  ZERO_4  = new Vec4 (0, 0, 0, 0); | ||||||
|  | 	public static final Vec4i ZERO_4i = new Vec4i(0, 0, 0, 0); | ||||||
|  | 	public static final Vec2  UNIT_2  = new Vec2 (1, 1); | ||||||
|  | 	public static final Vec2i UNIT_2i = new Vec2i(1, 1); | ||||||
|  | 	public static final Vec3  UNIT_3  = new Vec3 (1, 1, 1); | ||||||
|  | 	public static final Vec3i UNIT_3i = new Vec3i(1, 1, 1); | ||||||
|  | 	public static final Vec4  UNIT_4  = new Vec4 (1, 1, 1, 1); | ||||||
|  | 	public static final Vec4i UNIT_4i = new Vec4i(1, 1, 1, 1); | ||||||
|  | 	 | ||||||
| 	private static final LowOverheadCache<Vec3i> VEC3IS = | 	private static final LowOverheadCache<Vec3i> VEC3IS = | ||||||
| 			new LowOverheadCache<>(Vec3i::new); | 			new LowOverheadCache<>(Vec3i::new); | ||||||
| 	 | 	 | ||||||
|   | |||||||
| @@ -80,7 +80,7 @@ public class ChunkData { | |||||||
| 		TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); | 		TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); | ||||||
| 		TileData sand = TileDataRegistry.getInstance().get("Test:Sand"); | 		TileData sand = TileDataRegistry.getInstance().get("Test:Sand"); | ||||||
| 	 | 	 | ||||||
| 		Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2); | 		Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2).sub(getPosition()); | ||||||
| 		Vec3i pos = new Vec3i(); | 		Vec3i pos = new Vec3i(); | ||||||
| 		 | 		 | ||||||
| 		for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { | 		for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) { | ||||||
| @@ -132,18 +132,28 @@ public class ChunkData { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); | 		if (!getPosition().any()) { | ||||||
| 		javapony.setEntityId(0x42); | //			EntityData javapony = EntityDataRegistry.getInstance().create("Test:Javapony"); | ||||||
| 		javapony.setPosition(new Vec3(-6, -6, 20)); | //			javapony.setEntityId(0x42); | ||||||
| 		javapony.setDirection(new Vec2( | //			javapony.setPosition(new Vec3(-6, -6, 20)); | ||||||
| 				(float) Math.toRadians(40), (float) Math.toRadians(45) | //			javapony.setDirection(new Vec2( | ||||||
| 		)); | //					(float) Math.toRadians(40), (float) Math.toRadians(45) | ||||||
| 		getEntities().add(javapony); | //			)); | ||||||
|  | //			getEntities().add(javapony); | ||||||
| 			 | 			 | ||||||
| 		EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); | 			EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); | ||||||
| 		statie.setEntityId(0xDEADBEEF); | 			player.setEntityId(0x42); | ||||||
| 		statie.setPosition(new Vec3(0, 15, 16)); | 			player.setPosition(new Vec3(-6, -6, 20)); | ||||||
| 		getEntities().add(statie); | 			player.setDirection(new Vec2( | ||||||
|  | 					(float) Math.toRadians(40), (float) Math.toRadians(45) | ||||||
|  | 			)); | ||||||
|  | 			getEntities().add(player); | ||||||
|  | 			 | ||||||
|  | 			EntityData statie = EntityDataRegistry.getInstance().create("Test:Statie"); | ||||||
|  | 			statie.setEntityId(0xDEADBEEF); | ||||||
|  | 			statie.setPosition(new Vec3(0, 15, 16)); | ||||||
|  | 			getEntities().add(statie); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public BlockData getBlock(Vec3i posInChunk) { | 	public BlockData getBlock(Vec3i posInChunk) { | ||||||
|   | |||||||
| @@ -24,8 +24,10 @@ import glm.vec._3.i.Vec3i; | |||||||
| import gnu.trove.impl.sync.TSynchronizedLongObjectMap; | import gnu.trove.impl.sync.TSynchronizedLongObjectMap; | ||||||
| import gnu.trove.map.TLongObjectMap; | import gnu.trove.map.TLongObjectMap; | ||||||
| import gnu.trove.map.hash.TLongObjectHashMap; | import gnu.trove.map.hash.TLongObjectHashMap; | ||||||
|  | import ru.windcorp.progressia.common.collision.CollisionModel; | ||||||
| import ru.windcorp.progressia.common.util.CoordinatePacker; | import ru.windcorp.progressia.common.util.CoordinatePacker; | ||||||
| import ru.windcorp.progressia.common.util.Vectors; | import ru.windcorp.progressia.common.util.Vectors; | ||||||
|  | import ru.windcorp.progressia.common.world.block.BlockData; | ||||||
| import ru.windcorp.progressia.common.world.entity.EntityData; | import ru.windcorp.progressia.common.world.entity.EntityData; | ||||||
|  |  | ||||||
| public class WorldData { | public class WorldData { | ||||||
| @@ -42,6 +44,8 @@ public class WorldData { | |||||||
| 	private final Collection<EntityData> entities = | 	private final Collection<EntityData> entities = | ||||||
| 			Collections.unmodifiableCollection(entitiesById.valueCollection()); | 			Collections.unmodifiableCollection(entitiesById.valueCollection()); | ||||||
| 	 | 	 | ||||||
|  | 	private float time = 0; | ||||||
|  | 	 | ||||||
| 	public WorldData() { | 	public WorldData() { | ||||||
| 		final int size = 1; | 		final int size = 1; | ||||||
| 		 | 		 | ||||||
| @@ -96,4 +100,25 @@ public class WorldData { | |||||||
| 		return entities; | 		return entities; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	public float getTime() { | ||||||
|  | 		return time; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public void advanceTime(float change) { | ||||||
|  | 		this.time += change; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public CollisionModel getCollisionModelOfBlock(Vec3i blockInWorld) { | ||||||
|  | 		ChunkData chunk = getChunkByBlock(blockInWorld); | ||||||
|  | 		if (chunk == null) return null; | ||||||
|  | 		 | ||||||
|  | 		Vec3i blockInChunk = Vectors.grab3i(); | ||||||
|  | 		Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk); | ||||||
|  | 		BlockData block = chunk.getBlock(blockInChunk); | ||||||
|  | 		Vectors.release(blockInChunk); | ||||||
|  | 		 | ||||||
|  | 		if (block == null) return null; | ||||||
|  | 		return block.getCollisionModel(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,6 +17,8 @@ | |||||||
|  *******************************************************************************/ |  *******************************************************************************/ | ||||||
| package ru.windcorp.progressia.common.world.block; | package ru.windcorp.progressia.common.world.block; | ||||||
|  |  | ||||||
|  | import ru.windcorp.progressia.common.collision.AABB; | ||||||
|  | import ru.windcorp.progressia.common.collision.CollisionModel; | ||||||
| import ru.windcorp.progressia.common.util.Namespaced; | import ru.windcorp.progressia.common.util.Namespaced; | ||||||
|  |  | ||||||
| public class BlockData extends Namespaced { | public class BlockData extends Namespaced { | ||||||
| @@ -25,4 +27,8 @@ public class BlockData extends Namespaced { | |||||||
| 		super(namespace, name); | 		super(namespace, name); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	public CollisionModel getCollisionModel() { | ||||||
|  | 		return AABB.UNIT_CUBE; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,35 +0,0 @@ | |||||||
| package ru.windcorp.progressia.test; |  | ||||||
|  |  | ||||||
| import ru.windcorp.progressia.client.graphics.model.Shape; |  | ||||||
| import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; |  | ||||||
| import ru.windcorp.progressia.client.graphics.model.Shapes; |  | ||||||
| import ru.windcorp.progressia.client.graphics.texture.Texture; |  | ||||||
| import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; |  | ||||||
| import ru.windcorp.progressia.common.collision.AABB; |  | ||||||
| import ru.windcorp.progressia.common.collision.CollisionModel; |  | ||||||
| import ru.windcorp.progressia.common.collision.CompoundCollisionModel; |  | ||||||
|  |  | ||||||
| public class AABBRenderer { |  | ||||||
| 	 |  | ||||||
| 	private static final Shape CUBE = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(1.0f, 0.7f, 0.2f).create(); |  | ||||||
| 	 |  | ||||||
| 	public static void renderAABB(AABB aabb, ShapeRenderHelper helper) { |  | ||||||
| 		helper.pushTransform().translate(aabb.getOrigin()).scale(aabb.getSize()); |  | ||||||
| 		CUBE.render(helper); |  | ||||||
| 		helper.popTransform(); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	public static void renderAABBsInCompound( |  | ||||||
| 			CompoundCollisionModel model, |  | ||||||
| 			ShapeRenderHelper helper |  | ||||||
| 	) { |  | ||||||
| 		for (CollisionModel part : model.getModels()) { |  | ||||||
| 			if (part instanceof CompoundCollisionModel) { |  | ||||||
| 				renderAABBsInCompound((CompoundCollisionModel) part, helper); |  | ||||||
| 			} else if (part instanceof AABB) { |  | ||||||
| 				renderAABB((AABB) part, helper); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,61 @@ | |||||||
|  | package ru.windcorp.progressia.test; | ||||||
|  |  | ||||||
|  | import glm.mat._4.Mat4; | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import glm.vec._3.i.Vec3i; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.Shape; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.Shapes; | ||||||
|  | import ru.windcorp.progressia.client.graphics.texture.Texture; | ||||||
|  | import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; | ||||||
|  | import ru.windcorp.progressia.common.collision.AABBoid; | ||||||
|  | import ru.windcorp.progressia.common.collision.CollisionModel; | ||||||
|  | import ru.windcorp.progressia.common.collision.CompoundCollisionModel; | ||||||
|  | import ru.windcorp.progressia.common.util.Vectors; | ||||||
|  |  | ||||||
|  | public class CollisionModelRenderer { | ||||||
|  | 	 | ||||||
|  | 	private static final Shape CUBE = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(1.0f, 0.7f, 0.2f).create(); | ||||||
|  | 	private static final Shape CUBE_GRAY = new Shapes.PppBuilder(WorldRenderProgram.getDefault(), (Texture) null).setColorMultiplier(0.5f, 0.5f, 0.5f).create(); | ||||||
|  | 	 | ||||||
|  | 	public static void renderCollisionModel(CollisionModel model, ShapeRenderHelper helper) { | ||||||
|  | 		if (model instanceof AABBoid) { | ||||||
|  | 			renderAABBoid((AABBoid) model, helper); | ||||||
|  | 		} else if (model instanceof CompoundCollisionModel) { | ||||||
|  | 			renderCompound((CompoundCollisionModel) model, helper); | ||||||
|  | 		} else { | ||||||
|  | 			// Ignore silently | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private static void renderAABBoid(AABBoid aabb, ShapeRenderHelper helper) { | ||||||
|  | 		Mat4 mat = helper.pushTransform(); | ||||||
|  | 		Vec3 tmp = Vectors.grab3(); | ||||||
|  | 		 | ||||||
|  | 		aabb.getOrigin(tmp); | ||||||
|  | 		mat.translate(tmp); | ||||||
|  | 		aabb.getSize(tmp); | ||||||
|  | 		mat.scale(tmp); | ||||||
|  | 		 | ||||||
|  | 		Vectors.release(tmp); | ||||||
|  | 		 | ||||||
|  | 		CUBE.render(helper); | ||||||
|  | 		helper.popTransform(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private static void renderCompound( | ||||||
|  | 			CompoundCollisionModel model, | ||||||
|  | 			ShapeRenderHelper helper | ||||||
|  | 	) { | ||||||
|  | 		for (CollisionModel part : model.getModels()) { | ||||||
|  | 			renderCollisionModel(part, helper); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public static void renderBlock(Vec3i coords, ShapeRenderHelper helper) { | ||||||
|  | 		helper.pushTransform().translate(coords.x, coords.y, coords.z); | ||||||
|  | 		CUBE_GRAY.render(helper); | ||||||
|  | 		helper.popTransform(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										155
									
								
								src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										155
									
								
								src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,155 @@ | |||||||
|  | /******************************************************************************* | ||||||
|  |  * Progressia | ||||||
|  |  * Copyright (C) 2020  Wind Corporation | ||||||
|  |  * | ||||||
|  |  * This program is free software: you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||||
|  |  *******************************************************************************/ | ||||||
|  | package ru.windcorp.progressia.test; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Collection; | ||||||
|  |  | ||||||
|  | import ru.windcorp.progressia.client.ClientState; | ||||||
|  | import ru.windcorp.progressia.client.graphics.font.Font; | ||||||
|  | import ru.windcorp.progressia.client.graphics.gui.GUILayer; | ||||||
|  | import ru.windcorp.progressia.client.graphics.gui.Label; | ||||||
|  | import ru.windcorp.progressia.client.graphics.gui.Panel; | ||||||
|  | import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; | ||||||
|  | import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical; | ||||||
|  |  | ||||||
|  | public class LayerTestGUI extends GUILayer { | ||||||
|  | 	 | ||||||
|  | 	public LayerTestGUI() { | ||||||
|  | 		super("LayerTestGui", new LayoutAlign(0, 1, 5)); | ||||||
|  | 		 | ||||||
|  | 		Panel panel = new Panel("ControlDisplays", new LayoutVertical(5)); | ||||||
|  | 		 | ||||||
|  | 		Collection<Label> labels = new ArrayList<>(); | ||||||
|  | 		 | ||||||
|  | 		panel.addChild(new Label( | ||||||
|  | 				"IsFlyingDisplay", new Font().withColor(0x37A3E6).deriveShadow(), | ||||||
|  | 				() -> String.format("Flying:         %5s (Space bar x2)", TestPlayerControls.getInstance().isFlying()) | ||||||
|  | 		)); | ||||||
|  | 		 | ||||||
|  | 		panel.addChild(new Label( | ||||||
|  | 				"IsMouseCapturedDisplay", new Font().withColor(0x37A3E6).deriveShadow(), | ||||||
|  | 				() -> String.format("Mouse captured: %5s (esc)", TestPlayerControls.getInstance().isMouseCaptured()) | ||||||
|  | 		)); | ||||||
|  | 		 | ||||||
|  | 		panel.addChild(new Label( | ||||||
|  | 				"CameraModeDisplay", new Font().withColor(0x37A3E6).deriveShadow(), | ||||||
|  | 				() -> String.format("Camera mode:    %5d (F5)", ClientState.getInstance().getCamera().getCurrentModeIndex()) | ||||||
|  | 		)); | ||||||
|  | 		 | ||||||
|  | 		panel.addChild(new Label( | ||||||
|  | 				"GravityModeDisplay", new Font().withColor(0x37A3E6).deriveShadow(), | ||||||
|  | 				() -> String.format("Gravity:    %9s (G)", TestPlayerControls.getInstance().useMinecraftGravity() ? "Minecraft" : "Realistic") | ||||||
|  | 		)); | ||||||
|  | 		 | ||||||
|  | 		panel.getChildren().forEach(c -> labels.add((Label) c)); | ||||||
|  | 		TestPlayerControls.getInstance().setUpdateCallback(() -> labels.forEach(Label::update)); | ||||||
|  |  | ||||||
|  | 		getRoot().addChild(panel); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | //	private static class DebugComponent extends Component { | ||||||
|  | //		private final int color; | ||||||
|  | //		 | ||||||
|  | //		public DebugComponent(String name, Vec2i size, int color) { | ||||||
|  | //			super(name); | ||||||
|  | //			this.color = color; | ||||||
|  | //			 | ||||||
|  | //			setPreferredSize(size); | ||||||
|  | //			 | ||||||
|  | //			addListener(new Object() { | ||||||
|  | //				@Subscribe | ||||||
|  | //				public void onHoverChanged(HoverEvent e) { | ||||||
|  | //					requestReassembly(); | ||||||
|  | //				} | ||||||
|  | //			}); | ||||||
|  | //			 | ||||||
|  | //			addListener(KeyEvent.class, this::onClicked); | ||||||
|  | //		} | ||||||
|  | //		 | ||||||
|  | //		private boolean onClicked(KeyEvent event) { | ||||||
|  | //			if (!event.isMouse()) { | ||||||
|  | //				return false; | ||||||
|  | //			} else if (event.isPress() && event.isLeftMouseButton()) { | ||||||
|  | //				System.out.println("You pressed a Component!"); | ||||||
|  | //			} | ||||||
|  | //			return true; | ||||||
|  | //		} | ||||||
|  | //		 | ||||||
|  | //		@Override | ||||||
|  | //		protected void assembleSelf(RenderTarget target) { | ||||||
|  | //			target.fill(getX(), getY(), getWidth(), getHeight(), Colors.BLACK); | ||||||
|  | //			 | ||||||
|  | //			target.fill( | ||||||
|  | //					getX() + 2, getY() + 2, | ||||||
|  | //					getWidth() - 4, getHeight() - 4, | ||||||
|  | //					isHovered() ? Colors.DEBUG_YELLOW : color | ||||||
|  | //			); | ||||||
|  | //		} | ||||||
|  | //	} | ||||||
|  | // | ||||||
|  | //	public LayerTestGUI() { | ||||||
|  | //		super("LayerTestGui", new LayoutAlign(1, 0.75, 5)); | ||||||
|  | //		 | ||||||
|  | //		Panel panel = new Panel("Alex", new LayoutVertical(5)); | ||||||
|  | //		 | ||||||
|  | //		panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44)); | ||||||
|  | //		 | ||||||
|  | //		Component charlie = new DebugComponent("Charlie", null, 0x222222); | ||||||
|  | //		charlie.setLayout(new LayoutVertical(5)); | ||||||
|  | // | ||||||
|  | //		//Debug | ||||||
|  | //		Localizer.getInstance().setLanguage("ru-RU"); | ||||||
|  | //		MutableString epsilon = new MutableStringLocalized("Epsilon") | ||||||
|  | //				.addListener(() -> ((Label)charlie.getChild(0)).update()).format(34, "thirty-four"); | ||||||
|  | //		// These two are swapped in code due to a bug in layouts, fixing ATM | ||||||
|  | //		charlie.addChild( | ||||||
|  | //				new Label( | ||||||
|  | //						"Delta", | ||||||
|  | //						new Font().withColor(0xCCBB44).deriveShadow().deriveBold(), | ||||||
|  | //						"Пре-альфа!" | ||||||
|  | //				) | ||||||
|  | //		); | ||||||
|  | //		charlie.addChild( | ||||||
|  | //				new Label( | ||||||
|  | //						"Epsilon", | ||||||
|  | //						new Font().withColor(0x4444BB).deriveItalic(), | ||||||
|  | //						() -> epsilon.get().concat("\u269b") | ||||||
|  | //				) | ||||||
|  | //		); | ||||||
|  | //		panel.addChild(charlie); | ||||||
|  | // | ||||||
|  | // | ||||||
|  | //		charlie.addListener(KeyEvent.class, e -> { | ||||||
|  | //			if(e.isPress() && e.getKey() == GLFW.GLFW_KEY_L) { | ||||||
|  | //				Localizer localizer = Localizer.getInstance(); | ||||||
|  | //				if (localizer.getLanguage().equals("ru-RU")) { | ||||||
|  | //					localizer.setLanguage("en-US"); | ||||||
|  | //				} else { | ||||||
|  | //					localizer.setLanguage("ru-RU"); | ||||||
|  | //				} | ||||||
|  | //				return true; | ||||||
|  | //			} return false; | ||||||
|  | //		}); | ||||||
|  | //		charlie.setFocusable(true); | ||||||
|  | //		charlie.takeFocus(); | ||||||
|  | // | ||||||
|  | //		getRoot().addChild(panel); | ||||||
|  | //	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -14,6 +14,7 @@ import ru.windcorp.progressia.client.world.block.*; | |||||||
| import ru.windcorp.progressia.client.world.entity.*; | import ru.windcorp.progressia.client.world.entity.*; | ||||||
| import ru.windcorp.progressia.client.world.tile.*; | import ru.windcorp.progressia.client.world.tile.*; | ||||||
| import ru.windcorp.progressia.common.collision.AABB; | import ru.windcorp.progressia.common.collision.AABB; | ||||||
|  | import ru.windcorp.progressia.common.collision.CollisionModel; | ||||||
| import ru.windcorp.progressia.common.comms.controls.*; | import ru.windcorp.progressia.common.comms.controls.*; | ||||||
| import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory; | import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory; | ||||||
| import ru.windcorp.progressia.common.world.ChunkData; | import ru.windcorp.progressia.common.world.ChunkData; | ||||||
| @@ -41,7 +42,12 @@ public class TestContent { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private static void registerBlocks() { | 	private static void registerBlocks() { | ||||||
| 		register(new BlockData("Test", "Air")); | 		register(new BlockData("Test", "Air") { | ||||||
|  | 				@Override | ||||||
|  | 				public CollisionModel getCollisionModel() { | ||||||
|  | 					return null; | ||||||
|  | 				} | ||||||
|  | 		}); | ||||||
| 		register(new BlockRenderNone("Test", "Air")); | 		register(new BlockRenderNone("Test", "Air")); | ||||||
| 		register(new BlockLogic("Test", "Air")); | 		register(new BlockLogic("Test", "Air")); | ||||||
|  |  | ||||||
| @@ -81,9 +87,14 @@ public class TestContent { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private static void registerEntities() { | 	private static void registerEntities() { | ||||||
| 		registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); | //		registerEntityData("Test", "Javapony", e -> e.setCollisionModel(new AABB(0, 0, -0.05f, 0.75f, 0.75f, 1.2f))); | ||||||
| 		register(new TestEntityRenderJavapony()); | //		register(new TestEntityRenderJavapony()); | ||||||
| 		register(new EntityLogic("Test", "Javapony")); | //		register(new EntityLogic("Test", "Javapony")); | ||||||
|  | 		 | ||||||
|  | 		float scale = 1.8f / 8; | ||||||
|  | 		registerEntityData("Test", "Player", e -> e.setCollisionModel(new AABB(0, 0, 4*scale, 0.75f, 0.75f, 1.8f))); | ||||||
|  | 		register(new TestEntityRenderHuman()); | ||||||
|  | 		register(new EntityLogic("Test", "Player")); | ||||||
| 		 | 		 | ||||||
| 		register("Test", "Statie", TestEntityDataStatie::new); | 		register("Test", "Statie", TestEntityDataStatie::new); | ||||||
| 		register(new TestEntityRenderStatie()); | 		register(new TestEntityRenderStatie()); | ||||||
| @@ -92,7 +103,7 @@ public class TestContent { | |||||||
|  |  | ||||||
| 	private static void regsiterControls() { | 	private static void regsiterControls() { | ||||||
| 		ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000")); | 		ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000")); | ||||||
| 		ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_G, new int[0], 0)::matches)); | 		ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_H, new int[0], 0)::matches)); | ||||||
| 		ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "Switch000") { | 		ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "Switch000") { | ||||||
| 			@Override | 			@Override | ||||||
| 			public void apply(Server server, PacketControl packet, Client client) { | 			public void apply(Server server, PacketControl packet, Client client) { | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| package ru.windcorp.progressia.test; | package ru.windcorp.progressia.test; | ||||||
|  |  | ||||||
| import ru.windcorp.progressia.common.collision.AABB; | import ru.windcorp.progressia.common.collision.AABB; | ||||||
| import ru.windcorp.progressia.common.collision.CompoundCollisionModel; |  | ||||||
| import ru.windcorp.progressia.common.state.IntStateField; | import ru.windcorp.progressia.common.state.IntStateField; | ||||||
| import ru.windcorp.progressia.common.world.entity.EntityData; | import ru.windcorp.progressia.common.world.entity.EntityData; | ||||||
|  |  | ||||||
| @@ -12,10 +11,7 @@ public class TestEntityDataStatie extends EntityData { | |||||||
|  |  | ||||||
| 	public TestEntityDataStatie() { | 	public TestEntityDataStatie() { | ||||||
| 		super("Test", "Statie"); | 		super("Test", "Statie"); | ||||||
| 		setCollisionModel(new CompoundCollisionModel( | 		setCollisionModel(new AABB(0, 0, 0, 1, 1, 1)); | ||||||
| 				new AABB(0, 0, 0,    1,    1,    1   ), |  | ||||||
| 				new AABB(0, 0, 0.7f, 0.6f, 0.6f, 0.6f) |  | ||||||
| 		)); |  | ||||||
| 		setSizeNow(16); | 		setSizeNow(16); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|   | |||||||
| @@ -0,0 +1,164 @@ | |||||||
|  | package ru.windcorp.progressia.test; | ||||||
|  |  | ||||||
|  | import static java.lang.Math.toRadians; | ||||||
|  |  | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.LambdaModel; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.Renderable; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder; | ||||||
|  | import ru.windcorp.progressia.client.graphics.model.StaticModel; | ||||||
|  | import ru.windcorp.progressia.client.graphics.texture.ComplexTexture; | ||||||
|  | import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; | ||||||
|  | import ru.windcorp.progressia.client.world.entity.HumanoidModel; | ||||||
|  | import ru.windcorp.progressia.client.world.entity.EntityRender; | ||||||
|  | import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; | ||||||
|  | import ru.windcorp.progressia.client.world.entity.EntityRenderable; | ||||||
|  | import ru.windcorp.progressia.common.util.FloatMathUtils; | ||||||
|  | import ru.windcorp.progressia.common.world.entity.EntityData; | ||||||
|  |  | ||||||
|  | import static java.lang.Math.*; | ||||||
|  |  | ||||||
|  | public class TestEntityRenderHuman extends EntityRender { | ||||||
|  | 	 | ||||||
|  | 	private static final float SECOND_LAYER_OFFSET = 1 / 12f; | ||||||
|  | 	 | ||||||
|  | 	private final Renderable body; | ||||||
|  | 	private final Renderable head; | ||||||
|  | 	private final Renderable leftArm; | ||||||
|  | 	private final Renderable rightArm; | ||||||
|  | 	private final Renderable leftLeg; | ||||||
|  | 	private final Renderable rightLeg; | ||||||
|  |  | ||||||
|  | 	public TestEntityRenderHuman() { | ||||||
|  | 		super("Test", "Player"); | ||||||
|  | 		 | ||||||
|  | 		ComplexTexture texture = new ComplexTexture( | ||||||
|  | 				EntityRenderRegistry.getEntityTexture("pyotr"), | ||||||
|  | 				16, 16 | ||||||
|  | 		); | ||||||
|  | 		 | ||||||
|  | 		this.body = createBody(texture); | ||||||
|  | 		this.head = createHead(texture); | ||||||
|  | 		 | ||||||
|  | 		this.leftArm  = createLimb(texture,  8, 0, 12, 0, true,  true); | ||||||
|  | 		this.rightArm = createLimb(texture, 10, 8, 10, 4, true,  false); | ||||||
|  | 		this.leftLeg  = createLimb(texture,  4, 0,  0, 0, false, true); | ||||||
|  | 		this.rightLeg = createLimb(texture,  0, 8,  0, 4, false, false); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private Renderable createBody(ComplexTexture texture) { | ||||||
|  | 		return createLayeredCuboid( | ||||||
|  | 				texture, | ||||||
|  | 				4, 8, | ||||||
|  | 				4, 4, | ||||||
|  | 				2, 3, 1, | ||||||
|  | 				-0.5f, -1, 3, | ||||||
|  | 				1, 2, 3 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private Renderable createHead(ComplexTexture texture) { | ||||||
|  | 		return createLayeredCuboid( | ||||||
|  | 				texture, | ||||||
|  | 				0, 12, | ||||||
|  | 				8, 12, | ||||||
|  | 				2, 2, 2, | ||||||
|  | 				-1, -1, 0, | ||||||
|  | 				2, 2, 2 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private Renderable createLimb( | ||||||
|  | 			ComplexTexture texture, | ||||||
|  | 			int tx, int ty, | ||||||
|  | 			int tx2, int ty2, | ||||||
|  | 			boolean isArm, boolean isLeft | ||||||
|  | 	) { | ||||||
|  | 		Renderable model = createLayeredCuboid( | ||||||
|  | 				texture, | ||||||
|  | 				tx, ty, | ||||||
|  | 				tx2, ty2, | ||||||
|  | 				1, 3, 1, | ||||||
|  | 				-0.5f, -0.5f, isArm ? -2.5f : -3f, | ||||||
|  | 				1, 1, 3 | ||||||
|  | 		); | ||||||
|  | 		 | ||||||
|  | 		if (isArm) { | ||||||
|  | 			return LambdaModel.animate( | ||||||
|  | 					model, | ||||||
|  | 					mat -> { | ||||||
|  | 						double phase = GraphicsInterface.getTime() + (isLeft ? 0 : Math.PI / 3); | ||||||
|  | 						mat.rotateX((isLeft ? +1 : -1) * 1/40f * (sin(phase) + 1)); | ||||||
|  | 						mat.rotateY(1/20f * sin(Math.PI / 3 * phase)); | ||||||
|  | 					} | ||||||
|  | 			); | ||||||
|  | 		} else { | ||||||
|  | 			return model; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private Renderable createLayeredCuboid( | ||||||
|  | 			ComplexTexture texture, | ||||||
|  | 			int tx,  int ty, | ||||||
|  | 			int tx2, int ty2, | ||||||
|  | 			int tw, int th, int td, | ||||||
|  | 			float ox, float oy, float oz, | ||||||
|  | 			float sx, float sy, float sz | ||||||
|  | 	) { | ||||||
|  | 		WorldRenderProgram program = WorldRenderProgram.getDefault(); | ||||||
|  | 		StaticModel.Builder b = StaticModel.builder(); | ||||||
|  | 		 | ||||||
|  | 		// First layer | ||||||
|  | 		b.addPart(new PppBuilder( | ||||||
|  | 				program, | ||||||
|  | 				texture.getCuboidTextures(tx, ty, tw, th, td) | ||||||
|  | 		).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create()); | ||||||
|  | 		 | ||||||
|  | 		ox -= SECOND_LAYER_OFFSET; | ||||||
|  | 		oy -= SECOND_LAYER_OFFSET; | ||||||
|  | 		oz -= SECOND_LAYER_OFFSET; | ||||||
|  | 		 | ||||||
|  | 		sx += SECOND_LAYER_OFFSET * 2; | ||||||
|  | 		sy += SECOND_LAYER_OFFSET * 2; | ||||||
|  | 		sz += SECOND_LAYER_OFFSET * 2; | ||||||
|  | 		 | ||||||
|  | 		// Second layer | ||||||
|  | 		b.addPart(new PppBuilder( | ||||||
|  | 				program, | ||||||
|  | 				texture.getCuboidTextures(tx2, ty2, tw, th, td) | ||||||
|  | 		).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create()); | ||||||
|  | 		 | ||||||
|  | 		return new StaticModel(b); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public EntityRenderable createRenderable(EntityData entity) { | ||||||
|  | 		return new HumanoidModel( | ||||||
|  | 				entity, | ||||||
|  | 				 | ||||||
|  | 				new HumanoidModel.Body(body), | ||||||
|  | 				new HumanoidModel.Head( | ||||||
|  | 						head, new Vec3(0, 0, 6), 70, 25, new Vec3(1.2f, 0, 1.5f) | ||||||
|  | 				), | ||||||
|  | 				new HumanoidModel.Arm( | ||||||
|  | 						leftArm,  new Vec3(0, +1.5f, 3 + 3 - 0.5f), 0.0f | ||||||
|  | 				), | ||||||
|  | 				new HumanoidModel.Arm( | ||||||
|  | 						rightArm, new Vec3(0, -1.5f, 3 + 3 - 0.5f), FloatMathUtils.PI_F | ||||||
|  | 				), | ||||||
|  | 				new HumanoidModel.Leg( | ||||||
|  | 						leftLeg,  new Vec3(0, +0.5f, 3), FloatMathUtils.PI_F | ||||||
|  | 				), | ||||||
|  | 				new HumanoidModel.Leg( | ||||||
|  | 						rightLeg, new Vec3(0, -0.5f, 3), 0.0f | ||||||
|  | 				), | ||||||
|  | 				 | ||||||
|  | 				1.8f / (3 + 3 + 2) | ||||||
|  | 		) | ||||||
|  | 		.setWalkingArmSwing((float) toRadians(30)) | ||||||
|  | 		.setWalkingLegSwing((float) toRadians(50)) | ||||||
|  | 		.setWalkingFrequency(0.15f / 60.0f); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,270 @@ | |||||||
|  | package ru.windcorp.progressia.test; | ||||||
|  |  | ||||||
|  | import org.lwjgl.glfw.GLFW; | ||||||
|  |  | ||||||
|  | import glm.Glm; | ||||||
|  | import glm.mat._3.Mat3; | ||||||
|  | import glm.vec._2.Vec2; | ||||||
|  | import glm.vec._3.Vec3; | ||||||
|  | import ru.windcorp.progressia.client.ClientState; | ||||||
|  | import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend; | ||||||
|  | import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; | ||||||
|  | import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; | ||||||
|  | import ru.windcorp.progressia.client.graphics.input.InputEvent; | ||||||
|  | import ru.windcorp.progressia.client.graphics.input.KeyEvent; | ||||||
|  | import ru.windcorp.progressia.client.graphics.input.bus.Input; | ||||||
|  | import ru.windcorp.progressia.common.Units; | ||||||
|  | import ru.windcorp.progressia.common.util.FloatMathUtils; | ||||||
|  | import ru.windcorp.progressia.common.util.Matrices; | ||||||
|  | import ru.windcorp.progressia.common.util.Vectors; | ||||||
|  | import ru.windcorp.progressia.common.world.entity.EntityData; | ||||||
|  |  | ||||||
|  | public class TestPlayerControls { | ||||||
|  | 	 | ||||||
|  | 	private static final TestPlayerControls INSTANCE = new TestPlayerControls(); | ||||||
|  | 	 | ||||||
|  | 	public static TestPlayerControls getInstance() { | ||||||
|  | 		return INSTANCE; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private TestPlayerControls() {} | ||||||
|  | 	 | ||||||
|  | 	private static final double MODE_SWITCH_MAX_DELAY = 100 * Units.MILLISECONDS; | ||||||
|  | 	private static final double MIN_JUMP_DELAY = 200 * Units.MILLISECONDS; | ||||||
|  |  | ||||||
|  | 	// Horizontal and vertical max control speed when flying | ||||||
|  | 	private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND; | ||||||
|  | 	 | ||||||
|  | 	// (0; 1], 1 is instant change, 0 is no control authority | ||||||
|  | 	private static final float FLYING_CONTROL_AUTHORITY = 0.05f; | ||||||
|  | 	 | ||||||
|  | 	// Horizontal and vertical max control speed when walking | ||||||
|  | 	private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND; | ||||||
|  | 	 | ||||||
|  | 	// (0; 1], 1 is instant change, 0 is no control authority | ||||||
|  | 	private static final float WALKING_CONTROL_AUTHORITY = 0.1f; | ||||||
|  | 	 | ||||||
|  | 	// Vertical velocity instantly add to player when they jump | ||||||
|  | 	private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND; | ||||||
|  | 	 | ||||||
|  | 	private boolean isFlying = true; | ||||||
|  | 	 | ||||||
|  | 	private int movementForward = 0; | ||||||
|  | 	private int movementRight = 0; | ||||||
|  | 	private int movementUp = 0; | ||||||
|  | 	 | ||||||
|  | 	private double lastSpacePress = Double.NEGATIVE_INFINITY; | ||||||
|  | 	 | ||||||
|  | 	private boolean captureMouse = true; | ||||||
|  | 	private boolean useMinecraftGravity = false; | ||||||
|  | 	 | ||||||
|  | 	private Runnable updateCallback = null; | ||||||
|  | 	 | ||||||
|  | 	public void applyPlayerControls() { | ||||||
|  | 		if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		EntityData player = getPlayer(); | ||||||
|  | 		Mat3 angMat = Matrices.grab3(); | ||||||
|  | 		 | ||||||
|  | 		angMat.identity().rotateZ(player.getYaw()); | ||||||
|  | 		 | ||||||
|  | 		Vec3 movement = Vectors.grab3(); | ||||||
|  | 		 | ||||||
|  | 		movement.set(movementForward, -movementRight, 0); | ||||||
|  | 		 | ||||||
|  | 		if (movementForward != 0 && movementRight != 0) { | ||||||
|  | 			movement.normalize(); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		angMat.mul_(movement); // bug in jglm, .mul() and mul_() are swapped | ||||||
|  | 		 | ||||||
|  | 		if (isFlying) { | ||||||
|  | 			movement.z = movementUp; | ||||||
|  | 			movement.mul(FLYING_SPEED); | ||||||
|  | 			movement.sub(player.getVelocity()); | ||||||
|  | 			movement.mul(FLYING_CONTROL_AUTHORITY); | ||||||
|  | 		} else { | ||||||
|  | 			movement.mul(WALKING_SPEED); | ||||||
|  | 			movement.sub(player.getVelocity()); | ||||||
|  | 			movement.mul(WALKING_CONTROL_AUTHORITY); | ||||||
|  | 			movement.z = 0; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		player.getVelocity().add(movement); | ||||||
|  | 		 | ||||||
|  | 		Matrices.release(angMat); | ||||||
|  | 		Vectors.release(movement); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public void handleInput(Input input) { | ||||||
|  | 		if (ClientState.getInstance() == null || ClientState.getInstance().getLocalPlayer() == null) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		InputEvent event = input.getEvent(); | ||||||
|  | 		 | ||||||
|  | 		if (event instanceof KeyEvent) { | ||||||
|  | 			if (onKeyEvent((KeyEvent) event)) { | ||||||
|  | 				input.consume(); | ||||||
|  | 			} | ||||||
|  | 		} else if (event instanceof CursorMoveEvent) { | ||||||
|  | 			onMouseMoved((CursorMoveEvent) event); | ||||||
|  | 			input.consume(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private boolean onKeyEvent(KeyEvent event) { | ||||||
|  | 		if (event.isRepeat()) return false; | ||||||
|  | 		 | ||||||
|  | 		int multiplier = event.isPress() ? 1 : -1; | ||||||
|  | 		 | ||||||
|  | 		switch (event.getKey()) { | ||||||
|  | 		case GLFW.GLFW_KEY_W: | ||||||
|  | 			movementForward += +1 * multiplier; | ||||||
|  | 			break; | ||||||
|  | 		case GLFW.GLFW_KEY_S: | ||||||
|  | 			movementForward += -1 * multiplier; | ||||||
|  | 			break; | ||||||
|  | 		case GLFW.GLFW_KEY_A: | ||||||
|  | 			movementRight += -1 * multiplier; | ||||||
|  | 			break; | ||||||
|  | 		case GLFW.GLFW_KEY_D: | ||||||
|  | 			movementRight += +1 * multiplier; | ||||||
|  | 			break; | ||||||
|  | 		case GLFW.GLFW_KEY_SPACE: | ||||||
|  | 			handleSpace(multiplier); | ||||||
|  | 			break; | ||||||
|  | 		case GLFW.GLFW_KEY_LEFT_SHIFT: | ||||||
|  | 			handleShift(multiplier); | ||||||
|  | 			break; | ||||||
|  | 			 | ||||||
|  | 		case GLFW.GLFW_KEY_ESCAPE: | ||||||
|  | 			if (!event.isPress()) return false; | ||||||
|  | 			handleEscape(); | ||||||
|  | 			break; | ||||||
|  | 			 | ||||||
|  | 		case GLFW.GLFW_KEY_F5: | ||||||
|  | 			if (!event.isPress()) return false; | ||||||
|  | 			handleCameraMode(); | ||||||
|  | 			break; | ||||||
|  | 			 | ||||||
|  | 		case GLFW.GLFW_KEY_G: | ||||||
|  | 			if (!event.isPress()) return false; | ||||||
|  | 			handleGravitySwitch(); | ||||||
|  | 			break; | ||||||
|  | 			 | ||||||
|  | 		default: | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void handleSpace(int multiplier) { | ||||||
|  | 		boolean isPressed = multiplier > 0; | ||||||
|  | 		 | ||||||
|  | 		double timeSinceLastSpacePress = GraphicsInterface.getTime() - lastSpacePress; | ||||||
|  | 		 | ||||||
|  | 		if (isPressed && timeSinceLastSpacePress < MODE_SWITCH_MAX_DELAY) { | ||||||
|  | 			isFlying = !isFlying; | ||||||
|  | 			updateGUI(); | ||||||
|  | 			movementUp = +1; | ||||||
|  | 		} else { | ||||||
|  | 			if (isFlying) { | ||||||
|  | 				movementUp += +1 * multiplier; | ||||||
|  | 			} else { | ||||||
|  | 				if (isPressed && timeSinceLastSpacePress > MIN_JUMP_DELAY) { | ||||||
|  | 					jump(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		lastSpacePress = GraphicsInterface.getTime(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void jump() { | ||||||
|  | 		getPlayer().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void handleShift(int multiplier) { | ||||||
|  | 		if (isFlying) { | ||||||
|  | 			movementUp += -1 * multiplier; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void handleEscape() { | ||||||
|  | 		if (captureMouse) { | ||||||
|  | 			GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); | ||||||
|  | 		} else { | ||||||
|  | 			GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		captureMouse = !captureMouse; | ||||||
|  | 		updateGUI(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private void handleCameraMode() { | ||||||
|  | 		if (ClientState.getInstance().getCamera().hasAnchor()) { | ||||||
|  | 			ClientState.getInstance().getCamera().selectNextMode(); | ||||||
|  | 			updateGUI(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void handleGravitySwitch() { | ||||||
|  | 		useMinecraftGravity = !useMinecraftGravity; | ||||||
|  | 		updateGUI(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void onMouseMoved(CursorMoveEvent event) { | ||||||
|  | 		if (!captureMouse) return; | ||||||
|  | 		 | ||||||
|  | 		final float yawScale = -0.002f; | ||||||
|  | 		final float pitchScale = yawScale; | ||||||
|  |  | ||||||
|  | 		EntityData player = getPlayer(); | ||||||
|  | 		 | ||||||
|  | 		normalizeAngles(player.getDirection().add( | ||||||
|  | 				(float) (event.getChangeX() * yawScale), | ||||||
|  | 				(float) (event.getChangeY() * pitchScale) | ||||||
|  | 		)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private void normalizeAngles(Vec2 dir) { | ||||||
|  | 		// Normalize yaw | ||||||
|  | 		dir.x = FloatMathUtils.normalizeAngle(dir.x); | ||||||
|  | 		 | ||||||
|  | 		// Clamp pitch | ||||||
|  | 		dir.y = Glm.clamp( | ||||||
|  | 				dir.y, -FloatMathUtils.PI_F/2, +FloatMathUtils.PI_F/2 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private EntityData getPlayer() { | ||||||
|  | 		return ClientState.getInstance().getLocalPlayer(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public void setUpdateCallback(Runnable updateCallback) { | ||||||
|  | 		this.updateCallback = updateCallback; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private void updateGUI() { | ||||||
|  | 		if (this.updateCallback != null) { | ||||||
|  | 			this.updateCallback.run(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public boolean isFlying() { | ||||||
|  | 		return isFlying; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public boolean isMouseCaptured() { | ||||||
|  | 		return captureMouse; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	public boolean useMinecraftGravity() { | ||||||
|  | 		return useMinecraftGravity; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/assets/textures/entities/pyotr.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/assets/textures/entities/pyotr.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 7.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/assets/textures/entities/test_skin.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/assets/textures/entities/test_skin.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/assets/textures/entities/test_skin_layout.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/assets/textures/entities/test_skin_layout.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.8 KiB | 
		Reference in New Issue
	
	Block a user