15 Commits

Author SHA1 Message Date
a984333c3a Radio Buttons
Created RadioButton and RadioManager.
They work together well I guess.
😄
2021-04-18 19:44:50 -04:00
4a1f1b7545 It actually works now
It threw errors before
2021-04-18 15:09:29 -04:00
af7b39d8e9 Better Interactables
Created Interactable class
Introduced dependency of Button and Checkbox on Interactable
Created Checkbox class
2021-04-18 14:58:53 -04:00
a0acd16008 Now actually works
Only captures events that it needs
2021-04-17 11:03:23 -04:00
330ed1ab9b Buttony things
Centered button text
Label object
New dynamic text color
2021-04-17 11:01:03 -04:00
7e852ff05f Attempting to add focus
it doesnt work but still
2021-04-17 09:34:10 -04:00
6e1b0e3f69 Buttons work
Clicking and disabling now works
2021-04-17 09:12:09 -04:00
411780b120 Made onClick
It should work but idk for sure
2021-04-17 08:21:29 -04:00
5359b93738 Bettering the Button
Not really
TPS is 20 now
2021-04-17 08:07:01 -04:00
f9aae94bbc Return TPS to 20
yeah
2021-04-16 21:26:57 -04:00
5c57a57e9a Button things
Moved button to LayerTestUI
Saved all_buttons.png in resources
Idk it still wont render the button
2021-04-16 21:26:28 -04:00
468b6dc327 Create Button.java
Created Button class
Trying to give good functionality
2021-04-16 19:25:26 -04:00
69942ad17b Reset Default TPS
TPS of 47 to TPS of 20
2021-04-12 20:38:21 -04:00
3c74a808f3 Added Queue
+Added Queue class
*Replaced TPS Averager with a Queue
2021-04-12 20:35:29 -04:00
8327fcfd19 Counter Class
+Added Counter class
*Replaced current TPS meter with a Counter
2021-04-12 19:49:51 -04:00
400 changed files with 5557 additions and 20674 deletions

View File

@ -1,69 +0,0 @@
# Contributing Guidelines
This document lists conventions adopted by Progressia developers.
## git
### Branches
Progressia repository contains a `master` branch and several "feature" branches.
`master` is expected to contain a version of the game suitable for demonstration and forking/branching. Do not commit directly to `master` without OLEGSHA's approval.
- `master` must always correctly build without compiler warnings (see below).
- `master` must always pass all unit tests.
- `master` must always be able to launch successfully.
- `master` must always only contain working features.
- `master` should not contain excessive debug code.
- `master` must always have its code and filenames formatted (see below).
"Feature" branches are branches dedicated to the development of a single feature. When the feature reaches completion the branch is merged into `master` and removed. Intermediate merges into `master` may occur when some fitting milestone is reached. Intermediate merges from `master` may be done as necessary. Merges between "feature" branches should generally be avoided.
When beginning work on a new feature, create a new branch based on `master` (or on another "feature" branch if absolutely necessary). Use `all-small-with-dashes` to name the branch: `add-trees` or `rebalance-plastics` are good names. Do not fix unrelated bugs or work on unrelated features in a "feature" branch - create a new one, switch to an existing one or commit directly to `master` if the changes are small enough.
"Feature" branches may not be formatted properly. Formatting is required before merging into `master` or other branches.
### Commits
- Commits must leave the branch in a state that builds without compiler warnings (see below).
- Changes should be grouped in commits semantically. Avoid committing many small related changes in sequence; if necessary, wait and accumulate them. Avoid committing unrelated changes together; if necessary, split staged changes into several commits. This should normally result in about 1-2 commits for a day's work.
- Commit bulk changes (renaming, formatting, ...) separately. Don't ever commit whitespace changes outside formatting commits.
- Message format:
```
Short description of changes
<empty line>
- Enumeration of changes
- Nest when appropriate
- Use dashes only
- List not needed for small commits
```
Example:
```
Changed packages for relations, renamed Face to ShapePart
- Added BlockRelation as an abstract superclass to existing relations
- Must be given an absolute "up" direction before use
- Moved AbsFace, AbsRelation and BlockRelation to .world.rels
- Renamed Face to ShapePart to reduce confusion with AbsFace
```
- Only commit changes described in the commit message. Please double-check staged files before committing.
- Avoid merge conflicts. Pull before committing.
- Better sign commits than not. See: [git](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work), [GitHub](https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification).
## Code
### Warnings
Make sure that all committed code contains no compiler warnings. This specifically includes unused imports, unused private members, missing `@Override`s and warnings related to generics.
Warnings about unknown tokens in `@SuppressWarnings` are temporarily ignored. Please disable them in your IDE.
### Code Style
Formatting code is important.
- The format is specified within the files inside `/templates_and_presets/eclipse_ide`. Import the specifications into Eclipse or IntelliJ IDEA and use the IDEs' format feature. Alternatively format the code manually in accordance with existing files.
- Only use tabs for indentation. Never indent with spaces even when wrapping lines. This is to ensure that indentation does not break when tab width is different.
- Don't use `I` prefix for interfaces (not `IDoable` but `Doable`).
- Prioritize readability over compactness. Do not hesitate to use (very) long identifiers if they aid comprehension.
- Document all mathematics unless it is trivial, especially when using math notation for variable names.
- Use proper English when writing comments. Avoid boxes in comments. Use `//` for single-line comments.

View File

@ -42,16 +42,3 @@ Run configurations are used by Intellij IDEA to specify how a project must be ru
9. Click 'Apply' to save changes. 9. Click 'Apply' to save changes.
Step 8 is required to specify that the game must run in some directory other than the project root, which is the default in Intellij IDEA. Step 8 is required to specify that the game must run in some directory other than the project root, which is the default in Intellij IDEA.
### Applying formatting templates
Windcorp's Progressia repository is formatted with a style defined for Eclipse IDE (sic) in
`templates_and_presets/eclipse_ide`.
Please apply these templates to the project to automatically format the source in a similar fashion.
1. In project context menu, click 'File->Properties'. (`Ctrl+Alt+S`)
2. In 'Editor' > 'Code Style' > 'Java', press gear icon, then click 'Import Scheme' > 'Eclipse code style'
3. In Scheme select 'Project'
4. Open the file `templates_and_presets/eclipse_ide/FormatterProfile.xml` in 'Select Path'.
5. Inside 'Import Scheme' widow click 'Current Scheme' check box after press OK

View File

@ -18,28 +18,18 @@
package ru.windcorp.progressia; package ru.windcorp.progressia;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer; import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
import ru.windcorp.progressia.common.util.crash.providers.*; import ru.windcorp.progressia.common.util.crash.providers.*;
import ru.windcorp.progressia.test.LayerTitle;
public class ProgressiaLauncher { public class ProgressiaLauncher {
public static String[] arguments; public static String[] arguments;
private static Proxy proxy;
public static void launch(String[] args, Proxy proxy) { public static void launch(String[] args, Proxy proxy) {
arguments = args.clone(); arguments = args.clone();
setupCrashReports(); setupCrashReports();
proxy.initialize(); proxy.initialize();
ProgressiaLauncher.proxy = proxy;
GUI.addTopLayer(new LayerTitle("Title"));
}
public static Proxy getProxy() {
return proxy;
} }
private static void setupCrashReports() { private static void setupCrashReports() {

View File

@ -24,7 +24,7 @@ import ru.windcorp.progressia.client.graphics.world.Camera;
import ru.windcorp.progressia.client.graphics.world.EntityAnchor; import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.client.world.WorldRender;
import ru.windcorp.progressia.common.world.DefaultWorldData; import ru.windcorp.progressia.common.world.WorldData;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
public class Client { public class Client {
@ -36,7 +36,7 @@ public class Client {
private final ServerCommsChannel comms; private final ServerCommsChannel comms;
public Client(DefaultWorldData world, ServerCommsChannel comms) { public Client(WorldData world, ServerCommsChannel comms) {
this.world = new WorldRender(world, this); this.world = new WorldRender(world, this);
this.comms = comms; this.comms = comms;

View File

@ -30,6 +30,7 @@ import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.localization.Localizer; import ru.windcorp.progressia.client.localization.Localizer;
import ru.windcorp.progressia.common.resource.ResourceManager; import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.server.ServerState;
import ru.windcorp.progressia.test.TestContent; import ru.windcorp.progressia.test.TestContent;
import ru.windcorp.progressia.test.TestMusicPlayer; import ru.windcorp.progressia.test.TestMusicPlayer;
@ -37,9 +38,7 @@ public class ClientProxy implements Proxy {
@Override @Override
public void initialize() { public void initialize() {
GraphicsBackend.initialize(); GraphicsBackend.initialize();
try { try {
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init); RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init); RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
@ -59,6 +58,10 @@ public class ClientProxy implements Proxy {
AudioSystem.initialize(); AudioSystem.initialize();
ServerState.startServer();
ClientState.connectToLocalServer();
TestMusicPlayer.start(); TestMusicPlayer.start();
} }
} }

View File

@ -20,13 +20,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.Layer;
import ru.windcorp.progressia.client.graphics.world.LayerWorld; import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.common.world.DefaultWorldData; import ru.windcorp.progressia.common.world.WorldData;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
import ru.windcorp.progressia.server.ServerState; import ru.windcorp.progressia.server.ServerState;
import ru.windcorp.progressia.test.LayerAbout; import ru.windcorp.progressia.test.LayerAbout;
import ru.windcorp.progressia.test.LayerTestText;
import ru.windcorp.progressia.test.LayerTestUI; import ru.windcorp.progressia.test.LayerTestUI;
import ru.windcorp.progressia.test.TestContent; import ru.windcorp.progressia.test.TestContent;
@ -44,7 +41,7 @@ public class ClientState {
public static void connectToLocalServer() { public static void connectToLocalServer() {
DefaultWorldData world = new DefaultWorldData(); WorldData world = new WorldData();
LocalServerCommsChannel channel = new LocalServerCommsChannel( LocalServerCommsChannel channel = new LocalServerCommsChannel(
ServerState.getInstance() ServerState.getInstance()
@ -55,39 +52,11 @@ public class ClientState {
channel.connect(TestContent.PLAYER_LOGIN); channel.connect(TestContent.PLAYER_LOGIN);
setInstance(client); setInstance(client);
displayLoadingScreen();
} GUI.addBottomLayer(new LayerWorld(client));
GUI.addTopLayer(new LayerTestUI());
GUI.addTopLayer(new LayerAbout());
private static void displayLoadingScreen() {
GUI.addTopLayer(new LayerTestText("Text", new MutableStringLocalized("LayerText.Load"), layer -> {
Client client = ClientState.getInstance();
// TODO refacetor and remove
if (client != null) {
client.getComms().processPackets();
}
if (client != null && client.getLocalPlayer().hasEntity()) {
GUI.removeLayer(layer);
// TODO refactor, this shouldn't be here
LayerWorld layerWorld = new LayerWorld(client);
LayerTestUI layerUI = new LayerTestUI();
LayerAbout layerAbout = new LayerAbout();
GUI.addBottomLayer(layerWorld);
GUI.addTopLayer(layerUI);
GUI.addTopLayer(layerAbout);
}
}));
}
public static void disconnectFromLocalServer() {
getInstance().getComms().disconnect();
for (Layer layer : GUI.getLayers()) {
GUI.removeLayer(layer);
}
} }
private ClientState() { private ClientState() {

View File

@ -21,7 +21,6 @@ package ru.windcorp.progressia.client.comms.localhost;
import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.client.comms.ServerCommsChannel;
import ru.windcorp.progressia.common.comms.packets.Packet; import ru.windcorp.progressia.common.comms.packets.Packet;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.ServerState;
public class LocalServerCommsChannel extends ServerCommsChannel { public class LocalServerCommsChannel extends ServerCommsChannel {
@ -55,7 +54,7 @@ public class LocalServerCommsChannel extends ServerCommsChannel {
@Override @Override
public void disconnect() { public void disconnect() {
ServerState.getInstance().getClientManager().disconnectClient(localClient); // Do nothing
} }
} }

View File

@ -34,13 +34,7 @@ public class Colors {
DEBUG_BLUE = toVector(0xFF0000FF), DEBUG_BLUE = toVector(0xFF0000FF),
DEBUG_CYAN = toVector(0xFF00FFFF), DEBUG_CYAN = toVector(0xFF00FFFF),
DEBUG_MAGENTA = toVector(0xFFFF00FF), DEBUG_MAGENTA = toVector(0xFFFF00FF),
DEBUG_YELLOW = toVector(0xFFFFFF00), DEBUG_YELLOW = toVector(0xFFFFFF00);
LIGHT_GRAY = toVector(0xFFCBCBD0),
BLUE = toVector(0xFF37A2E6),
HOVER_BLUE = toVector(0xFFC3E4F7),
DISABLED_GRAY = toVector(0xFFE5E5E5),
DISABLED_BLUE = toVector(0xFFB2D8ED);
public static Vec4 toVector(int argb) { public static Vec4 toVector(int argb) {
return toVector(argb, new Vec4()); return toVector(argb, new Vec4());

View File

@ -21,11 +21,9 @@ package ru.windcorp.progressia.client.graphics;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.input.CursorEvent; import ru.windcorp.progressia.client.graphics.input.CursorEvent;
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent; import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent;
@ -59,27 +57,15 @@ public class GUI {
} }
public static void addBottomLayer(Layer layer) { public static void addBottomLayer(Layer layer) {
Objects.requireNonNull(layer, "layer"); modify(layers -> layers.add(layer));
modify(layers -> {
layers.add(layer);
layer.onAdded();
});
} }
public static void addTopLayer(Layer layer) { public static void addTopLayer(Layer layer) {
Objects.requireNonNull(layer, "layer"); modify(layers -> layers.add(0, layer));
modify(layers -> {
layers.add(0, layer);
layer.onAdded();
});
} }
public static void removeLayer(Layer layer) { public static void removeLayer(Layer layer) {
Objects.requireNonNull(layer, "layer"); modify(layers -> layers.remove(layer));
modify(layers -> {
layers.remove(layer);
layer.onRemoved();
});
} }
private static void modify(LayerStackModification mod) { private static void modify(LayerStackModification mod) {
@ -92,33 +78,12 @@ public class GUI {
public static void render() { public static void render() {
synchronized (LAYERS) { synchronized (LAYERS) {
if (!MODIFICATION_QUEUE.isEmpty()) {
MODIFICATION_QUEUE.forEach(action -> action.affect(LAYERS)); MODIFICATION_QUEUE.forEach(action -> action.affect(LAYERS));
MODIFICATION_QUEUE.clear(); MODIFICATION_QUEUE.clear();
boolean isMouseCurrentlyCaptured = GraphicsInterface.isMouseCaptured();
Layer.CursorPolicy policy = Layer.CursorPolicy.REQUIRE;
for (Layer layer : LAYERS) {
Layer.CursorPolicy currentPolicy = layer.getCursorPolicy();
if (currentPolicy != Layer.CursorPolicy.INDIFFERENT) {
policy = currentPolicy;
break;
}
}
boolean shouldCaptureMouse = (policy == Layer.CursorPolicy.FORBID);
if (shouldCaptureMouse != isMouseCurrentlyCaptured) {
GraphicsInterface.setMouseCaptured(shouldCaptureMouse);
}
}
for (int i = LAYERS.size() - 1; i >= 0; --i) { for (int i = LAYERS.size() - 1; i >= 0; --i) {
LAYERS.get(i).render(); LAYERS.get(i).render();
} }
} }
} }

View File

@ -31,52 +31,15 @@ public abstract class Layer {
private final AtomicBoolean isValid = new AtomicBoolean(false); private final AtomicBoolean isValid = new AtomicBoolean(false);
/**
* Represents various requests that a {@link Layer} can make regarding the
* presence of a visible cursor. The value of the highest layer that is not
* {@link #INDIFFERENT} is used.
*/
public static enum CursorPolicy {
/**
* Require that a cursor is visible.
*/
REQUIRE,
/**
* The {@link Layer} should not affect the presence or absence of a
* visible cursor; lower layers should be consulted.
*/
INDIFFERENT,
/**
* Forbid a visible cursor.
*/
FORBID
}
private CursorPolicy cursorPolicy = CursorPolicy.INDIFFERENT;
public Layer(String name) { public Layer(String name) {
this.name = name; this.name = name;
} }
public String getName() {
return name;
}
@Override @Override
public String toString() { public String toString() {
return "Layer " + name; return "Layer " + name;
} }
public CursorPolicy getCursorPolicy() {
return cursorPolicy;
}
public void setCursorPolicy(CursorPolicy cursorPolicy) {
this.cursorPolicy = cursorPolicy;
}
void render() { void render() {
GraphicsInterface.startNextLayer(); GraphicsInterface.startNextLayer();
@ -116,12 +79,4 @@ public abstract class Layer {
return GraphicsInterface.getFrameHeight(); return GraphicsInterface.getFrameHeight();
} }
protected void onAdded() {
// Do nothing
}
protected void onRemoved() {
// Do nothing
}
} }

View File

@ -19,7 +19,6 @@
package ru.windcorp.progressia.client.graphics.backend; package ru.windcorp.progressia.client.graphics.backend;
import glm.vec._2.i.Vec2i; import glm.vec._2.i.Vec2i;
import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.glfw.GLFWVidMode;
import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.glfw.GLFW.*;
@ -44,13 +43,6 @@ public class GraphicsBackend {
private static boolean isGLFWInitialized = false; private static boolean isGLFWInitialized = false;
private static boolean isOpenGLInitialized = false; private static boolean isOpenGLInitialized = false;
private static boolean allowDisablingCursor;
static {
String key = GraphicsBackend.class.getName() + ".allowDisablingCursor";
allowDisablingCursor = Boolean.parseBoolean(System.getProperty(key, "true"));
}
private static boolean forceCursorToCenter = false;
private GraphicsBackend() { private GraphicsBackend() {
} }
@ -122,10 +114,6 @@ public class GraphicsBackend {
frameLength = now - frameStart; frameLength = now - frameStart;
frameStart = now; frameStart = now;
} }
if (forceCursorToCenter) {
glfwSetCursorPos(windowHandle, FRAME_SIZE.x / 2.0, FRAME_SIZE.y / 2.0);
}
} }
static void endFrame() { static void endFrame() {
@ -204,26 +192,4 @@ public class GraphicsBackend {
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
return vidmode.refreshRate(); return vidmode.refreshRate();
} }
public static boolean isMouseCaptured() {
if (!allowDisablingCursor) {
return forceCursorToCenter;
}
return glfwGetInputMode(windowHandle, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
}
public static void setMouseCaptured(boolean capture) {
if (!allowDisablingCursor) {
forceCursorToCenter = capture;
return;
}
int mode = capture ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;
glfwSetInputMode(windowHandle, GLFW_CURSOR, mode);
if (!capture) {
glfwSetCursorPos(windowHandle, FRAME_SIZE.x / 2.0, FRAME_SIZE.y / 2.0);
}
}
} }

View File

@ -82,12 +82,4 @@ public class GraphicsInterface {
GraphicsBackend.setVSyncEnabled(GraphicsBackend.isVSyncEnabled()); GraphicsBackend.setVSyncEnabled(GraphicsBackend.isVSyncEnabled());
} }
public static boolean isMouseCaptured() {
return GraphicsBackend.isMouseCaptured();
}
public static void setMouseCaptured(boolean capture) {
GraphicsBackend.setMouseCaptured(capture);
}
} }

View File

@ -65,6 +65,8 @@ class LWJGLInitializer {
GraphicsBackend.setWindowHandle(handle); GraphicsBackend.setWindowHandle(handle);
glfwSetInputMode(handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwMakeContextCurrent(handle); glfwMakeContextCurrent(handle);
glfwSwapInterval(0); // TODO: remove after config system is added glfwSwapInterval(0); // TODO: remove after config system is added
} }

View File

@ -29,8 +29,8 @@ import glm.vec._3.Vec3;
import glm.vec._4.Vec4; import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.Usage; import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.ShapePart; import ru.windcorp.progressia.client.graphics.model.Face;
import ru.windcorp.progressia.client.graphics.model.ShapeParts; import ru.windcorp.progressia.client.graphics.model.Faces;
import ru.windcorp.progressia.client.graphics.model.Shape; import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
@ -84,7 +84,7 @@ public class RenderTarget {
private final Deque<TransformedMask> maskStack = new LinkedList<>(); private final Deque<TransformedMask> maskStack = new LinkedList<>();
private final Deque<Mat4> transformStack = new LinkedList<>(); private final Deque<Mat4> transformStack = new LinkedList<>();
private final List<ShapePart> currentClipFaces = new ArrayList<>(); private final List<Face> currentClipFaces = new ArrayList<>();
private int depth = 0; private int depth = 0;
@ -94,8 +94,8 @@ public class RenderTarget {
protected void assembleCurrentClipFromFaces() { protected void assembleCurrentClipFromFaces() {
if (!currentClipFaces.isEmpty()) { if (!currentClipFaces.isEmpty()) {
ShapePart[] faces = currentClipFaces.toArray( Face[] faces = currentClipFaces.toArray(
new ShapePart[currentClipFaces.size()] new Face[currentClipFaces.size()]
); );
currentClipFaces.clear(); currentClipFaces.clear();
@ -189,13 +189,16 @@ public class RenderTarget {
public void addCustomRenderer(Renderable renderable) { public void addCustomRenderer(Renderable renderable) {
assembleCurrentClipFromFaces(); assembleCurrentClipFromFaces();
assembled.add(
float depth = this.depth--; new Clip(
Mat4 transform = new Mat4().translate(0, 0, depth).mul(getTransform()); maskStack,
assembled.add(new Clip(maskStack, transform, renderable)); getTransform(),
renderable
)
);
} }
protected void addFaceToCurrentClip(ShapePart face) { protected void addFaceToCurrentClip(Face face) {
currentClipFaces.add(face); currentClipFaces.add(face);
} }
@ -267,7 +270,7 @@ public class RenderTarget {
fill(Colors.toVector(color)); fill(Colors.toVector(color));
} }
public ShapePart createRectagleFace( public Face createRectagleFace(
int x, int x,
int y, int y,
int width, int width,
@ -277,7 +280,7 @@ public class RenderTarget {
) { ) {
float depth = this.depth--; float depth = this.depth--;
return ShapeParts.createRectangle( return Faces.createRectangle(
FlatRenderProgram.getDefault(), FlatRenderProgram.getDefault(),
texture, texture,
color, color,
@ -288,7 +291,7 @@ public class RenderTarget {
); );
} }
public ShapePart createRectagleFace( public Face createRectagleFace(
int x, int x,
int y, int y,
int width, int width,

View File

@ -33,8 +33,8 @@ import gnu.trove.stack.TIntStack;
import gnu.trove.stack.array.TIntArrayStack; import gnu.trove.stack.array.TIntArrayStack;
import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.Usage; import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.ShapePart; import ru.windcorp.progressia.client.graphics.model.Face;
import ru.windcorp.progressia.client.graphics.model.ShapeParts; import ru.windcorp.progressia.client.graphics.model.Faces;
import ru.windcorp.progressia.client.graphics.model.Shape; import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram; import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
@ -144,7 +144,7 @@ public abstract class SpriteTypeface extends Typeface {
return new Shape( return new Shape(
Usage.STATIC, Usage.STATIC,
getProgram(), getProgram(),
ShapeParts.createRectangle( Faces.createRectangle(
getProgram(), getProgram(),
getTexture(c), getTexture(c),
Colors.WHITE, Colors.WHITE,
@ -167,7 +167,7 @@ public abstract class SpriteTypeface extends Typeface {
private final Renderable unitLine = new Shape( private final Renderable unitLine = new Shape(
Usage.STATIC, Usage.STATIC,
getProgram(), getProgram(),
ShapeParts.createRectangle( Faces.createRectangle(
getProgram(), getProgram(),
null, null,
Vectors.UNIT_4, Vectors.UNIT_4,
@ -257,7 +257,7 @@ public abstract class SpriteTypeface extends Typeface {
private class SDWorkspace extends SpriteTypeface.Workspace { private class SDWorkspace extends SpriteTypeface.Workspace {
private final Collection<ShapePart> faces = new ArrayList<>(); private final Collection<Face> faces = new ArrayList<>();
private final Vec3 origin = new Vec3(); private final Vec3 origin = new Vec3();
private final Vec3 width = new Vec3(); private final Vec3 width = new Vec3();
@ -298,7 +298,7 @@ public abstract class SpriteTypeface extends Typeface {
workspace.height.sub(workspace.origin); workspace.height.sub(workspace.origin);
workspace.faces.add( workspace.faces.add(
ShapeParts.createRectangle( Faces.createRectangle(
getProgram(), getProgram(),
texture, texture,
color, color,
@ -314,7 +314,7 @@ public abstract class SpriteTypeface extends Typeface {
return new Shape( return new Shape(
Usage.STATIC, Usage.STATIC,
getProgram(), getProgram(),
workspace.faces.toArray(new ShapePart[workspace.faces.size()]) workspace.faces.toArray(new Face[workspace.faces.size()])
); );
} }

View File

@ -1,156 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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 java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.function.Consumer;
import org.lwjgl.glfw.GLFW;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.event.ButtonEvent;
import ru.windcorp.progressia.client.graphics.gui.event.EnableEvent;
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
public abstract class BasicButton extends Component {
private final Label label;
private boolean isPressed = false;
private final Collection<Consumer<BasicButton>> actions = Collections.synchronizedCollection(new ArrayList<>());
public BasicButton(String name, Label label) {
super(name);
this.label = label;
setLayout(new LayoutAlign(10));
addChild(this.label);
setFocusable(true);
reassembleAt(ARTrigger.HOVER, ARTrigger.FOCUS, ARTrigger.ENABLE);
// Click triggers
addListener(KeyEvent.class, e -> {
if (e.isRepeat()) {
return false;
} else if (
e.isLeftMouseButton() ||
e.getKey() == GLFW.GLFW_KEY_SPACE ||
e.getKey() == GLFW.GLFW_KEY_ENTER
) {
setPressed(e.isPress());
return true;
} else {
return false;
}
});
addListener(new Object() {
// Release when losing focus
@Subscribe
public void onFocusChange(FocusEvent e) {
if (!e.getNewState()) {
setPressed(false);
}
}
// Release when hover ends
@Subscribe
public void onHoverEnded(HoverEvent e) {
if (!e.isNowHovered()) {
setPressed(false);
}
}
// Release when disabled
@Subscribe
public void onDisabled(EnableEvent e) {
if (!e.getComponent().isEnabled()) {
setPressed(false);
}
}
// Trigger virtualClick when button is released
@Subscribe
public void onRelease(ButtonEvent.Release e) {
virtualClick();
}
});
}
public BasicButton(String name, String label, Font labelFont) {
this(name, new Label(name + ".Label", labelFont, label));
}
public BasicButton(String name, String label) {
this(name, label, new Font());
}
public boolean isPressed() {
return isPressed;
}
public void click() {
setPressed(true);
setPressed(false);
}
public void setPressed(boolean isPressed) {
if (this.isPressed != isPressed) {
this.isPressed = isPressed;
requestReassembly();
if (isPressed) {
takeFocus();
}
dispatchEvent(ButtonEvent.create(this, this.isPressed));
}
}
public BasicButton addAction(Consumer<BasicButton> action) {
this.actions.add(Objects.requireNonNull(action, "action"));
return this;
}
public boolean removeAction(Consumer<BasicButton> action) {
return this.actions.remove(action);
}
public void virtualClick() {
this.actions.forEach(action -> {
action.accept(this);
});
}
public Label getLabel() {
return label;
}
}

View File

@ -1,83 +1,83 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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; package ru.windcorp.progressia.client.graphics.gui;
import glm.vec._4.Vec4; import java.util.function.Consumer;
import java.util.function.Supplier;
import com.google.common.eventbus.Subscribe;
import glm.mat._4.Mat4;
import glm.vec._2.i.Vec2i;
import org.lwjgl.glfw.GLFW;
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
public class Button extends BasicButton { public class Button extends Interactable {
public Button(String name, String label, Font labelFont) { public <T extends InputEvent> Button(String name, Label textLabel, Consumer<Button> onClick) {//, InputListener<T> onClick, Class<? extends T> onClickClass) {
super(name, label, labelFont); super(name, textLabel);
setPreferredSize(107,34);
//Button inButton = (Button) setFocusable(true);
addListener((Class<KeyEvent>) KeyEvent.class, (InputListener<KeyEvent>) e -> {if ((e.isLeftMouseButton() && containsCursor()) || (e.getKey()==GLFW.GLFW_KEY_ENTER && isFocused()) )
{
isClicked = e.isPress();
if (!isDisabled())
{
onClick.accept(this);
takeFocus();
} }
requestReassembly();
public Button(String name, Label label) { return true;
super(name, label);
} }
return false;});
public Button(String name, String label) {
this(name, label, new Font());
} }
@Override @Override
protected void assembleSelf(RenderTarget target) { protected void assembleSelf(RenderTarget target) {
// Border //Border
if (isDisabled())
Vec4 borderColor; {
if (isPressed() || isHovered() || isFocused()) { target.fill(getX(), getY(), getWidth(), getHeight(), 0xFFE5E5E5);
borderColor = Colors.BLUE;
} else {
borderColor = Colors.LIGHT_GRAY;
} }
target.fill(getX(), getY(), getWidth(), getHeight(), borderColor); else if (isClicked() || isHovered() || isFocused())
{
// Inside area target.fill(getX(), getY(), getWidth(), getHeight(), 0xFF37A2E6);
if (isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (isHovered() && isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
} }
target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, backgroundColor); else
{
target.fill(getX(), getY(), getWidth(), getHeight(), 0xFFCBCBD0);
}
//Inside area
if (!isClicked() && isHovered() && !isDisabled())
{
target.fill(getX()+2, getY()+2, getWidth()-4, getHeight()-4, 0xFFC3E4F7);
}
else if (!isClicked() || isDisabled())
{
target.fill(getX()+2, getY()+2, getWidth()-4, getHeight()-4, Colors.WHITE);
}
Font tempFont = new Font().withColor(Colors.BLACK);
if (isDisabled())
{
tempFont = tempFont.withColor(Colors.GRAY_A);
}
else if (isClicked())
{
tempFont = tempFont.withColor(Colors.WHITE);
} }
// Change label font color target.pushTransform(new Mat4().identity().translate( getX()+.5f*getWidth()-.5f*label.getPreferredSize().x, getY(), 0));
label = new Label(label.getName(), tempFont, label.getContentSupplier());
if (isPressed()) { label.assembleSelf(target);
getLabel().setFont(getLabel().getFont().withColor(Colors.WHITE)); target.popTransform();
} else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK));
}
}
@Override
protected void postAssembleSelf(RenderTarget target) {
// Apply disable tint
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
}
} }
} }

View File

@ -1,149 +1,117 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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; package ru.windcorp.progressia.client.graphics.gui;
import java.util.function.Consumer;
import java.util.function.Supplier;
import com.google.common.eventbus.Subscribe;
import glm.mat._4.Mat4;
import glm.vec._2.i.Vec2i; import glm.vec._2.i.Vec2i;
import glm.vec._4.Vec4; import org.lwjgl.glfw.GLFW;
import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.backend.InputTracker;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typefaces; import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
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.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutHorizontal; import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
public class Checkbox extends BasicButton { public class Checkbox extends Interactable {
private class Tick extends Component { private boolean isActive;
public Tick() { public <T extends InputEvent> Checkbox(String name, Label textLabel, Consumer<Checkbox> onSet, Consumer<Checkbox> onReset) {//, InputListener<T> onClick, Class<? extends T> onClickClass) {
super(Checkbox.this.getName() + ".Tick"); super(name, textLabel);
setPreferredSize(44 + textLabel.getPreferredSize().x,textLabel.getPreferredSize().y);
//Checkbox inCheck = (Checkbox) setFocusable(true);
setPreferredSize(new Vec2i(Typefaces.getDefault().getLineHeight() * 3 / 2)); addListener((Class<KeyEvent>) KeyEvent.class, (InputListener<KeyEvent>) e -> {if (e.isLeftMouseButton() && containsCursor()|| (e.getKey()==GLFW.GLFW_KEY_ENTER && isFocused()))
{
isClicked = e.isPress();
if (!isDisabled())
{
if (!isClicked && !isActive)
{
onSet.accept(this);
isActive = !isActive;
}
else if (!isClicked && isActive)
{
onReset.accept(this);
isActive = !isActive;
}
else if (isClicked)
{
takeFocus();
}
}
requestReassembly();
return true;
}
return false;});
}
public boolean isActive()
{
return isActive;
} }
@Override @Override
protected void assembleSelf(RenderTarget target) { protected void assembleSelf(RenderTarget target) {
//Border
int size = getPreferredSize().x; if (isDisabled())
int x = getX(); {
int y = getY() + (getHeight() - size) / 2; target.fill(getX()+label.getPreferredSize().x, getY(), getWidth()-label.getPreferredSize().x, getHeight(), 0xFFE5E5E5);
// Border
Vec4 borderColor;
if (Checkbox.this.isPressed() || Checkbox.this.isHovered() || Checkbox.this.isFocused()) {
borderColor = Colors.BLUE;
} else {
borderColor = Colors.LIGHT_GRAY;
} }
target.fill(x, y, size, size, borderColor); else if (isClicked() || isHovered() || isFocused())
{
// Inside area target.fill(getX()+label.getPreferredSize().x, getY(), getWidth()-label.getPreferredSize().x, getHeight(), 0xFF37A2E6); // blue
if (Checkbox.this.isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (Checkbox.this.isHovered() && Checkbox.this.isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
} }
target.fill(x + 2, y + 2, size - 4, size - 4, backgroundColor); else
{
target.fill(getX()+label.getPreferredSize().x, getY(), getWidth()-label.getPreferredSize().x, getHeight(), 0xFFCBCBD0);
} }
//Inside area
// "Tick" if (!isClicked() && isHovered() && !isDisabled())
{
if (Checkbox.this.isChecked()) { target.fill(getX()+2+label.getPreferredSize().x, getY()+2, getWidth()-label.getPreferredSize().x-4, getHeight()-4, 0xFFC3E4F7); // light blue
target.fill(x + 4, y + 4, size - 8, size - 8, Colors.BLUE); }
else if (!isClicked() || isDisabled())
{
target.fill(getX()+2+label.getPreferredSize().x, getY()+2, getWidth()-label.getPreferredSize().x-4, getHeight()-4, Colors.WHITE);
}
if (isActive() && !isClicked())
{
if (isDisabled())
{
target.fill(getX()+getWidth()-getHeight()+4, getY()+4, getHeight()-8, getHeight()-8, 0xFFB3D7EF);
}
else
{
target.fill(getX()+getWidth()-getHeight()+4, getY()+4, getHeight()-8, getHeight()-8, 0xFF37A2E6); // blue
} }
} }
else if (!isClicked())
{
if (isDisabled())
{
target.fill(getX()+label.getPreferredSize().x+4, getY()+4, getHeight()-8, getHeight()-8, 0xFFE5E5E5);
} }
else if (isFocused() || isHovered())
private boolean checked; {
target.fill(getX()+label.getPreferredSize().x+4, getY()+4, getHeight()-8, getHeight()-8, 0xFF37A2E6); // blue
public Checkbox(String name, String label, Font labelFont, boolean check) {
super(name, label, labelFont);
this.checked = check;
assert getChildren().size() == 1 : "Checkbox expects that BasicButton contains exactly one child";
Component basicChild = getChild(0);
Group group = new Group(getName() + ".LabelAndTick", new LayoutHorizontal(0, 10));
removeChild(basicChild);
setLayout(new LayoutAlign(0, 0.5f, 10));
group.setLayoutHint(basicChild.getLayoutHint());
group.addChild(new Tick());
group.addChild(basicChild);
addChild(group);
addAction(b -> switchState());
} }
else
public Checkbox(String name, String label, Font labelFont) { {
this(name, label, labelFont, false); target.fill(getX()+label.getPreferredSize().x+4, getY()+4, getHeight()-8, getHeight()-8, 0xFFCBCBD0);
} }
target.fill(getX()+label.getPreferredSize().x+6, getY()+6, getHeight()-12, getHeight()-12, Colors.WHITE);
public Checkbox(String name, String label, boolean check) {
this(name, label, new Font(), check);
} }
target.pushTransform(new Mat4().identity().translate( getX(), getY(), 0));
public Checkbox(String name, String label) { label.assembleSelf(target);
this(name, label, false); target.popTransform();
} }
public void switchState() {
setChecked(!isChecked());
}
/**
* @return the checked
*/
public boolean isChecked() {
return checked;
}
/**
* @param checked the checked to set
*/
public void setChecked(boolean checked) {
this.checked = checked;
}
@Override
protected void assembleSelf(RenderTarget target) {
// Change label font color
if (isPressed()) {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLUE));
} else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK));
}
}
@Override
protected void postAssembleSelf(RenderTarget target) {
// Apply disable tint
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
}
}
} }

View File

@ -19,23 +19,18 @@
package ru.windcorp.progressia.client.graphics.gui; package ru.windcorp.progressia.client.graphics.gui;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import glm.vec._2.i.Vec2i; import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.backend.InputTracker; import ru.windcorp.progressia.client.graphics.backend.InputTracker;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.gui.event.ChildAddedEvent; import ru.windcorp.progressia.client.graphics.gui.event.ChildAddedEvent;
import ru.windcorp.progressia.client.graphics.gui.event.ChildRemovedEvent; import ru.windcorp.progressia.client.graphics.gui.event.ChildRemovedEvent;
import ru.windcorp.progressia.client.graphics.gui.event.EnableEvent;
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent; import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent; import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
import ru.windcorp.progressia.client.graphics.gui.event.ParentChangedEvent; import ru.windcorp.progressia.client.graphics.gui.event.ParentChangedEvent;
@ -67,8 +62,6 @@ public class Component extends Named {
private Object layoutHint = null; private Object layoutHint = null;
private Layout layout = null; private Layout layout = null;
private boolean isEnabled = true;
private boolean isFocusable = false; private boolean isFocusable = false;
private boolean isFocused = false; private boolean isFocused = false;
@ -292,31 +285,10 @@ public class Component extends Named {
return this; return this;
} }
/**
* Checks whether this component is focusable. A component needs to be
* focusable to become focused. A component that is focusable may not
* necessarily be ready to gain focus (see {@link #canGainFocusNow()}).
*
* @return {@code true} iff the component is focusable
* @see #canGainFocusNow()
*/
public boolean isFocusable() { public boolean isFocusable() {
return isFocusable; return isFocusable;
} }
/**
* Checks whether this component can become focused at this moment.
* <p>
* The implementation of this method in {@link Component} considers the
* component a focus candidate if it is both focusable and enabled.
*
* @return {@code true} iff the component can receive focus
* @see #isFocusable()
*/
public boolean canGainFocusNow() {
return isFocusable() && isEnabled();
}
public Component setFocusable(boolean focusable) { public Component setFocusable(boolean focusable) {
this.isFocusable = focusable; this.isFocusable = focusable;
return this; return this;
@ -365,7 +337,7 @@ public class Component extends Named {
return; return;
} }
if (component.canGainFocusNow()) { if (component.isFocusable()) {
setFocused(false); setFocused(false);
component.setFocused(true); component.setFocused(true);
return; return;
@ -407,7 +379,7 @@ public class Component extends Named {
return; return;
} }
if (component.canGainFocusNow()) { if (component.isFocusable()) {
setFocused(false); setFocused(false);
component.setFocused(true); component.setFocused(true);
return; return;
@ -461,51 +433,12 @@ public class Component extends Named {
return null; return null;
} }
public boolean isEnabled() {
return isEnabled;
}
/**
* Enables or disables this component. An {@link EnableEvent} is dispatched
* if the state changes.
*
* @param enabled {@code true} to enable the component, {@code false} to
* disable the component
* @see #setEnabledRecursively(boolean)
*/
public void setEnabled(boolean enabled) {
if (this.isEnabled != enabled) {
if (isFocused() && isEnabled()) {
focusNext();
}
if (isEnabled()) {
setHovered(false);
}
this.isEnabled = enabled;
dispatchEvent(new EnableEvent(this));
}
}
/**
* Enables or disables this component and all of its children recursively.
*
* @param enabled {@code true} to enable the components, {@code false} to
* disable the components
* @see #setEnabled(boolean)
*/
public void setEnabledRecursively(boolean enabled) {
setEnabled(enabled);
getChildren().forEach(c -> c.setEnabledRecursively(enabled));
}
public boolean isHovered() { public boolean isHovered() {
return isHovered; return isHovered;
} }
protected void setHovered(boolean isHovered) { protected void setHovered(boolean isHovered) {
if (this.isHovered != isHovered && isEnabled()) { if (this.isHovered != isHovered) {
this.isHovered = isHovered; this.isHovered = isHovered;
if (!isHovered && !getChildren().isEmpty()) { if (!isHovered && !getChildren().isEmpty()) {
@ -569,7 +502,7 @@ public class Component extends Named {
} }
protected void handleInput(Input input) { protected void handleInput(Input input) {
if (inputBus != null && isEnabled()) { if (inputBus != null) {
inputBus.dispatch(input); inputBus.dispatch(input);
} }
} }
@ -665,17 +598,6 @@ public class Component extends Named {
} }
} }
/**
* Schedules the reassembly to occur.
* <p>
* This method is invoked in root components whenever a
* {@linkplain #requestReassembly() reassembly request} is made by one of
* its children. When creating the dedicated root component, override this
* method to perform any implementation-specific actions that will cause a
* reassembly as soon as possible.
* <p>
* The default implementation of this method does nothing.
*/
protected void handleReassemblyRequest() { protected void handleReassemblyRequest() {
// To be overridden // To be overridden
} }
@ -716,135 +638,6 @@ public class Component extends Named {
getChildren().forEach(child -> child.assemble(target)); getChildren().forEach(child -> child.assemble(target));
} }
/*
* Automatic Reassembly
*/
/**
* The various kinds of changes that may be used with
* {@link Component#reassembleAt(ARTrigger...)}.
*/
protected static enum ARTrigger {
/**
* Reassemble the component whenever its hover status changes, e.g.
* whenever the pointer enters or leaves its bounds.
*/
HOVER,
/**
* Reassemble the component whenever it gains or loses focus.
* <p>
* <em>Component must be focusable to be able to gain focus.</em> The
* component will not be reassembled unless
* {@link Component#setFocusable(boolean) setFocusable(true)} has been
* invoked.
*/
FOCUS,
/**
* Reassemble the component whenever it is enabled or disabled.
*/
ENABLE
}
/**
* All trigger objects (event listeners) that are currently registered with
* {@link #eventBus}. The field is {@code null} until the first trigger is
* installed.
*/
private Map<ARTrigger, Object> autoReassemblyTriggerObjects = null;
private Object createTriggerObject(ARTrigger type) {
switch (type) {
case HOVER:
return new Object() {
@Subscribe
public void onHoverChanged(HoverEvent e) {
requestReassembly();
}
};
case FOCUS:
return new Object() {
@Subscribe
public void onFocusChanged(FocusEvent e) {
requestReassembly();
}
};
case ENABLE:
return new Object() {
@Subscribe
public void onEnabled(EnableEvent e) {
requestReassembly();
}
};
default:
throw new NullPointerException("type");
}
}
/**
* Requests that {@link #requestReassembly()} is invoked on this component
* whenever any of the specified changes occur. Duplicate attempts to
* register the same trigger are silently ignored.
* <p>
* {@code triggers} may be empty, which results in a no-op. It must not be
* {@code null}.
*
* @param triggers the {@linkplain ARTrigger triggers} to
* request reassembly with.
* @see #disableAutoReassemblyAt(ARTrigger...)
*/
protected synchronized void reassembleAt(ARTrigger... triggers) {
Objects.requireNonNull(triggers, "triggers");
if (triggers.length == 0)
return;
if (autoReassemblyTriggerObjects == null) {
autoReassemblyTriggerObjects = new EnumMap<>(ARTrigger.class);
}
for (ARTrigger trigger : triggers) {
if (!autoReassemblyTriggerObjects.containsKey(trigger)) {
Object triggerObject = createTriggerObject(trigger);
addListener(triggerObject);
autoReassemblyTriggerObjects.put(trigger, triggerObject);
}
}
}
/**
* Requests that {@link #requestReassembly()} is no longer invoked on this
* component whenever any of the specified changes occur. After a trigger is
* removed, it may be reinstalled with
* {@link #reassembleAt(ARTrigger...)}. Attempts to remove a
* nonexistant trigger are silently ignored.
* <p>
* {@code triggers} may be empty, which results in a no-op. It must not be
* {@code null}.
*
* @param triggers the {@linkplain ARTrigger triggers} to remove
* @see #reassemblyAt(ARTrigger...)
*/
protected synchronized void disableAutoReassemblyAt(ARTrigger... triggers) {
Objects.requireNonNull(triggers, "triggers");
if (triggers.length == 0)
return;
if (autoReassemblyTriggerObjects == null)
return;
for (ARTrigger trigger : triggers) {
Object triggerObject = autoReassemblyTriggerObjects.remove(trigger);
if (triggerObject != null) {
removeListener(trigger);
}
}
}
// /** // /**
// * Returns a component that displays this component in its center. // * Returns a component that displays this component in its center.
// * @return a {@link Aligner} initialized to center this component // * @return a {@link Aligner} initialized to center this component

View File

@ -0,0 +1,62 @@
package ru.windcorp.progressia.client.graphics.gui;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import glm.vec._2.i.Vec2i;
public class Interactable extends Component {
private Vec2i currentSize;
protected boolean isDisabled;
protected boolean isClicked;
protected Label label;
public Interactable(String name, Label textLabel)
{
super(name);
label = textLabel;
addChild(textLabel);
addListener(new Object() {
@Subscribe
public void onHoverChanged(HoverEvent e) {
requestReassembly();
}
});
addListener(new Object() {
@Subscribe
public void onFocusChanged(FocusEvent e) {
//inButton.setText(new Label("dummy",new Font().withColor(Colors.BLACK),e.getNewState() ? "Is Focused" : "Isn't focused"));
requestReassembly();
}
});
}
public boolean isClicked()
{
return isClicked;
}
public void setDisable(boolean isDisabled)
{
this.isDisabled = isDisabled;
setFocusable(isDisabled);
}
public boolean isDisabled()
{
return isDisabled;
}
public void setText(Label newText)
{
removeChild(label);
label = newText;
addChild(label);
requestReassembly();
}
}

View File

@ -83,10 +83,6 @@ public class Label extends Component {
return font; return font;
} }
public void setFont(Font font) {
this.font = font;
}
public String getCurrentText() { public String getCurrentText() {
return currentText; return currentText;
} }
@ -100,7 +96,11 @@ public class Label extends Component {
float startX = getX() + font.getAlign() * (getWidth() - currentSize.x); float startX = getX() + font.getAlign() * (getWidth() - currentSize.x);
target.pushTransform( target.pushTransform(
new Mat4().identity().translate(startX, getY(), 0).scale(2) new Mat4().identity().translate(startX, getY(), -1000) // TODO wtf
// is this
// magic
// <---
.scale(2)
); );
target.addCustomRenderer(font.assemble(currentText, maxWidth)); target.addCustomRenderer(font.assemble(currentText, maxWidth));

View File

@ -15,66 +15,14 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package ru.windcorp.progressia.client.graphics.gui; package ru.windcorp.progressia.client.graphics.gui;
import java.util.Objects; public class Panel extends Component {
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
public class Panel extends Group {
private Vec4 fill;
private Vec4 border;
public Panel(String name, Layout layout, Vec4 fill, Vec4 border) {
super(name, layout);
this.fill = Objects.requireNonNull(fill, "fill");
this.border = border;
}
public Panel(String name, Layout layout) { public Panel(String name, Layout layout) {
this(name, layout, Colors.WHITE, Colors.LIGHT_GRAY); super(name);
} setLayout(layout);
/**
* @return the fill
*/
public Vec4 getFill() {
return fill;
}
/**
* @param fill the fill to set
*/
public void setFill(Vec4 fill) {
this.fill = Objects.requireNonNull(fill, "fill");
}
/**
* @return the border
*/
public Vec4 getBorder() {
return border;
}
/**
* @param border the border to set
*/
public void setBorder(Vec4 border) {
this.border = border;
}
@Override
protected void assembleSelf(RenderTarget target) {
if (border == null) {
target.fill(getX(), getY(), getWidth(), getHeight(), fill);
} else {
target.fill(getX(), getY(), getWidth(), getHeight(), border);
target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, fill);
}
} }
} }

View File

@ -1,208 +1,87 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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; package ru.windcorp.progressia.client.graphics.gui;
import java.util.function.Consumer;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import glm.mat._4.Mat4;
import glm.vec._2.i.Vec2i; import glm.vec._2.i.Vec2i;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typefaces;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutHorizontal;
import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
public class RadioButton extends BasicButton { public class RadioButton extends Interactable {
private RadioManager manager;
private boolean isSelected;
private class Tick extends Component { public RadioButton(String name, Label textLabel, Consumer<RadioButton> onSelect, RadioManager myManager)
{
super(name, textLabel);
setPreferredSize(textLabel.getPreferredSize().x+23,textLabel.getPreferredSize().y);
manager = myManager;
manager.addOption(this);
public Tick() { addListener((Class<KeyEvent>) KeyEvent.class, (InputListener<KeyEvent>) e -> {if ((e.isLeftMouseButton() && containsCursor()) || (e.getKey()==GLFW.GLFW_KEY_ENTER && isFocused()) )
super(RadioButton.this.getName() + ".Tick"); {
isClicked = e.isPress();
setPreferredSize(new Vec2i(Typefaces.getDefault().getLineHeight() * 3 / 2)); if (!isDisabled() && !isClicked)
} {
onSelect.accept(this);
private void cross(RenderTarget target, int x, int y, int size, Vec4 color) { manager.selectSelf(this);
target.fill(x + 4, y, size - 8, size, color);
target.fill(x + 2, y + 2, size - 4, size - 4, color);
target.fill(x, y + 4, size, size - 8, color);
}
@Override
protected void assembleSelf(RenderTarget target) {
int size = getPreferredSize().x;
int x = getX();
int y = getY() + (getHeight() - size) / 2;
// Border
Vec4 borderColor;
if (RadioButton.this.isPressed() || RadioButton.this.isHovered() || RadioButton.this.isFocused()) {
borderColor = Colors.BLUE;
} else {
borderColor = Colors.LIGHT_GRAY;
}
cross(target, x, y, size, borderColor);
// Inside area
if (RadioButton.this.isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (RadioButton.this.isHovered() && RadioButton.this.isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
}
cross(target, x + 2, y + 2, size - 4, backgroundColor);
}
// "Tick"
if (RadioButton.this.isChecked()) {
cross(target, x + 4, y + 4, size - 8, Colors.BLUE);
}
}
}
private boolean checked;
private RadioButtonGroup group = null;
public RadioButton(String name, String label, Font labelFont, boolean check) {
super(name, label, labelFont);
this.checked = check;
assert getChildren().size() == 1 : "RadioButton expects that BasicButton contains exactly one child";
Component basicChild = getChild(0);
Group group = new Group(getName() + ".LabelAndTick", new LayoutHorizontal(0, 10));
removeChild(basicChild);
setLayout(new LayoutAlign(0, 0.5f, 10));
group.setLayoutHint(basicChild.getLayoutHint());
group.addChild(new Tick());
group.addChild(basicChild);
addChild(group);
addListener(KeyEvent.class, e -> {
if (e.isRelease())
return false;
if (e.getKey() == GLFW.GLFW_KEY_LEFT || e.getKey() == GLFW.GLFW_KEY_UP) {
if (this.group != null) {
this.group.selectPrevious();
this.group.getSelected().takeFocus();
}
return true;
} else if (e.getKey() == GLFW.GLFW_KEY_RIGHT || e.getKey() == GLFW.GLFW_KEY_DOWN) {
if (this.group != null) {
this.group.selectNext();
this.group.getSelected().takeFocus();
} }
requestReassembly();
return true; return true;
} }
return false;});
return false;
});
addAction(b -> setChecked(true));
} }
public RadioButton(String name, String label, Font labelFont) { public boolean isSelected()
this(name, label, labelFont, false); {
return isSelected;
} }
public RadioButton(String name, String label, boolean check) { public void setSelected(boolean selected)
this(name, label, new Font(), check); {
isSelected = selected;
} }
public RadioButton(String name, String label) {
this(name, label, false);
}
/**
* @param group the group to set
*/
public RadioButton setGroup(RadioButtonGroup group) {
if (this.group != null) {
group.selectNext();
removeAction(group.listener);
group.buttons.remove(this);
group.getSelected(); // Clear reference if this was the only button
// in the group
}
this.group = group;
if (this.group != null) {
group.buttons.add(this);
addAction(group.listener);
}
setChecked(false);
return this;
}
/**
* @return the checked
*/
public boolean isChecked() {
return checked;
}
/**
* @param checked the checked to set
*/
public void setChecked(boolean checked) {
this.checked = checked;
if (group != null) {
group.listener.accept(this); // Failsafe for manual invocations of
// setChecked()
}
}
@Override
protected void assembleSelf(RenderTarget target) { protected void assembleSelf(RenderTarget target) {
// Change label font color if (isDisabled())
{
if (isPressed()) { target.fill(getX()+getWidth()-getHeight(), getY(), getHeight(), getHeight(), 0xFFE5E5E5);
getLabel().setFont(getLabel().getFont().withColor(Colors.BLUE)); }
} else { else if (isClicked() || isHovered() || isFocused())
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK)); {
target.fill(getX()+getWidth()-getHeight(), getY(), getHeight(), getHeight(), 0xFF37A2E6);
}
else
{
target.fill(getX()+getWidth()-getHeight(), getY(), getHeight(), getHeight(), 0xFFCBCBD0);
}
//Inside area
if (!isClicked() && isHovered() && !isDisabled())
{
target.fill(getX()+getWidth()-getHeight()+2, getY()+2, getHeight()-4, getHeight()-4, 0xFFC3E4F7);
}
else if (!isClicked() || isDisabled())
{
target.fill(getX()+getWidth()-getHeight()+2, getY()+2, getHeight()-4, getHeight()-4, Colors.WHITE);
}
if (isSelected())
{
if (!isDisabled())
{
target.fill(getX()+getWidth()-getHeight()+4, getY()+4, getHeight()-8, getHeight()-8, 0xFF37A2E6);
}
else
{
target.fill(getX()+getWidth()-getHeight()+4, getY()+4, getHeight()-8, getHeight()-8, 0xFFC3E4F7);
} }
} }
@Override target.pushTransform(new Mat4().identity().translate( getX(), getY(), 0));
protected void postAssembleSelf(RenderTarget target) { label.assembleSelf(target);
// Apply disable tint target.popTransform();
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
} }
}
} }

View File

@ -1,119 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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 java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
public class RadioButtonGroup {
private final Collection<Consumer<RadioButtonGroup>> actions = Collections.synchronizedCollection(new ArrayList<>());
final List<RadioButton> buttons = Collections.synchronizedList(new ArrayList<>());
private RadioButton selected = null;
Consumer<BasicButton> listener = b -> {
if (b instanceof RadioButton && ((RadioButton) b).isChecked() && buttons.contains(b)) {
select((RadioButton) b);
}
};
public RadioButtonGroup addAction(Consumer<RadioButtonGroup> action) {
this.actions.add(Objects.requireNonNull(action, "action"));
return this;
}
public boolean removeAction(Consumer<BasicButton> action) {
return this.actions.remove(action);
}
public List<RadioButton> getButtons() {
return Collections.unmodifiableList(buttons);
}
public synchronized RadioButton getSelected() {
if (!buttons.contains(selected)) {
selected = null;
}
return selected;
}
public synchronized void select(RadioButton button) {
if (button != null && !buttons.contains(button)) {
throw new IllegalArgumentException("Button " + button + " is not in the group");
}
getSelected(); // Clear if invalid
if (selected == button) {
return; // Terminate listener-setter recursion
}
if (selected != null) {
selected.setChecked(false);
}
selected = button;
if (selected != null) {
selected.setChecked(true);
}
actions.forEach(action -> action.accept(this));
}
public void selectNext() {
selectNeighbour(+1);
}
public void selectPrevious() {
selectNeighbour(-1);
}
private synchronized void selectNeighbour(int direction) {
if (getSelected() == null) {
if (buttons.isEmpty()) {
throw new IllegalStateException("Cannot select neighbour button: group empty");
}
select(buttons.get(0));
} else {
RadioButton button;
int index = buttons.indexOf(selected);
do {
index += direction;
if (index >= buttons.size()) {
index = 0;
} else if (index < 0) {
index = buttons.size() - 1;
}
button = buttons.get(index);
} while (button != getSelected() && !button.isEnabled());
select(button);
}
}
}

View File

@ -0,0 +1,37 @@
package ru.windcorp.progressia.client.graphics.gui;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class RadioManager {
private List<RadioButton> options;
private int selectedOption;
public RadioManager()
{
options = Collections.synchronizedList(new CopyOnWriteArrayList<>());
}
public void addOption(RadioButton option)
{
options.add(option);
}
public int getSelected()
{
return selectedOption;
}
public void selectSelf(RadioButton option)
{
if (!options.contains(option))
{
return;
}
options.get(selectedOption).setSelected(false);
selectedOption = options.indexOf(option);
option.takeFocus();
option.setSelected(true);
}
}

View File

@ -1,60 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.event;
import ru.windcorp.progressia.client.graphics.gui.BasicButton;
public class ButtonEvent extends ComponentEvent {
public static class Press extends ButtonEvent {
public Press(BasicButton button) {
super(button, true);
}
}
public static class Release extends ButtonEvent {
public Release(BasicButton button) {
super(button, false);
}
}
private final boolean isPress;
protected ButtonEvent(BasicButton button, boolean isPress) {
super(button);
this.isPress = isPress;
}
public static ButtonEvent create(BasicButton button, boolean isPress) {
if (isPress) {
return new Press(button);
} else {
return new Release(button);
}
}
public boolean isPress() {
return isPress;
}
public boolean isRelease() {
return !isPress;
}
}

View File

@ -1,11 +0,0 @@
package ru.windcorp.progressia.client.graphics.gui.event;
import ru.windcorp.progressia.client.graphics.gui.Component;
public class EnableEvent extends ComponentEvent {
public EnableEvent(Component component) {
super(component);
}
}

View File

@ -1,78 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.layout;
import static java.lang.Math.max;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Layout;
public class LayoutFill implements Layout {
private final int margin;
public LayoutFill(int margin) {
this.margin = margin;
}
public LayoutFill() {
this(0);
}
@Override
public void layout(Component c) {
c.getChildren().forEach(child -> {
int cWidth = c.getWidth() - 2 * margin;
int cHeight = c.getHeight() - 2 * margin;
child.setBounds(
c.getX() + margin,
c.getY() + margin,
cWidth,
cHeight
);
});
}
@Override
public Vec2i calculatePreferredSize(Component c) {
Vec2i result = new Vec2i(0, 0);
c.getChildren().stream()
.map(child -> child.getPreferredSize())
.forEach(size -> {
result.x = max(size.x, result.x);
result.y = max(size.y, result.y);
});
result.x += 2 * margin;
result.y += 2 * margin;
return result;
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + margin + ")";
}
}

View File

@ -98,26 +98,15 @@ public class LayoutGrid implements Layout {
if (!isSummed) if (!isSummed)
throw new IllegalStateException("Not summed yet"); throw new IllegalStateException("Not summed yet");
int width, height;
if (column == columns.length - 1) {
width = parent.getWidth() - margin - columns[column];
} else {
width = columns[column + 1] - columns[column] - gap;
}
if (row == rows.length - 1) {
height = parent.getHeight() - margin - rows[row];
} else {
height = rows[row + 1] - rows[row] - gap;
}
child.setBounds( child.setBounds(
parent.getX() + columns[column], parent.getX() + columns[column],
parent.getY() + parent.getHeight() - (rows[row] + height), parent.getY() + rows[row],
width, (column != (columns.length - 1) ? (columns[column + 1] - columns[column] - gap)
height : (parent.getWidth() - margin - columns[column])),
(row != (rows.length - 1) ? (rows[row + 1] - rows[row] - gap)
: (parent.getHeight() - margin - rows[row]))
); );
} }
} }
@ -143,9 +132,10 @@ public class LayoutGrid implements Layout {
GridDimensions grid = calculateGrid(c); GridDimensions grid = calculateGrid(c);
grid.sum(); grid.sum();
int[] coords;
for (Component child : c.getChildren()) { for (Component child : c.getChildren()) {
Vec2i coords = (Vec2i) child.getLayoutHint(); coords = (int[]) child.getLayoutHint();
grid.setBounds(coords.x, coords.y, child, c); grid.setBounds(coords[0], coords[1], child, c);
} }
} }
} }
@ -159,10 +149,11 @@ public class LayoutGrid implements Layout {
private GridDimensions calculateGrid(Component parent) { private GridDimensions calculateGrid(Component parent) {
GridDimensions result = new GridDimensions(); GridDimensions result = new GridDimensions();
int[] coords;
for (Component child : parent.getChildren()) { for (Component child : parent.getChildren()) {
Vec2i coords = (Vec2i) child.getLayoutHint(); coords = (int[]) child.getLayoutHint();
result.add(coords.x, coords.y, child.getPreferredSize()); result.add(coords[0], coords[1], child.getPreferredSize());
} }
return result; return result;

View File

@ -1,117 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.menu;
import org.lwjgl.glfw.GLFW;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.Layout;
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.LayoutFill;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
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.localization.MutableString;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
public class MenuLayer extends GUILayer {
private final Component content;
private final Component background;
private final Runnable closeAction = () -> {
GUI.removeLayer(this);
};
public MenuLayer(String name, Component content) {
super(name, new LayoutFill(0));
setCursorPolicy(CursorPolicy.REQUIRE);
this.background = new Panel(name + ".Background", new LayoutAlign(10), Colors.toVector(0x66000000), null);
this.content = content;
background.addChild(content);
getRoot().addChild(background);
}
public MenuLayer(String name, Layout contentLayout) {
this(name, new Panel(name + ".Content", contentLayout));
}
public MenuLayer(String name) {
this(name, new LayoutVertical(20, 10));
}
public Component getContent() {
return content;
}
public Component getBackground() {
return background;
}
protected void addTitle() {
String translationKey = "Layer" + getName() + ".Title";
MutableString titleText = new MutableStringLocalized(translationKey);
Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(0.5f);
Label label = new Label(getName() + ".Title", titleFont, titleText);
getContent().addChild(label);
Panel panel = new Panel(getName() + ".Title.Underscore", null, Colors.BLUE, null);
panel.setLayout(new LayoutFill() {
@Override
public Vec2i calculatePreferredSize(Component c) {
return new Vec2i(label.getPreferredSize().x + 40, 4);
}
});
getContent().addChild(panel);
}
protected Runnable getCloseAction() {
return closeAction;
}
@Override
protected void handleInput(Input input) {
if (!input.isConsumed()) {
InputEvent event = input.getEvent();
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent) event;
if (keyEvent.isPress() && keyEvent.getKey() == GLFW.GLFW_KEY_ESCAPE) {
getCloseAction().run();
}
}
}
super.handleInput(input);
input.consume();
}
}

View File

@ -18,23 +18,23 @@
package ru.windcorp.progressia.client.graphics.model; package ru.windcorp.progressia.client.graphics.model;
import static ru.windcorp.progressia.common.world.rels.AbsFace.*; import static ru.windcorp.progressia.common.world.block.BlockFace.*;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
class BlockFaceVectors { class BlockFaceVectors {
private static BlockFaceVectors createInner(BlockFaceVectors outer) { private static BlockFaceVectors createInner(BlockFaceVectors outer) {
ImmutableMap.Builder<AbsFace, Vec3> originBuilder = ImmutableMap.builder(); ImmutableMap.Builder<BlockFace, Vec3> originBuilder = ImmutableMap.builder();
ImmutableMap.Builder<AbsFace, Vec3> widthBuilder = ImmutableMap.builder(); ImmutableMap.Builder<BlockFace, Vec3> widthBuilder = ImmutableMap.builder();
ImmutableMap.Builder<AbsFace, Vec3> heightBuilder = ImmutableMap.builder(); ImmutableMap.Builder<BlockFace, Vec3> heightBuilder = ImmutableMap.builder();
for (AbsFace face : getFaces()) { for (BlockFace face : getFaces()) {
Vec3 width = outer.getWidth(face); Vec3 width = outer.getWidth(face);
Vec3 height = outer.getHeight(face); Vec3 height = outer.getHeight(face);
@ -59,36 +59,36 @@ class BlockFaceVectors {
static { static {
OUTER = new BlockFaceVectors( OUTER = new BlockFaceVectors(
ImmutableMap.<AbsFace, Vec3>builder() ImmutableMap.<BlockFace, Vec3>builder()
.put(POS_Z, new Vec3(-0.5f, +0.5f, +0.5f)) .put(TOP, new Vec3(-0.5f, +0.5f, +0.5f))
.put(NEG_Z, new Vec3(-0.5f, -0.5f, -0.5f)) .put(BOTTOM, new Vec3(-0.5f, -0.5f, -0.5f))
.put(POS_X, new Vec3(+0.5f, -0.5f, -0.5f)) .put(NORTH, new Vec3(+0.5f, -0.5f, -0.5f))
.put(NEG_X, new Vec3(-0.5f, +0.5f, -0.5f)) .put(SOUTH, new Vec3(-0.5f, +0.5f, -0.5f))
.put(POS_Y, new Vec3(+0.5f, +0.5f, -0.5f)) .put(WEST, new Vec3(+0.5f, +0.5f, -0.5f))
.put(NEG_Y, new Vec3(-0.5f, -0.5f, -0.5f)) .put(EAST, new Vec3(-0.5f, -0.5f, -0.5f))
.build(), .build(),
ImmutableMap.<AbsFace, Vec3>builder() ImmutableMap.<BlockFace, Vec3>builder()
.put(POS_Z, new Vec3(0, -1, 0)) .put(TOP, new Vec3(0, -1, 0))
.put(NEG_Z, new Vec3(0, +1, 0)) .put(BOTTOM, new Vec3(0, +1, 0))
.put(POS_X, new Vec3(0, +1, 0)) .put(NORTH, new Vec3(0, +1, 0))
.put(NEG_X, new Vec3(0, -1, 0)) .put(SOUTH, new Vec3(0, -1, 0))
.put(POS_Y, new Vec3(-1, 0, 0)) .put(WEST, new Vec3(-1, 0, 0))
.put(NEG_Y, new Vec3(+1, 0, 0)) .put(EAST, new Vec3(+1, 0, 0))
.build(), .build(),
ImmutableMap.<AbsFace, Vec3>builder() ImmutableMap.<BlockFace, Vec3>builder()
.put(POS_Z, new Vec3(+1, 0, 0)) .put(TOP, new Vec3(+1, 0, 0))
.put(NEG_Z, new Vec3(+1, 0, 0)) .put(BOTTOM, new Vec3(+1, 0, 0))
.put(POS_X, new Vec3(0, 0, +1)) .put(NORTH, new Vec3(0, 0, +1))
.put(NEG_X, new Vec3(0, 0, +1)) .put(SOUTH, new Vec3(0, 0, +1))
.put(POS_Y, new Vec3(0, 0, +1)) .put(WEST, new Vec3(0, 0, +1))
.put(NEG_Y, new Vec3(0, 0, +1)) .put(EAST, new Vec3(0, 0, +1))
.build() .build()
); );
@ -100,29 +100,29 @@ class BlockFaceVectors {
return inner ? INNER : OUTER; return inner ? INNER : OUTER;
} }
private final ImmutableMap<AbsFace, Vec3> origins; private final ImmutableMap<BlockFace, Vec3> origins;
private final ImmutableMap<AbsFace, Vec3> widths; private final ImmutableMap<BlockFace, Vec3> widths;
private final ImmutableMap<AbsFace, Vec3> heights; private final ImmutableMap<BlockFace, Vec3> heights;
public BlockFaceVectors( public BlockFaceVectors(
ImmutableMap<AbsFace, Vec3> origins, ImmutableMap<BlockFace, Vec3> origins,
ImmutableMap<AbsFace, Vec3> widths, ImmutableMap<BlockFace, Vec3> widths,
ImmutableMap<AbsFace, Vec3> heights ImmutableMap<BlockFace, Vec3> heights
) { ) {
this.origins = origins; this.origins = origins;
this.widths = widths; this.widths = widths;
this.heights = heights; this.heights = heights;
} }
public Vec3 getOrigin(AbsFace face) { public Vec3 getOrigin(BlockFace face) {
return origins.get(face); return origins.get(face);
} }
public Vec3 getWidth(AbsFace face) { public Vec3 getWidth(BlockFace face) {
return widths.get(face); return widths.get(face);
} }
public Vec3 getHeight(AbsFace face) { public Vec3 getHeight(BlockFace face) {
return heights.get(face); return heights.get(face);
} }
} }

View File

@ -24,7 +24,7 @@ import java.util.Objects;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
public class ShapePart implements Comparable<ShapePart> { public class Face implements Comparable<Face> {
private static final ShortBuffer GENERATE_SUCCESSIVE_LATER = null; private static final ShortBuffer GENERATE_SUCCESSIVE_LATER = null;
@ -40,7 +40,7 @@ public class ShapePart implements Comparable<ShapePart> {
private ShortBuffer userIndices; private ShortBuffer userIndices;
private boolean userIndicesUpdated = true; private boolean userIndicesUpdated = true;
public ShapePart( public Face(
Texture texture, Texture texture,
ByteBuffer vertices, ByteBuffer vertices,
ShortBuffer indices ShortBuffer indices
@ -50,7 +50,7 @@ public class ShapePart implements Comparable<ShapePart> {
setIndices(indices); setIndices(indices);
} }
public ShapePart( public Face(
Texture texture, Texture texture,
ByteBuffer vertices ByteBuffer vertices
) { ) {
@ -155,7 +155,7 @@ public class ShapePart implements Comparable<ShapePart> {
return vertices; return vertices;
} }
public ShapePart setVertices(ByteBuffer vertices) { public Face setVertices(ByteBuffer vertices) {
this.vertices = Objects.requireNonNull(vertices, "vertices"); this.vertices = Objects.requireNonNull(vertices, "vertices");
markForVertexUpdate(); markForVertexUpdate();
return this; return this;
@ -202,7 +202,7 @@ public class ShapePart implements Comparable<ShapePart> {
return userIndices.remaining(); return userIndices.remaining();
} }
public ShapePart setIndices(ShortBuffer indices) { public Face setIndices(ShortBuffer indices) {
if (indices == null) { if (indices == null) {
indices = GENERATE_SUCCESSIVE_LATER; indices = GENERATE_SUCCESSIVE_LATER;
} }
@ -245,7 +245,7 @@ public class ShapePart implements Comparable<ShapePart> {
} }
@Override @Override
public int compareTo(ShapePart o) { public int compareTo(Face o) {
return Integer.compare(getSortingIndex(), o.getSortingIndex()); return Integer.compare(getSortingIndex(), o.getSortingIndex());
} }

View File

@ -21,13 +21,13 @@ package ru.windcorp.progressia.client.graphics.model;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive; import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
public class ShapePartGroup { public class FaceGroup {
private final TexturePrimitive texture; private final TexturePrimitive texture;
private final int indexCount; private final int indexCount;
private final int byteOffsetOfIndices; private final int byteOffsetOfIndices;
ShapePartGroup(ShapePart[] faces, int start, int end) { FaceGroup(Face[] faces, int start, int end) {
Texture t = faces[start].getTexture(); Texture t = faces[start].getTexture();
this.texture = t == null ? null : t.getSprite().getPrimitive(); this.texture = t == null ? null : t.getSprite().getPrimitive();
@ -36,7 +36,7 @@ public class ShapePartGroup {
int indexCount = 0; int indexCount = 0;
for (int i = start; i < end; ++i) { for (int i = start; i < end; ++i) {
ShapePart face = faces[i]; Face face = faces[i];
assert this.texture == null assert this.texture == null
? (face.getTexture() == null) ? (face.getTexture() == null)

View File

@ -25,14 +25,14 @@ import glm.vec._3.Vec3;
import glm.vec._4.Vec4; import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram.VertexBuilder; import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram.VertexBuilder;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class ShapeParts { public class Faces {
private ShapeParts() { private Faces() {
} }
public static ShapePart createRectangle( public static Face createRectangle(
ShapeRenderProgram program, ShapeRenderProgram program,
Texture texture, Texture texture,
Vec4 colorMultiplier, Vec4 colorMultiplier,
@ -82,19 +82,19 @@ public class ShapeParts {
} }
); );
return new ShapePart( return new Face(
texture, texture,
builder.assemble(), builder.assemble(),
buffer buffer
); );
} }
public static ShapePart createBlockFace( public static Face createBlockFace(
ShapeRenderProgram program, ShapeRenderProgram program,
Texture texture, Texture texture,
Vec4 colorMultiplier, Vec4 colorMultiplier,
Vec3 blockCenter, Vec3 blockCenter,
AbsFace face, BlockFace face,
boolean inner boolean inner
) { ) {
BlockFaceVectors vectors = BlockFaceVectors.get(inner); BlockFaceVectors vectors = BlockFaceVectors.get(inner);

View File

@ -30,10 +30,10 @@ import ru.windcorp.progressia.client.graphics.backend.VertexBufferObject;
public class Shape implements Renderable { public class Shape implements Renderable {
private final ShapeRenderProgram program; private final ShapeRenderProgram program;
private final ShapePart[] parts; private final Face[] faces;
private final Usage usage; private final Usage usage;
private ShapePartGroup[] groups; private FaceGroup[] groups;
private ByteBuffer vertices; private ByteBuffer vertices;
private ShortBuffer indices; private ShortBuffer indices;
@ -45,33 +45,33 @@ public class Shape implements Renderable {
private VertexBufferObject verticesVbo; private VertexBufferObject verticesVbo;
private VertexBufferObject indicesVbo; private VertexBufferObject indicesVbo;
public Shape(Usage usage, ShapeRenderProgram program, ShapePart... parts) { public Shape(Usage usage, ShapeRenderProgram program, Face... faces) {
this.program = program; this.program = program;
this.parts = parts; this.faces = faces;
this.usage = usage; this.usage = usage;
configureParts(); configureFaces();
program.preprocess(this); program.preprocess(this);
assembleBuffers(); assembleBuffers();
} }
private void configureParts() { private void configureFaces() {
for (ShapePart part : parts) { for (Face face : faces) {
part.setShape(this); face.setShape(this);
} }
} }
private void assembleBuffers() { private void assembleBuffers() {
// TODO optimize: only update faces that requested it // TODO optimize: only update faces that requested it
sortParts(); sortFaces();
resizeBuffers(); resizeBuffers();
for (ShapePart part : parts) { for (Face face : faces) {
assembleVertices(part); assembleVertices(face);
assembleIndices(part); assembleIndices(face);
part.resetUpdateFlags(); face.resetUpdateFlags();
} }
this.vertices.flip(); this.vertices.flip();
@ -85,110 +85,110 @@ public class Shape implements Renderable {
private void resizeBuffers() { private void resizeBuffers() {
int verticesRequired = 0, indicesRequired = 0; int verticesRequired = 0, indicesRequired = 0;
for (ShapePart part : parts) { for (Face face : faces) {
verticesRequired += part.getVertices().remaining(); verticesRequired += face.getVertices().remaining();
indicesRequired += part.getIndices().remaining(); indicesRequired += face.getIndices().remaining();
} }
if (vertices == null || vertices.capacity() < verticesRequired) { if (this.vertices == null || vertices.capacity() < verticesRequired) {
this.vertices = BufferUtils.createByteBuffer(verticesRequired); this.vertices = BufferUtils.createByteBuffer(verticesRequired);
} else { } else {
vertices.position(0).limit(verticesRequired); this.vertices.position(0).limit(verticesRequired);
} }
if (indices == null || indices.capacity() < indicesRequired) { if (this.indices == null || this.indices.capacity() < indicesRequired) {
this.indices = BufferUtils.createShortBuffer(indicesRequired); this.indices = BufferUtils.createShortBuffer(indicesRequired);
} else { } else {
indices.position(0).limit(indicesRequired); this.indices.position(0).limit(indicesRequired);
} }
} }
private void assembleVertices(ShapePart part) { private void assembleVertices(Face face) {
part.locationOfVertices = this.vertices.position(); face.locationOfVertices = this.vertices.position();
insertVertices(part); insertVertices(face);
linkVerticesWith(part); linkVerticesWith(face);
} }
private void insertVertices(ShapePart part) { private void insertVertices(Face face) {
ByteBuffer partVertices = part.getVertices(); ByteBuffer faceVertices = face.getVertices();
partVertices.mark(); faceVertices.mark();
this.vertices.put(partVertices); this.vertices.put(faceVertices);
partVertices.reset(); faceVertices.reset();
} }
private void linkVerticesWith(ShapePart part) { private void linkVerticesWith(Face face) {
int limit = vertices.limit(); int limit = vertices.limit();
int position = vertices.position(); int position = vertices.position();
vertices.limit(position).position(part.getLocationOfVertices()); vertices.limit(position).position(face.getLocationOfVertices());
part.vertices = vertices.slice(); face.vertices = vertices.slice();
vertices.position(position).limit(limit); vertices.position(position).limit(limit);
} }
private void assembleIndices(ShapePart part) { private void assembleIndices(Face face) {
short vertexOffset = (short) (part.getLocationOfVertices() / program.getBytesPerVertex()); short vertexOffset = (short) (face.getLocationOfVertices() / program.getBytesPerVertex());
part.locationOfIndices = indices.position(); face.locationOfIndices = indices.position();
ShortBuffer partIndices = part.getIndices(); ShortBuffer faceIndices = face.getIndices();
if (partIndices == null) { if (faceIndices == null) {
for (int i = 0; i < part.getVertexCount(); ++i) { for (int i = 0; i < face.getVertexCount(); ++i) {
this.indices.put((short) (vertexOffset + i)); this.indices.put((short) (vertexOffset + i));
} }
} else { } else {
for (int i = partIndices.position(); i < partIndices.limit(); ++i) { for (int i = faceIndices.position(); i < faceIndices.limit(); ++i) {
short partIndex = partIndices.get(i); short faceIndex = faceIndices.get(i);
partIndex += vertexOffset; faceIndex += vertexOffset;
this.indices.put(partIndex); this.indices.put(faceIndex);
} }
} }
} }
private void sortParts() { private void sortFaces() {
Arrays.sort(parts); Arrays.sort(faces);
} }
private void assembleGroups() { private void assembleGroups() {
int unique = countUniqueParts(); int unique = countUniqueFaces();
this.groups = new ShapePartGroup[unique]; this.groups = new FaceGroup[unique];
if (parts.length == 0) if (faces.length == 0)
return; return;
int previousHandle = parts[0].getSortingIndex(); int previousHandle = faces[0].getSortingIndex();
int start = 0; int start = 0;
int groupIndex = 0; int groupIndex = 0;
for (int i = 1; i < parts.length; ++i) { for (int i = 1; i < faces.length; ++i) {
if (previousHandle != parts[i].getSortingIndex()) { if (previousHandle != faces[i].getSortingIndex()) {
groups[groupIndex] = new ShapePartGroup(parts, start, i); groups[groupIndex] = new FaceGroup(faces, start, i);
start = i; start = i;
groupIndex++; groupIndex++;
previousHandle = parts[i].getSortingIndex(); previousHandle = faces[i].getSortingIndex();
} }
} }
assert groupIndex == groups.length - 1; assert groupIndex == groups.length - 1;
groups[groupIndex] = new ShapePartGroup(parts, start, parts.length); groups[groupIndex] = new FaceGroup(faces, start, faces.length);
} }
private int countUniqueParts() { private int countUniqueFaces() {
if (parts.length == 0) if (faces.length == 0)
return 0; return 0;
int result = 1; int result = 1;
int previousHandle = parts[0].getSortingIndex(); int previousHandle = faces[0].getSortingIndex();
for (int i = 1; i < parts.length; ++i) { for (int i = 1; i < faces.length; ++i) {
if (previousHandle != parts[i].getSortingIndex()) { if (previousHandle != faces[i].getSortingIndex()) {
result++; result++;
previousHandle = parts[i].getSortingIndex(); previousHandle = faces[i].getSortingIndex();
} }
} }
@ -238,11 +238,11 @@ public class Shape implements Renderable {
return program; return program;
} }
public ShapePart[] getParts() { public Face[] getFaces() {
return parts; return faces;
} }
public ShapePartGroup[] getGroups() { public FaceGroup[] getGroups() {
return groups; return groups;
} }

View File

@ -116,7 +116,7 @@ public class ShapeRenderProgram extends Program {
try { try {
enableAttributes(); enableAttributes();
for (ShapePartGroup group : shape.getGroups()) { for (FaceGroup group : shape.getGroups()) {
renderFaceGroup(group); renderFaceGroup(group);
} }
} finally { } finally {
@ -182,7 +182,7 @@ public class ShapeRenderProgram extends Program {
indices.bind(BindTarget.ELEMENT_ARRAY); indices.bind(BindTarget.ELEMENT_ARRAY);
} }
protected void renderFaceGroup(ShapePartGroup group) { protected void renderFaceGroup(FaceGroup group) {
TexturePrimitive texture = group.getTexture(); TexturePrimitive texture = group.getTexture();
if (texture != null) { if (texture != null) {
@ -206,12 +206,12 @@ public class ShapeRenderProgram extends Program {
} }
public void preprocess(Shape shape) { public void preprocess(Shape shape) {
for (ShapePart face : shape.getParts()) { for (Face face : shape.getFaces()) {
applySprites(face); applySprites(face);
} }
} }
private void applySprites(ShapePart face) { private void applySprites(Face face) {
if (face.getTexture() == null) if (face.getTexture() == null)
return; return;

View File

@ -20,13 +20,11 @@ package ru.windcorp.progressia.client.graphics.model;
import java.util.Map; import java.util.Map;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._4.Vec4; import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.backend.Usage; import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.rels.AbsFace;
public class Shapes { public class Shapes {
@ -52,7 +50,7 @@ public class Shapes {
boolean flip boolean flip
) { ) {
ShapePart top = ShapeParts.createRectangle( Face top = Faces.createRectangle(
program, program,
topTexture, topTexture,
colorMultiplier, colorMultiplier,
@ -62,7 +60,7 @@ public class Shapes {
flip flip
); );
ShapePart bottom = ShapeParts.createRectangle( Face bottom = Faces.createRectangle(
program, program,
bottomTexture, bottomTexture,
colorMultiplier, colorMultiplier,
@ -72,7 +70,7 @@ public class Shapes {
flip flip
); );
ShapePart north = ShapeParts.createRectangle( Face north = Faces.createRectangle(
program, program,
northTexture, northTexture,
colorMultiplier, colorMultiplier,
@ -82,7 +80,7 @@ public class Shapes {
flip flip
); );
ShapePart south = ShapeParts.createRectangle( Face south = Faces.createRectangle(
program, program,
southTexture, southTexture,
colorMultiplier, colorMultiplier,
@ -92,7 +90,7 @@ public class Shapes {
flip flip
); );
ShapePart east = ShapeParts.createRectangle( Face east = Faces.createRectangle(
program, program,
eastTexture, eastTexture,
colorMultiplier, colorMultiplier,
@ -102,7 +100,7 @@ public class Shapes {
flip flip
); );
ShapePart west = ShapeParts.createRectangle( Face west = Faces.createRectangle(
program, program,
westTexture, westTexture,
colorMultiplier, colorMultiplier,
@ -167,16 +165,16 @@ public class Shapes {
public PppBuilder( public PppBuilder(
ShapeRenderProgram program, ShapeRenderProgram program,
Map<AbsFace, Texture> textureMap Map<BlockFace, Texture> textureMap
) { ) {
this( this(
program, program,
textureMap.get(AbsFace.POS_Z), textureMap.get(BlockFace.TOP),
textureMap.get(AbsFace.NEG_Z), textureMap.get(BlockFace.BOTTOM),
textureMap.get(AbsFace.POS_X), textureMap.get(BlockFace.NORTH),
textureMap.get(AbsFace.NEG_X), textureMap.get(BlockFace.SOUTH),
textureMap.get(AbsFace.NEG_Y), textureMap.get(BlockFace.EAST),
textureMap.get(AbsFace.POS_Y) textureMap.get(BlockFace.WEST)
); );
} }
@ -262,34 +260,6 @@ public class Shapes {
return this.setSize(size, size, size); return this.setSize(size, size, size);
} }
public PppBuilder centerAt(float x, float y, float z) {
origin.set(x, y, z);
origin.mul(2);
origin.sub(width);
origin.sub(height);
origin.sub(depth);
origin.div(2);
return this;
}
public PppBuilder apply(Mat4 transform) {
VectorUtil.applyMat4(origin, transform);
VectorUtil.rotateOnly(width, transform);
VectorUtil.rotateOnly(height, transform);
VectorUtil.rotateOnly(depth, transform);
return this;
}
public PppBuilder scale(float factor) {
origin.mul(factor);
width.mul(factor);
height.mul(factor);
depth.mul(factor);
return this;
}
public PppBuilder flip() { public PppBuilder flip() {
this.flip = true; this.flip = true;
return this; return this;

View File

@ -21,7 +21,7 @@ package ru.windcorp.progressia.client.graphics.texture;
import java.util.Map; import java.util.Map;
import glm.vec._2.Vec2; import glm.vec._2.Vec2;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class ComplexTexture { public class ComplexTexture {
@ -54,14 +54,14 @@ public class ComplexTexture {
); );
} }
public Map<AbsFace, Texture> getCuboidTextures( public Map<BlockFace, Texture> getCuboidTextures(
int x, int x,
int y, int y,
int width, int width,
int height, int height,
int depth int depth
) { ) {
return AbsFace.mapToFaces( return BlockFace.mapToFaces(
get( get(
x + depth + width, x + depth + width,
y + height + depth, y + height + depth,
@ -86,7 +86,7 @@ public class ComplexTexture {
); );
} }
public Map<AbsFace, Texture> getCuboidTextures( public Map<BlockFace, Texture> getCuboidTextures(
int x, int x,
int y, int y,
int size int size

View File

@ -29,9 +29,6 @@ import glm.mat._4.Mat4;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.world.Camera.Anchor.Mode; import ru.windcorp.progressia.client.graphics.world.Camera.Anchor.Mode;
import ru.windcorp.progressia.client.world.entity.NPedModel;
import ru.windcorp.progressia.common.util.Matrices;
import ru.windcorp.progressia.common.util.Vectors;
public class Camera { public class Camera {
@ -63,13 +60,13 @@ public class Camera {
} }
} }
Vec3 getCameraPosition(Vec3 output); void getCameraPosition(Vec3 output);
Vec3 getCameraVelocity(Vec3 output); void getCameraVelocity(Vec3 output);
Vec3 getLookingAt(Vec3 output); float getCameraYaw();
Vec3 getUpVector(Vec3 output); float getCameraPitch();
Collection<Mode> getCameraModes(); Collection<Mode> getCameraModes();
@ -87,11 +84,14 @@ public class Camera {
*/ */
private final Vec3 lastAnchorPosition = new Vec3(); private final Vec3 lastAnchorPosition = new Vec3();
private final Vec3 lastAnchorLookingAt = new Vec3(); private float lastAnchorYaw;
private final Vec3 lastAnchorUpVector = new Vec3(); private float lastAnchorPitch;
private final Mat4 lastCameraMatrix = new Mat4(); private final Mat4 lastCameraMatrix = new Mat4();
private final Vec3 lastAnchorLookingAt = new Vec3();
private final Vec3 lastAnchorUp = new Vec3();
{ {
invalidateCache(); invalidateCache();
} }
@ -108,9 +108,6 @@ public class Camera {
*/ */
public void apply(WorldRenderHelper helper) { public void apply(WorldRenderHelper helper) {
if (NPedModel.flag) {
// System.out.println("Camera.apply()");
}
applyPerspective(helper); applyPerspective(helper);
rotateCoordinateSystem(helper); rotateCoordinateSystem(helper);
@ -152,34 +149,26 @@ public class Camera {
} }
private void applyDirection(WorldRenderHelper helper) { private void applyDirection(WorldRenderHelper helper) {
anchor.getLookingAt(lastAnchorLookingAt); float pitch = anchor.getCameraPitch();
anchor.getUpVector(lastAnchorUpVector); float yaw = anchor.getCameraYaw();
lookAt(helper.pushViewTransform()); helper.pushViewTransform()
} .rotateY(-pitch)
.rotateZ(-yaw);
private void lookAt(Mat4 result) { this.lastAnchorYaw = yaw;
Vec3 f = this.lastAnchorLookingAt; this.lastAnchorPitch = pitch;
Vec3 s = Vectors.grab3();
Vec3 u = Vectors.grab3();
f.cross(this.lastAnchorUpVector, s); this.lastAnchorLookingAt.set(
s.normalize(); cos(pitch) * cos(yaw),
cos(pitch) * sin(yaw),
s.cross(f, u); sin(pitch)
);
Mat4 workspace = Matrices.grab4(); this.lastAnchorUp.set(
workspace.set( cos(pitch + PI_F / 2) * cos(yaw),
+f.x, -s.x, +u.x, 0, cos(pitch + PI_F / 2) * sin(yaw),
+f.y, -s.y, +u.y, 0, sin(pitch + PI_F / 2)
+f.z, -s.z, +u.z, 0,
0, 0, 0, 1
); );
result.mul(workspace);
Matrices.release(workspace);
Vectors.release(s);
Vectors.release(u);
} }
private void applyPosition(WorldRenderHelper helper) { private void applyPosition(WorldRenderHelper helper) {
@ -258,6 +247,8 @@ public class Camera {
private void invalidateCache() { private void invalidateCache() {
this.lastAnchorPosition.set(Float.NaN); this.lastAnchorPosition.set(Float.NaN);
this.lastAnchorYaw = Float.NaN;
this.lastAnchorPitch = Float.NaN;
this.lastCameraMatrix.set( this.lastCameraMatrix.set(
Float.NaN, Float.NaN,
@ -279,7 +270,7 @@ public class Camera {
); );
this.lastAnchorLookingAt.set(Float.NaN); this.lastAnchorLookingAt.set(Float.NaN);
this.lastAnchorUpVector.set(Float.NaN); this.lastAnchorUp.set(Float.NaN);
} }
public Anchor.Mode getMode() { public Anchor.Mode getMode() {
@ -298,6 +289,14 @@ public class Camera {
return currentModeIndex; return currentModeIndex;
} }
public float getLastAnchorYaw() {
return lastAnchorYaw;
}
public float getLastAnchorPitch() {
return lastAnchorPitch;
}
public Vec3 getLastAnchorPosition() { public Vec3 getLastAnchorPosition() {
return lastAnchorPosition; return lastAnchorPosition;
} }
@ -311,7 +310,7 @@ public class Camera {
} }
public Vec3 getLastAnchorUp() { public Vec3 getLastAnchorUp() {
return lastAnchorUpVector; return lastAnchorUp;
} }
} }

View File

@ -59,32 +59,24 @@ public class EntityAnchor implements Anchor {
} }
@Override @Override
public Vec3 getCameraPosition(Vec3 output) { public void getCameraPosition(Vec3 output) {
if (output == null) output = new Vec3();
model.getViewPoint(output); model.getViewPoint(output);
output.add(model.getPosition()); output.add(entity.getPosition());
return output;
} }
@Override @Override
public Vec3 getCameraVelocity(Vec3 output) { public void getCameraVelocity(Vec3 output) {
if (output == null) output = new Vec3();
output.set(entity.getVelocity()); output.set(entity.getVelocity());
return output;
} }
@Override @Override
public Vec3 getLookingAt(Vec3 output) { public float getCameraYaw() {
if (output == null) output = new Vec3(); return entity.getYaw();
model.getLookingAt(output);
return output;
} }
@Override @Override
public Vec3 getUpVector(Vec3 output) { public float getCameraPitch() {
if (output == null) output = new Vec3(); return entity.getPitch();
model.getUpVector(output);
return output;
} }
@Override @Override

View File

@ -41,8 +41,6 @@ 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.colliders.Collider; import ru.windcorp.progressia.common.collision.colliders.Collider;
import ru.windcorp.progressia.common.util.FloatMathUtil; import ru.windcorp.progressia.common.util.FloatMathUtil;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.GravityModel;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.test.CollisionModelRenderer; import ru.windcorp.progressia.test.CollisionModelRenderer;
import ru.windcorp.progressia.test.TestPlayerControls; import ru.windcorp.progressia.test.TestPlayerControls;
@ -59,8 +57,6 @@ public class LayerWorld extends Layer {
super("World"); super("World");
this.client = client; this.client = client;
this.inputBasedControls = new InputBasedControls(client); this.inputBasedControls = new InputBasedControls(client);
setCursorPolicy(CursorPolicy.FORBID);
} }
@Override @Override
@ -76,8 +72,6 @@ public class LayerWorld extends Layer {
@Override @Override
protected void doRender() { protected void doRender() {
client.getComms().processPackets();
Camera camera = client.getCamera(); Camera camera = client.getCamera();
if (camera.hasAnchor()) { if (camera.hasAnchor()) {
renderWorld(); renderWorld();
@ -203,25 +197,16 @@ public class LayerWorld extends Layer {
entity.getVelocity().mul((float) Math.exp(-FRICTION_COEFF / entity.getCollisionMass() * tickLength)); entity.getVelocity().mul((float) Math.exp(-FRICTION_COEFF / entity.getCollisionMass() * tickLength));
} }
private static final float MC_g = Units.get("32 m/s^2");
private static final float IRL_g = Units.get("9.8 m/s^2");
private void tmp_applyGravity(EntityData entity, float tickLength) { private void tmp_applyGravity(EntityData entity, float tickLength) {
GravityModel gm = ClientState.getInstance().getWorld().getData().getGravityModel();
Vec3 upVector = Vectors.grab3();
gm.getUp(entity.getPosition(), upVector);
entity.changeUpVector(upVector);
Vectors.release(upVector);
if (ClientState.getInstance().getLocalPlayer().getEntity() == entity && tmp_testControls.isFlying()) { if (ClientState.getInstance().getLocalPlayer().getEntity() == entity && tmp_testControls.isFlying()) {
return; return;
} }
Vec3 gravitationalAcceleration = Vectors.grab3(); final float gravitationalAcceleration = tmp_testControls.useMinecraftGravity() ? MC_g : IRL_g;
gm.getGravity(entity.getPosition(), gravitationalAcceleration); entity.getVelocity().add(0, 0, -gravitationalAcceleration * tickLength);
gravitationalAcceleration.mul(tickLength);
entity.getVelocity().add(gravitationalAcceleration);
Vectors.release(gravitationalAcceleration);
} }
@Override @Override

View File

@ -23,13 +23,13 @@ import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.client.world.WorldRender;
import ru.windcorp.progressia.common.world.BlockRay; import ru.windcorp.progressia.common.world.BlockRay;
import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.rels.AbsFace;
public class Selection { public class Selection {
private final Vec3i block = new Vec3i(); private final Vec3i block = new Vec3i();
private AbsFace surface = null; private BlockFace surface = null;
private final Vec2 pointOnSurface = new Vec2(0.5f, 0.5f); private final Vec2 pointOnSurface = new Vec2(0.5f, 0.5f);
private final Vec3 point = new Vec3(); private final Vec3 point = new Vec3();
@ -38,9 +38,10 @@ public class Selection {
private BlockRay ray = new BlockRay(); private BlockRay ray = new BlockRay();
public void update(WorldRender world, EntityData player) { public void update(WorldRender world, EntityData player) {
Vec3 direction = new Vec3();
Vec3 start = new Vec3(); Vec3 start = new Vec3();
Vec3 direction = player.getLookingAt();
player.getLookingAtVector(direction);
world.getEntityRenderable(player).getViewPoint(start); world.getEntityRenderable(player).getViewPoint(start);
start.add(player.getPosition()); start.add(player.getPosition());
@ -70,7 +71,7 @@ public class Selection {
return exists ? point : null; return exists ? point : null;
} }
public AbsFace getSurface() { public BlockFace getSurface() {
return exists ? surface : null; return exists ? surface : null;
} }

View File

@ -33,7 +33,7 @@ import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.backend.VertexBufferObject; import ru.windcorp.progressia.client.graphics.backend.VertexBufferObject;
import ru.windcorp.progressia.client.graphics.backend.shaders.attributes.*; import ru.windcorp.progressia.client.graphics.backend.shaders.attributes.*;
import ru.windcorp.progressia.client.graphics.backend.shaders.uniforms.*; import ru.windcorp.progressia.client.graphics.backend.shaders.uniforms.*;
import ru.windcorp.progressia.client.graphics.model.ShapePart; import ru.windcorp.progressia.client.graphics.model.Face;
import ru.windcorp.progressia.client.graphics.model.Shape; import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram; import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
@ -138,12 +138,12 @@ public class WorldRenderProgram extends ShapeRenderProgram {
public void preprocess(Shape shape) { public void preprocess(Shape shape) {
super.preprocess(shape); super.preprocess(shape);
for (ShapePart face : shape.getParts()) { for (Face face : shape.getFaces()) {
computeNormals(face); computeNormals(face);
} }
} }
private void computeNormals(ShapePart face) { private void computeNormals(Face face) {
Vec3 a = Vectors.grab3(); Vec3 a = Vectors.grab3();
Vec3 b = Vectors.grab3(); Vec3 b = Vectors.grab3();
Vec3 c = Vectors.grab3(); Vec3 c = Vectors.grab3();
@ -183,7 +183,7 @@ public class WorldRenderProgram extends ShapeRenderProgram {
normal.normalize(); normal.normalize();
} }
private void loadVertexPosition(ShapePart face, int index, Vec3 result) { private void loadVertexPosition(Face face, int index, Vec3 result) {
ByteBuffer vertices = face.getVertices(); ByteBuffer vertices = face.getVertices();
int offset = vertices.position() + index * getBytesPerVertex(); int offset = vertices.position() + index * getBytesPerVertex();
@ -194,7 +194,7 @@ public class WorldRenderProgram extends ShapeRenderProgram {
); );
} }
private void saveVertexNormal(ShapePart face, int index, Vec3 normal) { private void saveVertexNormal(Face face, int index, Vec3 normal) {
ByteBuffer vertices = face.getVertices(); ByteBuffer vertices = face.getVertices();
int offset = vertices.position() + index * getBytesPerVertex() + (3 * Float.BYTES + int offset = vertices.position() + index * getBytesPerVertex() + (3 * Float.BYTES +
4 * Float.BYTES + 4 * Float.BYTES +

View File

@ -27,29 +27,25 @@ import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.world.block.BlockRender; import ru.windcorp.progressia.client.world.block.BlockRender;
import ru.windcorp.progressia.client.world.block.BlockRenderRegistry; import ru.windcorp.progressia.client.world.block.BlockRenderRegistry;
import ru.windcorp.progressia.client.world.tile.TileRender; import ru.windcorp.progressia.client.world.tile.TileRender;
import ru.windcorp.progressia.client.world.tile.TileRenderReference;
import ru.windcorp.progressia.client.world.tile.TileRenderRegistry; import ru.windcorp.progressia.client.world.tile.TileRenderRegistry;
import ru.windcorp.progressia.client.world.tile.TileRenderStack; import ru.windcorp.progressia.client.world.tile.TileRenderStack;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.TileDataReference; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.TileDataStack; import ru.windcorp.progressia.common.world.generic.GenericChunk;
import ru.windcorp.progressia.common.world.generic.ChunkGenericRO; import ru.windcorp.progressia.common.world.tile.TileDataStack;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.rels.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class ChunkRender public class ChunkRender
implements ChunkGenericRO<BlockRender, TileRender, TileRenderStack, TileRenderReference, ChunkRender> { implements GenericChunk<ChunkRender, BlockRender, TileRender, TileRenderStack> {
private final WorldRender world; private final WorldRender world;
private final DefaultChunkData data; private final ChunkData data;
private final ChunkRenderModel model; private final ChunkRenderModel model;
private final Map<TileDataStack, TileRenderStackImpl> tileRenderLists = Collections private final Map<TileDataStack, TileRenderStackImpl> tileRenderLists = Collections
.synchronizedMap(new WeakHashMap<>()); .synchronizedMap(new WeakHashMap<>());
public ChunkRender(WorldRender world, DefaultChunkData data) { public ChunkRender(WorldRender world, ChunkData data) {
this.world = world; this.world = world;
this.data = data; this.data = data;
this.model = new ChunkRenderModel(this); this.model = new ChunkRenderModel(this);
@ -60,11 +56,6 @@ public class ChunkRender
return getData().getPosition(); return getData().getPosition();
} }
@Override
public AbsFace getUp() {
return getData().getUp();
}
@Override @Override
public BlockRender getBlock(Vec3i posInChunk) { public BlockRender getBlock(Vec3i posInChunk) {
return BlockRenderRegistry.getInstance().get( return BlockRenderRegistry.getInstance().get(
@ -93,11 +84,11 @@ public class ChunkRender
return world; return world;
} }
public DefaultChunkData getData() { public ChunkData getData() {
return data; return data;
} }
public void markForUpdate() { public synchronized void markForUpdate() {
getWorld().markChunkForUpdate(getPosition()); getWorld().markChunkForUpdate(getPosition());
} }
@ -110,28 +101,6 @@ public class ChunkRender
} }
private class TileRenderStackImpl extends TileRenderStack { private class TileRenderStackImpl extends TileRenderStack {
private class TileRenderReferenceImpl implements TileRenderReference {
private final TileDataReference parent;
public TileRenderReferenceImpl(TileDataReference parent) {
this.parent = parent;
}
@Override
public TileRender get() {
return TileRenderRegistry.getInstance().get(parent.get().getId());
}
@Override
public int getIndex() {
return parent.getIndex();
}
@Override
public TileRenderStack getStack() {
return TileRenderStackImpl.this;
}
}
private final TileDataStack parent; private final TileDataStack parent;
@ -150,25 +119,10 @@ public class ChunkRender
} }
@Override @Override
public RelFace getFace() { public BlockFace getFace() {
return parent.getFace(); return parent.getFace();
} }
@Override
public TileRenderReference getReference(int index) {
return new TileRenderReferenceImpl(parent.getReference(index));
}
@Override
public int getIndexByTag(int tag) {
return parent.getIndexByTag(tag);
}
@Override
public int getTagByIndex(int index) {
return parent.getTagByIndex(index);
}
@Override @Override
public TileRender get(int index) { public TileRender get(int index) {
return TileRenderRegistry.getInstance().get(parent.get(index).getId()); return TileRenderRegistry.getInstance().get(parent.get(index).getId());

View File

@ -35,10 +35,8 @@ import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
import ru.windcorp.progressia.client.world.tile.TileRender; import ru.windcorp.progressia.client.world.tile.TileRender;
import ru.windcorp.progressia.client.world.tile.TileRenderNone; import ru.windcorp.progressia.client.world.tile.TileRenderNone;
import ru.windcorp.progressia.client.world.tile.TileRenderStack; import ru.windcorp.progressia.client.world.tile.TileRenderStack;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.generic.GenericChunks; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.rels.AxisRotations;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class ChunkRenderModel implements Renderable { public class ChunkRenderModel implements Renderable {
@ -55,15 +53,11 @@ public class ChunkRenderModel implements Renderable {
public void render(ShapeRenderHelper renderer) { public void render(ShapeRenderHelper renderer) {
if (model == null) return; if (model == null) return;
float offset = DefaultChunkData.BLOCKS_PER_CHUNK / 2 - 0.5f;
renderer.pushTransform().translate( renderer.pushTransform().translate(
chunk.getX() * DefaultChunkData.BLOCKS_PER_CHUNK, chunk.getX() * ChunkData.BLOCKS_PER_CHUNK,
chunk.getY() * DefaultChunkData.BLOCKS_PER_CHUNK, chunk.getY() * ChunkData.BLOCKS_PER_CHUNK,
chunk.getZ() * DefaultChunkData.BLOCKS_PER_CHUNK chunk.getZ() * ChunkData.BLOCKS_PER_CHUNK
).translate(offset, offset, offset) );
.mul(AxisRotations.getResolutionMatrix4(chunk.getUp()))
.translate(-offset, -offset, -offset);
model.render(renderer); model.render(renderer);
@ -77,8 +71,8 @@ public class ChunkRenderModel implements Renderable {
optimizers.forEach(ChunkRenderOptimizer::startRender); optimizers.forEach(ChunkRenderOptimizer::startRender);
GenericChunks.forEachBiC(relBlockInChunk -> { chunk.forEachBiC(blockInChunk -> {
processBlockAndTiles(relBlockInChunk, sink); processBlockAndTiles(blockInChunk, sink);
}); });
for (ChunkRenderOptimizer optimizer : optimizers) { for (ChunkRenderOptimizer optimizer : optimizers) {
@ -102,16 +96,16 @@ public class ChunkRenderModel implements Renderable {
} }
} }
private void processBlockAndTiles(Vec3i relBlockInChunk, Builder sink) { private void processBlockAndTiles(Vec3i blockInChunk, Builder sink) {
processBlock(relBlockInChunk, sink); processBlock(blockInChunk, sink);
for (RelFace face : RelFace.getFaces()) { for (BlockFace face : BlockFace.getFaces()) {
processTileStack(relBlockInChunk, face, sink); processTileStack(blockInChunk, face, sink);
} }
} }
private void processBlock(Vec3i relBlockInChunk, Builder sink) { private void processBlock(Vec3i blockInChunk, Builder sink) {
BlockRender block = chunk.getBlockRel(relBlockInChunk); BlockRender block = chunk.getBlock(blockInChunk);
if (block instanceof BlockRenderNone) { if (block instanceof BlockRenderNone) {
return; return;
@ -119,48 +113,48 @@ public class ChunkRenderModel implements Renderable {
if (block.needsOwnRenderable()) { if (block.needsOwnRenderable()) {
sink.addPart( sink.addPart(
block.createRenderable(chunk.getData(), relBlockInChunk), block.createRenderable(chunk.getData(), blockInChunk),
new Mat4().identity().translate(relBlockInChunk.x, relBlockInChunk.y, relBlockInChunk.z) new Mat4().identity().translate(blockInChunk.x, blockInChunk.y, blockInChunk.z)
); );
} }
processBlockWithCROs(block, relBlockInChunk); processBlockWithCROs(block, blockInChunk);
} }
private void processBlockWithCROs(BlockRender block, Vec3i relBlockInChunk) { private void processBlockWithCROs(BlockRender block, Vec3i blockInChunk) {
for (ChunkRenderOptimizer optimizer : optimizers) { for (ChunkRenderOptimizer optimizer : optimizers) {
optimizer.addBlock(block, relBlockInChunk); optimizer.addBlock(block, blockInChunk);
} }
} }
private void processTileStack(Vec3i relBlockInChunk, RelFace face, Builder sink) { private void processTileStack(Vec3i blockInChunk, BlockFace face, Builder sink) {
TileRenderStack trs = chunk.getTilesOrNullRel(relBlockInChunk, face); TileRenderStack trs = chunk.getTilesOrNull(blockInChunk, face);
if (trs == null || trs.isEmpty()) { if (trs == null || trs.isEmpty()) {
return; return;
} }
trs.forEach(tile -> processTile(tile, relBlockInChunk, face, sink)); trs.forEach(tile -> processTile(tile, blockInChunk, face, sink));
} }
private void processTile(TileRender tile, Vec3i relBlockInChunk, RelFace face, Builder sink) { private void processTile(TileRender tile, Vec3i blockInChunk, BlockFace face, Builder sink) {
if (tile instanceof TileRenderNone) { if (tile instanceof TileRenderNone) {
return; return;
} }
if (tile.needsOwnRenderable()) { if (tile.needsOwnRenderable()) {
sink.addPart( sink.addPart(
tile.createRenderable(chunk.getData(), relBlockInChunk, face), tile.createRenderable(chunk.getData(), blockInChunk, face),
new Mat4().identity().translate(relBlockInChunk.x, relBlockInChunk.y, relBlockInChunk.z) new Mat4().identity().translate(blockInChunk.x, blockInChunk.y, blockInChunk.z)
); );
} }
processTileWithCROs(tile, relBlockInChunk, face); processTileWithCROs(tile, blockInChunk, face);
} }
private void processTileWithCROs(TileRender tile, Vec3i relBlockInChunk, RelFace face) { private void processTileWithCROs(TileRender tile, Vec3i blockInChunk, BlockFace face) {
for (ChunkRenderOptimizer optimizer : optimizers) { for (ChunkRenderOptimizer optimizer : optimizers) {
optimizer.addTile(tile, relBlockInChunk, face); optimizer.addTile(tile, blockInChunk, face);
} }
} }

View File

@ -21,11 +21,10 @@ package ru.windcorp.progressia.client.world;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.ChunkDataListener; import ru.windcorp.progressia.common.world.ChunkDataListener;
import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileData;
class ChunkUpdateListener implements ChunkDataListener { class ChunkUpdateListener implements ChunkDataListener {
@ -37,14 +36,14 @@ class ChunkUpdateListener implements ChunkDataListener {
} }
@Override @Override
public void onChunkChanged(DefaultChunkData chunk) { public void onChunkChanged(ChunkData chunk) {
world.getChunk(chunk).markForUpdate(); world.getChunk(chunk).markForUpdate();
} }
@Override @Override
public void onChunkLoaded(DefaultChunkData chunk) { public void onChunkLoaded(ChunkData chunk) {
Vec3i cursor = new Vec3i(); Vec3i cursor = new Vec3i();
for (AbsFace face : AbsFace.getFaces()) { for (BlockFace face : BlockFace.getFaces()) {
cursor.set(chunk.getX(), chunk.getY(), chunk.getZ()); cursor.set(chunk.getX(), chunk.getY(), chunk.getZ());
cursor.add(face.getVector()); cursor.add(face.getVector());
world.markChunkForUpdate(cursor); world.markChunkForUpdate(cursor);
@ -52,22 +51,22 @@ class ChunkUpdateListener implements ChunkDataListener {
} }
@Override @Override
public void onChunkBlockChanged(DefaultChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current) { public void onChunkBlockChanged(ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current) {
onLocationChanged(chunk, blockInChunk); onLocationChanged(chunk, blockInChunk);
} }
@Override @Override
public void onChunkTilesChanged( public void onChunkTilesChanged(
DefaultChunkData chunk, ChunkData chunk,
Vec3i blockInChunk, Vec3i blockInChunk,
RelFace face, BlockFace face,
TileData tile, TileData tile,
boolean wasAdded boolean wasAdded
) { ) {
onLocationChanged(chunk, blockInChunk); onLocationChanged(chunk, blockInChunk);
} }
private void onLocationChanged(DefaultChunkData chunk, Vec3i blockInChunk) { private void onLocationChanged(ChunkData chunk, Vec3i blockInChunk) {
Vec3i chunkPos = Vectors.grab3i().set(chunk.getX(), chunk.getY(), chunk.getZ()); Vec3i chunkPos = Vectors.grab3i().set(chunk.getX(), chunk.getY(), chunk.getZ());
checkCoordinate(blockInChunk, chunkPos, VectorUtil.Axis.X); checkCoordinate(blockInChunk, chunkPos, VectorUtil.Axis.X);
@ -83,7 +82,7 @@ class ChunkUpdateListener implements ChunkDataListener {
if (block == 0) { if (block == 0) {
diff = -1; diff = -1;
} else if (block == DefaultChunkData.BLOCKS_PER_CHUNK - 1) { } else if (block == ChunkData.BLOCKS_PER_CHUNK - 1) {
diff = +1; diff = +1;
} else { } else {
return; return;

View File

@ -34,58 +34,57 @@ import ru.windcorp.progressia.client.world.block.BlockRender;
import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry;
import ru.windcorp.progressia.client.world.entity.EntityRenderable; import ru.windcorp.progressia.client.world.entity.EntityRenderable;
import ru.windcorp.progressia.client.world.tile.TileRender; import ru.windcorp.progressia.client.world.tile.TileRender;
import ru.windcorp.progressia.client.world.tile.TileRenderReference;
import ru.windcorp.progressia.client.world.tile.TileRenderStack; import ru.windcorp.progressia.client.world.tile.TileRenderStack;
import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.ChunkDataListeners; import ru.windcorp.progressia.common.world.ChunkDataListeners;
import ru.windcorp.progressia.common.world.DefaultWorldData; import ru.windcorp.progressia.common.world.WorldData;
import ru.windcorp.progressia.common.world.WorldDataListener; import ru.windcorp.progressia.common.world.WorldDataListener;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.generic.ChunkSet; import ru.windcorp.progressia.common.world.generic.ChunkSet;
import ru.windcorp.progressia.common.world.generic.ChunkSets; import ru.windcorp.progressia.common.world.generic.ChunkSets;
import ru.windcorp.progressia.common.world.generic.WorldGenericRO; import ru.windcorp.progressia.common.world.generic.GenericWorld;
public class WorldRender public class WorldRender
implements WorldGenericRO<BlockRender, TileRender, TileRenderStack, TileRenderReference, ChunkRender, EntityRenderable> { implements GenericWorld<BlockRender, TileRender, TileRenderStack, ChunkRender, EntityRenderable> {
private final DefaultWorldData data; private final WorldData data;
private final Client client; private final Client client;
private final Map<DefaultChunkData, ChunkRender> chunks = Collections.synchronizedMap(new HashMap<>()); private final Map<ChunkData, ChunkRender> chunks = Collections.synchronizedMap(new HashMap<>());
private final Map<EntityData, EntityRenderable> entityModels = Collections.synchronizedMap(new WeakHashMap<>()); private final Map<EntityData, EntityRenderable> entityModels = Collections.synchronizedMap(new WeakHashMap<>());
private final ChunkSet chunksToUpdate = ChunkSets.newSyncHashSet(); private final ChunkSet chunksToUpdate = ChunkSets.newSyncHashSet();
public WorldRender(DefaultWorldData data, Client client) { public WorldRender(WorldData data, Client client) {
this.data = data; this.data = data;
this.client = client; this.client = client;
data.addListener(ChunkDataListeners.createAdder(new ChunkUpdateListener(this))); data.addListener(ChunkDataListeners.createAdder(new ChunkUpdateListener(this)));
data.addListener(new WorldDataListener() { data.addListener(new WorldDataListener() {
@Override @Override
public void onChunkLoaded(DefaultWorldData world, DefaultChunkData chunk) { public void onChunkLoaded(WorldData world, ChunkData chunk) {
addChunk(chunk); addChunk(chunk);
} }
@Override @Override
public void beforeChunkUnloaded(DefaultWorldData world, DefaultChunkData chunk) { public void beforeChunkUnloaded(WorldData world, ChunkData chunk) {
removeChunk(chunk); removeChunk(chunk);
} }
}); });
} }
protected void addChunk(DefaultChunkData chunk) { protected void addChunk(ChunkData chunk) {
chunks.put(chunk, new ChunkRender(WorldRender.this, chunk)); chunks.put(chunk, new ChunkRender(WorldRender.this, chunk));
markChunkForUpdate(chunk.getPosition()); markChunkForUpdate(chunk.getPosition());
} }
protected void removeChunk(DefaultChunkData chunk) { protected void removeChunk(ChunkData chunk) {
chunks.remove(chunk); chunks.remove(chunk);
} }
public DefaultWorldData getData() { public WorldData getData() {
return data; return data;
} }
@ -93,7 +92,7 @@ public class WorldRender
return client; return client;
} }
public ChunkRender getChunk(DefaultChunkData chunkData) { public ChunkRender getChunk(ChunkData chunkData) {
return chunks.get(chunkData); return chunks.get(chunkData);
} }
@ -112,13 +111,6 @@ public class WorldRender
return entityModels.values(); return entityModels.values();
} }
@Override
public EntityRenderable getEntity(long entityId) {
EntityData entityData = getData().getEntity(entityId);
if (entityData == null) return null;
return getEntityRenderable(entityData);
}
public void render(ShapeRenderHelper renderer) { public void render(ShapeRenderHelper renderer) {
updateChunks(); updateChunks();

View File

@ -18,19 +18,26 @@
package ru.windcorp.progressia.client.world.block; package ru.windcorp.progressia.client.world.block;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.generic.BlockGeneric; import ru.windcorp.progressia.common.world.generic.GenericBlock;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
public abstract class BlockRender extends Namespaced implements BlockGeneric { public abstract class BlockRender extends Namespaced implements GenericBlock {
public BlockRender(String id) { public BlockRender(String id) {
super(id); super(id);
} }
public Renderable createRenderable(DefaultChunkData chunk, Vec3i relBlockInChunk) { public void render(ShapeRenderHelper renderer) {
throw new UnsupportedOperationException(
"BlockRender.render() not implemented in " + this
);
}
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
return null; return null;
} }

View File

@ -21,7 +21,7 @@ package ru.windcorp.progressia.client.world.block;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.model.EmptyModel; import ru.windcorp.progressia.client.graphics.model.EmptyModel;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
public class BlockRenderNone extends BlockRender { public class BlockRenderNone extends BlockRender {
@ -30,7 +30,7 @@ public class BlockRenderNone extends BlockRender {
} }
@Override @Override
public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk) { public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
return EmptyModel.getInstance(); return EmptyModel.getInstance();
} }

View File

@ -19,7 +19,7 @@
package ru.windcorp.progressia.client.world.block; package ru.windcorp.progressia.client.world.block;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class BlockRenderOpaqueCube extends BlockRenderTexturedCube { public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
@ -29,8 +29,8 @@ public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
Texture bottomTexture, Texture bottomTexture,
Texture northTexture, Texture northTexture,
Texture southTexture, Texture southTexture,
Texture westTexture, Texture eastTexture,
Texture eastTexture Texture westTexture
) { ) {
super( super(
id, id,
@ -38,8 +38,8 @@ public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
bottomTexture, bottomTexture,
northTexture, northTexture,
southTexture, southTexture,
westTexture, eastTexture,
eastTexture westTexture
); );
} }
@ -55,20 +55,8 @@ public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
); );
} }
public BlockRenderOpaqueCube(String id, Texture topTexture, Texture bottomTexture, Texture sideTexture) {
this(
id,
topTexture,
bottomTexture,
sideTexture,
sideTexture,
sideTexture,
sideTexture
);
}
@Override @Override
public boolean isOpaque(RelFace face) { public boolean isOpaque(BlockFace face) {
return true; return true;
} }

View File

@ -18,8 +18,9 @@
package ru.windcorp.progressia.client.world.block; package ru.windcorp.progressia.client.world.block;
import static ru.windcorp.progressia.common.world.rels.AbsFace.*; import static ru.windcorp.progressia.common.world.block.BlockFace.*;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -28,23 +29,22 @@ import glm.vec._3.i.Vec3i;
import glm.vec._4.Vec4; import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.Usage; import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.ShapePart; import ru.windcorp.progressia.client.graphics.model.Face;
import ru.windcorp.progressia.client.graphics.model.ShapeParts; import ru.windcorp.progressia.client.graphics.model.Faces;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shape; import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface.BlockOptimizedSurface; import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface.BlockOptimizedSurface;
import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace;
public abstract class BlockRenderTexturedCube public abstract class BlockRenderTexturedCube
extends BlockRender extends BlockRender
implements BlockOptimizedSurface { implements BlockOptimizedSurface {
private final Map<RelFace, Texture> textures; private final Map<BlockFace, Texture> textures = new HashMap<>();
public BlockRenderTexturedCube( public BlockRenderTexturedCube(
String id, String id,
@ -52,59 +52,65 @@ public abstract class BlockRenderTexturedCube
Texture bottomTexture, Texture bottomTexture,
Texture northTexture, Texture northTexture,
Texture southTexture, Texture southTexture,
Texture westTexture, Texture eastTexture,
Texture eastTexture Texture westTexture
) { ) {
super(id); super(id);
this.textures = RelFace.mapToFaces(topTexture, bottomTexture, northTexture, southTexture, westTexture, eastTexture);
textures.put(TOP, topTexture);
textures.put(BOTTOM, bottomTexture);
textures.put(NORTH, northTexture);
textures.put(SOUTH, southTexture);
textures.put(EAST, eastTexture);
textures.put(WEST, westTexture);
} }
public Texture getTexture(RelFace blockFace) { public Texture getTexture(BlockFace blockFace) {
return textures.get(blockFace); return textures.get(blockFace);
} }
public Vec4 getColorMultiplier(RelFace blockFace) { public Vec4 getColorMultiplier(BlockFace blockFace) {
return Colors.WHITE; return Colors.WHITE;
} }
@Override @Override
public final void getShapeParts( public final void getFaces(
DefaultChunkData chunk, Vec3i blockInChunk, RelFace blockFace, ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
boolean inner, boolean inner,
Consumer<ShapePart> output, Consumer<Face> output,
Vec3 offset Vec3 offset
) { ) {
output.accept(createFace(chunk, blockInChunk, blockFace, inner, offset)); output.accept(createFace(chunk, blockInChunk, blockFace, inner, offset));
} }
private ShapePart createFace( private Face createFace(
DefaultChunkData chunk, Vec3i blockInChunk, RelFace blockFace, ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
boolean inner, boolean inner,
Vec3 offset Vec3 offset
) { ) {
return ShapeParts.createBlockFace( return Faces.createBlockFace(
WorldRenderProgram.getDefault(), WorldRenderProgram.getDefault(),
getTexture(blockFace), getTexture(blockFace),
getColorMultiplier(blockFace), getColorMultiplier(blockFace),
offset, offset,
blockFace.resolve(AbsFace.POS_Z), blockFace,
inner inner
); );
} }
@Override @Override
public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk) { public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
boolean opaque = isBlockOpaque(); boolean opaque = isBlockOpaque();
ShapePart[] faces = new ShapePart[BLOCK_FACE_COUNT + (opaque ? BLOCK_FACE_COUNT : 0)]; Face[] faces = new Face[BLOCK_FACE_COUNT + (opaque ? BLOCK_FACE_COUNT : 0)];
for (int i = 0; i < BLOCK_FACE_COUNT; ++i) { for (int i = 0; i < BLOCK_FACE_COUNT; ++i) {
faces[i] = createFace(chunk, blockInChunk, RelFace.getFaces().get(i), false, Vectors.ZERO_3); faces[i] = createFace(chunk, blockInChunk, BlockFace.getFaces().get(i), false, Vectors.ZERO_3);
} }
if (!opaque) { if (!opaque) {
for (int i = 0; i < BLOCK_FACE_COUNT; ++i) { for (int i = 0; i < BLOCK_FACE_COUNT; ++i) {
faces[i + BLOCK_FACE_COUNT] = createFace(chunk, blockInChunk, RelFace.getFaces().get(i), true, Vectors.ZERO_3); faces[i + BLOCK_FACE_COUNT] = createFace(chunk, blockInChunk, BlockFace.getFaces().get(i), true, Vectors.ZERO_3);
} }
} }

View File

@ -19,7 +19,7 @@
package ru.windcorp.progressia.client.world.block; package ru.windcorp.progressia.client.world.block;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class BlockRenderTransparentCube extends BlockRenderTexturedCube { public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
@ -29,8 +29,8 @@ public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
Texture bottomTexture, Texture bottomTexture,
Texture northTexture, Texture northTexture,
Texture southTexture, Texture southTexture,
Texture westTexture, Texture eastTexture,
Texture eastTexture Texture westTexture
) { ) {
super( super(
id, id,
@ -38,8 +38,8 @@ public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
bottomTexture, bottomTexture,
northTexture, northTexture,
southTexture, southTexture,
westTexture, eastTexture,
eastTexture westTexture
); );
} }
@ -55,20 +55,8 @@ public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
); );
} }
public BlockRenderTransparentCube(String id, Texture topTexture, Texture bottomTexture, Texture sideTexture) {
this(
id,
topTexture,
bottomTexture,
sideTexture,
sideTexture,
sideTexture,
sideTexture
);
}
@Override @Override
public boolean isOpaque(RelFace face) { public boolean isOpaque(BlockFace face) {
return false; return false;
} }

View File

@ -24,7 +24,7 @@ import ru.windcorp.progressia.client.world.ChunkRender;
import ru.windcorp.progressia.client.world.block.BlockRender; import ru.windcorp.progressia.client.world.block.BlockRender;
import ru.windcorp.progressia.client.world.tile.TileRender; import ru.windcorp.progressia.client.world.tile.TileRender;
import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.block.BlockFace;
/** /**
* Chunk render optimizer (CRO) is an object that produces optimized models for * Chunk render optimizer (CRO) is an object that produces optimized models for
@ -35,12 +35,6 @@ import ru.windcorp.progressia.common.world.rels.RelFace;
* tiles. An example of a CRO is {@link ChunkRenderOptimizerSurface}: it removes * tiles. An example of a CRO is {@link ChunkRenderOptimizerSurface}: it removes
* block surfaces and tiles that it knows cannot be seen, thus significantly * block surfaces and tiles that it knows cannot be seen, thus significantly
* reducing total polygon count. * reducing total polygon count.
* <p>
* As with everything related to rendering chunks, CROs are interacted with
* using the relative local chunk coordinate system. In this coordinate system,
* the coordinates are the chunk coordinates relativized using the chunks's up
* direction. In simpler terms, coordinates are {@code [0; BLOCKS_PER_CHUNK)}
* and Z is always up.
* <h3>CRO lifecycle</h3> * <h3>CRO lifecycle</h3>
* A CRO instance is created by {@link ChunkRenderOptimizerRegistry}. It may * A CRO instance is created by {@link ChunkRenderOptimizerRegistry}. It may
* then be used to work on multiple chunks sequentially. Each chunk is processed * then be used to work on multiple chunks sequentially. Each chunk is processed
@ -50,7 +44,7 @@ import ru.windcorp.progressia.common.world.rels.RelFace;
* instance.</li> * instance.</li>
* <li>{@link #startRender()} is invoked. The CRO must reset its state.</li> * <li>{@link #startRender()} is invoked. The CRO must reset its state.</li>
* <li>{@link #addBlock(BlockRender, Vec3i)} and * <li>{@link #addBlock(BlockRender, Vec3i)} and
* {@link #addTile(TileRender, Vec3i, RelFace)} are invoked for each block and * {@link #addTile(TileRender, Vec3i, BlockFace)} are invoked for each block and
* tile that this CRO should optimize. {@code addTile} specifies tiles in order * tile that this CRO should optimize. {@code addTile} specifies tiles in order
* of ascension within a tile stack.</li> * of ascension within a tile stack.</li>
* <li>{@link #endRender()} is invoked. The CRO may perform any pending * <li>{@link #endRender()} is invoked. The CRO may perform any pending
@ -104,13 +98,12 @@ public abstract class ChunkRenderOptimizer extends Namespaced {
* method is only invoked once per block. This method is not necessarily * method is only invoked once per block. This method is not necessarily
* invoked for each block. * invoked for each block.
* *
* @param block a {@link BlockRender} instance describing the * @param block a {@link BlockRender} instance describing the block.
* block.
* It corresponds to * It corresponds to
* {@code getChunk().getBlock(blockInChunk)}. * {@code getChunk().getBlock(blockInChunk)}.
* @param relBlockInChunk the relative position of the block * @param blockInChunk the position of the block
*/ */
public abstract void addBlock(BlockRender block, Vec3i relBlockInChunk); public abstract void addBlock(BlockRender block, Vec3i blockInChunk);
/** /**
* Requests that this CRO processes the provided tile. This method may only * Requests that this CRO processes the provided tile. This method may only
@ -120,11 +113,10 @@ public abstract class ChunkRenderOptimizer extends Namespaced {
* this method is invoked for lower tiles first. * this method is invoked for lower tiles first.
* *
* @param tile a {@link BlockRender} instance describing the tile * @param tile a {@link BlockRender} instance describing the tile
* @param relBlockInChunk the relative position of the block that the tile * @param blockInChunk the position of the block that the tile belongs to
* belongs to
* @param blockFace the face that the tile belongs to * @param blockFace the face that the tile belongs to
*/ */
public abstract void addTile(TileRender tile, Vec3i relBlockInChunk, RelFace blockFace); public abstract void addTile(TileRender tile, Vec3i blockInChunk, BlockFace blockFace);
/** /**
* Requests that the CRO assembles and outputs its model. This method may * Requests that the CRO assembles and outputs its model. This method may

View File

@ -1,99 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.world.cro;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapePart;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.block.BlockRender;
import ru.windcorp.progressia.client.world.tile.TileRender;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class ChunkRenderOptimizerSimple extends ChunkRenderOptimizer {
public interface BlockOptimizedSimple {
void getShapeParts(
DefaultChunkData chunk,
Vec3i relBlockInChunk,
Consumer<ShapePart> output
);
}
public interface TileOptimizedCustom {
void getShapeParts(
DefaultChunkData chunk,
Vec3i relBlockInChunk,
RelFace blockFace,
Consumer<ShapePart> output
);
}
private final Collection<ShapePart> parts = new ArrayList<>();
private final Consumer<ShapePart> partAdder = parts::add;
public ChunkRenderOptimizerSimple(String id) {
super(id);
}
@Override
public void startRender() {
parts.clear();
}
@Override
public void addBlock(BlockRender block, Vec3i relBlockInChunk) {
if (block instanceof BlockOptimizedSimple) {
((BlockOptimizedSimple) block).getShapeParts(chunk.getData(), relBlockInChunk, partAdder);
}
}
@Override
public void addTile(TileRender tile, Vec3i relBlockInChunk, RelFace blockFace) {
if (tile instanceof TileOptimizedCustom) {
((TileOptimizedCustom) tile).getShapeParts(chunk.getData(), relBlockInChunk, blockFace, partAdder);
}
}
@Override
public Renderable endRender() {
if (parts.isEmpty()) {
return null;
}
return new Shape(
Usage.STATIC,
WorldRenderProgram.getDefault(),
parts.toArray(new ShapePart[parts.size()])
);
}
}

View File

@ -18,9 +18,9 @@
package ru.windcorp.progressia.client.world.cro; package ru.windcorp.progressia.client.world.cro;
import static ru.windcorp.progressia.common.world.DefaultChunkData.BLOCKS_PER_CHUNK; import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK;
import static ru.windcorp.progressia.common.world.generic.TileGenericStackRO.TILES_PER_FACE; import static ru.windcorp.progressia.common.world.block.BlockFace.BLOCK_FACE_COUNT;
import static ru.windcorp.progressia.common.world.rels.AbsFace.BLOCK_FACE_COUNT; import static ru.windcorp.progressia.common.world.generic.GenericTileStack.TILES_PER_FACE;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -29,7 +29,7 @@ import java.util.function.Consumer;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.backend.Usage; import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.ShapePart; import ru.windcorp.progressia.client.graphics.model.Face;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shape; import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
@ -37,9 +37,8 @@ import ru.windcorp.progressia.client.world.ChunkRender;
import ru.windcorp.progressia.client.world.block.BlockRender; import ru.windcorp.progressia.client.world.block.BlockRender;
import ru.windcorp.progressia.client.world.tile.TileRender; import ru.windcorp.progressia.client.world.tile.TileRender;
import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.generic.GenericChunks; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer { public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
@ -53,42 +52,39 @@ public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
private static interface OptimizedSurface { private static interface OptimizedSurface {
/** /**
* Creates and outputs a set of shape parts that correspond to this * Creates and outputs a set of faces that correspond to this surface.
* surface. The coordinates of the face vertices must be in chunk * The coordinates of the face vertices must be in chunk coordinate
* coordinate system. * system.
* *
* @param chunk the chunk that contains the requested face * @param chunk the chunk that contains the requested face
* @param relBlockInChunk the relative block in chunk * @param blockInChunk the block in chunk
* @param blockFace the requested face * @param blockFace the requested face
* @param inner whether this face should be visible from * @param inner whether this face should be visible from inside
* inside
* ({@code true}) or outside ({@code false}) * ({@code true}) or outside ({@code false})
* @param output a consumer that the created shape parts must * @param output a consumer that the created faces must be given
* be * to
* given to * @param offset an additional offset that must be applied to all
* @param offset an additional offset that must be applied to
* all
* vertices * vertices
*/ */
void getShapeParts( void getFaces(
DefaultChunkData chunk, ChunkData chunk,
Vec3i relBlockInChunk, Vec3i blockInChunk,
RelFace blockFace, BlockFace blockFace,
boolean inner, boolean inner,
Consumer<ShapePart> output, Consumer<Face> output,
Vec3 offset /* kostyl 156% */ Vec3 offset /* kostyl 156% */
); );
/** /**
* Returns the opacity of the surface identified by the provided * Returns the opacity of the surface identified by the provided
* {@link RelFace}. * {@link BlockFace}.
* Opaque surfaces prevent surfaces behind them from being included in * Opaque surfaces prevent surfaces behind them from being included in
* chunk models. * chunk models.
* *
* @param blockFace the face to query * @param blockFace the face to query
* @return {@code true} iff the surface is opaque. * @return {@code true} iff the surface is opaque.
*/ */
boolean isOpaque(RelFace blockFace); boolean isOpaque(BlockFace blockFace);
} }
/** /**
@ -163,29 +159,29 @@ public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
} }
@Override @Override
public void addBlock(BlockRender block, Vec3i relBlockInChunk) { public void addBlock(BlockRender block, Vec3i pos) {
if (!(block instanceof BlockOptimizedSurface)) if (!(block instanceof BlockOptimizedSurface))
return; return;
BlockOptimizedSurface bos = (BlockOptimizedSurface) block; BlockOptimizedSurface bos = (BlockOptimizedSurface) block;
addBlock(relBlockInChunk, bos); addBlock(pos, bos);
} }
@Override @Override
public void addTile(TileRender tile, Vec3i relBlockInChunk, RelFace face) { public void addTile(TileRender tile, Vec3i pos, BlockFace face) {
if (!(tile instanceof TileOptimizedSurface)) if (!(tile instanceof TileOptimizedSurface))
return; return;
TileOptimizedSurface tos = (TileOptimizedSurface) tile; TileOptimizedSurface tos = (TileOptimizedSurface) tile;
addTile(relBlockInChunk, face, tos); addTile(pos, face, tos);
} }
private void addBlock(Vec3i relBlockInChunk, BlockOptimizedSurface block) { protected void addBlock(Vec3i pos, BlockOptimizedSurface block) {
getBlock(relBlockInChunk).block = block; getBlock(pos).block = block;
} }
private void addTile(Vec3i relBlockInChunk, RelFace face, TileOptimizedSurface tile) { private void addTile(Vec3i pos, BlockFace face, TileOptimizedSurface tile) {
FaceInfo faceInfo = getFace(relBlockInChunk, face); FaceInfo faceInfo = getFace(pos, face);
int index = faceInfo.tileCount; int index = faceInfo.tileCount;
faceInfo.tileCount++; faceInfo.tileCount++;
@ -201,58 +197,63 @@ public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
} }
} }
protected BlockInfo getBlock(Vec3i relBlockInChunk) { protected BlockInfo getBlock(Vec3i cursor) {
return data[relBlockInChunk.x][relBlockInChunk.y][relBlockInChunk.z]; return data[cursor.x][cursor.y][cursor.z];
} }
protected FaceInfo getFace(Vec3i relBlockInChunk, RelFace face) { protected FaceInfo getFace(Vec3i cursor, BlockFace face) {
return getBlock(relBlockInChunk).faces[face.getId()]; return getBlock(cursor).faces[face.getId()];
} }
@Override @Override
public Renderable endRender() { public Renderable endRender() {
Collection<ShapePart> shapeParts = new ArrayList<>( Collection<Face> shapeFaces = new ArrayList<>(
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3 BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3
); );
Consumer<ShapePart> consumer = shapeParts::add; Vec3i cursor = new Vec3i();
Consumer<Face> consumer = shapeFaces::add;
GenericChunks.forEachBiC(relBlockInChunk -> { for (cursor.x = 0; cursor.x < BLOCKS_PER_CHUNK; ++cursor.x) {
processInnerFaces(relBlockInChunk, consumer); for (cursor.y = 0; cursor.y < BLOCKS_PER_CHUNK; ++cursor.y) {
processOuterFaces(relBlockInChunk, consumer); for (cursor.z = 0; cursor.z < BLOCKS_PER_CHUNK; ++cursor.z) {
}); processInnerFaces(cursor, consumer);
processOuterFaces(cursor, consumer);
}
}
}
if (shapeParts.isEmpty()) { if (shapeFaces.isEmpty()) {
return null; return null;
} }
return new Shape( return new Shape(
Usage.STATIC, Usage.STATIC,
WorldRenderProgram.getDefault(), WorldRenderProgram.getDefault(),
shapeParts.toArray(new ShapePart[shapeParts.size()]) shapeFaces.toArray(new Face[shapeFaces.size()])
); );
} }
private void processOuterFaces( private void processOuterFaces(
Vec3i relBlockInChunk, Vec3i blockInChunk,
Consumer<ShapePart> output Consumer<Face> output
) { ) {
for (RelFace blockFace : RelFace.getFaces()) { for (BlockFace blockFace : BlockFace.getFaces()) {
processOuterFace(relBlockInChunk, blockFace, output); processOuterFace(blockInChunk, blockFace, output);
} }
} }
private void processOuterFace(Vec3i relBlockInChunk, RelFace blockFace, Consumer<ShapePart> output) { private void processOuterFace(Vec3i blockInChunk, BlockFace blockFace, Consumer<Face> output) {
if (!shouldRenderOuterFace(relBlockInChunk, blockFace)) if (!shouldRenderOuterFace(blockInChunk, blockFace))
return; return;
FaceInfo info = getFace(relBlockInChunk, blockFace); FaceInfo info = getFace(blockInChunk, blockFace);
if (info.tileCount == 0 && info.block.block == null) if (info.tileCount == 0 && info.block.block == null)
return; return;
Vec3 faceOrigin = new Vec3(relBlockInChunk.x, relBlockInChunk.y, relBlockInChunk.z); Vec3 faceOrigin = new Vec3(blockInChunk.x, blockInChunk.y, blockInChunk.z);
Vec3 offset = new Vec3(blockFace.getRelFloatVector()).mul(OVERLAY_OFFSET); Vec3 offset = new Vec3(blockFace.getFloatVector()).mul(OVERLAY_OFFSET);
for ( for (
int layer = info.topOpaqueSurface; int layer = info.topOpaqueSurface;
@ -263,29 +264,32 @@ public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
if (surface == null) if (surface == null)
continue; // layer may be BLOCK_LAYER, then block may be null continue; // layer may be BLOCK_LAYER, then block may be null
surface.getShapeParts(chunk.getData(), relBlockInChunk, blockFace, false, output, faceOrigin); surface.getFaces(chunk.getData(), blockInChunk, blockFace, false, output, faceOrigin);
faceOrigin.add(offset); faceOrigin.add(offset);
} }
} }
private void processInnerFaces(Vec3i relBlockInChunk, Consumer<ShapePart> output) { private void processInnerFaces(
for (RelFace blockFace : RelFace.getFaces()) { Vec3i blockInChunk,
processInnerFace(relBlockInChunk, blockFace, output); Consumer<Face> output
) {
for (BlockFace blockFace : BlockFace.getFaces()) {
processInnerFace(blockInChunk, blockFace, output);
} }
} }
private void processInnerFace(Vec3i relBlockInChunk, RelFace blockFace, Consumer<ShapePart> output) { private void processInnerFace(Vec3i blockInChunk, BlockFace blockFace, Consumer<Face> output) {
if (!shouldRenderInnerFace(relBlockInChunk, blockFace)) if (!shouldRenderInnerFace(blockInChunk, blockFace))
return; return;
FaceInfo info = getFace(relBlockInChunk, blockFace); FaceInfo info = getFace(blockInChunk, blockFace);
if (info.tileCount == 0 && info.block.block == null) if (info.tileCount == 0 && info.block.block == null)
return; return;
Vec3 faceOrigin = new Vec3(relBlockInChunk.x, relBlockInChunk.y, relBlockInChunk.z); Vec3 faceOrigin = new Vec3(blockInChunk.x, blockInChunk.y, blockInChunk.z);
Vec3 offset = new Vec3(blockFace.getRelFloatVector()).mul(OVERLAY_OFFSET); Vec3 offset = new Vec3(blockFace.getFloatVector()).mul(OVERLAY_OFFSET);
for ( for (
int layer = FaceInfo.BLOCK_LAYER; int layer = FaceInfo.BLOCK_LAYER;
@ -296,35 +300,35 @@ public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
if (surface == null) if (surface == null)
continue; // layer may be BLOCK_LAYER, then block may be null continue; // layer may be BLOCK_LAYER, then block may be null
surface.getShapeParts(chunk.getData(), relBlockInChunk, blockFace, true, output, faceOrigin); surface.getFaces(chunk.getData(), blockInChunk, blockFace, true, output, faceOrigin);
faceOrigin.add(offset); faceOrigin.add(offset);
} }
} }
private boolean shouldRenderOuterFace(Vec3i relBlockInChunk, RelFace face) { private boolean shouldRenderOuterFace(Vec3i blockInChunk, BlockFace face) {
relBlockInChunk.add(face.getRelVector()); blockInChunk.add(face.getVector());
try { try {
return shouldRenderWhenFacing(relBlockInChunk, face); return shouldRenderWhenFacing(blockInChunk, face);
} finally { } finally {
relBlockInChunk.sub(face.getRelVector()); blockInChunk.sub(face.getVector());
} }
} }
private boolean shouldRenderInnerFace(Vec3i relBlockInChunk, RelFace face) { private boolean shouldRenderInnerFace(Vec3i blockInChunk, BlockFace face) {
return shouldRenderWhenFacing(relBlockInChunk, face); return shouldRenderWhenFacing(blockInChunk, face);
} }
private boolean shouldRenderWhenFacing(Vec3i relBlockInChunk, RelFace face) { private boolean shouldRenderWhenFacing(Vec3i blockInChunk, BlockFace face) {
if (GenericChunks.containsBiC(relBlockInChunk)) { if (chunk.containsBiC(blockInChunk)) {
return shouldRenderWhenFacingLocal(relBlockInChunk, face); return shouldRenderWhenFacingLocal(blockInChunk, face);
} else { } else {
return shouldRenderWhenFacingNeighbor(relBlockInChunk, face); return shouldRenderWhenFacingNeighbor(blockInChunk, face);
} }
} }
private boolean shouldRenderWhenFacingLocal(Vec3i relBlockInChunk, RelFace face) { private boolean shouldRenderWhenFacingLocal(Vec3i blockInChunk, BlockFace face) {
BlockOptimizedSurface block = getBlock(relBlockInChunk).block; BlockOptimizedSurface block = getBlock(blockInChunk).block;
if (block == null) { if (block == null) {
return true; return true;
@ -336,37 +340,36 @@ public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
return true; return true;
} }
private boolean shouldRenderWhenFacingNeighbor(Vec3i relBlockInLocalChunk, RelFace face) { private boolean shouldRenderWhenFacingNeighbor(Vec3i blockInLocalChunk, BlockFace face) {
Vec3i blockInChunk = Vectors.grab3i(); Vec3i blockInChunk = Vectors.grab3i().set(blockInLocalChunk.x, blockInLocalChunk.y, blockInLocalChunk.z);
chunk.resolve(relBlockInLocalChunk, blockInChunk);
Vec3i chunkPos = Vectors.grab3i().set(chunk.getX(), chunk.getY(), chunk.getZ()); Vec3i chunkPos = Vectors.grab3i().set(chunk.getX(), chunk.getY(), chunk.getZ());
try { try {
// Determine blockInChunk and chunkPos // Determine blockInChunk and chunkPos
if (blockInChunk.x == -1) { if (blockInLocalChunk.x == -1) {
blockInChunk.x = BLOCKS_PER_CHUNK - 1; blockInChunk.x = BLOCKS_PER_CHUNK - 1;
chunkPos.x -= 1; chunkPos.x -= 1;
} else if (blockInChunk.x == BLOCKS_PER_CHUNK) { } else if (blockInLocalChunk.x == BLOCKS_PER_CHUNK) {
blockInChunk.x = 0; blockInChunk.x = 0;
chunkPos.x += 1; chunkPos.x += 1;
} else if (blockInChunk.y == -1) { } else if (blockInLocalChunk.y == -1) {
blockInChunk.y = BLOCKS_PER_CHUNK - 1; blockInChunk.y = BLOCKS_PER_CHUNK - 1;
chunkPos.y -= 1; chunkPos.y -= 1;
} else if (blockInChunk.y == BLOCKS_PER_CHUNK) { } else if (blockInLocalChunk.y == BLOCKS_PER_CHUNK) {
blockInChunk.y = 0; blockInChunk.y = 0;
chunkPos.y += 1; chunkPos.y += 1;
} else if (blockInChunk.z == -1) { } else if (blockInLocalChunk.z == -1) {
blockInChunk.z = BLOCKS_PER_CHUNK - 1; blockInChunk.z = BLOCKS_PER_CHUNK - 1;
chunkPos.z -= 1; chunkPos.z -= 1;
} else if (blockInChunk.z == BLOCKS_PER_CHUNK) { } else if (blockInLocalChunk.z == BLOCKS_PER_CHUNK) {
blockInChunk.z = 0; blockInChunk.z = 0;
chunkPos.z += 1; chunkPos.z += 1;
} else { } else {
throw new AssertionError( throw new AssertionError(
"Requested incorrent neighbor (" "Requested incorrent neighbor ("
+ relBlockInLocalChunk.x + "; " + blockInLocalChunk.x + "; "
+ relBlockInLocalChunk.y + "; " + blockInLocalChunk.y + "; "
+ relBlockInLocalChunk.z + ")" + blockInLocalChunk.z + ")"
); );
} }
@ -379,11 +382,8 @@ public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
return true; return true;
BlockOptimizedSurface bos = (BlockOptimizedSurface) block; BlockOptimizedSurface bos = (BlockOptimizedSurface) block;
RelFace rotatedFace = face.rotate(this.chunk.getUp(), chunk.getUp()); if (!bos.isOpaque(face))
if (!bos.isOpaque(rotatedFace)) {
return true; return true;
}
return false; return false;

View File

@ -19,45 +19,18 @@
package ru.windcorp.progressia.client.world.entity; package ru.windcorp.progressia.client.world.entity;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
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.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.generic.EntityGeneric; import ru.windcorp.progressia.common.world.generic.GenericEntity;
public abstract class EntityRenderable implements Renderable, EntityGeneric { public abstract class EntityRenderable implements Renderable, GenericEntity {
private final EntityData data; private final EntityData data;
private long stateComputedForFrame = -1;
public EntityRenderable(EntityData data) { public EntityRenderable(EntityData data) {
this.data = data; this.data = data;
} }
/**
* Updates the state of this model. This method is invoked exactly once per
* renderable per frame before this entity is queried for the first time.
*/
protected void update() {
// Do nothing
}
private void updateIfNecessary() {
if (stateComputedForFrame != GraphicsInterface.getFramesRendered()) {
update();
stateComputedForFrame = GraphicsInterface.getFramesRendered();
}
}
@Override
public final void render(ShapeRenderHelper renderer) {
updateIfNecessary();
doRender(renderer);
}
protected abstract void doRender(ShapeRenderHelper renderer);
public EntityData getData() { public EntityData getData() {
return data; return data;
} }
@ -72,41 +45,7 @@ public abstract class EntityRenderable implements Renderable, EntityGeneric {
return getData().getId(); return getData().getId();
} }
@Override public void getViewPoint(Vec3 output) {
public long getEntityId() {
return getData().getEntityId();
}
public final Vec3 getLookingAt(Vec3 output) {
if (output == null) output = new Vec3();
updateIfNecessary();
doGetLookingAt(output);
return output;
}
protected void doGetLookingAt(Vec3 output) {
output.set(getData().getLookingAt());
}
public final Vec3 getUpVector(Vec3 output) {
if (output == null) output = new Vec3();
updateIfNecessary();
doGetUpVector(output);
return output;
}
protected void doGetUpVector(Vec3 output) {
output.set(getData().getUpVector());
}
public final Vec3 getViewPoint(Vec3 output) {
if (output == null) output = new Vec3();
updateIfNecessary();
doGetViewPoint(output);
return output;
}
protected void doGetViewPoint(Vec3 output) {
output.set(0, 0, 0); output.set(0, 0, 0);
} }

View File

@ -43,7 +43,6 @@ public class HumanoidModel extends NPedModel {
@Override @Override
protected void applyTransform(Mat4 mat, NPedModel model) { protected void applyTransform(Mat4 mat, NPedModel model) {
super.applyTransform(mat, model);
float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset; float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset;
float value = sin(phase); float value = sin(phase);
float amplitude = getSwingAmplitude((HumanoidModel) model) * model.getVelocityParameter(); float amplitude = getSwingAmplitude((HumanoidModel) model) * model.getVelocityParameter();

View File

@ -18,8 +18,11 @@
package ru.windcorp.progressia.client.world.entity; 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.pow;
import static java.lang.Math.toRadians; import static java.lang.Math.toRadians;
import static ru.windcorp.progressia.common.util.FloatMathUtil.normalizeAngle;
import glm.Glm; import glm.Glm;
import glm.mat._4.Mat4; import glm.mat._4.Mat4;
@ -29,9 +32,6 @@ 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.Units; import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.util.FloatMathUtil;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
public abstract class NPedModel extends EntityRenderable { public abstract class NPedModel extends EntityRenderable {
@ -51,30 +51,29 @@ public abstract class NPedModel extends EntityRenderable {
ShapeRenderHelper renderer, ShapeRenderHelper renderer,
NPedModel model NPedModel model
) { ) {
renderer.pushTransform().translate(translation);
applyTransform(renderer.pushTransform(), model); applyTransform(renderer.pushTransform(), model);
renderable.render(renderer); renderable.render(renderer);
renderer.popTransform(); renderer.popTransform();
renderer.popTransform();
} }
protected void applyTransform(Mat4 mat, NPedModel model) { protected abstract void applyTransform(Mat4 mat, NPedModel model);
mat.translate(getTranslation());
}
public Vec3 getTranslation() { public Vec3 getTranslation() {
return translation; return translation;
} }
public Mat4 getTransform(Mat4 output, NPedModel model) {
if (output == null) output = new Mat4().identity();
applyTransform(output, model);
return output;
}
} }
public static class Body extends BodyPart { public static class Body extends BodyPart {
public Body(Renderable renderable) { public Body(Renderable renderable) {
super(renderable, null); super(renderable, null);
} }
@Override
protected void applyTransform(Mat4 mat, NPedModel model) {
// Do nothing
}
} }
public static class Head extends BodyPart { public static class Head extends BodyPart {
@ -98,8 +97,7 @@ public abstract class NPedModel extends EntityRenderable {
@Override @Override
protected void applyTransform(Mat4 mat, NPedModel model) { protected void applyTransform(Mat4 mat, NPedModel model) {
super.applyTransform(mat, model); mat.rotateZ(model.getHeadYaw()).rotateY(model.getHeadPitch());
mat.rotateZ(-model.getHeadYaw()).rotateY(-model.getHeadPitch());
} }
public Vec3 getViewPoint() { public Vec3 getViewPoint() {
@ -107,8 +105,6 @@ public abstract class NPedModel extends EntityRenderable {
} }
} }
public static boolean flag;
protected final Body body; protected final Body body;
protected final Head head; protected final Head head;
@ -132,9 +128,7 @@ public abstract class NPedModel extends EntityRenderable {
private float walkingFrequency; private float walkingFrequency;
private final Vec3 bodyLookingAt = new Vec3().set(0); private float bodyYaw = Float.NaN;
private final Mat4 bodyTransform = new Mat4();
private float headYaw; private float headYaw;
private float headPitch; private float headPitch;
@ -144,14 +138,17 @@ public abstract class NPedModel extends EntityRenderable {
this.head = head; this.head = head;
this.scale = scale; this.scale = scale;
computeRotations(); evaluateAngles();
} }
@Override @Override
protected void doRender(ShapeRenderHelper renderer) { public void render(ShapeRenderHelper renderer) {
renderer.pushTransform().scale(scale).mul(bodyTransform); renderer.pushTransform().scale(scale).rotateZ(bodyYaw);
renderBodyParts(renderer); renderBodyParts(renderer);
renderer.popTransform(); renderer.popTransform();
accountForVelocity();
evaluateAngles();
} }
protected void renderBodyParts(ShapeRenderHelper renderer) { protected void renderBodyParts(ShapeRenderHelper renderer) {
@ -159,106 +156,50 @@ public abstract class NPedModel extends EntityRenderable {
head.render(renderer, this); head.render(renderer, this);
} }
@Override private void evaluateAngles() {
protected void update() { float globalYaw = normalizeAngle(getData().getYaw());
advanceTime();
computeRotations();
}
private void computeRotations() { if (Float.isNaN(bodyYaw)) {
if (!bodyLookingAt.any()) { bodyYaw = globalYaw;
getData().getForwardVector(bodyLookingAt);
headYaw = 0; headYaw = 0;
} else { } else {
ensureBodyLookingAtIsPerpendicularToUpVector(); headYaw = normalizeAngle(globalYaw - bodyYaw);
computeDesiredHeadYaw();
clampHeadYawAndChangeBodyLookingAt();
}
recomputeBodyTransform();
setHeadPitch();
}
private void ensureBodyLookingAtIsPerpendicularToUpVector() {
Vec3 up = getData().getUpVector();
if (up.dot(bodyLookingAt) > 1 - 1e-4) return;
Vec3 tmp = Vectors.grab3();
tmp.set(up).mul(-up.dot(bodyLookingAt)).add(bodyLookingAt);
float tmpLength = tmp.length();
if (tmpLength > 1e-4) {
bodyLookingAt.set(tmp).div(tmpLength);
} else {
// bodyLookingAt is suddenly parallel to up vector -- PANIC! ENTERING RESCUE MODE!
getData().getForwardVector(bodyLookingAt);
}
Vectors.release(tmp);
}
private void computeDesiredHeadYaw() {
Vec3 newDirection = getData().getForwardVector(null);
Vec3 oldDirection = bodyLookingAt;
Vec3 up = getData().getUpVector();
headYaw = (float) VectorUtil.getAngle(oldDirection, newDirection, up);
}
private void clampHeadYawAndChangeBodyLookingAt() {
float bodyYawChange = 0;
if (headYaw > +head.maxYaw) { if (headYaw > +head.maxYaw) {
bodyYawChange = headYaw - +head.maxYaw; bodyYaw += headYaw - +head.maxYaw;
headYaw = +head.maxYaw; headYaw = +head.maxYaw;
} else if (headYaw < -head.maxYaw) { } else if (headYaw < -head.maxYaw) {
bodyYawChange = headYaw - -head.maxYaw; bodyYaw += headYaw - -head.maxYaw;
headYaw = -head.maxYaw; headYaw = -head.maxYaw;
} }
if (bodyYawChange != 0) {
VectorUtil.rotate(bodyLookingAt, getData().getUpVector(), bodyYawChange);
}
} }
private void recomputeBodyTransform() { bodyYaw = normalizeAngle(bodyYaw);
Vec3 u = getData().getUpVector();
Vec3 f = bodyLookingAt;
Vec3 s = Vectors.grab3();
s.set(u).cross(f); headPitch = Glm.clamp(
getData().getPitch(),
bodyTransform.identity().set( -head.maxPitch,
+f.x, +f.y, +f.z, 0, head.maxPitch
-s.x, -s.y, -s.z, 0,
+u.x, +u.y, +u.z, 0,
0, 0, 0, 1
); );
Vectors.release(s);
} }
private void setHeadPitch() { private void accountForVelocity() {
headPitch = Glm.clamp((float) getData().getPitch(), -head.maxPitch, +head.maxPitch); Vec3 horizontal = new Vec3(getData().getVelocity());
} horizontal.z = 0;
private void advanceTime() {
Vec3 horizontal = getData().getUpVector()
.mul_(-getData().getUpVector().dot(getData().getVelocity()))
.add(getData().getVelocity());
velocity = horizontal.length(); velocity = horizontal.length();
computeVelocityParameter(); evaluateVelocityCoeff();
// TODO switch to world time
walkingParameter += velocity * GraphicsInterface.getFrameLength() * 1000; walkingParameter += velocity * GraphicsInterface.getFrameLength() * 1000;
rotateBodyWithMovement(horizontal); bodyYaw += velocityParameter * normalizeAngle(
(float) (atan2(horizontal.y, horizontal.x) - bodyYaw)
) * min(1, GraphicsInterface.getFrameLength() * 10);
} }
private void computeVelocityParameter() { private void evaluateVelocityCoeff() {
if (velocity > maxEffectiveVelocity) { if (velocity > maxEffectiveVelocity) {
velocityParameter = 1; velocityParameter = 1;
} else { } else {
@ -266,39 +207,17 @@ public abstract class NPedModel extends EntityRenderable {
} }
} }
private void rotateBodyWithMovement(Vec3 target) {
if (velocityParameter == 0 || !target.any() || Glm.equals(target, bodyLookingAt)) {
return;
}
Vec3 axis = getData().getUpVector();
float yawDifference = FloatMathUtil.normalizeAngle(
(float) VectorUtil.getAngle(
bodyLookingAt,
target.normalize_(),
axis
)
);
float bodyYawChange =
velocityParameter *
yawDifference *
(float) Math.expm1(GraphicsInterface.getFrameLength() * 10);
VectorUtil.rotate(bodyLookingAt, axis, bodyYawChange);
}
@Override @Override
protected void doGetViewPoint(Vec3 output) { public void getViewPoint(Vec3 output) {
Mat4 m = new Mat4(); Mat4 m = new Mat4();
Vec4 v = new Vec4(); Vec4 v = new Vec4();
m.identity() m.identity()
.scale(scale) .scale(scale)
.mul(bodyTransform); .rotateZ(bodyYaw)
.translate(head.getTranslation())
head.getTransform(m, this); .rotateZ(headYaw)
.rotateY(headPitch);
v.set(head.getViewPoint(), 1); v.set(head.getViewPoint(), 1);
m.mul(v); m.mul(v);
@ -314,8 +233,8 @@ public abstract class NPedModel extends EntityRenderable {
return head; return head;
} }
public Vec3 getBodyLookingAt() { public float getBodyYaw() {
return bodyLookingAt; return bodyYaw;
} }
public float getHeadYaw() { public float getHeadYaw() {

View File

@ -44,7 +44,6 @@ public class QuadripedModel extends NPedModel {
@Override @Override
protected void applyTransform(Mat4 mat, NPedModel model) { protected void applyTransform(Mat4 mat, NPedModel model) {
super.applyTransform(mat, model);
float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset; float phase = model.getWalkingFrequency() * model.getWalkingParameter() + animationOffset;
float value = sin(phase); float value = sin(phase);
float amplitude = ((QuadripedModel) model).getWalkingSwing() * model.getVelocityParameter(); float amplitude = ((QuadripedModel) model).getWalkingSwing() * model.getVelocityParameter();

View File

@ -18,21 +18,28 @@
package ru.windcorp.progressia.client.world.tile; package ru.windcorp.progressia.client.world.tile;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer; import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.generic.TileGeneric; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.generic.GenericTile;
public class TileRender extends Namespaced implements TileGeneric { public class TileRender extends Namespaced implements GenericTile {
public TileRender(String id) { public TileRender(String id) {
super(id); super(id);
} }
public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk, RelFace face) { public void render(ShapeRenderHelper renderer, BlockFace face) {
throw new UnsupportedOperationException(
"TileRender.render() not implemented in " + this
);
}
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace face) {
return null; return null;
} }

View File

@ -1,170 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.world.tile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
import glm.mat._3.Mat3;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapePart;
import ru.windcorp.progressia.client.graphics.model.ShapeParts;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSimple.TileOptimizedCustom;
import ru.windcorp.progressia.common.util.Matrices;
import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.rels.AxisRotations;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class TileRenderCross extends TileRender implements TileOptimizedCustom {
private static final float SQRT_2_OVER_2 = (float) Math.sqrt(2) / 2;
private static final float[] ONE_AND_NEGATIVE_ONE = new float[] { 1, -1 };
private final Texture texture;
private final float width;
public TileRenderCross(String id, Texture texture, boolean allowStretching) {
super(id);
this.texture = texture;
this.width = allowStretching ? 1 : SQRT_2_OVER_2;
}
public Texture getTexture(RelFace blockFace) {
return texture;
}
public Vec4 getColorMultiplier(RelFace blockFace) {
return Colors.WHITE;
}
@Override
public void getShapeParts(
DefaultChunkData chunk,
Vec3i bic,
RelFace blockFace,
Consumer<ShapePart> output
) {
Mat4 transform = Matrices.grab4();
Vec3 origin = Vectors.grab3();
Vec3 width = Vectors.grab3();
Vec3 height = Vectors.grab3();
Mat3 resolutionMatrix = AxisRotations.getResolutionMatrix3(blockFace.resolve(AbsFace.POS_Z));
Vec4 color = getColorMultiplier(blockFace);
Texture texture = getTexture(blockFace);
float originOffset = (1 - this.width) / 2;
WorldRenderProgram program = WorldRenderProgram.getDefault();
for (int i = 0; getTransform(chunk, bic, blockFace, i, transform); i++) {
for (float flip : ONE_AND_NEGATIVE_ONE) {
origin.set(flip * (originOffset - 0.5f), originOffset - 0.5f, 0);
width.set(flip * this.width, this.width, 0);
height.set(0, 0, 1);
VectorUtil.applyMat4(origin, transform);
VectorUtil.rotateOnly(width, transform);
VectorUtil.rotateOnly(height, transform);
origin.z += 1 - 0.5f;
if (blockFace != RelFace.UP) {
resolutionMatrix.mul(origin);
resolutionMatrix.mul(width);
resolutionMatrix.mul(height);
}
origin.add(bic.x, bic.y, bic.z);
output.accept(
ShapeParts.createRectangle(
program,
texture,
color,
origin,
width,
height,
false
)
);
output.accept(
ShapeParts.createRectangle(
program,
texture,
color,
origin,
width,
height,
true
)
);
}
}
Matrices.release(transform);
Vectors.release(origin);
Vectors.release(width);
Vectors.release(height);
}
protected boolean getTransform(
DefaultChunkData chunk,
Vec3i relBlockInChunk,
RelFace blockFace,
int count,
Mat4 output
) {
output.identity();
return count == 0;
}
@Override
public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk, RelFace blockFace) {
Collection<ShapePart> parts = new ArrayList<>(4);
getShapeParts(chunk, blockInChunk, blockFace, parts::add);
return new Shape(
Usage.STATIC,
WorldRenderProgram.getDefault(),
parts.toArray(new ShapePart[parts.size()])
);
}
@Override
public boolean needsOwnRenderable() {
return false;
}
}

View File

@ -16,38 +16,34 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package ru.windcorp.progressia.test; package ru.windcorp.progressia.client.world.tile;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.world.tile.TileRenderSurface; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace;
public class TestTileRenderGrass extends TileRenderSurface { public class TileRenderGrass extends TileRenderSurface {
private final Texture topTexture; private final Texture topTexture;
private final Texture sideTexture; private final Texture sideTexture;
private final boolean isOpaque;
public TestTileRenderGrass( public TileRenderGrass(
String id, String id,
Texture top, Texture top,
Texture side, Texture side
boolean isOpaque
) { ) {
super(id); super(id);
this.topTexture = top; this.topTexture = top;
this.sideTexture = side; this.sideTexture = side;
this.isOpaque = isOpaque;
} }
@Override @Override
public Texture getTexture(RelFace face) { public Texture getTexture(BlockFace face) {
return (face == RelFace.UP) ? topTexture : sideTexture; return (face == BlockFace.TOP) ? topTexture : sideTexture;
} }
@Override @Override
public boolean isOpaque(RelFace face) { public boolean isOpaque(BlockFace face) {
return isOpaque && face == RelFace.UP; return face == BlockFace.TOP;
} }
} }

View File

@ -20,8 +20,8 @@ package ru.windcorp.progressia.client.world.tile;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.graphics.model.EmptyModel; import ru.windcorp.progressia.client.graphics.model.EmptyModel;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class TileRenderNone extends TileRender { public class TileRenderNone extends TileRender {
@ -30,7 +30,7 @@ public class TileRenderNone extends TileRender {
} }
@Override @Override
public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk, RelFace face) { public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace face) {
return EmptyModel.getInstance(); return EmptyModel.getInstance();
} }

View File

@ -19,7 +19,7 @@
package ru.windcorp.progressia.client.world.tile; package ru.windcorp.progressia.client.world.tile;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class TileRenderOpaqueSurface extends TileRenderSurface { public class TileRenderOpaqueSurface extends TileRenderSurface {
@ -28,7 +28,7 @@ public class TileRenderOpaqueSurface extends TileRenderSurface {
} }
@Override @Override
public boolean isOpaque(RelFace face) { public boolean isOpaque(BlockFace face) {
return true; return true;
} }

View File

@ -1,27 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.world.tile;
import ru.windcorp.progressia.client.world.ChunkRender;
import ru.windcorp.progressia.client.world.block.BlockRender;
import ru.windcorp.progressia.common.world.generic.TileGenericReferenceRO;
public interface TileRenderReference
extends TileGenericReferenceRO<BlockRender, TileRender, TileRenderStack, TileRenderReference, ChunkRender> {
}

View File

@ -18,15 +18,12 @@
package ru.windcorp.progressia.client.world.tile; package ru.windcorp.progressia.client.world.tile;
import java.util.AbstractList;
import ru.windcorp.progressia.client.world.ChunkRender; import ru.windcorp.progressia.client.world.ChunkRender;
import ru.windcorp.progressia.client.world.block.BlockRender; import ru.windcorp.progressia.common.world.generic.GenericTileStack;
import ru.windcorp.progressia.common.world.TileDataStack; import ru.windcorp.progressia.common.world.tile.TileDataStack;
import ru.windcorp.progressia.common.world.generic.TileGenericStackRO;
public abstract class TileRenderStack public abstract class TileRenderStack
extends AbstractList<TileRender> extends GenericTileStack<TileRenderStack, TileRender, ChunkRender> {
implements TileGenericStackRO<BlockRender, TileRender, TileRenderStack, TileRenderReference, ChunkRender> {
public abstract TileDataStack getData(); public abstract TileDataStack getData();

View File

@ -25,17 +25,16 @@ import glm.vec._3.i.Vec3i;
import glm.vec._4.Vec4; import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.Usage; import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.model.ShapePart; import ru.windcorp.progressia.client.graphics.model.Face;
import ru.windcorp.progressia.client.graphics.model.ShapeParts; import ru.windcorp.progressia.client.graphics.model.Faces;
import ru.windcorp.progressia.client.graphics.model.Shape; import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface.TileOptimizedSurface; import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface.TileOptimizedSurface;
import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace;
public abstract class TileRenderSurface extends TileRender implements TileOptimizedSurface { public abstract class TileRenderSurface extends TileRender implements TileOptimizedSurface {
@ -50,41 +49,41 @@ public abstract class TileRenderSurface extends TileRender implements TileOptimi
this(id, null); this(id, null);
} }
public Texture getTexture(RelFace blockFace) { public Texture getTexture(BlockFace blockFace) {
return texture; return texture;
} }
public Vec4 getColorMultiplier(RelFace blockFace) { public Vec4 getColorMultiplier(BlockFace blockFace) {
return Colors.WHITE; return Colors.WHITE;
} }
@Override @Override
public final void getShapeParts( public final void getFaces(
DefaultChunkData chunk, Vec3i relBlockInChunk, RelFace blockFace, ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
boolean inner, boolean inner,
Consumer<ShapePart> output, Consumer<Face> output,
Vec3 offset Vec3 offset
) { ) {
output.accept(createFace(chunk, relBlockInChunk, blockFace, inner, offset)); output.accept(createFace(chunk, blockInChunk, blockFace, inner, offset));
} }
private ShapePart createFace( private Face createFace(
DefaultChunkData chunk, Vec3i blockInChunk, RelFace blockFace, ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
boolean inner, boolean inner,
Vec3 offset Vec3 offset
) { ) {
return ShapeParts.createBlockFace( return Faces.createBlockFace(
WorldRenderProgram.getDefault(), WorldRenderProgram.getDefault(),
getTexture(blockFace), getTexture(blockFace),
getColorMultiplier(blockFace), getColorMultiplier(blockFace),
offset, offset,
blockFace.resolve(AbsFace.POS_Z), blockFace,
inner inner
); );
} }
@Override @Override
public Renderable createRenderable(DefaultChunkData chunk, Vec3i blockInChunk, RelFace blockFace) { public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace) {
return new Shape( return new Shape(
Usage.STATIC, Usage.STATIC,
WorldRenderProgram.getDefault(), WorldRenderProgram.getDefault(),

View File

@ -19,7 +19,7 @@
package ru.windcorp.progressia.client.world.tile; package ru.windcorp.progressia.client.world.tile;
import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class TileRenderTransparentSurface extends TileRenderSurface { public class TileRenderTransparentSurface extends TileRenderSurface {
@ -28,7 +28,7 @@ public class TileRenderTransparentSurface extends TileRenderSurface {
} }
@Override @Override
public boolean isOpaque(RelFace face) { public boolean isOpaque(BlockFace face) {
return false; return false;
} }

View File

@ -1,134 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.collision;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableList;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.rels.AxisRotations;
public class AABBRotator implements AABBoid {
private class AABBRotatorWall implements Wall {
private final int id;
public AABBRotatorWall(int id) {
this.id = id;
}
@Override
public void getOrigin(Vec3 output) {
parent.getWall(id).getOrigin(output);
AxisRotations.resolve(output, upSupplier.get(), output);
}
@Override
public void getWidth(Vec3 output) {
parent.getWall(id).getWidth(output);
AxisRotations.resolve(output, upSupplier.get(), output);
}
@Override
public void getHeight(Vec3 output) {
parent.getWall(id).getHeight(output);
AxisRotations.resolve(output, upSupplier.get(), output);
}
}
private final Supplier<AbsFace> upSupplier;
private final Supplier<Vec3> hingeSupplier;
private final AABBoid parent;
private final AABBRotatorWall[] walls = new AABBRotatorWall[AbsFace.BLOCK_FACE_COUNT];
{
for (int id = 0; id < walls.length; ++id) {
walls[id] = new AABBRotatorWall(id);
}
}
public AABBRotator(Supplier<AbsFace> upSupplier, Supplier<Vec3> hingeSupplier, AABBoid parent) {
this.upSupplier = upSupplier;
this.hingeSupplier = hingeSupplier;
this.parent = parent;
}
@Override
public void setOrigin(Vec3 origin) {
Vec3 relativeOrigin = Vectors.grab3();
Vec3 hinge = hingeSupplier.get();
origin.sub(hinge, relativeOrigin);
AxisRotations.relativize(relativeOrigin, upSupplier.get(), relativeOrigin);
relativeOrigin.add(hinge);
parent.setOrigin(relativeOrigin);
Vectors.release(relativeOrigin);
}
@Override
public void moveOrigin(Vec3 displacement) {
parent.moveOrigin(displacement);
}
@Override
public void getOrigin(Vec3 output) {
parent.getOrigin(output);
Vec3 hinge = hingeSupplier.get();
output.sub(hinge);
AxisRotations.resolve(output, upSupplier.get(), output);
output.add(hinge);
}
@Override
public void getSize(Vec3 output) {
parent.getSize(output);
AxisRotations.resolve(output, upSupplier.get(), output);
output.abs();
}
@Override
public Wall getWall(int faceId) {
return walls[faceId];
}
public static CollisionModel rotate(Supplier<AbsFace> upSupplier, Supplier<Vec3> hingeSupplier, CollisionModel parent) {
if (parent instanceof AABBoid) {
return new AABBRotator(upSupplier, hingeSupplier, (AABBoid) parent);
} else if (parent instanceof CompoundCollisionModel) {
ImmutableList.Builder<CollisionModel> models = ImmutableList.builder();
for (CollisionModel original : ((CompoundCollisionModel) parent).getModels()) {
models.add(rotate(upSupplier, hingeSupplier, original));
}
return new CompoundCollisionModel(models.build());
} else {
throw new RuntimeException("not supported");
}
}
}

View File

@ -19,7 +19,7 @@
package ru.windcorp.progressia.common.collision; package ru.windcorp.progressia.common.collision;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public interface AABBoid extends CollisionModel { public interface AABBoid extends CollisionModel {
@ -27,7 +27,7 @@ public interface AABBoid extends CollisionModel {
void getSize(Vec3 output); void getSize(Vec3 output);
default Wall getWall(AbsFace face) { default Wall getWall(BlockFace face) {
return getWall(face.getId()); return getWall(face.getId());
} }

View File

@ -20,7 +20,7 @@ package ru.windcorp.progressia.common.collision;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
public class TranslatedAABB implements AABBoid { public class TranslatedAABB implements AABBoid {
@ -51,7 +51,7 @@ public class TranslatedAABB implements AABBoid {
private AABBoid parent; private AABBoid parent;
private final Vec3 translation = new Vec3(); private final Vec3 translation = new Vec3();
private final TranslatedAABBWall[] walls = new TranslatedAABBWall[AbsFace.BLOCK_FACE_COUNT]; private final TranslatedAABBWall[] walls = new TranslatedAABBWall[BlockFace.BLOCK_FACE_COUNT];
{ {
for (int id = 0; id < walls.length; ++id) { for (int id = 0; id < walls.length; ++id) {

View File

@ -24,7 +24,7 @@ import java.util.Collection;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.LowOverheadCache; import ru.windcorp.progressia.common.util.LowOverheadCache;
import ru.windcorp.progressia.common.world.DefaultWorldData; import ru.windcorp.progressia.common.world.WorldData;
public class WorldCollisionHelper { public class WorldCollisionHelper {
@ -79,7 +79,7 @@ public class WorldCollisionHelper {
* checked against * checked against
* @param maxTime maximum collision time * @param maxTime maximum collision time
*/ */
public void tuneToCollideable(DefaultWorldData world, Collideable collideable, float maxTime) { public void tuneToCollideable(WorldData world, Collideable collideable, float maxTime) {
activeBlockModels.forEach(blockModelCache::release); activeBlockModels.forEach(blockModelCache::release);
activeBlockModels.clear(); activeBlockModels.clear();
CollisionPathComputer.forEveryBlockInCollisionPath( CollisionPathComputer.forEveryBlockInCollisionPath(

View File

@ -25,7 +25,7 @@ import ru.windcorp.progressia.common.collision.colliders.Collider.ColliderWorksp
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.rels.AbsFace; import ru.windcorp.progressia.common.world.block.BlockFace;
class AABBoidCollider { class AABBoidCollider {
@ -50,7 +50,7 @@ class AABBoidCollider {
computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody); computeCollisionVelocity(collisionVelocity, obstacleBody, colliderBody);
// For every wall of collision space // For every wall of collision space
for (int i = 0; i < AbsFace.BLOCK_FACE_COUNT; ++i) { for (int i = 0; i < BlockFace.BLOCK_FACE_COUNT; ++i) {
Wall wall = originCollisionSpace.getWall(i); Wall wall = originCollisionSpace.getWall(i);
Collision collision = computeWallCollision( Collision collision = computeWallCollision(
@ -122,51 +122,46 @@ class AABBoidCollider {
return output; return output;
} }
// @formatter:off
/* /*
* Here we determine whether a collision has actually happened, and if it did, at what moment. * Here we determine whether a collision has actually happened, and if it
* * did, at what moment.
* The basic idea is to compute the moment of collision and impact coordinates in wall coordinate space. * The basic idea is to compute the moment of collision and impact
* Then, we can check impact coordinates to determine if we actually hit the wall or flew by and then * coordinates in wall coordinate space.
* check time to make sure the collision is not too far in the future and not in the past. * Then, we can check impact coordinates to determine if we actually hit the
* * wall or flew by and then
* check time to make sure the collision is not too far in the future and
* not in the past.
* DETAILED EXPLANATION: * DETAILED EXPLANATION:
* * Consider a surface defined by an origin r_wall and two noncollinear
* Consider a surface defined by an origin r_wall and two noncollinear nonzero vectors w and h. * nonzero vectors w and h.
* Consider a line defined by an origin r_line and a nonzero vector v. * Consider a line defined by an origin r_line and a nonzero vector v.
*
* Then, a collision occurs if there exist x, y and t such that * Then, a collision occurs if there exist x, y and t such that
* ______ _ * ______ _
* r_line + v * t * r_line + v * t
*
* and * and
* ______ _ _ * ______ _ _
* r_wall + w * x + h * y * r_wall + w * x + h * y
* * describe the same location (indeed, this corresponds to a collision at
* describe the same location (indeed, this corresponds to a collision at moment t0 + t * moment t0 + t
* with a point on the wall with coordinates (x; y) in (w; h) coordinate system). * with a point on the wall with coordinates (x; y) in (w; h) coordinate
* * system).
* Therefore, * Therefore,
* ______ _ ______ _ _ * ______ _ ______ _ _
* r_line + v*t = r_wall + w*x + h*y; * r_line + v*t = r_wall + w*x + h*y;
*
* _ ⎡w_x h_x -v_x⎤ ⎡x⎤ _ ______ ______ * _ ⎡w_x h_x -v_x⎤ ⎡x⎤ _ ______ ______
* r = ⎢w_y h_y -v_y⎥ * ⎢y⎥, where r = r_line - r_wall; * r = ⎢w_y h_y -v_y⎥ * ⎢y⎥, where r = r_line - r_wall;
* ⎣w_z h_z -v_z⎦ ⎣t⎦ * ⎣w_z h_z -v_z⎦ ⎣t⎦
*
* ⎡x⎤ ⎡w_x h_x -v_x⎤ -1 _ * ⎡x⎤ ⎡w_x h_x -v_x⎤ -1 _
* ⎢y⎥ = ⎢w_y h_y -v_y⎥ * r, if the matrix is invertible. * ⎢y⎥ = ⎢w_y h_y -v_y⎥ * r, if the matrix is invertible.
* ⎣t⎦ ⎣w_z h_z -v_z⎦ * ⎣t⎦ ⎣w_z h_z -v_z⎦
*
* Then, one only needs to ensure that: * Then, one only needs to ensure that:
* 0 < x < 1, * 0 < x < 1,
* 0 < y < 1, and * 0 < y < 1, and
* 0 < t < T, where T is remaining tick time. * 0 < t < T, where T is remaining tick time.
* * If the matrix is not invertible or any of the conditions are not met, no
* If the matrix is not invertible or any of the conditions are not met, no collision happened. * collision happened.
* 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.
*/ */
// @formatter:on
private static Collision computeWallCollision( private static Collision computeWallCollision(
Wall obstacleWall, Wall obstacleWall,
AABBoid colliderModel, AABBoid colliderModel,

View File

@ -27,7 +27,7 @@ import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.collision.*; import ru.windcorp.progressia.common.collision.*;
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.DefaultWorldData; import ru.windcorp.progressia.common.world.WorldData;
public class Collider { public class Collider {
@ -36,7 +36,7 @@ public class Collider {
/** /**
* Dear Princess Celestia, * Dear Princess Celestia,
* <p> * <p>
* When {@linkplain #advanceTime(Collection, Collision, DefaultWorldData, float) * When {@linkplain #advanceTime(Collection, Collision, WorldData, float)
* advancing time}, * advancing time},
* time step for all entities <em>except</em> currently colliding bodies is * time step for all entities <em>except</em> currently colliding bodies is
* the current * the current
@ -61,7 +61,7 @@ public class Collider {
public static void performCollisions( public static void performCollisions(
List<? extends Collideable> colls, List<? extends Collideable> colls,
DefaultWorldData world, WorldData world,
float tickLength, float tickLength,
ColliderWorkspace workspace ColliderWorkspace workspace
) { ) {
@ -96,7 +96,7 @@ public class Collider {
private static Collision getFirstCollision( private static Collision getFirstCollision(
List<? extends Collideable> colls, List<? extends Collideable> colls,
float tickLength, float tickLength,
DefaultWorldData world, WorldData world,
ColliderWorkspace workspace ColliderWorkspace workspace
) { ) {
Collision result = null; Collision result = null;
@ -126,7 +126,7 @@ public class Collider {
private static void tuneWorldCollisionHelper( private static void tuneWorldCollisionHelper(
Collideable coll, Collideable coll,
float tickLength, float tickLength,
DefaultWorldData world, WorldData world,
ColliderWorkspace workspace ColliderWorkspace workspace
) { ) {
WorldCollisionHelper wch = workspace.worldCollisionHelper; WorldCollisionHelper wch = workspace.worldCollisionHelper;
@ -194,7 +194,7 @@ public class Collider {
Collision collision, Collision collision,
Collection<? extends Collideable> colls, Collection<? extends Collideable> colls,
DefaultWorldData world, WorldData world,
float tickLength, float tickLength,
ColliderWorkspace workspace ColliderWorkspace workspace
) { ) {
@ -212,10 +212,8 @@ public class Collider {
handlePhysics(collision); handlePhysics(collision);
} }
// @formatter:off
/* /*
* Here we compute the change in body velocities due to a collision. * Here we compute the change in body velocities due to a collision.
*
* We make the following simplifications: * We make the following simplifications:
* 1) The bodies are perfectly rigid; * 1) The bodies are perfectly rigid;
* 2) The collision is perfectly inelastic * 2) The collision is perfectly inelastic
@ -224,13 +222,12 @@ public class Collider {
* 4) No tangential friction exists * 4) No tangential friction exists
* (bodies do not experience friction when sliding against each other); * (bodies do not experience friction when sliding against each other);
* 5) Velocities are not relativistic. * 5) Velocities are not relativistic.
*
* Angular momentum is ignored per 3) and 4), * Angular momentum is ignored per 3) and 4),
* e.g. when something pushes an end of a long stick, the stick does not rotate. * e.g. when something pushes an end of a long stick, the stick does not
* * rotate.
* DETAILED EXPLANATION: * DETAILED EXPLANATION:
* * Two spherical (sic) bodies, a and b, experience a perfectly inelastic
* Two spherical (sic) bodies, a and b, experience a perfectly inelastic collision * collision
* along a unit vector * along a unit vector
* _ _ _ _ _ * _ _ _ _ _
* n = (w h) / (|w h|), * n = (w h) / (|w h|),
@ -241,43 +238,40 @@ public class Collider {
* ___ ___ * ___ ___
* After the collision desired velocities are u_a and u_b, respectively. * After the collision desired velocities are u_a and u_b, respectively.
* _ * _
* (Notation convention: suffix 'n' denotes a vector projection onto vector n, * (Notation convention: suffix 'n' denotes a vector projection onto vector
* n,
* and suffix 't' denotes a vector projection onto the dividing plane.) * and suffix 't' denotes a vector projection onto the dividing plane.)
* * Consider the law of conservation of momentum for axis n and the dividing
* Consider the law of conservation of momentum for axis n and the dividing plane: * plane:
* ____________ ____________ ________________ * ____________ ____________ ________________
* n: ⎧ p_a_before_n + p_b_before_n = p_common_after_n; * n: ⎧ p_a_before_n + p_b_before_n = p_common_after_n;
* ⎨ ___________ ____________ * ⎨ ___________ ____________
* t: ⎩ p_i_after_t = p_i_before_t for any i in {a, b}. * t: ⎩ p_i_after_t = p_i_before_t for any i in {a, b}.
*
* Expressing all p_* in given terms: * Expressing all p_* in given terms:
* ___ _ ___ _ ___ ___ ____ ____ * ___ _ ___ _ ___ ___ ____ ____
* n: ⎧ M_a * (v_a ⋅ n) + M_b * (v_b ⋅ n) = (M_a + M_b) * u_n, where u_n ≡ u_an = u_bn; * n: ⎧ M_a * (v_a ⋅ n) + M_b * (v_b ⋅ n) = (M_a + M_b) * u_n, where u_n ≡
* u_an = u_bn;
* ⎨ ____ ___ _ ___ _ * ⎨ ____ ___ _ ___ _
* t: ⎩ u_it = v_i - n * (v_i ⋅ n) for any i in {a, b}. * t: ⎩ u_it = v_i - n * (v_i ⋅ n) for any i in {a, b}.
*
* Therefore: * Therefore:
* ___ _ ___ _ ___ _ * ___ _ ___ _ ___ _
* u_n = n * ( M_a/(M_a + M_b) * v_a ⋅ n + M_b/(M_a + M_b) * v_b ⋅ n ); * u_n = n * ( M_a/(M_a + M_b) * v_a ⋅ n + M_b/(M_a + M_b) * v_b ⋅ n );
*
* or, equivalently, * or, equivalently,
* ___ _ ___ _ ___ _ * ___ _ ___ _ ___ _
* u_n = n * ( m_a * v_a ⋅ n + m_b * v_b ⋅ n ), * u_n = n * ( m_a * v_a ⋅ n + m_b * v_b ⋅ n ),
*
* where m_a and m_b are relative masses (see below). * where m_a and m_b are relative masses (see below).
*
* Finally, * Finally,
* ___ ____ ___ * ___ ____ ___
* u_i = u_it + u_n for any i in {a, b}. * u_i = u_it + u_n for any i in {a, b}.
* * The usage of relative masses m_i permits a convenient generalization of
* The usage of relative masses m_i permits a convenient generalization of the algorithm * the algorithm
* for infinite masses, signifying masses "significantly greater" than finite masses: * for infinite masses, signifying masses "significantly greater" than
* * finite masses:
* 1) If both M_a and M_b are finite, let m_i = M_i / (M_a + M_b) for any i in {a, b}. * 1) If both M_a and M_b are finite, let m_i = M_i / (M_a + M_b) for any i
* in {a, b}.
* 2) If M_i is finite but M_j is infinite, let m_i = 0 and m_j = 1. * 2) If M_i is finite but M_j is infinite, let m_i = 0 and m_j = 1.
* 3) If both M_a and M_b are infinite, let m_i = 1/2 for any i in {a, b}. * 3) If both M_a and M_b are infinite, let m_i = 1/2 for any i in {a, b}.
*/ */
// @formatter:on
private static void handlePhysics(Collision collision) { private static void handlePhysics(Collision collision) {
// Fuck JGLM // Fuck JGLM
Vec3 n = Vectors.grab3(); Vec3 n = Vectors.grab3();
@ -361,7 +355,7 @@ public class Collider {
private static void advanceTime( private static void advanceTime(
Collection<? extends Collideable> colls, Collection<? extends Collideable> colls,
Collision exceptions, Collision exceptions,
DefaultWorldData world, WorldData world,
float step float step
) { ) {
world.advanceTime(step); world.advanceTime(step);

View File

@ -22,7 +22,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import ru.windcorp.progressia.common.comms.packets.Packet; import ru.windcorp.progressia.common.comms.packets.Packet;
@ -55,8 +54,6 @@ public abstract class CommsChannel {
private final Collection<CommsListener> listeners = Collections.synchronizedCollection(new ArrayList<>()); private final Collection<CommsListener> listeners = Collections.synchronizedCollection(new ArrayList<>());
private final List<Packet> pendingPackets = Collections.synchronizedList(new ArrayList<>());
protected abstract void doSendPacket(Packet packet) throws IOException; protected abstract void doSendPacket(Packet packet) throws IOException;
private synchronized void sendPacket( private synchronized void sendPacket(
@ -104,20 +101,9 @@ public abstract class CommsChannel {
public abstract void disconnect(); public abstract void disconnect();
protected void onPacketReceived(Packet packet) { protected void onPacketReceived(Packet packet) {
pendingPackets.add(packet);
}
protected void forwardPacketToListeners(Packet packet) {
listeners.forEach(l -> l.onPacketReceived(packet)); listeners.forEach(l -> l.onPacketReceived(packet));
} }
public void processPackets() {
synchronized (pendingPackets) {
pendingPackets.forEach(this::forwardPacketToListeners);
pendingPackets.clear();
}
}
public void addListener(CommsListener listener) { public void addListener(CommsListener listener) {
listeners.add(listener); listeners.add(listener);
} }

View File

@ -29,14 +29,15 @@ import com.google.common.util.concurrent.MoreExecutors;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
/** /**
* This class had to be written because there is no legal way to instantiate a * This class had to be written because there is not legal way to instantiate a
* non-async {@link EventBus} with both a custom identifier and a custom * non-async
* exception handler. Which is a shame. Guava maintainers know about the issue * {@link EventBus} with both a custom identifier and a custom exception
* but have rejected solutions multiple times <em>without a clearly stated * handler. Which
* reason</em>; looks like some dirty reflection will have to do. * is a shame. Guava maintainers know about the issue but have rejected
* <p> * solutions multiple
* When explicitly referencing this class, please mention its usage in * times <em>without a clearly stated reason</em>; looks like some dirty
* implementation notes because it is unreliable long-term. * reflection will
* have to do.
* *
* @author javapony * @author javapony
*/ */

View File

@ -29,20 +29,6 @@ public abstract class AbstractStatefulObjectLayout
super(objectId); super(objectId);
} }
@Override
public StateStorage createStorage() {
StateStorage storage = instantiateStorage();
int fieldCount = getFieldCount();
for (int i = 0; i < fieldCount; ++i) {
getField(i).setDefault(storage);
}
return storage;
}
protected abstract StateStorage instantiateStorage();
protected abstract int getFieldCount(); protected abstract int getFieldCount();
protected abstract StateField getField(int fieldIndex); protected abstract StateField getField(int fieldIndex);

View File

@ -1,64 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.state;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public interface Encodable {
/**
* Sets the state of this object according to the binary representation read
* from {@code input}. If {@code context == COMMS}, the state of local
* fields is unspecified after this operation.
*
* @param input a {@link DataInput} that a state can be read from
* @param context the context
* @throws IOException if the state is encoded poorly or an error occurs
* in {@code input}
*/
void read(DataInput input, IOContext context) throws IOException;
/**
* Writes the binary representation of the state of this object to the
* {@code output}.
*
* @param output a {@link DataOutput} that a state can be written to
* @param context the context
* @throws IOException if an error occurs in {@code output}
*/
void write(DataOutput output, IOContext context) throws IOException;
/**
* Turns {@code destination} into a deep copy of this object.
* <p>
* Changes the provided object so that:
* <ul>
* <li>the provided object equals this object; and</li>
* <li>the provided object is independent of this object, meaning no change
* to {@code destination} can affect this object.</li>
* </ul>
*
* @param destination the object to copy this object into. Runtime class
* must match this class
* @return {@code destination}
*/
void copy(Encodable destination);
}

View File

@ -19,14 +19,11 @@
package ru.windcorp.progressia.common.state; package ru.windcorp.progressia.common.state;
import gnu.trove.map.TIntIntMap; import gnu.trove.map.TIntIntMap;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntIntHashMap;
import gnu.trove.map.hash.TIntObjectHashMap;
public class HashMapStateStorage extends StateStorage { public class HashMapStateStorage extends StateStorage {
private final TIntIntMap ints = new TIntIntHashMap(); private final TIntIntMap ints = new TIntIntHashMap();
private final TIntObjectMap<Object> objects = new TIntObjectHashMap<>();
@Override @Override
public int getInt(int index) { public int getInt(int index) {
@ -38,14 +35,4 @@ public class HashMapStateStorage extends StateStorage {
ints.put(index, value); ints.put(index, value);
} }
@Override
public Object getObject(int index) {
return objects.get(index);
}
@Override
public void setObject(int index, Object object) {
objects.put(index, object);
}
} }

View File

@ -20,9 +20,6 @@ package ru.windcorp.progressia.common.state;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import ru.windcorp.progressia.common.state.codec.ObjectCodec;
public class InspectingStatefulObjectLayout public class InspectingStatefulObjectLayout
extends AbstractStatefulObjectLayout { extends AbstractStatefulObjectLayout {
@ -36,7 +33,7 @@ public class InspectingStatefulObjectLayout
} }
@Override @Override
public StateStorage instantiateStorage() { public StateStorage createStorage() {
return new HashMapStateStorage(); return new HashMapStateStorage();
} }
@ -85,31 +82,6 @@ public class InspectingStatefulObjectLayout
} }
private class Obj<T> implements StateFieldBuilder.Obj<T> {
private final ObjectCodec<T> codec;
private final Supplier<T> defaultValue;
public Obj(ObjectCodec<T> codec, Supplier<T> defaultValue) {
this.codec = codec;
this.defaultValue = defaultValue;
}
@Override
public ObjectStateField<T> build() {
return registerField(
new ObjectStateField<T>(
id,
isLocal,
fieldIndexCounters.getObjectsThenIncrement(),
codec,
defaultValue
)
);
}
}
private final String id; private final String id;
private boolean isLocal = true; private boolean isLocal = true;
@ -123,11 +95,6 @@ public class InspectingStatefulObjectLayout
return new Int(); return new Int();
} }
@Override
public <T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue) {
return new Obj<T>(codec, defaultValue);
}
@Override @Override
public StateFieldBuilder setLocal(boolean isLocal) { public StateFieldBuilder setLocal(boolean isLocal) {
this.isLocal = isLocal; this.isLocal = isLocal;

View File

@ -79,9 +79,4 @@ public class IntStateField extends StateField {
return get(a) == get(b); return get(a) == get(b);
} }
@Override
public void setDefault(StateStorage storage) {
storage.setInt(getIndex(), 0);
}
} }

View File

@ -1,107 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.state;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.function.Supplier;
import ru.windcorp.progressia.common.state.codec.ObjectCodec;
public class ObjectStateField<T> extends StateField {
private final ObjectCodec<T> codec;
private final Supplier<T> defaultValue;
public ObjectStateField(
String id,
boolean isLocal,
int index,
ObjectCodec<T> codec,
Supplier<T> defaultValue
) {
super(id, isLocal, index);
this.codec = codec;
this.defaultValue = defaultValue;
}
public ObjectCodec<T> getCodec() {
return codec;
}
@SuppressWarnings("unchecked")
public T get(StatefulObject object) {
return (T) object.getStorage().getObject(getIndex());
}
public void setNow(StatefulObject object, T value) {
object.getStorage().setObject(getIndex(), value);
}
public void set(StateChanger changer, T value) {
changer.setObject(this, value);
}
@Override
public void read(
StatefulObject object,
DataInput input,
IOContext context
)
throws IOException {
T previous = get(object);
T result = codec.read(previous, input, context);
object.getStorage().setObject(getIndex(), result);
}
@Override
public void write(
StatefulObject object,
DataOutput output,
IOContext context
)
throws IOException {
codec.write(object.getStorage().getObject(getIndex()), output, context);
}
@Override
public void copy(StatefulObject from, StatefulObject to) {
setNow(to, get(from));
}
@Override
public int computeHashCode(StatefulObject object) {
return codec.computeHashCode(get(object));
}
@Override
public boolean areEqual(StatefulObject a, StatefulObject b) {
return codec.areEqual(get(a), get(b));
}
@Override
public void setDefault(StateStorage storage) {
storage.setObject(getIndex(), defaultValue.get());
}
}

View File

@ -21,11 +21,9 @@ package ru.windcorp.progressia.common.state;
public class OptimizedStateStorage extends StateStorage { public class OptimizedStateStorage extends StateStorage {
private final int[] ints; private final int[] ints;
private final Object[] objects;
public OptimizedStateStorage(PrimitiveCounters sizes) { public OptimizedStateStorage(PrimitiveCounters sizes) {
this.ints = new int[sizes.getInts()]; this.ints = new int[sizes.getInts()];
this.objects = new Object[sizes.getObjects()];
} }
@Override @Override
@ -38,14 +36,4 @@ public class OptimizedStateStorage extends StateStorage {
ints[index] = value; ints[index] = value;
} }
@Override
public Object getObject(int index) {
return objects[index];
}
@Override
public void setObject(int index, Object object) {
objects[index] = object;
}
} }

View File

@ -19,12 +19,9 @@
package ru.windcorp.progressia.common.state; package ru.windcorp.progressia.common.state;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import ru.windcorp.progressia.common.state.codec.ObjectCodec;
public class OptimizedStatefulObjectLayout public class OptimizedStatefulObjectLayout
extends AbstractStatefulObjectLayout { extends AbstractStatefulObjectLayout {
@ -52,7 +49,7 @@ public class OptimizedStatefulObjectLayout
} }
@Override @Override
public StateStorage instantiateStorage() { public StateStorage createStorage() {
return new OptimizedStateStorage(sizes); return new OptimizedStateStorage(sizes);
} }
@ -75,17 +72,6 @@ public class OptimizedStatefulObjectLayout
}; };
} }
@Override
public <T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue) {
return new Obj<T>() {
@SuppressWarnings("unchecked")
@Override
public ObjectStateField<T> build() {
return (ObjectStateField<T>) result;
}
};
}
@Override @Override
public StateFieldBuilder setLocal(boolean isLocal) { public StateFieldBuilder setLocal(boolean isLocal) {
return this; return this;

View File

@ -21,14 +21,12 @@ package ru.windcorp.progressia.common.state;
class PrimitiveCounters { class PrimitiveCounters {
private int ints = 0; private int ints = 0;
private int objects = 0;
public PrimitiveCounters() { public PrimitiveCounters() {
} }
public PrimitiveCounters(PrimitiveCounters copyFrom) { public PrimitiveCounters(PrimitiveCounters copyFrom) {
this.ints = copyFrom.ints; this.ints = copyFrom.ints;
this.objects = copyFrom.objects;
} }
public int getInts() { public int getInts() {
@ -39,12 +37,4 @@ class PrimitiveCounters {
return this.ints++; return this.ints++;
} }
public int getObjects() {
return objects;
}
public int getObjectsThenIncrement() {
return this.objects++;
}
} }

View File

@ -21,6 +21,5 @@ package ru.windcorp.progressia.common.state;
public interface StateChanger { public interface StateChanger {
void setInt(IntStateField field, int value); void setInt(IntStateField field, int value);
<T> void setObject(ObjectStateField<T> field, T value);
} }

View File

@ -67,6 +67,4 @@ public abstract class StateField extends Namespaced {
public abstract boolean areEqual(StatefulObject a, StatefulObject b); public abstract boolean areEqual(StatefulObject a, StatefulObject b);
public abstract void setDefault(StateStorage storage);
} }

View File

@ -18,59 +18,14 @@
package ru.windcorp.progressia.common.state; package ru.windcorp.progressia.common.state;
import java.util.function.Supplier;
import ru.windcorp.progressia.common.state.codec.ObjectCodec;
import ru.windcorp.progressia.common.state.codec.ObjectCodecRegistry;
public interface StateFieldBuilder { public interface StateFieldBuilder {
public static interface Int { public static interface Int {
IntStateField build(); IntStateField build();
} }
public static interface Obj<T> {
ObjectStateField<T> build();
}
Int ofInt(); Int ofInt();
<T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue);
default <T> Obj<T> of(Class<T> clazz, Supplier<T> defaultValue) {
ObjectCodec<T> codec = ObjectCodecRegistry.get(clazz);
return of(codec, defaultValue);
}
default <T> Obj<T> of(ObjectCodec<T> codec, T defaultValue) {
return of(codec, (Supplier<T>) () -> codec.copy(defaultValue, null));
}
default <T> Obj<T> of(Class<T> clazz, T defaultValue) {
ObjectCodec<T> codec = ObjectCodecRegistry.get(clazz);
return of(codec, (Supplier<T>) () -> codec.copy(defaultValue, null));
}
default <T> Obj<T> of(ObjectCodec<T> codec) {
return of(codec, (Supplier<T>) () -> null);
}
default <T> Obj<T> of(Class<T> clazz) {
ObjectCodec<T> codec = ObjectCodecRegistry.get(clazz);
return of(codec, (Supplier<T>) () -> null);
}
@SuppressWarnings("unchecked")
default <T> Obj<T> def(Supplier<T> defaultValue) {
Class<T> clazz = (Class<T>) defaultValue.get().getClass();
return of(clazz, defaultValue);
}
@SuppressWarnings("unchecked")
default <T> Obj<T> def(T defaultValue) {
return of((Class<T>) defaultValue.getClass(), defaultValue);
}
StateFieldBuilder setLocal(boolean isLocal); StateFieldBuilder setLocal(boolean isLocal);
default StateFieldBuilder setLocal() { default StateFieldBuilder setLocal() {

View File

@ -24,8 +24,4 @@ public abstract class StateStorage {
public abstract void setInt(int index, int value); public abstract void setInt(int index, int value);
public abstract Object getObject(int index);
public abstract void setObject(int index, Object object);
} }

View File

@ -52,7 +52,7 @@ import ru.windcorp.progressia.common.util.namespaces.Namespaced;
* type.</li> * type.</li>
* </ul> * </ul>
*/ */
public abstract class StatefulObject extends Namespaced implements Encodable { public abstract class StatefulObject extends Namespaced {
private final StatefulObjectLayout layout; private final StatefulObjectLayout layout;
@ -133,7 +133,6 @@ public abstract class StatefulObject extends Namespaced implements Encodable {
* @throws IOException if the state is encoded poorly or an error occurs * @throws IOException if the state is encoded poorly or an error occurs
* in {@code input} * in {@code input}
*/ */
@Override
public void read(DataInput input, IOContext context) throws IOException { public void read(DataInput input, IOContext context) throws IOException {
getLayout().read(this, input, context); getLayout().read(this, input, context);
} }
@ -146,7 +145,6 @@ public abstract class StatefulObject extends Namespaced implements Encodable {
* @param context the context * @param context the context
* @throws IOException if an error occurs in {@code output} * @throws IOException if an error occurs in {@code output}
*/ */
@Override
public void write(DataOutput output, IOContext context) throws IOException { public void write(DataOutput output, IOContext context) throws IOException {
getLayout().write(this, output, context); getLayout().write(this, output, context);
} }
@ -168,8 +166,7 @@ public abstract class StatefulObject extends Namespaced implements Encodable {
* *
* @param destination the object to copy this object into. * @param destination the object to copy this object into.
*/ */
@Override public StatefulObject copy(StatefulObject destination) {
public void copy(Encodable destination) {
Objects.requireNonNull(destination, "destination"); Objects.requireNonNull(destination, "destination");
if (destination == this) { if (destination == this) {
@ -185,7 +182,8 @@ public abstract class StatefulObject extends Namespaced implements Encodable {
); );
} }
getLayout().copy(this, (StatefulObject) destination); getLayout().copy(this, destination);
return destination;
} }
/** /**

View File

@ -1,52 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.state.codec;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Objects;
import ru.windcorp.progressia.common.state.Encodable;
import ru.windcorp.progressia.common.state.IOContext;
public class EncodableCodec extends ReusableObjectCodec<Encodable> {
public EncodableCodec() {
super(Encodable.class);
}
@Override
protected Encodable doRead(Encodable previous, DataInput input, IOContext context) throws IOException {
previous.read(input, context);
return previous;
}
@Override
protected void doWrite(Encodable obj, DataOutput output, IOContext context) throws IOException {
obj.write(output, context);
}
@Override
protected Encodable doCopy(Encodable object, Encodable previous) {
Objects.requireNonNull(previous, "previous");
object.copy(previous);
return previous;
}
}

View File

@ -1,43 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.state.codec;
import java.io.DataInput;
import java.io.IOException;
import ru.windcorp.progressia.common.state.IOContext;
public abstract class ImmutableObjectCodec<T> extends ObjectCodec<T> {
public ImmutableObjectCodec(Class<T> clazz) {
super(clazz);
}
@Override
public final T copy(T object, T previous) {
return object;
}
@Override
protected final T doRead(T previous, DataInput input, IOContext context) throws IOException {
return doRead(input, context);
}
protected abstract T doRead(DataInput input, IOContext context) throws IOException;
}

View File

@ -1,73 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.state.codec;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Objects;
import ru.windcorp.progressia.common.state.IOContext;
public abstract class ObjectCodec<T> {
private final Class<T> clazz;
public ObjectCodec(Class<T> clazz) {
this.clazz = clazz;
}
public Class<T> getDataType() {
return clazz;
}
@SuppressWarnings("unchecked")
public final T read(Object previous, DataInput input, IOContext context) throws IOException {
assert previous == null || clazz.isInstance(previous)
: "Cannot use codec " + this + " on object of type " + previous.getClass();
T result = doRead((T) previous, input, context);
assert result == null || clazz.isInstance(previous)
: "Codec " + this + " read object of type " + previous.getClass();
return result;
}
protected abstract T doRead(T previous, DataInput input, IOContext context) throws IOException;
@SuppressWarnings("unchecked")
public final void write(Object value, DataOutput output, IOContext context) throws IOException {
assert value == null || clazz.isInstance(value)
: "Cannot use codec " + this + " on object of type " + value.getClass();
doWrite((T) value, output, context);
}
protected abstract void doWrite(T obj, DataOutput output, IOContext context) throws IOException;
public abstract T copy(T object, T previous);
public int computeHashCode(T object) {
return Objects.hashCode(object);
}
public boolean areEqual(T a, T b) {
return Objects.equals(a, b);
}
}

View File

@ -1,49 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.state.codec;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import ru.windcorp.progressia.common.state.Encodable;
public class ObjectCodecRegistry {
private static final Map<Class<?>, ObjectCodec<?>> CODECS = Collections.synchronizedMap(new HashMap<>());
private static final EncodableCodec ENCODABLE_FALLBACK = new EncodableCodec();
public static void register(ObjectCodec<?> codec) {
CODECS.put(codec.getDataType(), codec);
}
@SuppressWarnings("unchecked")
public static <T> ObjectCodec<T> get(Class<T> clazz) {
ObjectCodec<?> codec = CODECS.get(clazz);
if (codec != null) {
return (ObjectCodec<T>) codec;
}
if (Encodable.class.isAssignableFrom(clazz)) {
return (ObjectCodec<T>) ENCODABLE_FALLBACK;
}
throw new IllegalArgumentException("No codec registered for class " + clazz);
}
}

View File

@ -1,42 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.state.codec;
public abstract class ReusableObjectCodec<T> extends ObjectCodec<T> {
public ReusableObjectCodec(Class<T> clazz) {
super(clazz);
}
@Override
public final T copy(T object, T previous) {
if (object == null) {
return null;
}
T result = doCopy(object, previous);
assert result != null : "copy() returned null";
assert areEqual(object, result) : "copy() does not equal original: " + result + " != " + object;
return result;
}
protected abstract T doCopy(T object, T previous);
}

View File

@ -1,225 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
public class ArrayFloatRangeMap<E> implements FloatRangeMap<E> {
protected static class Node<E> implements Comparable<Node<E>> {
public float pos;
public E value;
public Node(float pos, E value) {
this.pos = pos;
this.value = value;
}
@Override
public int compareTo(Node<E> o) {
return Float.compare(pos, o.pos);
}
}
/*
* Expects a random-access list
*/
protected final List<Node<E>> nodes;
protected int ranges = 0;
protected static final int DEFAULT_CAPACITY = 16;
public ArrayFloatRangeMap(int capacity) {
this.nodes = new ArrayList<>(2 * capacity);
}
public ArrayFloatRangeMap() {
this(DEFAULT_CAPACITY);
}
@Override
public int size() {
return this.ranges;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
private int nextIndex = 0;
{
assert nodes.isEmpty() || nodes.get(nextIndex).value != null;
}
private void findNext() {
while (nextIndex < nodes.size()) {
nextIndex++;
Node<E> node = nodes.get(nextIndex);
if (node.value != null) return;
}
}
@Override
public boolean hasNext() {
return nextIndex < nodes.size();
}
@Override
public E next() {
E result = nodes.get(nextIndex).value;
findNext();
return result;
}
};
}
/**
* Returns an index of the smallest {@link Node} larger than or exactly at
* {@code position}.
*
* @param position the position to look up
* @return an index in the {@link #nodes} list containing the first
* {@link Node} whose {@link Node#pos} is not smaller than
* {@code position}, or {@code nodes.size()} if no such index exists
*/
protected int findCeiling(float position) {
/*
* Implementation based on OpenJDK's
* Collections.indexedBinarySearch(List, Comparator)
*/
int low = 0;
int high = nodes.size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
float midVal = nodes.get(mid).pos;
int cmp = Float.compare(midVal, position);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return low; // the insertion point is the desired index
}
/**
* Returns an index of the largest {@link Node} smaller than or exactly at
* {@code position}.
*
* @param position the position to look up
* @return an index in the {@link #nodes} list containing the last
* {@link Node} whose {@link Node#pos} is not greater than
* {@code position}, or {@code -1} if no such index exists
*/
protected int findFloor(float position) {
/*
* Implementation based on OpenJDK's
* Collections.indexedBinarySearch(List, Comparator)
*/
int low = 0;
int high = nodes.size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
float midVal = nodes.get(mid).pos;
int cmp = Float.compare(midVal, position);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return low - 1; // the insertion point immediately follows the desired index
}
protected Node<E> getEffectiveNode(float at) {
int effectiveNodeIndex = findFloor(at);
if (effectiveNodeIndex < 0) return null;
return nodes.get(effectiveNodeIndex);
}
@Override
public E get(float at) {
Node<E> effectiveNode = getEffectiveNode(at);
return effectiveNode == null ? null : effectiveNode.value;
}
@Override
public void put(float min, float max, E element) {
Objects.requireNonNull(element, "element");
if (!(max > min)) // This funky construction also deals with NaNs since NaNs always fail any comparison
{
throw new IllegalArgumentException(max + " is not greater than " + min);
}
int indexOfInsertionOfMin = findCeiling(min);
nodes.add(indexOfInsertionOfMin, new Node<E>(min, element));
ranges++;
ListIterator<Node<E>> it = nodes.listIterator(indexOfInsertionOfMin + 1);
E elementEffectiveImmediatelyAfterInsertedRange = null;
if (indexOfInsertionOfMin > 0) {
elementEffectiveImmediatelyAfterInsertedRange = nodes.get(indexOfInsertionOfMin - 1).value;
}
while (it.hasNext()) {
Node<E> node = it.next();
if (node.pos >= max) {
break;
}
elementEffectiveImmediatelyAfterInsertedRange = node.value;
if (elementEffectiveImmediatelyAfterInsertedRange != null) {
// Removing an actual range
ranges--;
}
it.remove();
}
if (max != Float.POSITIVE_INFINITY) {
nodes.add(indexOfInsertionOfMin + 1, new Node<E>(max, elementEffectiveImmediatelyAfterInsertedRange));
if (elementEffectiveImmediatelyAfterInsertedRange != null) {
// We might have added one right back
ranges++;
}
}
}
}

View File

@ -1,36 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.util;
public interface FloatRangeMap<E> extends Iterable<E> {
void put(float min, float max, E element);
E get(float at);
int size();
default boolean defines(float position) {
return get(position) != null;
}
default E getOrDefault(float at, E def) {
E result = get(at);
return result == null ? def : result;
}
}

View File

@ -20,8 +20,6 @@ package ru.windcorp.progressia.common.util;
import java.util.function.Consumer; import java.util.function.Consumer;
import glm.Glm;
import glm.mat._3.Mat3;
import glm.mat._4.Mat4; import glm.mat._4.Mat4;
import glm.vec._2.Vec2; import glm.vec._2.Vec2;
import glm.vec._2.d.Vec2d; import glm.vec._2.d.Vec2d;
@ -39,40 +37,6 @@ public class VectorUtil {
X, Y, Z, W; X, Y, Z, W;
} }
public static enum SignedAxis {
POS_X(Axis.X, +1),
NEG_X(Axis.X, -1),
POS_Y(Axis.Y, +1),
NEG_Y(Axis.Y, -1),
POS_Z(Axis.Z, +1),
NEG_Z(Axis.Z, -1),
POS_W(Axis.W, +1),
NEG_W(Axis.W, -1);
private final Axis axis;
private final boolean isPositive;
private SignedAxis(Axis axis, int sign) {
this.axis = axis;
this.isPositive = (sign == +1 ? true : false);
}
/**
* @return the axis
*/
public Axis getAxis() {
return axis;
}
public boolean isPositive() {
return isPositive;
}
public int getSign() {
return isPositive ? +1 : -1;
}
}
public static void iterateCuboid( public static void iterateCuboid(
int x0, int x0,
int y0, int y0,
@ -160,11 +124,7 @@ public class VectorUtil {
iterateCuboidAround(center.x, center.y, center.z, diameter, action); iterateCuboidAround(center.x, center.y, center.z, diameter, action);
} }
public static Vec3 applyMat4(Vec3 in, Mat4 mat, Vec3 out) { public static void applyMat4(Vec3 in, Mat4 mat, Vec3 out) {
if (out == null) {
out = new Vec3();
}
Vec4 vec4 = Vectors.grab4(); Vec4 vec4 = Vectors.grab4();
vec4.set(in, 1f); vec4.set(in, 1f);
@ -172,173 +132,15 @@ public class VectorUtil {
out.set(vec4.x, vec4.y, vec4.z); out.set(vec4.x, vec4.y, vec4.z);
Vectors.release(vec4); Vectors.release(vec4);
return out;
} }
public static Vec3 applyMat4(Vec3 inOut, Mat4 mat) { public static void applyMat4(Vec3 inOut, Mat4 mat) {
return applyMat4(inOut, mat, inOut); Vec4 vec4 = Vectors.grab4();
} vec4.set(inOut, 1f);
public static Vec3 rotateOnly(Vec3 in, Mat4 mat, Vec3 out) { mat.mul(vec4);
if (out == null) {
out = new Vec3();
}
Mat3 mat3 = Matrices.grab3(); inOut.set(vec4.x, vec4.y, vec4.z);
mat3.set(mat);
mat3.mul(in, out);
Matrices.release(mat3);
return out;
}
public static Vec3 rotateOnly(Vec3 inOut, Mat4 mat) {
return rotateOnly(inOut, mat, inOut);
}
public static Mat4 lookAt(Mat4 applyTo, Vec3 target, Vec3 up, Mat4 output) {
if (output == null) {
output = new Mat4();
}
Mat4 lookAtTransform = Matrices.grab4();
// Adapted from Glm.lookAt - we use Z as up
// f(normalize(target))
float fX = target.x;
float fY = target.y;
float fZ = target.z;
float inverseSqrt = 1f / (float) Math.sqrt(fX * fX + fY * fY + fZ * fZ);
fX *= inverseSqrt;
fY *= inverseSqrt;
fZ *= inverseSqrt;
// s(normalize(cross(up, f)))
float sX = up.y * fZ - up.z * fY;
float sY = up.z * fX - up.x * fZ;
float sZ = up.x * fY - up.y * fX;
inverseSqrt = 1.0f / (float) Math.sqrt(sX * sX + sY * sY + sZ * sZ);
sX *= inverseSqrt;
sY *= inverseSqrt;
sZ *= inverseSqrt;
// u(cross(f, s))
float uX = fY * sZ - fZ * sY;
float uY = fZ * sX - fX * sZ;
float uZ = fX * sY - fY * sX;
lookAtTransform.m00 = fX;
lookAtTransform.m01 = fY;
lookAtTransform.m02 = fZ;
lookAtTransform.m03 = 0f;
lookAtTransform.m10 = sX;
lookAtTransform.m11 = sY;
lookAtTransform.m12 = sZ;
lookAtTransform.m13 = 0f;
lookAtTransform.m20 = uX;
lookAtTransform.m21 = uY;
lookAtTransform.m22 = uZ;
lookAtTransform.m23 = 0f;
lookAtTransform.m30 = 0;
lookAtTransform.m31 = 0;
lookAtTransform.m32 = 0;
lookAtTransform.m33 = 1f;
applyTo.mul(lookAtTransform, output);
Matrices.release(lookAtTransform);
return output;
}
public static Mat4 lookAt(Vec3 center, Vec3 up, Mat4 inOut) {
return lookAt(inOut, center, up, inOut);
}
public static Vec3 rotate(Vec3 in, Vec3 axis, float angle, Vec3 out) {
if (out == null) {
out = new Vec3();
}
Mat3 mat = Matrices.grab3();
mat.identity().rotate(angle, axis);
mat.mul(in, out);
Matrices.release(mat);
return out;
}
public static Vec3 rotate(Vec3 inOut, Vec3 axis, float angle) {
return rotate(inOut, axis, angle, inOut);
}
public static double getAngle(Vec3 from, Vec3 to, Vec3 normal) {
Vec3 left = Vectors.grab3();
left.set(normal).cross(from);
double sign = Math.signum(left.dot(to));
double result = (float) Math.acos(Glm.clamp(from.dot(to), -1, +1)) * sign;
Vectors.release(left);
return result;
}
public static Vec3 projectOnSurface(Vec3 in, Vec3 normal, Vec3 out) {
if (in == out) {
return projectOnSurface(in, normal);
}
if (out == null) {
out = new Vec3();
}
out.set(normal).mul(-normal.dot(in)).add(in);
return out;
}
public static Vec3 projectOnSurface(Vec3 inOut, Vec3 normal) {
Vec3 buffer = Vectors.grab3();
projectOnSurface(inOut, normal, buffer);
inOut.set(buffer);
Vectors.release(buffer);
return inOut;
}
public static Vec3 projectOnVector(Vec3 in, Vec3 vector, Vec3 out) {
if (out == null) {
out = new Vec3();
}
float dot = vector.dot(in);
out.set(vector).mul(dot);
return out;
}
public static Vec3 projectOnVector(Vec3 inOut, Vec3 vector) {
return projectOnVector(inOut, vector);
}
public static float distanceSq(Vec3 a, Vec3 b) {
float x = a.x - b.x;
float y = a.y - b.y;
float z = a.z - b.z;
return x * x + y * y + z * z;
}
public static float distance(Vec3 a, Vec3 b) {
float x = a.x - b.x;
float y = a.y - b.y;
float z = a.z - b.z;
return (float) Math.sqrt(x * x + y * y + z * z);
} }
public static Vec3 linearCombination( public static Vec3 linearCombination(
@ -348,10 +150,6 @@ public class VectorUtil {
float kb, float kb,
Vec3 output Vec3 output
) { ) {
if (output == null) {
output = new Vec3();
}
output.set( output.set(
va.x * ka + vb.x * kb, va.x * ka + vb.x * kb,
va.y * ka + vb.y * kb, va.y * ka + vb.y * kb,
@ -369,10 +167,6 @@ public class VectorUtil {
float kc, float kc,
Vec3 output Vec3 output
) { ) {
if (output == null) {
output = new Vec3();
}
output.set( output.set(
va.x * ka + vb.x * kb + vc.x * kc, va.x * ka + vb.x * kb + vc.x * kc,
va.y * ka + vb.y * kb + vc.y * kc, va.y * ka + vb.y * kb + vc.y * kc,
@ -381,88 +175,6 @@ public class VectorUtil {
return output; return output;
} }
public static Vec3i sort(Vec3i input, Vec3i output) {
if (output == null) {
output = new Vec3i();
}
int ax = input.x, ay = input.y, az = input.z;
if (ax > ay) {
if (ax > az) {
output.x = ax;
output.y = ay > az ? ay : az;
output.z = ay > az ? az : ay;
} else {
output.x = az;
output.y = ax;
output.z = ay;
}
} else {
if (ay > az) {
output.x = ay;
output.y = ax > az ? ax : az;
output.z = ax > az ? az : ax;
} else {
output.x = az;
output.y = ay;
output.z = ax;
}
}
return output;
}
public static Vec3 sort(Vec3 input, Vec3 output) {
if (output == null) {
output = new Vec3();
}
float ax = input.x, ay = input.y, az = input.z;
if (ax > ay) {
if (ax > az) {
output.x = ax;
output.y = ay > az ? ay : az;
output.z = ay > az ? az : ay;
} else {
output.x = az;
output.y = ax;
output.z = ay;
}
} else {
if (ay > az) {
output.x = ay;
output.y = ax > az ? ax : az;
output.z = ax > az ? az : ax;
} else {
output.x = az;
output.y = ay;
output.z = ax;
}
}
return output;
}
public static Vec3i sortAfterAbs(Vec3i input, Vec3i output) {
if (output == null) {
output = new Vec3i();
}
input.abs(output);
return sort(output, output);
}
public static Vec3 sortAfterAbs(Vec3 input, Vec3 output) {
if (output == null) {
output = new Vec3();
}
input.abs(output);
return sort(output, output);
}
public static float get(Vec2 v, Axis a) { public static float get(Vec2 v, Axis a) {
switch (a) { switch (a) {
case X: case X:

View File

@ -22,27 +22,11 @@ import com.google.common.eventbus.EventBus;
import ru.windcorp.progressia.common.hacks.GuavaEventBusHijacker; import ru.windcorp.progressia.common.hacks.GuavaEventBusHijacker;
/**
* A utility for creating Guava's {@link EventBus}es that
* {@linkplain CrashReports report} exceptions instead of suppressing them.
*
* @author javapony
*/
public class ReportingEventBus { public class ReportingEventBus {
private ReportingEventBus() { private ReportingEventBus() {
} }
/**
* Instantiates a new {@link EventBus} with the provided identifier that
* reports any unhandled exceptions with {@link CrashReports}.
*
* @param identifier the identifier of the new bus
* @return the created event bus
* @implNote This implementation relies on {@link GuavaEventBusHijacker} for
* creating buses with custom identifiers and uncaught exception
* handlers. It may break suddenly with a Guava update.
*/
public static EventBus create(String identifier) { public static EventBus create(String identifier) {
return GuavaEventBusHijacker.newEventBus( return GuavaEventBusHijacker.newEventBus(
identifier, identifier,

View File

@ -1,25 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* 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.common.util.math;
@FunctionalInterface
public interface FloatFunction {
float apply(float x);
}

Some files were not shown because too many files have changed in this diff Show More