Initial commit

This commit is contained in:
2020-07-29 12:30:34 +03:00
commit e96a30c275
108 changed files with 7929 additions and 0 deletions

View File

@ -0,0 +1,22 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica;
public class Optica {
}

View File

@ -0,0 +1,26 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica;
public class OpticaLauncher {
public static void launch(String[] args, Proxy proxy) {
proxy.initialize();
}
}

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica;
public interface Proxy {
void initialize();
}

View File

@ -0,0 +1,42 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client;
import ru.windcorp.optica.Proxy;
import ru.windcorp.optica.client.graphics.GUI;
import ru.windcorp.optica.client.graphics.backend.GraphicsBackend;
import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue;
import ru.windcorp.optica.client.graphics.model.ShapeRenderProgram;
import ru.windcorp.optica.client.graphics.world.LayerWorld;
public class ClientProxy implements Proxy {
@Override
public void initialize() {
GraphicsBackend.initialize();
try {
RenderTaskQueue.waitAndInvoke(ShapeRenderProgram::init);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
GUI.addBottomLayer(new LayerWorld());
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client;
import ru.windcorp.optica.OpticaLauncher;
public class OpticaClientMain {
public static void main(String[] args) {
OpticaLauncher.launch(args, new ClientProxy());
}
}

View File

@ -0,0 +1,167 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client;
import org.lwjgl.glfw.GLFW;
import com.google.common.eventbus.Subscribe;
import glm.mat._3.Mat3;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import ru.windcorp.optica.client.graphics.Layer;
import ru.windcorp.optica.client.graphics.backend.GraphicsBackend;
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
import ru.windcorp.optica.client.graphics.input.KeyEvent;
import ru.windcorp.optica.client.graphics.input.CursorMoveEvent;
import ru.windcorp.optica.client.graphics.model.Model;
import ru.windcorp.optica.client.graphics.model.DynamicModel;
import ru.windcorp.optica.client.graphics.model.Shapes;
import ru.windcorp.optica.client.graphics.texture.SimpleTexture;
import ru.windcorp.optica.client.graphics.texture.Sprite;
import ru.windcorp.optica.client.graphics.texture.Texture;
import ru.windcorp.optica.client.graphics.texture.TextureManager;
import ru.windcorp.optica.client.graphics.world.Camera;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
public class TestLayer extends Layer {
private final Model model;
private final Camera camera = new Camera(
new Vec3(),
0, (float) Math.PI,
(float) Math.toRadians(70)
);
private final Vec3 velocity = new Vec3();
private final Vec3 tmp = new Vec3();
private final Mat3 angMat = new Mat3();
private int movementX = 0;
private int movementY = 0;
private int movementZ = 0;
public TestLayer() {
super("Test");
Texture top = qtex("grass_top");
Texture side = qtex("grass_side");
Texture bottom = qtex("grass_bottom");
model = new DynamicModel(DynamicModel.builder()
.addDynamicPart(
new Shapes.PppBuilder(top, bottom, side, side, side, side)
.create()
)
) {
@Override
protected void getDynamicTransform(int shapeIndex, Mat4 result) {
result.translate(0, 0, +5);
}
};
}
private Texture qtex(String name) {
return new SimpleTexture(new Sprite(TextureManager.load(name, false)));
}
@Override
protected void initialize() {
GraphicsInterface.subscribeToInputEvents(this);
}
private final WorldRenderer renderer = new WorldRenderer();
@Override
protected void doRender() {
camera.apply(renderer);
angMat.set().rotateY(-camera.getYaw());
tmp.set(movementX, 0, movementZ);
angMat.mul_(tmp); // bug in jglm
tmp.y = movementY;
tmp.mul(0.1f);
tmp.sub(velocity);
tmp.mul(0.1f);
velocity.add(tmp);
camera.move(velocity);
model.render(renderer);
renderer.reset();
}
private boolean flag = true;
@Subscribe
public void onKeyEvent(KeyEvent event) {
if (event.isRepeat()) return;
int multiplier = event.isPress() ? 1 : -1;
switch (event.getKey()) {
case GLFW.GLFW_KEY_W:
movementZ += -1 * multiplier;
break;
case GLFW.GLFW_KEY_S:
movementZ += +1 * multiplier;
break;
case GLFW.GLFW_KEY_A:
movementX += -1 * multiplier;
break;
case GLFW.GLFW_KEY_D:
movementX += +1 * multiplier;
break;
case GLFW.GLFW_KEY_SPACE:
movementY += +1 * multiplier;
break;
case GLFW.GLFW_KEY_LEFT_SHIFT:
movementY += -1 * multiplier;
break;
case GLFW.GLFW_KEY_ESCAPE:
if (!event.isPress()) return;
if (flag) {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
} else {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
}
flag = !flag;
break;
}
}
@Subscribe
public void onMouseMoved(CursorMoveEvent event) {
if (!flag) return;
final float yawScale = 0.002f;
final float pitchScale = yawScale;
camera.turn(
(float) (event.getChangeY() * pitchScale),
(float) (event.getChangeX() * yawScale)
);
}
}

View File

@ -0,0 +1,47 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics;
import java.util.ArrayList;
import java.util.List;
public class GUI {
private static final List<Layer> LAYERS = new ArrayList<>();
private GUI() {}
public synchronized static void addBottomLayer(Layer layer) {
LAYERS.add(layer);
}
public synchronized static void addTopLayer(Layer layer) {
LAYERS.add(0, layer);
}
public synchronized static void removeLayer(Layer layer) {
LAYERS.remove(layer);
}
public synchronized static void render() {
for (int i = LAYERS.size() - 1; i >= 0; --i) {
LAYERS.get(i).render();
}
}
}

View File

@ -0,0 +1,47 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics;
public abstract class Layer {
private final String name;
private boolean hasInitialized = false;
public Layer(String name) {
this.name = name;
}
@Override
public String toString() {
return "Layer " + name;
}
public void render() {
if (!hasInitialized) {
initialize();
hasInitialized = true;
}
doRender();
}
protected abstract void initialize();
protected abstract void doRender();
}

View File

@ -0,0 +1,103 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.glfw.GLFW.*;
public class GraphicsBackend {
private static RenderThread renderThread;
private static long windowHandle;
private static int framebufferWidth;
private static int framebufferHeight;
private static double frameLength = 1.0 / 60; // TODO do something about it
private static int framesRendered = 0;
private static double frameStart = Double.NaN;
private GraphicsBackend() {}
public static void initialize() {
startRenderThread();
}
private static void startRenderThread() {
renderThread = new RenderThread();
renderThread.start();
}
public static Thread getRenderThread() {
return renderThread;
}
static void setWindowHandle(long windowHandle) {
GraphicsBackend.windowHandle = windowHandle;
}
public static long getWindowHandle() {
return windowHandle;
}
public static int getFramebufferWidth() {
return framebufferWidth;
}
public static int getFramebufferHeight() {
return framebufferHeight;
}
static void onFramebufferResized(long window, int newWidth, int newHeight) {
if (window != windowHandle) return;
framebufferWidth = newWidth;
framebufferHeight = newHeight;
glViewport(0, 0, framebufferWidth, framebufferHeight);
}
static void startFrame() {
double now = glfwGetTime();
if (Double.isNaN(frameStart)) {
frameStart = now;
} else {
frameLength = now - frameStart;
frameStart = now;
}
}
static void endFrame() {
framesRendered++;
}
public static double getFrameStart() {
return frameStart;
}
public static double getFrameLength() {
return frameLength;
}
public static int getFramesRendered() {
return framesRendered;
}
}

View File

@ -0,0 +1,74 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import glm.vec._2.d.Vec2d;
public class GraphicsInterface {
private GraphicsInterface() {}
public static Thread getRenderThread() {
return GraphicsBackend.getRenderThread();
}
public static boolean isRenderThread() {
return Thread.currentThread() == getRenderThread();
}
public static int getFramebufferWidth() {
return GraphicsBackend.getFramebufferWidth();
}
public static int getFramebufferHeight() {
return GraphicsBackend.getFramebufferHeight();
}
public static float getAspectRatio() {
return ((float) getFramebufferWidth()) / getFramebufferHeight();
}
public static double getTime() {
return GraphicsBackend.getFrameStart();
}
public static double getFrameLength() {
return GraphicsBackend.getFrameLength();
}
public static double getFPS() {
return 1 / GraphicsBackend.getFrameLength();
}
public static void subscribeToInputEvents(Object listener) {
InputHandler.register(listener);
}
public static double getCursorX() {
return InputHandler.getCursorX();
}
public static double getCursorY() {
return InputHandler.getCursorY();
}
public static Vec2d getCursorPosition() {
return InputHandler.getCursorPosition();
}
}

View File

@ -0,0 +1,107 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import com.google.common.eventbus.EventBus;
import glm.vec._2.d.Vec2d;
import ru.windcorp.optica.client.graphics.input.*;
public class InputHandler {
private static final EventBus INPUT_EVENT_BUS = new EventBus("Input");
private static class ModifiableKeyEvent extends KeyEvent {
public void initialize(int key, int scancode, int action, int mods) {
this.key = key;
this.scancode = scancode;
this.action = action;
this.mods = mods;
}
}
private static final ModifiableKeyEvent THE_KEY_EVENT = new ModifiableKeyEvent();
static void handleKeyInput(
long window,
int key,
int scancode,
int action,
int mods
) {
if (GraphicsBackend.getWindowHandle() != window) return;
THE_KEY_EVENT.initialize(key, scancode, action, mods);
dispatch(THE_KEY_EVENT);
}
private static class ModifiableCursorMoveEvent extends CursorMoveEvent {
public void initialize(double x, double y) {
Vec2d newPos = getNewPosition();
newPos.x = x;
newPos.y = y;
}
}
private static final Vec2d CURSOR_POSITION = new Vec2d().set(
Double.NaN, Double.NaN
);
private static final ModifiableCursorMoveEvent THE_CURSOR_MOVE_EVENT =
new ModifiableCursorMoveEvent();
static void handleMouseMoveInput(
long window,
double x, double y
) {
if (GraphicsBackend.getWindowHandle() != window) return;
if (Double.isNaN(CURSOR_POSITION.x)) {
CURSOR_POSITION.set(x, y);
}
THE_CURSOR_MOVE_EVENT.initialize(x, y);
dispatch(THE_CURSOR_MOVE_EVENT);
CURSOR_POSITION.set(x, y);
}
public static double getCursorX() {
return CURSOR_POSITION.x;
}
public static double getCursorY() {
return CURSOR_POSITION.y;
}
public static Vec2d getCursorPosition() {
return CURSOR_POSITION;
}
private static void dispatch(InputEvent event) {
INPUT_EVENT_BUS.post(event);
}
public static void register(Object listener) {
INPUT_EVENT_BUS.register(listener);
}
}

View File

@ -0,0 +1,97 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.*;
import org.lwjgl.opengl.GL;
class LWJGLInitializer {
private LWJGLInitializer() {}
public static void initialize() {
checkEnvironment();
initializeGLFW();
createWindow();
positionWindow();
createWindowIcons();
initializeOpenGL();
setupWindowCallbacks();
glfwShowWindow(GraphicsBackend.getWindowHandle());
}
private static void checkEnvironment() {
// TODO Auto-generated method stub
}
private static void initializeGLFW() {
// TODO Do GLFW error handling: check glfwInit, setup error callback
glfwInit();
}
private static void createWindow() {
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
long handle = glfwCreateWindow(900, 900, "OpticaTest", NULL, NULL);
// TODO Check that handle != NULL
GraphicsBackend.setWindowHandle(handle);
glfwSetInputMode(handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwMakeContextCurrent(handle);
glfwSwapInterval(0);
}
private static void positionWindow() {
// TODO Auto-generated method stub
}
private static void createWindowIcons() {
// TODO Auto-generated method stub
}
private static void initializeOpenGL() {
GL.createCapabilities();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
private static void setupWindowCallbacks() {
long handle = GraphicsBackend.getWindowHandle();
glfwSetFramebufferSizeCallback(handle,
GraphicsBackend::onFramebufferResized);
glfwSetKeyCallback(handle, InputHandler::handleKeyInput);
glfwSetCursorPosCallback(handle, InputHandler::handleMouseMoveInput);
}
}

View File

@ -0,0 +1,45 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import java.util.ArrayList;
import java.util.Collection;
/*
* FIXME deal with client invocations of .delete() when properly disposing of
* objects mid-execution
*/
public class OpenGLObjectTracker {
public static interface OpenGLDeletable {
void delete();
}
private static final Collection<OpenGLDeletable> TO_DELETE = new ArrayList<>();
public synchronized static void register(OpenGLDeletable object) {
TO_DELETE.add(object);
}
public synchronized static void deleteAllObjects() {
TO_DELETE.forEach(OpenGLDeletable::delete);
TO_DELETE.clear();
}
}

View File

@ -0,0 +1,107 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import ru.windcorp.optica.common.util.ThrowingRunnable;
public class RenderTaskQueue {
private static final Queue<Runnable> QUEUE = new ConcurrentLinkedQueue<>();
private RenderTaskQueue() {}
public static void invokeLater(Runnable task) {
QUEUE.add(task);
}
public static void invokeNow(Runnable task) {
if (GraphicsInterface.isRenderThread()) {
task.run();
} else {
invokeLater(task);
}
}
private static final Object WAIT_AND_INVOKE_MONITOR = new Object();
@SuppressWarnings("unchecked")
public static <T extends Throwable> void waitAndInvoke(
ThrowingRunnable<T> task
) throws InterruptedException, T {
if (GraphicsInterface.isRenderThread()) {
task.run();
return;
}
final AtomicBoolean flag =
new AtomicBoolean(false);
final AtomicReference<Throwable> thrownContainer =
new AtomicReference<>(null);
invokeLater(() -> {
try {
task.run();
} catch (Throwable t) {
thrownContainer.set(t);
}
flag.set(true);
synchronized (WAIT_AND_INVOKE_MONITOR) {
WAIT_AND_INVOKE_MONITOR.notifyAll();
}
});
while (!flag.get()) {
synchronized (WAIT_AND_INVOKE_MONITOR) {
WAIT_AND_INVOKE_MONITOR.wait();
}
}
Throwable thrown = thrownContainer.get();
if (thrown != null) {
if (thrown instanceof RuntimeException) {
throw (RuntimeException) thrown;
}
if (thrown instanceof Error) {
throw (Error) thrown;
}
throw (T) thrown; // Guaranteed
}
}
public static void runTasks() {
Iterator<Runnable> tasks = QUEUE.iterator();
while (tasks.hasNext()) {
tasks.next().run();
tasks.remove();
}
}
}

View File

@ -0,0 +1,75 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import ru.windcorp.optica.client.graphics.GUI;
class RenderThread extends Thread {
public RenderThread() {
super("Render");
}
@Override
public void run() {
LWJGLInitializer.initialize();
mainLoop();
freeResources();
}
private void mainLoop() {
while (shouldRun()) {
GraphicsBackend.startFrame();
RenderTaskQueue.runTasks();
render();
waitForFrame();
GraphicsBackend.endFrame();
}
}
private void render() {
clear();
doRender();
glfwPollEvents();
}
private void clear() {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
private void doRender() {
GUI.render();
}
private void waitForFrame() {
glfwSwapBuffers(GraphicsBackend.getWindowHandle());
}
private void freeResources() {
OpenGLObjectTracker.deleteAllObjects();
}
private boolean shouldRun() {
return !glfwWindowShouldClose(GraphicsBackend.getWindowHandle());
}
}

View File

@ -0,0 +1,38 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import static org.lwjgl.opengl.GL15.GL_DYNAMIC_DRAW;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.GL_STREAM_DRAW;
public enum Usage { // TODO add _COPY and _READ, pref. as another enum
STATIC(GL_STATIC_DRAW),
DYNAMIC(GL_DYNAMIC_DRAW),
STREAM(GL_STREAM_DRAW);
private final int glCode;
private Usage(int glCode) {
this.glCode = glCode;
}
public int getGlCode() {
return glCode;
}
}

View File

@ -0,0 +1,203 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend;
import static org.lwjgl.opengl.GL20.*;
import java.nio.*;
import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
public class VertexBufferObject implements OpenGLDeletable {
public static enum BindTarget {
ARRAY(GL_ARRAY_BUFFER),
ELEMENT_ARRAY(GL_ELEMENT_ARRAY_BUFFER);
private final int glCode;
private BindTarget(int glCode) {
this.glCode = glCode;
}
public int getGlCode() {
return glCode;
}
}
private final int handle;
private long length = 0;
private final Usage usage;
public VertexBufferObject(Usage usage) {
handle = glGenBuffers();
OpenGLObjectTracker.register(this);
this.usage = usage;
}
public void setData(ByteBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.remaining();
}
public void setData(double[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.length * Double.BYTES;
}
public void setData(DoubleBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.remaining() * Double.BYTES;
}
public void setData(float[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.length * Float.BYTES;
}
public void setData(FloatBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.remaining() * Float.BYTES;
}
public void setData(int[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.length * Integer.BYTES;
}
public void setData(IntBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.remaining() * Integer.BYTES;
}
public void setData(long[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.length * Long.BYTES;
}
public void setData(LongBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.remaining() * Long.BYTES;
}
public void setData(short[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.length * Short.BYTES;
}
public void setData(ShortBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, data, usage.getGlCode());
length = data.remaining() * Short.BYTES;
}
public void initData(long length) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferData(GL_ARRAY_BUFFER, length, usage.getGlCode());
this.length = length;
}
public void setData(int offset, ByteBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, double[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, DoubleBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, float[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, FloatBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, int[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, IntBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, long[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, LongBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, short[] data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void setData(int offset, ShortBuffer data) {
glBindBuffer(GL_ARRAY_BUFFER, handle);
glBufferSubData(GL_ARRAY_BUFFER, offset, data);
}
public void bind(BindTarget target) {
glBindBuffer(target.getGlCode(), handle);
}
public long getLength() {
return length;
}
public Usage getUsage() {
return usage;
}
public int getHandle() {
return handle;
}
@Override
public void delete() {
glDeleteBuffers(handle);
}
}

View File

@ -0,0 +1,93 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders;
import ru.windcorp.optica.common.resource.Resource;
public class CombinedShader extends Shader {
public CombinedShader(String... resources) {
super(getTypeOf(resources), combine(resources));
}
private static ShaderType getTypeOf(String[] resources) {
ShaderType first = ShaderType.guessByResourceName(resources[0]);
for (int i = 1; i < resources.length; ++i) {
if (ShaderType.guessByResourceName(resources[i]) != first) {
throw new IllegalArgumentException(
"Deduced shader types of "
+ resources[0]
+ " and "
+ resources[i]
+ " differ"
);
}
}
return first;
}
private static String combine(String[] resources) {
StringBuilder accumulator = new StringBuilder("#version 120\n");
for (String resourceName : resources) {
Resource resource = getShaderResource(resourceName);
accumulator.append("\n// START " + resourceName);
accumulator.append(stripVersionAnnotations(resource));
accumulator.append('\n');
}
return accumulator.toString();
}
private static String stripVersionAnnotations(Resource resource) {
String contents = resource.readAsString();
int versionIndex;
for (versionIndex = 0; versionIndex < contents.length(); ++versionIndex)
{
if (!Character.isWhitespace(contents.codePointAt(versionIndex)))
break;
}
if (versionIndex < contents.length()) {
if (contents.codePointAt(versionIndex) == '#') {
final String versionAnnotation = "#version ";
if (contents.regionMatches(
versionIndex,
versionAnnotation,
0,
versionAnnotation.length()
)) {
contents = contents.substring(
versionIndex
+ versionAnnotation.length()
+ "120".length()
);
}
}
}
return contents;
}
}

View File

@ -0,0 +1,63 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders;
import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker;
import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
import ru.windcorp.optica.client.graphics.backend.shaders.attributes.Attribute;
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
public class Program implements OpenGLDeletable {
private int handle;
public Program(Shader vertexShader, Shader fragmentShader) {
handle = glCreateProgram();
OpenGLObjectTracker.register(this);
glAttachShader(handle, vertexShader.getHandle());
glAttachShader(handle, fragmentShader.getHandle());
glLinkProgram(handle);
if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) {
throw new RuntimeException("Bad program:\n" + glGetProgramInfoLog(handle));
}
}
public Attribute getAttribute(String name) {
return new Attribute(glGetAttribLocation(handle, name), this);
}
public Uniform getUniform(String name) {
return new Uniform(glGetUniformLocation(handle, name), this);
}
public void use() {
glUseProgram(handle);
}
@Override
public void delete() {
glDeleteProgram(handle);
}
}

View File

@ -0,0 +1,106 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders;
import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker;
import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
import ru.windcorp.optica.common.resource.Resource;
import ru.windcorp.optica.common.resource.ResourceManager;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
import java.util.Locale;
public class Shader implements OpenGLDeletable {
public static enum ShaderType {
VERTEX(GL_VERTEX_SHADER),
FRAGMENT(GL_FRAGMENT_SHADER);
private final int glCode;
private ShaderType(int glCode) {
this.glCode = glCode;
}
public int getGlCode() {
return glCode;
}
public static ShaderType guessByResourceName(String resource) {
resource = resource.toLowerCase(Locale.ENGLISH);
if (resource.contains("vertex")) return VERTEX;
if (resource.contains("fragment")) return FRAGMENT;
if (resource.contains("vsh")) return VERTEX;
if (resource.contains("fsh")) return FRAGMENT;
throw new IllegalArgumentException(
"Cannot deduce shader type from resource name \"" +
resource + "\""
);
}
}
private static final String SHADER_ASSETS_PREFIX = "assets/shaders/";
protected static Resource getShaderResource(String name) {
return ResourceManager.getResource(SHADER_ASSETS_PREFIX + name);
}
private final int handle;
private final ShaderType type;
public Shader(ShaderType type, String source) {
handle = glCreateShader(type.getGlCode());
OpenGLObjectTracker.register(this);
this.type = type;
glShaderSource(handle, source);
glCompileShader(handle);
if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) {
System.out.println("***************** ERROR ******************");
System.out.println(source);
throw new RuntimeException("Bad shader:\n" + glGetShaderInfoLog(handle));
}
}
public Shader(String resource) {
this(
ShaderType.guessByResourceName(resource),
getShaderResource(resource).readAsString()
);
}
@Override
public void delete() {
glDeleteShader(handle);
}
public int getHandle() {
return handle;
}
public ShaderType getType() {
return type;
}
}

View File

@ -0,0 +1,48 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.attributes;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
public class Attribute {
protected final int handle;
private final Program program;
public Attribute(int handle, Program program) {
if (handle < 0) {
throw new RuntimeException("Bad handle: " + handle);
}
this.handle = handle;
this.program = program;
}
public int getHandle() {
return handle;
}
public Program getProgram() {
return program;
}
public AttributeVertexArray asVertexArray() {
return new AttributeVertexArray(handle, program);
}
}

View File

@ -0,0 +1,114 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.attributes;
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
public class AttributeVertexArray extends Attribute {
private boolean isEnabled = false;
public AttributeVertexArray(int handle, Program program) {
super(handle, program);
}
public void enable() {
if (!isEnabled) {
glEnableVertexAttribArray(handle);
isEnabled = true;
}
}
public void disable() {
if (isEnabled) {
glDisableVertexAttribArray(handle);
isEnabled = false;
}
}
public void set(
int size, boolean normalized, int stride,
ByteBuffer pointer
) {
glVertexAttribPointer(
handle,
size, GL_BYTE, normalized, stride, pointer
);
}
public void set(
int size, boolean normalized, int stride,
FloatBuffer pointer
) {
glVertexAttribPointer(
handle,
size, GL_FLOAT, normalized, stride, pointer
);
}
public void set(
int size, boolean normalized, int stride,
IntBuffer pointer
) {
glVertexAttribPointer(
handle,
size, GL_INT, normalized, stride, pointer
);
}
public void set(
int size, boolean normalized, int stride,
ShortBuffer pointer
) {
glVertexAttribPointer(
handle,
size, GL_SHORT, normalized, stride, pointer
);
}
public void set(
int size, int type, boolean normalized, int stride,
long pointer
) {
glVertexAttribPointer(
handle,
size, type, normalized, stride, pointer
);
}
public void set(
int size, int type, boolean normalized, int stride,
VertexBufferObject vbo, long offset
) {
glBindBuffer(GL_ARRAY_BUFFER, vbo.getHandle());
glVertexAttribPointer(
handle,
size, type, normalized, stride, offset
);
}
}

View File

@ -0,0 +1,88 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
public class Uniform {
protected final int handle;
private final Program program;
public Uniform(int handle, Program program) {
if (handle < 0) {
throw new RuntimeException("Bad handle: " + handle);
}
this.handle = handle;
this.program = program;
}
public int getHandle() {
return handle;
}
public Program getProgram() {
return program;
}
public Uniform1Float as1Float() {
return new Uniform1Float(handle, program);
}
public Uniform1Int as1Int() {
return new Uniform1Int(handle, program);
}
public Uniform2Float as2Float() {
return new Uniform2Float(handle, program);
}
public Uniform2Int as2Int() {
return new Uniform2Int(handle, program);
}
public Uniform3Float as3Float() {
return new Uniform3Float(handle, program);
}
public Uniform3Int as3Int() {
return new Uniform3Int(handle, program);
}
public Uniform4Float as4Float() {
return new Uniform4Float(handle, program);
}
public Uniform4Int as4Int() {
return new Uniform4Int(handle, program);
}
public Uniform2Matrix as2Matrix() {
return new Uniform2Matrix(handle, program);
}
public Uniform3Matrix as3Matrix() {
return new Uniform3Matrix(handle, program);
}
public Uniform4Matrix as4Matrix() {
return new Uniform4Matrix(handle, program);
}
}

View File

@ -0,0 +1,43 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
public class Uniform1Float extends Uniform {
public Uniform1Float(int handle, Program program) {
super(handle, program);
}
public void set(float value) {
glUniform1f(handle, value);
}
public void set(float[] value) {
glUniform1fv(handle, value);
}
public void set(FloatBuffer value) {
glUniform1fv(handle, value);
}
}

View File

@ -0,0 +1,43 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.IntBuffer;
public class Uniform1Int extends Uniform {
public Uniform1Int(int handle, Program program) {
super(handle, program);
}
public void set(int value) {
glUniform1i(handle, value);
}
public void set(int[] value) {
glUniform1iv(handle, value);
}
public void set(IntBuffer value) {
glUniform1iv(handle, value);
}
}

View File

@ -0,0 +1,49 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
import glm.vec._2.Vec2;
public class Uniform2Float extends Uniform {
public Uniform2Float(int handle, Program program) {
super(handle, program);
}
public void set(float x, float y) {
glUniform2f(handle, x, y);
}
public void set(float[] value) {
glUniform2fv(handle, value);
}
public void set(FloatBuffer value) {
glUniform2fv(handle, value);
}
public void set(Vec2 value) {
glUniform2f(handle, value.x, value.y);
}
}

View File

@ -0,0 +1,49 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.IntBuffer;
import glm.vec._2.i.Vec2i;
public class Uniform2Int extends Uniform {
public Uniform2Int(int handle, Program program) {
super(handle, program);
}
public void set(int x, int y) {
glUniform2i(handle, x, y);
}
public void set(int[] value) {
glUniform2iv(handle, value);
}
public void set(IntBuffer value) {
glUniform2iv(handle, value);
}
public void set(Vec2i value) {
glUniform2i(handle, value.x, value.y);
}
}

View File

@ -0,0 +1,39 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
public class Uniform2Matrix extends Uniform {
public Uniform2Matrix(int handle, Program program) {
super(handle, program);
}
public void set(float[] value) {
glUniformMatrix2fv(handle, false, value);
}
public void set(FloatBuffer value) {
glUniformMatrix2fv(handle, false, value);
}
}

View File

@ -0,0 +1,49 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
import glm.vec._3.Vec3;
public class Uniform3Float extends Uniform {
public Uniform3Float(int handle, Program program) {
super(handle, program);
}
public void set(float x, float y, float z) {
glUniform3f(handle, x, y, z);
}
public void set(float[] value) {
glUniform3fv(handle, value);
}
public void set(FloatBuffer value) {
glUniform3fv(handle, value);
}
public void set(Vec3 value) {
glUniform3f(handle, value.x, value.y, value.z);
}
}

View File

@ -0,0 +1,49 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.IntBuffer;
import glm.vec._3.i.Vec3i;
public class Uniform3Int extends Uniform {
public Uniform3Int(int handle, Program program) {
super(handle, program);
}
public void set(int x, int y, int z) {
glUniform3i(handle, x, y, z);
}
public void set(int[] value) {
glUniform3iv(handle, value);
}
public void set(IntBuffer value) {
glUniform3iv(handle, value);
}
public void set(Vec3i value) {
glUniform3i(handle, value.x, value.y, value.z);
}
}

View File

@ -0,0 +1,47 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
import glm.mat._3.Mat3;
public class Uniform3Matrix extends Uniform {
public Uniform3Matrix(int handle, Program program) {
super(handle, program);
}
public void set(float[] value) {
glUniformMatrix3fv(handle, false, value);
}
public void set(FloatBuffer value) {
glUniformMatrix3fv(handle, false, value);
}
private static final float[] BUFFER = new float[3 * 3];
public void set(Mat3 value) {
glUniformMatrix3fv(handle, false, value.toFa(BUFFER));
}
}

View File

@ -0,0 +1,49 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
import glm.vec._4.Vec4;
public class Uniform4Float extends Uniform {
public Uniform4Float(int handle, Program program) {
super(handle, program);
}
public void set(float x, float y, float z, float w) {
glUniform4f(handle, x, y, z, w);
}
public void set(float[] value) {
glUniform4fv(handle, value);
}
public void set(FloatBuffer value) {
glUniform4fv(handle, value);
}
public void set(Vec4 value) {
glUniform4f(handle, value.x, value.y, value.z, value.w);
}
}

View File

@ -0,0 +1,49 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.IntBuffer;
import glm.vec._4.i.Vec4i;
public class Uniform4Int extends Uniform {
public Uniform4Int(int handle, Program program) {
super(handle, program);
}
public void set(int x, int y, int z, int w) {
glUniform4i(handle, x, y, z, w);
}
public void set(int[] value) {
glUniform4iv(handle, value);
}
public void set(IntBuffer value) {
glUniform4iv(handle, value);
}
public void set(Vec4i value) {
glUniform4i(handle, value.x, value.y, value.z, value.w);
}
}

View File

@ -0,0 +1,47 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.backend.shaders.uniforms;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import static org.lwjgl.opengl.GL20.*;
import java.nio.FloatBuffer;
import glm.mat._4.Mat4;
public class Uniform4Matrix extends Uniform {
public Uniform4Matrix(int handle, Program program) {
super(handle, program);
}
public void set(float[] value) {
glUniformMatrix4fv(handle, false, value);
}
public void set(FloatBuffer value) {
glUniformMatrix4fv(handle, false, value);
}
private static final float[] BUFFER = new float[4 * 4];
public void set(Mat4 value) {
glUniformMatrix4fv(handle, false, value.toFa(BUFFER));
}
}

View File

@ -0,0 +1,40 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.input;
import glm.vec._2.d.Vec2d;
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
public abstract class CursorEvent extends InputEvent {
public double getCursorX() {
return GraphicsInterface.getCursorX();
}
public double getCursorY() {
return GraphicsInterface.getCursorY();
}
public Vec2d getCursorPosition() {
return GraphicsInterface.getCursorPosition();
}
@Override
public abstract CursorEvent snapshot();
}

View File

@ -0,0 +1,129 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.input;
import glm.vec._2.Vec2;
import glm.vec._2.d.Vec2d;
public class CursorMoveEvent extends CursorEvent {
private final Vec2d newPosition = new Vec2d();
protected CursorMoveEvent(double newX, double newY) {
newPosition.set(newX, newY);
}
protected CursorMoveEvent(Vec2d newPos) {
newPosition.set(newPos.x, newPos.y);
}
@Override
public double getCursorX() {
return getCursorPosition().x;
}
@Override
public double getCursorY() {
return getCursorPosition().y;
}
@Override
public Vec2d getCursorPosition() {
return getNewPosition();
}
public double getNewX() {
return getNewPosition().x;
}
public double getNewY() {
return getNewPosition().y;
}
public Vec2d getNewPosition() {
return newPosition;
}
public double getPreviousX() {
return getPreviousPosition().x;
}
public double getPreviousY() {
return getPreviousPosition().y;
}
public Vec2d getPreviousPosition() {
return super.getCursorPosition();
}
public double getChangeX() {
return getNewX() - getPreviousX();
}
public double getChangeY() {
return getNewY() - getPreviousY();
}
public Vec2 getChange(Vec2 result) {
return result.set(getChangeX(), getChangeY());
}
protected CursorMoveEvent() {}
@Override
public CursorMoveEvent snapshot() {
return new StaticMouseMoveEvent(
getPreviousPosition(),
getNewPosition(),
getTime()
);
}
private class StaticMouseMoveEvent extends CursorMoveEvent {
private final double time;
private final Vec2d previousPosition = new Vec2d();
public StaticMouseMoveEvent(
Vec2d previousPosition,
Vec2d newPosition,
double time
) {
super(newPosition);
this.previousPosition.set(previousPosition.x, previousPosition.y);
this.time = time;
}
@Override
public Vec2d getPreviousPosition() {
return previousPosition;
}
@Override
public double getTime() {
return time;
}
@Override
public CursorMoveEvent snapshot() {
return this;
}
}
}

View File

@ -0,0 +1,30 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.input;
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
public abstract class InputEvent {
public double getTime() {
return GraphicsInterface.getTime();
}
public abstract InputEvent snapshot();
}

View File

@ -0,0 +1,96 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.input;
import org.lwjgl.glfw.GLFW;
public class KeyEvent extends InputEvent {
protected int key;
protected int scancode;
protected int action;
protected int mods;
protected KeyEvent(int key, int scancode, int action, int mods) {
this();
this.key = key;
this.scancode = scancode;
this.action = action;
this.mods = mods;
}
protected KeyEvent() {}
public int getKey() {
return key;
}
public int getScancode() {
return scancode;
}
public int getAction() {
return action;
}
public boolean isPress() {
return action == GLFW.GLFW_PRESS;
}
public boolean isRelease() {
return action == GLFW.GLFW_RELEASE;
}
public boolean isRepeat() {
return action == GLFW.GLFW_REPEAT;
}
public int getMods() {
return mods;
}
@Override
public InputEvent snapshot() {
return new StaticKeyEvent(key, scancode, action, mods, getTime());
}
private class StaticKeyEvent extends KeyEvent {
private final double time;
public StaticKeyEvent(
int key, int scancode, int action, int mods,
double time
) {
super(key, scancode, action, mods);
this.time = time;
}
@Override
public double getTime() {
return time;
}
@Override
public InputEvent snapshot() {
return this;
}
}
}

View File

@ -0,0 +1,124 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.google.common.primitives.Booleans;
import glm.mat._4.Mat4;
public abstract class DynamicModel extends Model {
private static final Mat4 IDENTITY = new Mat4();
private final Mat4[] transforms;
private final boolean[] dynamics;
public DynamicModel(
WorldRenderable[] parts,
Mat4[] transforms,
boolean[] dynamic
) {
super(parts);
this.transforms = transforms;
this.dynamics = dynamic;
}
public DynamicModel(Builder builder) {
this(
builder.getParts(),
builder.getTransforms(),
builder.getDynamics()
);
}
@Override
protected Mat4 getTransform(int shapeIndex) {
Mat4 transform = transforms[shapeIndex];
if (dynamics[shapeIndex]) {
transform.identity();
getDynamicTransform(shapeIndex, transform);
}
return transform;
}
protected abstract void getDynamicTransform(int shapeIndex, Mat4 result);
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final List<WorldRenderable> parts = new ArrayList<>();
private final List<Mat4> transforms = new ArrayList<>();
private final List<Boolean> dynamics = new ArrayList<>();
protected Builder() {}
private Builder addPart(
WorldRenderable part,
Mat4 transform,
boolean isDynamic
) {
parts.add(Objects.requireNonNull(part, "part"));
transforms.add(Objects.requireNonNull(transform, "transform"));
dynamics.add(isDynamic);
return this;
}
public Builder addStaticPart(
WorldRenderable part,
Mat4 transform
) {
return addPart(part, new Mat4(transform), false);
}
public Builder addDynamicPart(
WorldRenderable part
) {
return addPart(part, new Mat4(), true);
}
public Builder addStaticPart(
WorldRenderable part
) {
return addStaticPart(part, IDENTITY);
}
private WorldRenderable[] getParts() {
return parts.toArray(new WorldRenderable[parts.size()]);
}
private Mat4[] getTransforms() {
return transforms.toArray(new Mat4[transforms.size()]);
}
private boolean[] getDynamics() {
return Booleans.toArray(dynamics);
}
}
}

View File

@ -0,0 +1,45 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import glm.mat._4.Mat4;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
public class EmptyModel extends Model {
private static final EmptyModel INSTANCE = new EmptyModel();
private EmptyModel() {
super(new WorldRenderable[0]);
}
public static EmptyModel getInstance() {
return INSTANCE;
}
@Override
public void render(WorldRenderer renderer) {
// Do nothing
}
@Override
protected Mat4 getTransform(int shapeIndex) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,299 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.util.Objects;
import glm.vec._3.Vec3;
import ru.windcorp.optica.client.graphics.texture.Texture;
public class Face {
private static final ShortBuffer GENERATE_SUCCESSIVE_LATER = null;
private Shape shape = null;
int locationOfIndices;
int locationOfVertices;
private Texture texture;
ByteBuffer vertices;
private boolean verticesUpdated = true;
private ShortBuffer userIndices;
private boolean userIndicesUpdated = true;
public Face(
Texture texture,
ByteBuffer vertices,
ShortBuffer indices
) {
setTexture(texture);
setVertices(vertices);
setIndices(indices);
}
public Face(
Texture texture,
ByteBuffer vertices
) {
this(texture, vertices, null);
}
void setShape(Shape shape) {
this.shape = shape;
checkVertices();
checkIndices();
}
void computeNormals() {
Vec3 a = new Vec3();
Vec3 b = new Vec3();
Vec3 c = new Vec3();
Vec3 normal = new Vec3();
for (int i = 0; i < getIndexCount(); i += 3) {
int indexA = getIndex(i + 0);
int indexB = getIndex(i + 1);
int indexC = getIndex(i + 2);
loadVertexPosition(indexA, a);
loadVertexPosition(indexB, b);
loadVertexPosition(indexC, c);
computeOneNormal(a, b, c, normal);
saveVertexNormal(indexA, normal);
saveVertexNormal(indexB, normal);
saveVertexNormal(indexC, normal);
}
}
private void computeOneNormal(
Vec3 a, Vec3 b, Vec3 c,
Vec3 normal
) {
b.sub(a);
c.sub(a);
b.cross(c, normal);
normal.normalize();
}
private void checkVertices() {
if (vertices.remaining() % getBytesPerVertex() != 0) {
throw new IllegalArgumentException(
"Invalid vertex buffer: " +
(vertices.remaining() % getBytesPerVertex()) +
" extra bytes after last vertex"
);
}
}
private void checkIndices() {
if (userIndices != GENERATE_SUCCESSIVE_LATER) {
if (userIndices.remaining() % 3 != 0) {
throw new IllegalArgumentException(
"Invalid vertex indices: " +
(userIndices.remaining() % 3) +
" extra indices after last triangle"
);
}
userIndices.mark();
int vertexCount = getVertexCount();
while (userIndices.hasRemaining()) {
short index = userIndices.get();
if (index < 0 || index >= vertexCount) {
throw new IllegalArgumentException(
"Invalid vertex index " + index +
" (" + vertexCount + " vertices available)"
);
}
}
userIndices.reset();
} else {
if (getVertexCount() % 3 != 0) {
throw new IllegalArgumentException(
"Invalid vertices: " +
(getVertexCount() % 3) +
" extra indices after last triangle " +
"(indices are automatic)"
);
}
}
}
boolean needsVerticesUpdate() {
return verticesUpdated;
}
public void markForIndexUpdate() {
if (shape != null) checkIndices();
markShapeForReassembly();
userIndicesUpdated = true;
}
boolean needsIndicesUpdate() {
return userIndicesUpdated;
}
void resetUpdateFlags() {
verticesUpdated = false;
userIndicesUpdated = false;
}
private void markShapeForReassembly() {
if (shape != null) {
shape.markForReassembly();
}
}
public int getVertexCount() {
return vertices.remaining() / getBytesPerVertex();
}
private int getBytesPerVertex() {
return shape.getProgram().getBytesPerVertex();
}
public ByteBuffer getVertices() {
return vertices;
}
private void loadVertexPosition(int index, Vec3 result) {
int offset = vertices.position() + index * getBytesPerVertex();
result.set(
vertices.getFloat(offset + 0 * Float.BYTES),
vertices.getFloat(offset + 1 * Float.BYTES),
vertices.getFloat(offset + 2 * Float.BYTES)
);
}
private void saveVertexNormal(int index, Vec3 normal) {
int offset = vertices.position() + index * getBytesPerVertex() + (
3 * Float.BYTES +
3 * Float.BYTES +
2 * Float.BYTES
);
vertices.putFloat(offset + 0 * Float.BYTES, normal.x);
vertices.putFloat(offset + 1 * Float.BYTES, normal.y);
vertices.putFloat(offset + 2 * Float.BYTES, normal.z);
verticesUpdated = true;
}
public Face setVertices(ByteBuffer vertices) {
this.vertices = Objects.requireNonNull(vertices, "vertices");
markShapeForReassembly();
this.verticesUpdated = true;
if (shape != null) checkVertices();
return this;
}
int getLocationOfVertices() {
return locationOfVertices;
}
int getByteOffsetOfVertices() {
return locationOfVertices;
}
public ShortBuffer getIndices() {
if (userIndices == GENERATE_SUCCESSIVE_LATER) {
userIndices = generateSuccessiveIndices(0);
}
return userIndices;
}
public int getIndex(int i) {
if (userIndices == GENERATE_SUCCESSIVE_LATER) {
return i;
} else {
ShortBuffer indices = getIndicesOrNull();
return indices.get(indices.position() + i);
}
}
ShortBuffer getIndicesOrNull() {
if (userIndices == GENERATE_SUCCESSIVE_LATER) {
return null;
}
return userIndices;
}
public int getIndexCount() {
if (userIndices == GENERATE_SUCCESSIVE_LATER) {
return getVertexCount();
}
return userIndices.remaining();
}
public Face setIndices(ShortBuffer indices) {
if (indices == null) {
indices = GENERATE_SUCCESSIVE_LATER;
}
this.userIndices = indices;
markForIndexUpdate();
if (shape != null) checkIndices();
return this;
}
private ShortBuffer generateSuccessiveIndices(int offset) {
int vertexCount = getVertexCount();
ShortBuffer result = ShortBuffer.allocate(vertexCount);
for (short vertex = 0; vertex < vertexCount; ++vertex) {
result.put((short) (vertex + offset));
}
result.flip();
return result;
}
int getLocationOfIndices() {
return locationOfIndices;
}
int getByteOffsetOfIndices() {
return locationOfIndices * Short.BYTES;
}
public Texture getTexture() {
return texture;
}
public void setTexture(Texture texture) {
this.texture = Objects.requireNonNull(texture, "texture");
}
}

View File

@ -0,0 +1,126 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import java.nio.ShortBuffer;
import glm.vec._2.Vec2;
import glm.vec._3.Vec3;
import ru.windcorp.optica.client.graphics.model.ShapeRenderProgram.VertexBuilder;
import ru.windcorp.optica.client.graphics.texture.Texture;
import ru.windcorp.optica.common.block.BlockFace;
public class Faces {
private Faces() {}
public static Face createRectangle(
Texture texture,
Vec3 colorMultiplier,
Vec3 origin,
Vec3 width,
Vec3 height
) {
VertexBuilder builder = new VertexBuilder();
Vec3 pos = new Vec3();
Vec2 texCoords = new Vec2();
builder.addVertex(
origin,
colorMultiplier,
texCoords.set(0, 0)
).addVertex(
pos.set(origin).add(height),
colorMultiplier,
texCoords.set(0, 1)
).addVertex(
pos.set(origin).add(width),
colorMultiplier,
texCoords.set(1, 0)
).addVertex(
pos.add(height),
colorMultiplier,
texCoords.set(1, 1)
);
return new Face(
texture,
builder.assemble(),
ShortBuffer.wrap(new short[] {
3, 1, 0,
2, 3, 0
})
);
}
public static Face createBlockFace(
Texture texture,
Vec3 colorMultiplier,
Vec3 blockCenter,
BlockFace face
) {
switch (face) {
case TOP:
return createRectangle(
texture, colorMultiplier,
blockCenter.add(-0.5f, +0.5f, +0.5f),
new Vec3( 0, -1, 0),
new Vec3(+1, 0, 0)
);
case BOTTOM:
return createRectangle(
texture, colorMultiplier,
blockCenter.add(-0.5f, -0.5f, -0.5f),
new Vec3( 0, +1, 0),
new Vec3(+1, 0, 0)
);
case NORTH:
return createRectangle(
texture, colorMultiplier,
blockCenter.add(+0.5f, -0.5f, -0.5f),
new Vec3( 0, +1, 0),
new Vec3( 0, 0, +1)
);
case SOUTH:
return createRectangle(
texture, colorMultiplier,
blockCenter.add(-0.5f, +0.5f, -0.5f),
new Vec3( 0, -1, 0),
new Vec3( 0, 0, +1)
);
case EAST:
return createRectangle(
texture, colorMultiplier,
blockCenter.add(-0.5f, -0.5f, -0.5f),
new Vec3(+1, 0, 0),
new Vec3( 0, 0, +1)
);
case WEST:
return createRectangle(
texture, colorMultiplier,
blockCenter.add(+0.5f, +0.5f, -0.5f),
new Vec3(-1, 0, 0),
new Vec3( 0, 0, +1)
);
default:
throw new NullPointerException("face");
}
}
}

View File

@ -0,0 +1,48 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import glm.mat._4.Mat4;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
public abstract class Model implements WorldRenderable {
private final WorldRenderable[] parts;
public Model(WorldRenderable[] parts) {
this.parts = parts;
}
protected abstract Mat4 getTransform(int partIndex);
@Override
public void render(WorldRenderer renderer) {
for (int i = 0; i < parts.length; ++i) {
WorldRenderable part = parts[i];
Mat4 transform = getTransform(i);
try {
renderer.pushWorldTransform().mul(transform);
part.render(renderer);
} finally {
renderer.popWorldTransform();
}
}
}
}

View File

@ -0,0 +1,199 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import org.lwjgl.BufferUtils;
import ru.windcorp.optica.client.graphics.backend.Usage;
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
public class Shape implements WorldRenderable {
private final ShapeRenderProgram program;
private final Face[] faces;
private final Usage usage;
private ByteBuffer vertices;
private ShortBuffer indices;
private boolean initialized = false;
private boolean needsAssembly = true;
private boolean needsVBOUpdate = true;
private VertexBufferObject verticesVbo;
private VertexBufferObject indicesVbo;
public Shape(Usage usage, ShapeRenderProgram program, Face... faces) {
this.program = program;
this.faces = faces;
this.usage = usage;
configureFaces();
assembleBuffers();
}
public Shape(Usage usage, Face... faces) {
this(usage, ShapeRenderProgram.getDefault(), faces);
}
private void configureFaces() {
for (Face face : faces) {
face.setShape(this);
face.computeNormals();
}
}
private void assembleBuffers() {
// TODO optimize: only update faces that requested it
resizeBuffers();
for (Face face : faces) {
assembleVertices(face);
assembleIndices(face);
face.resetUpdateFlags();
}
this.vertices.flip();
this.indices.flip();
needsAssembly = false;
needsVBOUpdate = true;
}
private void resizeBuffers() {
int verticesRequired = 0, indicesRequired = 0;
for (Face face : faces) {
verticesRequired += face.getVertices().remaining();
indicesRequired += face.getIndices().remaining();
}
if (this.vertices == null || vertices.capacity() < verticesRequired) {
this.vertices = BufferUtils.createByteBuffer(verticesRequired);
} else {
this.vertices.position(0).limit(verticesRequired);
}
if (this.indices == null || this.indices.capacity() < indicesRequired) {
this.indices = BufferUtils.createShortBuffer(indicesRequired);
} else {
this.indices.position(0).limit(indicesRequired);
}
}
private void assembleVertices(Face face) {
face.locationOfVertices = this.vertices.position();
insertVertices(face);
linkVerticesWith(face);
}
private void insertVertices(Face face) {
ByteBuffer faceVertices = face.getVertices();
faceVertices.mark();
this.vertices.put(faceVertices);
faceVertices.reset();
}
private void linkVerticesWith(Face face) {
int limit = vertices.limit();
int position = vertices.position();
vertices.limit(position).position(face.getLocationOfVertices());
face.vertices = vertices.slice();
vertices.position(position).limit(limit);
}
private void assembleIndices(Face face) {
short vertexOffset = (short) (
face.getLocationOfVertices() / program.getBytesPerVertex()
);
face.locationOfIndices = indices.position();
ShortBuffer faceIndices = face.getIndices();
if (faceIndices == null) {
for (int i = 0; i < face.getVertexCount(); ++i) {
this.indices.put((short) (vertexOffset + i));
}
} else {
for (int i = faceIndices.position(); i < faceIndices.limit(); ++i) {
short faceIndex = faceIndices.get(i);
faceIndex += vertexOffset;
this.indices.put(faceIndex);
}
}
}
void markForReassembly() {
needsAssembly = true;
}
@Override
public void render(WorldRenderer renderer) {
if (!initialized) initialize();
if (needsAssembly) assembleBuffers();
if (needsVBOUpdate) updateVBO();
program.render(renderer, this);
}
private void initialize() {
verticesVbo = new VertexBufferObject(usage);
indicesVbo = new VertexBufferObject(usage);
needsVBOUpdate = true;
initialized = true;
}
private void updateVBO() {
verticesVbo.setData(vertices);
indicesVbo.setData(indices);
needsVBOUpdate = false;
}
VertexBufferObject getVerticesVbo() {
return verticesVbo;
}
VertexBufferObject getIndicesVbo() {
return indicesVbo;
}
public ShapeRenderProgram getProgram() {
return program;
}
public Face[] getFaces() {
return faces;
}
public Usage getUsage() {
return usage;
}
}

View File

@ -0,0 +1,298 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import com.google.common.collect.ObjectArrays;
import glm.vec._2.Vec2;
import glm.vec._3.Vec3;
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject;
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject.BindTarget;
import ru.windcorp.optica.client.graphics.backend.shaders.CombinedShader;
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
import ru.windcorp.optica.client.graphics.backend.shaders.attributes.AttributeVertexArray;
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform1Int;
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform2Float;
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform4Matrix;
import ru.windcorp.optica.client.graphics.texture.Sprite;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
public class ShapeRenderProgram extends Program {
private static ShapeRenderProgram def = null;
public static void init() {
def = new ShapeRenderProgram(
new String[] {"WorldDefault.vertex.glsl"},
new String[] {"WorldDefault.fragment.glsl"}
);
}
public static ShapeRenderProgram getDefault() {
return def;
}
private static final int DEFAULT_BYTES_PER_VERTEX =
3 * Float.BYTES + // Position
3 * Float.BYTES + // Color multiplier
2 * Float.BYTES + // Texture coordinates
3 * Float.BYTES; // Normals
private static final String SHAPE_VERTEX_SHADER_RESOURCE =
"Shape.vertex.glsl";
private static final String SHAPE_FRAGMENT_SHADER_RESOURCE =
"Shape.fragment.glsl";
private static final String
FINAL_TRANSFORM_UNIFORM_NAME = "finalTransform",
WORLD_TRANSFORM_UNIFORM_NAME = "worldTransform",
POSITIONS_ATTRIBUTE_NAME = "inputPositions",
COLOR_MULTIPLER_ATTRIBUTE_NAME = "inputColorMultiplier",
TEXTURE_COORDS_ATTRIBUTE_NAME = "inputTextureCoords",
TEXTURE_SLOT_UNIFORM_NAME = "textureSlot",
TEXTURE_START_UNIFORM_NAME = "textureStart",
TEXTURE_SIZE_UNIFORM_NAME = "textureSize",
NORMALS_ATTRIBUTE_NAME = "inputNormals";
private final Uniform4Matrix finalTransformUniform;
private final Uniform4Matrix worldTransformUniform;
private final AttributeVertexArray positionsAttribute;
private final AttributeVertexArray colorsAttribute;
private final AttributeVertexArray textureCoordsAttribute;
private final Uniform1Int textureSlotUniform;
private final Uniform2Float textureStartUniform;
private final Uniform2Float textureSizeUniform;
private final AttributeVertexArray normalsAttribute;
public ShapeRenderProgram(
String[] vertexShaderResources,
String[] fragmentShaderResources
) {
super(
new CombinedShader(
attachVertexShader(vertexShaderResources)
),
new CombinedShader(
attachFragmentShader(fragmentShaderResources)
)
);
this.finalTransformUniform = getUniform(FINAL_TRANSFORM_UNIFORM_NAME)
.as4Matrix();
this.worldTransformUniform = getUniform(WORLD_TRANSFORM_UNIFORM_NAME)
.as4Matrix();
this.positionsAttribute =
getAttribute(POSITIONS_ATTRIBUTE_NAME).asVertexArray();
this.colorsAttribute =
getAttribute(COLOR_MULTIPLER_ATTRIBUTE_NAME).asVertexArray();
this.textureCoordsAttribute =
getAttribute(TEXTURE_COORDS_ATTRIBUTE_NAME).asVertexArray();
this.textureSlotUniform = getUniform(TEXTURE_SLOT_UNIFORM_NAME)
.as1Int();
this.textureStartUniform = getUniform(TEXTURE_START_UNIFORM_NAME)
.as2Float();
this.textureSizeUniform = getUniform(TEXTURE_SIZE_UNIFORM_NAME)
.as2Float();
this.normalsAttribute = getAttribute(NORMALS_ATTRIBUTE_NAME)
.asVertexArray();
}
private static String[] attachVertexShader(String[] others) {
return ObjectArrays.concat(SHAPE_VERTEX_SHADER_RESOURCE, others);
}
private static String[] attachFragmentShader(String[] others) {
return ObjectArrays.concat(SHAPE_FRAGMENT_SHADER_RESOURCE, others);
}
public void render(
WorldRenderer renderer,
Shape shape
) {
use();
configure(renderer);
bindVertices(shape.getVerticesVbo());
bindIndices(shape.getIndicesVbo());
try {
positionsAttribute.enable();
colorsAttribute.enable();
textureCoordsAttribute.enable();
normalsAttribute.enable();
for (Face face : shape.getFaces()) {
renderFace(face);
}
} finally {
positionsAttribute.disable();
colorsAttribute.disable();
textureCoordsAttribute.disable();
normalsAttribute.disable();
}
}
protected void configure(WorldRenderer renderer) {
finalTransformUniform.set(renderer.getFinalTransform());
worldTransformUniform.set(renderer.getWorldTransform());
}
protected int bindVertices(VertexBufferObject vertices) {
int vertexStride = getBytesPerVertex();
int offset = 0;
positionsAttribute.set(
3, GL11.GL_FLOAT, false, vertexStride, vertices,
offset
);
offset += 3 * Float.BYTES;
colorsAttribute.set(
3, GL11.GL_FLOAT, false, vertexStride, vertices,
offset
);
offset += 3 * Float.BYTES;
textureCoordsAttribute.set(
2, GL11.GL_FLOAT, false, vertexStride, vertices,
offset
);
offset += 2 * Float.BYTES;
normalsAttribute.set(
3, GL11.GL_FLOAT, false, vertexStride, vertices,
offset
);
offset += 3 * Float.BYTES;
return offset;
}
protected void bindIndices(VertexBufferObject indices) {
indices.bind(BindTarget.ELEMENT_ARRAY);
}
protected void renderFace(Face face) {
Sprite sprite = face.getTexture().getSprite();
sprite.getPrimitive().bind(0);
textureSlotUniform.set(0);
textureStartUniform.set(sprite.getStart());
textureSizeUniform.set(sprite.getSize());
GL11.glDrawElements(
GL11.GL_TRIANGLES,
face.getIndexCount(),
GL11.GL_UNSIGNED_SHORT,
face.getByteOffsetOfIndices()
);
}
public int getBytesPerVertex() {
return DEFAULT_BYTES_PER_VERTEX;
}
public static class VertexBuilder {
private static class Vertex {
final Vec3 position;
final Vec3 colorMultiplier;
final Vec2 textureCoords;
Vertex(Vec3 position, Vec3 colorMultiplier, Vec2 textureCoords) {
this.position = position;
this.colorMultiplier = colorMultiplier;
this.textureCoords = textureCoords;
}
}
private final List<Vertex> vertices = new ArrayList<>();
public VertexBuilder addVertex(
float x, float y, float z,
float r, float g, float b,
float tx, float ty
) {
vertices.add(new Vertex(
new Vec3(x, y, z),
new Vec3(r, g, b),
new Vec2(tx, ty)
));
return this;
}
public VertexBuilder addVertex(
Vec3 position,
Vec3 colorMultiplier,
Vec2 textureCoords
) {
vertices.add(new Vertex(
new Vec3(position),
new Vec3(colorMultiplier),
new Vec2(textureCoords)
));
return this;
}
public ByteBuffer assemble() {
ByteBuffer result = BufferUtils.createByteBuffer(
DEFAULT_BYTES_PER_VERTEX * vertices.size()
);
for (Vertex v : vertices) {
result
.putFloat(v.position.x)
.putFloat(v.position.y)
.putFloat(v.position.z)
.putFloat(v.colorMultiplier.x)
.putFloat(v.colorMultiplier.y)
.putFloat(v.colorMultiplier.z)
.putFloat(v.textureCoords.x)
.putFloat(v.textureCoords.y)
.putFloat(Float.NaN)
.putFloat(Float.NaN)
.putFloat(Float.NaN);
}
result.flip();
return result;
}
}
}

View File

@ -0,0 +1,218 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import glm.vec._3.Vec3;
import ru.windcorp.optica.client.graphics.backend.Usage;
import ru.windcorp.optica.client.graphics.texture.Texture;
public class Shapes {
public static Shape createParallelepiped( // Try saying that 10 times fast
Vec3 origin,
Vec3 width,
Vec3 height,
Vec3 depth,
Vec3 colorMultiplier,
Texture topTexture,
Texture bottomTexture,
Texture northTexture,
Texture southTexture,
Texture eastTexture,
Texture westTexture
) {
Vec3 faceOrigin = new Vec3();
Vec3 faceWidth = new Vec3();
Face top = Faces.createRectangle(
topTexture, colorMultiplier,
faceOrigin.set(origin).add(height).add(width),
faceWidth.set(width).negate(),
depth
);
Face bottom = Faces.createRectangle(
bottomTexture, colorMultiplier,
origin,
width,
depth
);
Face north = Faces.createRectangle(
northTexture, colorMultiplier,
faceOrigin.set(origin).add(depth),
width,
height
);
Face south = Faces.createRectangle(
southTexture, colorMultiplier,
faceOrigin.set(origin).add(width),
faceWidth.set(width).negate(),
height
);
Face east = Faces.createRectangle(
eastTexture, colorMultiplier,
origin,
depth,
height
);
Face west = Faces.createRectangle(
westTexture, colorMultiplier,
faceOrigin.set(origin).add(width).add(depth),
faceWidth.set(depth).negate(),
height
);
Shape result = new Shape(
Usage.STATIC,
top, bottom, north, south, east, west
);
return result;
}
public static class PppBuilder {
private final Vec3 origin = new Vec3(-0.5f, -0.5f, -0.5f);
private final Vec3 depth = new Vec3(1, 0, 0);
private final Vec3 width = new Vec3(0, 1, 0);
private final Vec3 height = new Vec3(0, 0, 1);
private final Vec3 colorMultiplier = new Vec3(1, 1, 1);
private final Texture topTexture;
private final Texture bottomTexture;
private final Texture northTexture;
private final Texture southTexture;
private final Texture eastTexture;
private final Texture westTexture;
public PppBuilder(
Texture top,
Texture bottom,
Texture north,
Texture south,
Texture east,
Texture west
) {
this.topTexture = top;
this.bottomTexture = bottom;
this.northTexture = north;
this.southTexture = south;
this.eastTexture = east;
this.westTexture = west;
}
public PppBuilder(Texture texture) {
this(texture, texture, texture, texture, texture, texture);
}
public PppBuilder setOrigin(Vec3 origin) {
this.origin.set(origin);
return this;
}
public PppBuilder setOrigin(float x, float y, float z) {
this.origin.set(x, y, z);
return this;
}
public PppBuilder setColorMultiplier(Vec3 colorMultiplier) {
this.colorMultiplier.set(colorMultiplier);
return this;
}
public PppBuilder setColorMultiplier(float r, float g, float b) {
this.colorMultiplier.set(r, g, b);
return this;
}
public PppBuilder setDepth(Vec3 vector) {
this.depth.set(vector);
return this;
}
public PppBuilder setDepth(float x, float y, float z) {
this.depth.set(x, y, z);
return this;
}
public PppBuilder setDepth(float x) {
this.depth.set(x, 0, 0);
return this;
}
public PppBuilder setWidth(Vec3 vector) {
this.width.set(vector);
return this;
}
public PppBuilder setWidth(float x, float y, float z) {
this.width.set(x, y, z);
return this;
}
public PppBuilder setWidth(float y) {
this.width.set(0, y, 0);
return this;
}
public PppBuilder setHeight(Vec3 vector) {
this.height.set(vector);
return this;
}
public PppBuilder setHeight(float x, float y, float z) {
this.height.set(x, y, z);
return this;
}
public PppBuilder setHeight(float z) {
this.height.set(0, 0, z);
return this;
}
public PppBuilder setSize(float x, float y, float z) {
return this.setWidth(x).setDepth(y).setHeight(z);
}
public Shape create() {
return createParallelepiped(
origin,
width, height, depth,
colorMultiplier,
topTexture,
bottomTexture,
northTexture,
southTexture,
eastTexture,
westTexture
);
}
}
}

View File

@ -0,0 +1,86 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import glm.mat._4.Mat4;
public class StaticModel extends Model {
private static final Mat4 IDENTITY = new Mat4();
private final Mat4[] transforms;
public StaticModel(
WorldRenderable[] parts,
Mat4[] transforms
) {
super(parts);
this.transforms = transforms;
}
public StaticModel(Builder builder) {
this(builder.getParts(), builder.getTransforms());
}
@Override
protected Mat4 getTransform(int partIndex) {
return transforms[partIndex];
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final List<WorldRenderable> parts = new ArrayList<>();
private final List<Mat4> transforms = new ArrayList<>();
protected Builder() {}
public Builder addPart(
WorldRenderable part,
Mat4 transform
) {
parts.add(Objects.requireNonNull(part, "part"));
transforms.add(Objects.requireNonNull(transform, "transform"));
return this;
}
public Builder addPart(
WorldRenderable part
) {
return addPart(part, IDENTITY);
}
private WorldRenderable[] getParts() {
return parts.toArray(new WorldRenderable[parts.size()]);
}
private Mat4[] getTransforms() {
return transforms.toArray(new Mat4[transforms.size()]);
}
}
}

View File

@ -0,0 +1,26 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.model;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
public interface WorldRenderable {
void render(WorldRenderer renderer);
}

View File

@ -0,0 +1,77 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.texture;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.*;
import java.nio.ByteBuffer;
class Pixels {
private final ByteBuffer data;
private final int bufferWidth;
private final int bufferHeight;
private final boolean filtered;
public Pixels(
ByteBuffer data,
int bufferWidth, int bufferHeight,
boolean filtered
) {
this.data = data;
this.bufferWidth = bufferWidth;
this.bufferHeight = bufferHeight;
this.filtered = filtered;
}
public int load() {
int handle = glGenTextures();
glBindTexture(GL_TEXTURE_2D, handle);
if (filtered) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(
GL_TEXTURE_2D, // Load 2D image
0, // Not mipmapped
GL_RGBA, // Use RGBA
bufferWidth, // Width
bufferHeight, // Height
0, // No border
GL_RGBA, // Use RGBA (required)
GL_UNSIGNED_BYTE, // Use unsigned bytes
data // Data buffer
);
glBindTexture(GL_TEXTURE_2D, 0);
return handle;
}
}

View File

@ -0,0 +1,33 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.texture;
public class SimpleTexture extends Texture {
private final Sprite sprite;
public SimpleTexture(Sprite sprite) {
this.sprite = sprite;
}
@Override
public Sprite getSprite() {
return sprite;
}
}

View File

@ -0,0 +1,56 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.texture;
import java.util.Objects;
import glm.vec._2.Vec2;
public class Sprite {
private static final Vec2 ORIGIN = new Vec2(0, 0);
private static final Vec2 FULL_PRIMITIVE = new Vec2(1, 1);
private final TexturePrimitive primitive;
private final Vec2 start;
private final Vec2 size;
public Sprite(TexturePrimitive primitive, Vec2 start, Vec2 size) {
this.primitive = Objects.requireNonNull(primitive, "primitive");
this.start = Objects.requireNonNull(start, "start");
this.size = Objects.requireNonNull(size, "size");
}
public Sprite(TexturePrimitive primitive) {
this(primitive, ORIGIN, FULL_PRIMITIVE);
}
public TexturePrimitive getPrimitive() {
return primitive;
}
public Vec2 getStart() {
return start;
}
public Vec2 getSize() {
return size;
}
}

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.texture;
public abstract class Texture {
public abstract Sprite getSprite();
}

View File

@ -0,0 +1,144 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.texture;
import java.awt.Graphics;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue;
import ru.windcorp.optica.common.resource.Resource;
import ru.windcorp.optica.common.resource.ResourceManager;
public class TextureManager {
private static final ColorModel COLOR_MODEL = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB), // Use RGB
new int[] {8, 8, 8, 8}, // Use every bit
true, // Has alpha
false, // Not premultiplied
ComponentColorModel.TRANSLUCENT, // Can have any alpha
DataBuffer.TYPE_BYTE // Alpha is one byte
);
private static final Hashtable<?, ?> CANVAS_PROPERTIES = new Hashtable<>();
private static final java.awt.Color CANVAS_BACKGROUND =
new java.awt.Color(0, 0, 0, 0);
private static final String TEXTURE_ASSETS_PREFIX = "assets/textures/";
private static Resource getResource(String textureName) {
return ResourceManager.getResource(
TEXTURE_ASSETS_PREFIX + textureName + ".png"
);
}
public static TexturePrimitive load(String textureName, boolean filtered) {
TexturePrimitive result = loadToByteBuffer(textureName, filtered);
RenderTaskQueue.invokeLater(result::load);
return result;
}
public static TexturePrimitive loadToByteBuffer(
String textureName, boolean filter
) {
Resource resource = getResource(textureName);
BufferedImage source = readImage(resource);
int bufferWidth = toPowerOf2(source.getWidth()),
bufferHeight = toPowerOf2(source.getHeight());
WritableRaster raster = Raster.createInterleavedRaster(
DataBuffer.TYPE_BYTE, // Storage model
bufferWidth, // Buffer width
bufferHeight, // Buffer height
4, // RGBA
null // Location (here (0; 0))
);
BufferedImage canvas = new BufferedImage(
COLOR_MODEL, // Color model
raster, // Backing raster
false, // Raster is not premultipied
CANVAS_PROPERTIES // Properties
);
Graphics g = canvas.createGraphics();
g.setColor(CANVAS_BACKGROUND);
g.fillRect(0, 0, source.getWidth(), source.getHeight());
g.drawImage(
source,
0, 0, source.getWidth(), source.getHeight(),
0, source.getHeight(), source.getWidth(), 0, // Flip the image
null
);
g.dispose();
byte[] data = (
(DataBufferByte) canvas.getRaster().getDataBuffer()
).getData();
ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
buffer.order(ByteOrder.nativeOrder());
buffer.put(data);
buffer.flip();
Pixels pixels = new Pixels(buffer, bufferWidth, bufferHeight, filter);
TexturePrimitive result = new TexturePrimitive(
pixels,
source.getWidth(),
source.getHeight(),
bufferWidth,
bufferHeight
);
return result;
}
private static BufferedImage readImage(Resource resource) {
try {
return ImageIO.read(resource.getInputStream());
} catch (Exception e) {
throw new RuntimeException("too bad. refresh project u stupid. must be " + resource.getName(), e);
}
}
private static int toPowerOf2(int i) {
// TODO optimize
int result = 1;
do {
result *= 2;
} while (result < i);
return result;
}
}

View File

@ -0,0 +1,101 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.texture;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker;
import ru.windcorp.optica.client.graphics.backend.OpenGLObjectTracker.OpenGLDeletable;
public class TexturePrimitive implements OpenGLDeletable {
private static final int NOT_LOADED = -1;
private int handle = NOT_LOADED;
private Pixels pixelsToLoad;
private final int width;
private final int height;
private final int bufferWidth;
private final int bufferHeight;
protected TexturePrimitive(
Pixels pixels,
int width, int height,
int bufferWidth, int bufferHeight
) {
this.pixelsToLoad = pixels;
this.width = width;
this.height = height;
this.bufferWidth = bufferWidth;
this.bufferHeight = bufferHeight;
OpenGLObjectTracker.register(this);
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getBufferWidth() {
return bufferWidth;
}
public int getBufferHeight() {
return bufferHeight;
}
public boolean isLoaded() {
return handle != NOT_LOADED;
}
public void bind(int slot) {
if (!isLoaded()) {
load();
}
int code = GL_TEXTURE0 + slot;
glActiveTexture(code);
glBindTexture(GL_TEXTURE_2D, handle);
}
protected void load() {
if (isLoaded()) return;
handle = pixelsToLoad.load();
if (handle < 0) {
throw new RuntimeException("oops");
}
pixelsToLoad = null;
}
@Override
public void delete() {
if (isLoaded())
glDeleteTextures(handle);
}
}

View File

@ -0,0 +1,121 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.world;
import static java.lang.Math.*;
import glm.Glm;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
public class Camera {
private final Vec3 position = new Vec3();
private float pitch;
private float yaw;
private float fieldOfView;
public Camera(Vec3 position, float pitch, float yaw, float fieldOfView) {
teleport(position);
setPitch(pitch);
setYaw(yaw);
setFieldOfView(fieldOfView);
}
public Camera() {}
public void apply(WorldRenderer renderer) {
Mat4 previous = renderer.getViewTransform();
Glm.perspective(
computeFovY(),
GraphicsInterface.getAspectRatio(),
0.01f, 10000.0f,
renderer.pushViewTransform()
).mul(previous);
renderer.pushViewTransform().rotateX(pitch).rotateY(yaw);
renderer.pushViewTransform().translate(position.negate());
position.negate();
}
private float computeFovY() {
float widthOverHeight = GraphicsInterface.getAspectRatio();
if (widthOverHeight >= 1) {
return fieldOfView;
} else {
return (float) (2 * atan(
1 / widthOverHeight
*
tan(fieldOfView / 2)
));
}
}
public Vec3 getPosition() {
return position;
}
public void teleport(Vec3 pos) {
position.set(pos);
}
public void move(Vec3 pos) {
position.add(pos);
}
public float getPitch() {
return pitch;
}
public void setPitch(float pitch) {
final float maxPitch = (float) (Math.PI / 2);
this.pitch = Glm.clamp(pitch, -maxPitch, +maxPitch);
}
public float getYaw() {
return yaw;
}
public void setYaw(float yaw) {
this.yaw = Glm.mod(yaw, 2 * (float) PI);
}
public void setDirection(float pitch, float yaw) {
setPitch(pitch);
setYaw(yaw);
}
public void turn(float pitchChange, float yawChange) {
setPitch(getPitch() + pitchChange);
setYaw(getYaw() + yawChange);
}
public float getFieldOfView() {
return fieldOfView;
}
public void setFieldOfView(float fieldOfView) {
this.fieldOfView = fieldOfView;
}
}

View File

@ -0,0 +1,148 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.world;
import org.lwjgl.glfw.GLFW;
import com.google.common.eventbus.Subscribe;
import glm.mat._3.Mat3;
import glm.vec._3.Vec3;
import ru.windcorp.optica.client.graphics.Layer;
import ru.windcorp.optica.client.graphics.backend.GraphicsBackend;
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
import ru.windcorp.optica.client.graphics.input.CursorMoveEvent;
import ru.windcorp.optica.client.graphics.input.KeyEvent;
import ru.windcorp.optica.client.world.WorldRender;
import ru.windcorp.optica.common.world.WorldData;
public class LayerWorld extends Layer {
private final Camera camera = new Camera(
new Vec3(8, 8, 8),
0, 0,
(float) Math.toRadians(70)
);
private final Vec3 velocity = new Vec3();
private final Vec3 tmp = new Vec3();
private final Mat3 angMat = new Mat3();
private int movementX = 0;
private int movementY = 0;
private int movementZ = 0;
private final WorldRenderer renderer = new WorldRenderer();
private final WorldRender world = new WorldRender(new WorldData());
public LayerWorld() {
super("World");
}
@Override
protected void initialize() {
// TODO Auto-generated method stub
GraphicsInterface.subscribeToInputEvents(this);
}
@Override
protected void doRender() {
camera.apply(renderer);
renderWorld();
renderer.reset();
angMat.set().rotateY(-camera.getYaw());
tmp.set(movementX, 0, movementZ);
angMat.mul_(tmp); // bug in jglm
tmp.y = movementY;
tmp.mul(0.1f);
tmp.sub(velocity);
tmp.mul(0.1f);
velocity.add(tmp);
tmp.set(velocity);
tmp.mul((float) (GraphicsInterface.getFrameLength() * 60));
camera.move(tmp);
}
private void renderWorld() {
world.render(renderer);
}
public Camera getCamera() {
return camera;
}
private boolean flag = true;
@Subscribe
public void onKeyEvent(KeyEvent event) {
if (event.isRepeat()) return;
int multiplier = event.isPress() ? 1 : -1;
switch (event.getKey()) {
case GLFW.GLFW_KEY_W:
movementZ += -1 * multiplier;
break;
case GLFW.GLFW_KEY_S:
movementZ += +1 * multiplier;
break;
case GLFW.GLFW_KEY_A:
movementX += -1 * multiplier;
break;
case GLFW.GLFW_KEY_D:
movementX += +1 * multiplier;
break;
case GLFW.GLFW_KEY_SPACE:
movementY += +1 * multiplier;
break;
case GLFW.GLFW_KEY_LEFT_SHIFT:
movementY += -1 * multiplier;
break;
case GLFW.GLFW_KEY_ESCAPE:
if (!event.isPress()) return;
if (flag) {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
} else {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
}
flag = !flag;
break;
}
}
@Subscribe
public void onMouseMoved(CursorMoveEvent event) {
if (!flag) return;
final float yawScale = 0.002f;
final float pitchScale = yawScale;
camera.turn(
(float) (event.getChangeY() * pitchScale),
(float) (event.getChangeX() * yawScale)
);
}
}

View File

@ -0,0 +1,78 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.graphics.world;
import glm.mat._4.Mat4;
import ru.windcorp.optica.common.util.StashingStack;
public class WorldRenderer {
private static final int TRANSFORM_STACK_SIZE = 64;
private final StashingStack<Mat4> worldTransformStack = new StashingStack<>(
TRANSFORM_STACK_SIZE, Mat4::new
);
private final StashingStack<Mat4> viewTransformStack = new StashingStack<>(
TRANSFORM_STACK_SIZE, Mat4::new
);
private final Mat4 finalTransform = new Mat4();
{
reset();
}
public Mat4 pushWorldTransform() {
Mat4 previous = worldTransformStack.getHead();
return worldTransformStack.push().set(previous);
}
public void popWorldTransform() {
worldTransformStack.removeHead();
}
public Mat4 getWorldTransform() {
return worldTransformStack.getHead();
}
public Mat4 pushViewTransform() {
Mat4 previous = viewTransformStack.getHead();
return viewTransformStack.push().set(previous);
}
public void popViewTransform() {
viewTransformStack.removeHead();
}
public Mat4 getViewTransform() {
return viewTransformStack.getHead();
}
public Mat4 getFinalTransform() {
return finalTransform.set(getViewTransform()).mul(getWorldTransform());
}
public void reset() {
worldTransformStack.removeAll();
worldTransformStack.push().identity();
viewTransformStack.removeAll();
viewTransformStack.push().identity();
}
}

View File

@ -0,0 +1,169 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world;
import java.util.HashMap;
import java.util.Map;
import glm.mat._4.Mat4;
import ru.windcorp.optica.client.graphics.model.Model;
import ru.windcorp.optica.client.graphics.model.Shape;
import ru.windcorp.optica.client.graphics.model.StaticModel;
import ru.windcorp.optica.client.graphics.model.StaticModel.Builder;
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
import ru.windcorp.optica.client.world.renders.BlockRender;
import ru.windcorp.optica.client.world.renders.BlockRenderNone;
import ru.windcorp.optica.client.world.renders.BlockRenders;
import ru.windcorp.optica.client.world.renders.bro.BlockRenderOptimizer;
import ru.windcorp.optica.client.world.renders.bro.BlockRenderOptimizerGenerator;
import ru.windcorp.optica.client.world.renders.bro.BlockRenderOptimizerGenerators;
import ru.windcorp.optica.common.world.ChunkData;
public class ChunkRender {
private final WorldRender world;
private final ChunkData data;
private Model model = null;
public ChunkRender(WorldRender world, ChunkData data) {
this.world = world;
this.data = data;
}
public WorldRender getWorld() {
return world;
}
public ChunkData getData() {
return data;
}
public BlockRender getBlock(int xInChunk, int yInChunk, int zInChunk) {
return BlockRenders.get(
getData().getBlock(xInChunk, yInChunk, zInChunk).getId()
);
}
public void render(WorldRenderer renderer) {
if (model == null) {
buildModel();
}
renderer.pushWorldTransform().translate(
data.getX() * ChunkData.BLOCKS_PER_CHUNK,
data.getY() * ChunkData.BLOCKS_PER_CHUNK,
data.getZ() * ChunkData.BLOCKS_PER_CHUNK
);
model.render(renderer);
renderer.popWorldTransform();
}
private void buildModel() {
Map<String, BlockRenderOptimizer> optimizers = new HashMap<>();
for (
BlockRenderOptimizerGenerator generator :
BlockRenderOptimizerGenerators.getAll()
) {
BlockRenderOptimizer optimizer = generator.createOptimizer();
optimizers.put(generator.getId(), optimizer);
optimizer.startRender(this);
}
StaticModel.Builder builder = StaticModel.builder();
for (int x = 0; x < ChunkData.BLOCKS_PER_CHUNK; ++x) {
for (int y = 0; y < ChunkData.BLOCKS_PER_CHUNK; ++y) {
for (int z = 0; z < ChunkData.BLOCKS_PER_CHUNK; ++z) {
BlockRender block = getBlock(x, y, z);
if (block instanceof BlockRenderNone) {
continue;
}
if (tryToForwardToOptimizers(block, x, y, z, optimizers)) {
continue;
}
if (tryToCreateRenderable(block, x, y, z, builder)) {
continue;
}
addRenderAsRenderable(block, x, y, z, builder);
}
}
}
for (BlockRenderOptimizer optimizer : optimizers.values()) {
Shape result = optimizer.endRender();
if (result != null) {
builder.addPart(result);
}
}
model = new StaticModel(builder);
}
private boolean tryToForwardToOptimizers(
BlockRender block, int x, int y, int z,
Map<String, BlockRenderOptimizer> optimizers
) {
if (!block.isOptimized()) {
return false;
}
BlockRenderOptimizer optimizer = optimizers.get(block.getOptimizer());
if (optimizer == null) {
return false;
}
optimizer.processBlock(block, x, y, z);
return true;
}
private boolean tryToCreateRenderable(
BlockRender block, int x, int y, int z,
Builder builder
) {
WorldRenderable renderable = block.createRenderable();
if (renderable == null) {
return false;
}
builder.addPart(renderable, new Mat4().identity().translate(x, y, z));
return true;
}
private void addRenderAsRenderable(
BlockRender block, int x, int y, int z,
Builder builder
) {
builder.addPart(
block::render,
new Mat4().identity().translate(x, y, z)
);
}
}

View File

@ -0,0 +1,53 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world;
import java.util.ArrayList;
import java.util.Collection;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
import ru.windcorp.optica.common.world.ChunkData;
import ru.windcorp.optica.common.world.WorldData;
public class WorldRender {
private final WorldData data;
private final Collection<ChunkRender> chunks = new ArrayList<>();
public WorldRender(WorldData data) {
this.data = data;
for (ChunkData chunkData : data.getChunks()) {
chunks.add(new ChunkRender(this, chunkData));
}
}
public WorldData getData() {
return data;
}
public void render(WorldRenderer renderer) {
renderer.pushWorldTransform().rotateX(-Math.PI / 2);
for (ChunkRender chunk : chunks) {
chunk.render(renderer);
}
}
}

View File

@ -0,0 +1,54 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders;
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
import ru.windcorp.optica.common.util.Namespaced;
public abstract class BlockRender extends Namespaced {
private String optimizer = null;
public BlockRender(String namespace, String name) {
super(namespace, name);
}
public String getOptimizer() {
return optimizer;
}
public boolean isOptimized() {
return getOptimizer() != null;
}
public void setOptimizer(String optimizer) {
this.optimizer = optimizer;
}
public void render(WorldRenderer renderer) {
throw new UnsupportedOperationException(
"BlockRender.render() not implemented"
);
}
public WorldRenderable createRenderable() {
return null;
}
}

View File

@ -0,0 +1,34 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders;
import ru.windcorp.optica.client.graphics.model.EmptyModel;
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
public class BlockRenderNone extends BlockRender {
public BlockRenderNone(String namespace, String name) {
super(namespace, name);
}
@Override
public WorldRenderable createRenderable() {
return EmptyModel.getInstance();
}
}

View File

@ -0,0 +1,63 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders;
import static ru.windcorp.optica.common.block.BlockFace.*;
import java.util.EnumMap;
import ru.windcorp.optica.client.graphics.model.Shapes;
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
import ru.windcorp.optica.client.graphics.texture.Texture;
import ru.windcorp.optica.common.block.BlockFace;
public abstract class BlockRenderTexturedCube extends BlockRender {
private final EnumMap<BlockFace, Texture> textures =
new EnumMap<>(BlockFace.class);
public BlockRenderTexturedCube(
String namespace, String name,
Texture topTexture, Texture bottomTexture,
Texture northTexture, Texture southTexture,
Texture eastTexture, Texture westTexture
) {
super(namespace, name);
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(BlockFace face) {
return textures.get(face);
}
@Override
public WorldRenderable createRenderable() {
return new Shapes.PppBuilder(
getTexture(TOP), getTexture(BOTTOM),
getTexture(NORTH), getTexture(SOUTH),
getTexture(EAST), getTexture(WEST)
).create();
}
}

View File

@ -0,0 +1,38 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders;
import ru.windcorp.optica.client.graphics.texture.Texture;
public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
public BlockRenderTransparentCube(
String namespace, String name,
Texture topTexture, Texture bottomTexture,
Texture northTexture, Texture southTexture,
Texture eastTexture, Texture westTexture
) {
super(
namespace, name,
topTexture, bottomTexture,
northTexture, southTexture,
eastTexture, westTexture
);
}
}

View File

@ -0,0 +1,65 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders;
import java.util.HashMap;
import java.util.Map;
import ru.windcorp.optica.client.graphics.texture.SimpleTexture;
import ru.windcorp.optica.client.graphics.texture.Sprite;
import ru.windcorp.optica.client.graphics.texture.Texture;
import ru.windcorp.optica.client.graphics.texture.TextureManager;
import ru.windcorp.optica.client.world.renders.bro.BlockRenderOpaqueCube;
public class BlockRenders {
private static Texture grassTop = qtex("grass_top");
private static Texture grassSide = qtex("grass_side");
private static Texture dirtT = qtex("grass_bottom");
private static Texture stoneT = qtex("stone");
private static Texture glassT = qtex("glass_clear");
private static final Map<String, BlockRender> BLOCK_RENDERS =
new HashMap<>();
private BlockRenders() {}
static {
register(new BlockRenderOpaqueCube("Grass", "Test", grassTop, dirtT, grassSide, grassSide, grassSide, grassSide));
register(new BlockRenderOpaqueCube("Dirt", "Test", dirtT, dirtT, dirtT, dirtT, dirtT, dirtT));
register(new BlockRenderOpaqueCube("Stone", "Test", stoneT, stoneT, stoneT, stoneT, stoneT, stoneT));
register(new BlockRenderOpaqueCube("Compass", "Test", qtex("compass"), qtex("compass"), qtex("side_north"), qtex("side_south"), qtex("side_east"), qtex("side_west")));
register(new BlockRenderNone("Air", "Test"));
register(new BlockRenderTransparentCube("Glass", "Test", glassT, glassT, glassT, glassT, glassT, glassT));
}
public static BlockRender get(String name) {
return BLOCK_RENDERS.get(name);
}
public static void register(BlockRender blockRender) {
BLOCK_RENDERS.put(blockRender.getId(), blockRender);
}
private static Texture qtex(String name) {
return new SimpleTexture(new Sprite(TextureManager.load(name, false)));
}
}

View File

@ -0,0 +1,40 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders.bro;
import ru.windcorp.optica.client.graphics.texture.Texture;
import ru.windcorp.optica.client.world.renders.BlockRenderTexturedCube;
public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
public BlockRenderOpaqueCube(
String namespace, String name,
Texture topTexture, Texture bottomTexture,
Texture northTexture, Texture southTexture,
Texture eastTexture, Texture westTexture
) {
super(
namespace, name,
topTexture, bottomTexture,
northTexture, southTexture,
eastTexture, westTexture
);
setOptimizer("Default:OpaqueCube");
}
}

View File

@ -0,0 +1,187 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders.bro;
import static ru.windcorp.optica.common.world.ChunkData.BLOCKS_PER_CHUNK;
import java.util.ArrayList;
import java.util.Collection;
import glm.vec._3.Vec3;
import ru.windcorp.optica.client.graphics.backend.Usage;
import ru.windcorp.optica.client.graphics.model.Face;
import ru.windcorp.optica.client.graphics.model.Faces;
import ru.windcorp.optica.client.graphics.model.Shape;
import ru.windcorp.optica.client.graphics.texture.Texture;
import ru.windcorp.optica.client.world.ChunkRender;
import ru.windcorp.optica.client.world.renders.BlockRender;
import ru.windcorp.optica.common.block.BlockFace;
import ru.windcorp.optica.common.world.ChunkData;
public class BlockRenderOpaqueCubeOptimizer extends BlockRenderOptimizer {
private static final int BLOCK_MASK = 1 << 7;
private static final BlockFace[] GOOD_FACES = new BlockFace[] {
BlockFace.TOP, BlockFace.NORTH, BlockFace.WEST
};
private static final Vec3 COLOR_MULTIPLIER = new Vec3(1, 1, 1);
private final byte[][][] data = new byte[BLOCKS_PER_CHUNK + 1]
[BLOCKS_PER_CHUNK + 1]
[BLOCKS_PER_CHUNK + 1];
private ChunkRender chunk;
private final Vec3 blockCenter = new Vec3();
@Override
public void startRender(ChunkRender chunk) {
this.chunk = chunk;
}
@Override
public void processBlock(BlockRender block, int x, int y, int z) {
addFace(x, y, z, BlockFace.TOP);
addFace(x, y, z, BlockFace.BOTTOM);
addFace(x, y, z, BlockFace.NORTH);
addFace(x, y, z, BlockFace.SOUTH);
addFace(x, y, z, BlockFace.EAST);
addFace(x, y, z, BlockFace.WEST);
addBlock(x, y, z);
}
protected void addFace(int x, int y, int z, BlockFace face) {
switch (face) {
case BOTTOM:
z -= 1;
face = BlockFace.TOP;
break;
case SOUTH:
x -= 1;
face = BlockFace.NORTH;
break;
case EAST:
y -= 1;
face = BlockFace.WEST;
break;
default:
}
data[x + 1][y + 1][z + 1] ^= 1 << face.ordinal();
}
protected void addBlock(int x, int y, int z) {
data[x + 1][y + 1][z + 1] |= BLOCK_MASK;
}
protected boolean hasFace(int x, int y, int z, BlockFace face) {
switch (face) {
case BOTTOM:
z -= 1;
face = BlockFace.TOP;
break;
case SOUTH:
x -= 1;
face = BlockFace.NORTH;
break;
case EAST:
y -= 1;
face = BlockFace.WEST;
break;
default:
}
return (data[x + 1][y + 1][z + 1] & 1 << face.ordinal()) != 0;
}
protected boolean hasBlock(int x, int y, int z) {
return (data[x + 1][y + 1][z + 1] & BLOCK_MASK) != 0;
}
@Override
public Shape endRender() {
Collection<Face> shapeFaces = new ArrayList<>(
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3 +
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3
);
for (int x = -1; x < ChunkData.BLOCKS_PER_CHUNK; ++x) {
for (int y = -1; y < ChunkData.BLOCKS_PER_CHUNK; ++y) {
for (int z = -1; z < ChunkData.BLOCKS_PER_CHUNK; ++z) {
for (BlockFace face : GOOD_FACES) {
if (!hasFace(x, y, z, face)) continue;
Face shapeFace = null;
if (!hasBlock(x, y, z)) {
switch (face) {
case TOP:
shapeFace = createFace(
x, y, z + 1,
BlockFace.BOTTOM
);
break;
case NORTH:
shapeFace = createFace(
x + 1, y, z,
BlockFace.SOUTH
);
break;
case WEST:
shapeFace = createFace(
x, y + 1, z,
BlockFace.EAST
);
break;
default:
}
} else {
shapeFace = createFace(x, y, z, face);
}
shapeFaces.add(shapeFace);
}
}
}
}
return new Shape(
Usage.STATIC,
shapeFaces.toArray(new Face[shapeFaces.size()])
);
}
private Face createFace(int x, int y, int z, BlockFace face) {
BlockRenderOpaqueCube blockRender =
(BlockRenderOpaqueCube) chunk.getBlock(x, y, z);
Texture texture = blockRender.getTexture(face);
return Faces.createBlockFace(
texture,
COLOR_MULTIPLIER,
blockCenter.set(x, y, z),
face
);
}
}

View File

@ -0,0 +1,32 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders.bro;
import ru.windcorp.optica.client.graphics.model.Shape;
import ru.windcorp.optica.client.world.ChunkRender;
import ru.windcorp.optica.client.world.renders.BlockRender;
public abstract class BlockRenderOptimizer {
public abstract void startRender(ChunkRender chunk);
public abstract void processBlock(BlockRender block, int x, int y, int z);
public abstract Shape endRender();
}

View File

@ -0,0 +1,30 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders.bro;
import ru.windcorp.optica.common.util.Namespaced;
public abstract class BlockRenderOptimizerGenerator extends Namespaced {
public BlockRenderOptimizerGenerator(String namespace, String name) {
super(namespace, name);
}
public abstract BlockRenderOptimizer createOptimizer();
}

View File

@ -0,0 +1,52 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.client.world.renders.bro;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class BlockRenderOptimizerGenerators {
private BlockRenderOptimizerGenerators() {}
private static final Map<String, BlockRenderOptimizerGenerator> GENERATORS =
new HashMap<>();
static {
register(new BlockRenderOptimizerGenerator("Default", "OpaqueCube") {
@Override
public BlockRenderOptimizer createOptimizer() {
return new BlockRenderOpaqueCubeOptimizer();
}
});
}
public static BlockRenderOptimizerGenerator get(String id) {
return GENERATORS.get(id);
}
public static void register(BlockRenderOptimizerGenerator generator) {
GENERATORS.put(generator.getId(), generator);
}
public static Collection<BlockRenderOptimizerGenerator> getAll() {
return GENERATORS.values();
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.block;
import ru.windcorp.optica.common.util.Namespaced;
public class BlockData extends Namespaced {
public BlockData(String namespace, String name) {
super(namespace, name);
}
}

View File

@ -0,0 +1,35 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.block;
import java.util.HashMap;
import java.util.Map;
public class BlockDataRegistry {
private static final Map<String, BlockData> REGISTRY = new HashMap<>();
public static BlockData get(String name) {
return REGISTRY.get(name);
}
public static void register(BlockData blockData) {
REGISTRY.put(blockData.getId(), blockData);
}
}

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.block;
public enum BlockFace {
TOP, BOTTOM, NORTH, SOUTH, EAST, WEST;
}

View File

@ -0,0 +1,58 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.resource;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import com.google.common.io.CharStreams;
import ru.windcorp.optica.Optica;
public class Resource {
private final String name;
public Resource(String name) {
this.name = name;
}
public String getName() {
return name;
}
public InputStream getInputStream() {
// TODO Do proper resource lookup
return Optica.class.getClassLoader().getResourceAsStream(name);
}
public Reader getReader() {
return new InputStreamReader(getInputStream());
}
public String readAsString() {
try (Reader reader = getReader()) {
return CharStreams.toString(reader);
} catch (IOException e) {
throw new RuntimeException(e); // TODO handle gracefully
}
}
}

View File

@ -0,0 +1,26 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.resource;
public class ResourceManager {
public static Resource getResource(String name) {
return new Resource(name);
}
}

View File

@ -0,0 +1,69 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.util;
public class CoordinatePacker {
private static final int BITS_3_INTS_INTO_LONG;
private static final long MASK_3_INTS_INTO_LONG;
static {
BITS_3_INTS_INTO_LONG = 64 / 3;
/*
* What happens below:
*
* 1. 1 << BITS_3_INTS_INTO_LONG:
* 0000 ... 00100 ... 0000
* \_________/ - BITS_3_INTS_INTO_LONG zeros
*
* 2. (1 << BITS_3_INTS_INTO_LONG) - 1:
* 0000 ... 00011 ... 1111
* \_________/ - BITS_3_INTS_INTO_LONG ones - WIN
*/
MASK_3_INTS_INTO_LONG = (1 << BITS_3_INTS_INTO_LONG) - 1;
}
public static long pack3IntsIntoLong(int a, int b, int c) {
return
((a & MASK_3_INTS_INTO_LONG) << (2 * BITS_3_INTS_INTO_LONG)) |
((b & MASK_3_INTS_INTO_LONG) << (1 * BITS_3_INTS_INTO_LONG)) |
((c & MASK_3_INTS_INTO_LONG) << (0 * BITS_3_INTS_INTO_LONG));
}
public static int unpack3IntsFromLong(long packed, int index) {
if (index < 0 || index >= 3) {
throw new IllegalArgumentException("Invalid index " + index);
}
int result = (int) (
(packed >>> ((2 - index) * BITS_3_INTS_INTO_LONG))
& MASK_3_INTS_INTO_LONG
);
final long signMask = ((MASK_3_INTS_INTO_LONG + 1) >> 1);
if ((result & signMask) != 0) {
result |= ~MASK_3_INTS_INTO_LONG;
}
return result;
}
}

View File

@ -0,0 +1,61 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.util;
import java.util.Objects;
public abstract class Named {
private final String name;
public Named(String name) {
this.name = Objects.requireNonNull(name, "name");
}
public String getName() {
return name;
}
@Override
public String toString() {
return getClass().getSimpleName() + " " + getName();
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Named other = (Named) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}

View File

@ -0,0 +1,91 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.util;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public abstract class Namespaced extends Named {
private static final char SEPARATOR = ':';
private static final String PART_REGEX = "^[A-Z][a-zA-Z0-9]{2,}$";
private static final Predicate<String> PART_CHECKER =
Pattern.compile(PART_REGEX).asPredicate();
private final String namespace;
private final String id;
public Namespaced(String namespace, String name) {
super(name);
this.namespace = Objects.requireNonNull(namespace, "namespace");
this.id = namespace + SEPARATOR + name;
if (!PART_CHECKER.test(name)) {
throw new IllegalArgumentException(
"Name \"" + name + "\" is invalid. "
+ "Allowed is: " + PART_REGEX
);
}
if (!PART_CHECKER.test(namespace)) {
throw new IllegalArgumentException(
"Namespace \"" + namespace + "\" is invalid. "
+ "Allowed is: " + PART_REGEX
);
}
}
public String getId() {
return id;
}
public String getNamespace() {
return namespace;
}
@Override
public String toString() {
return getId();
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
Namespaced other = (Namespaced) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}

View File

@ -0,0 +1,227 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.util;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Supplier;
import com.google.common.collect.Iterables;
/**
* A low-overhead, fixed-capacity stack that does not dispose of popped elements
* but rather <i>stashes</i> them for later pushing. This allows the stack to
* operate without creating new objects.
* <p>
* This object always contains references to {@link #getCapacity()} elements, of
* which first {@link #getSize()} elements are present in the stack proper and
* accessable, and the rest are <i>stashed</i>. When an element is popped, is
* becomes stashed. When an element is pushed, it ceases to be stashed.
* <p>
* Stashed elements can be replaced with {@link #push(Object)}.
*
* @author Javapony
*/
@SuppressWarnings("unchecked")
public class StashingStack<T> implements Iterable<T> {
/**
* Stores all elements. Elements with indices
* <tt>[0;&nbsp;{@link #head}]</tt>
* are present in the stack, elements with indices
* <tt>({@link #head};&nbsp;contents.length]</tt> are stashed.
*/
private final Object[] contents;
private transient List<T> contentsAsList;
/**
* Index of the head of the stack in the {@link #contents} array, or
* <tt>-1</tt>, if the stack is empty.
*/
private int head = -1;
protected StashingStack(Object[] stash, int dummy) {
this.contents = stash;
}
/**
* Creates a new stack. Its stash is filled with {@code null}s.
* @param capacity stack's capacity
*/
public StashingStack(int capacity) {
this((T[]) new Object[capacity], 0);
}
/**
* Creates a new stack with the supplied stash.
* @param contents elements that are put in the stash initially.
*/
public StashingStack(T[] contents) {
this(contents.clone(), 0);
}
/**
* Creates a new stack with the supplied stash.
* @param contents elements that are put in the stash initially.
*/
public StashingStack(Iterable<T> contents) {
this(Iterables.toArray(contents, Object.class), 0);
}
/**
* Creates a new stack. Its stash is filled with objects provided by
* {@code generator}. The generator's {@link Supplier#get() get()} method
* will only be invoked {@code capacity} times from within this constructor.
* @param capacity stack's capacity
* @param generator a supplier of objects for the stash
*/
public StashingStack(int capacity, Supplier<T> generator) {
this(capacity);
for (int i = 0; i < contents.length; ++i) {
contents[i] = generator.get();
}
}
/**
* Returns the amount of elements this stack can store.
* @return the capacity
*/
public int getCapacity() {
return contents.length;
}
/**
* Returns the amount of elements that are currently in the stack.
* @return the size
*/
public int getSize() {
return head + 1;
}
/**
* Checks whether this stack does not contain any elements.
* @return {@code true} is this stack is empty
*/
public boolean isEmpty() {
return getSize() == 0;
}
/**
* Checks whether this stack is full.
* @return {@code true} is this stack is full
*/
public boolean isFull() {
return getSize() == getCapacity();
}
/**
* Returns, but does not remove, the head of this stack. If the stack is
* empty returns {@code null}.
* @return head of this stack or {@code null}
* @see #getHead()
*/
public T peek() {
if (head < 0) return null;
return (T) contents[head];
}
/**
* Returns, but does not remove, the head of this stack. If the stack is
* empty throws a {@link NoSuchElementException}.
* @return head of this stack
* @throws NoSuchElementException is the stack is empty
* @see #peek()
*/
public T getHead() {
if (head < 0) throw new NoSuchElementException();
return (T) contents[head];
}
/**
* Returns and removes the head of this stack. If the stack is
* empty returns {@code null}.
* @return head of this stack or {@code null}
* @see #removeHead()
*/
public T pop() {
if (head < 0) return null;
return (T) contents[head--];
}
/**
* Returns and removes the head of this stack. If the stack is
* empty throws a {@link NoSuchElementException}.
* @return head of this stack
* @throws NoSuchElementException is the stack is empty
* @see #pop()
*/
public T removeHead() {
if (head < 0) throw new NoSuchElementException();
return (T) contents[head--];
}
/**
* Pushes a new element from the stash onto the stack. If the stack is
* already full throws an {@link IllegalStateException}. The state of the
* new element is not specified.
* @return the new head
*/
public T push() {
if (head == contents.length - 1) {
throw new IllegalStateException();
}
return (T) contents[++head];
}
/**
* Pushes the specified element onto the stack. A stashed element is
* removed. If the stack is already full throws an
* {@link IllegalStateException}.
* @param newElement the element to push
* @return the new head
*/
public T push(T newElement) {
if (head == contents.length - 1) {
throw new IllegalStateException();
}
contents[++head] = newElement;
return newElement;
}
/**
* Removes all elements from the stack.
*/
public void removeAll() {
head = -1;
}
@Override
public Iterator<T> iterator() {
if (contentsAsList == null) {
contentsAsList = Arrays.asList((T[]) contents);
}
return contentsAsList.subList(0, getSize()).iterator();
}
}

View File

@ -0,0 +1,63 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.util;
import java.util.function.Consumer;
import com.google.common.base.Throwables;
@FunctionalInterface
public interface ThrowingRunnable<T extends Throwable> {
void run() throws T;
default Runnable withCatcher(
Consumer<T> catcher,
Class<T> throwableClass
) {
return () -> {
try {
ThrowingRunnable.this.run();
} catch (Throwable t) {
if (t.getClass() == throwableClass) {
catcher.accept(throwableClass.cast(t));
}
Throwables.throwIfUnchecked(t);
// This should never happen
throw new AssertionError("This should not have been thrown", t);
}
};
}
default Runnable withCatcher(
Consumer<Throwable> catcher
) {
return () -> {
try {
ThrowingRunnable.this.run();
} catch (Throwable t) {
catcher.accept(t);
}
};
}
}

View File

@ -0,0 +1,114 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.world;
import glm.vec._3.i.Vec3i;
import ru.windcorp.optica.common.block.BlockData;
public class ChunkData {
public static final int BLOCKS_PER_CHUNK = 16;
private final int x;
private final int y;
private final int z;
private final BlockData[][][] blocks = new BlockData[BLOCKS_PER_CHUNK]
[BLOCKS_PER_CHUNK]
[BLOCKS_PER_CHUNK];
private final BlockData grass = new BlockData("Grass", "Test");
private final BlockData dirt = new BlockData("Dirt", "Test");
private final BlockData stone = new BlockData("Stone", "Test");
private final BlockData air = new BlockData("Air", "Test");
// private final BlockData glass = new BlockData("Glass", "Test");
// private final BlockData compass = new BlockData("Compass", "Test");
public ChunkData(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
tmp_generate();
}
private void tmp_generate() {
Vec3i aPoint = new Vec3i(5, 0, BLOCKS_PER_CHUNK + BLOCKS_PER_CHUNK/2);
Vec3i pos = new Vec3i();
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) {
for (int z = 0; z < BLOCKS_PER_CHUNK; ++z) {
pos.set(x, y, z);
float f = aPoint.sub(pos, pos).length();
if (f > 17) {
blocks[x][y][z] = stone;
} else if (f > 14) {
blocks[x][y][z] = dirt;
} else {
blocks[x][y][z] = air;
}
}
}
}
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) {
int z;
for (z = BLOCKS_PER_CHUNK - 1; z >= 0 && blocks[x][y][z] == air; --z);
blocks[x][y][z] = grass;
}
}
}
public BlockData getBlock(int xInChunk, int yInChunk, int zInChunk) {
if (!isInBounds(xInChunk, yInChunk, zInChunk)) {
throw new IllegalArgumentException(
"Coordinates (" + x + "; " + y + "; " + z + ") "
+ "are not legal chunk coordinates"
);
}
return blocks[xInChunk][yInChunk][zInChunk];
}
private boolean isInBounds(int xInChunk, int yInChunk, int zInChunk) {
return
xInChunk >= 0 && xInChunk < BLOCKS_PER_CHUNK ||
yInChunk >= 0 && yInChunk < BLOCKS_PER_CHUNK ||
zInChunk >= 0 && zInChunk < BLOCKS_PER_CHUNK;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
}

View File

@ -0,0 +1,50 @@
/*******************************************************************************
* Optica
* Copyright (C) 2020 Wind Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*******************************************************************************/
package ru.windcorp.optica.common.world;
import java.util.Collection;
import java.util.Collections;
import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import ru.windcorp.optica.common.util.CoordinatePacker;
public class WorldData {
private final TLongObjectMap<ChunkData> chunks = new TLongObjectHashMap<>();
public WorldData() {
final int size = 1;
for (int x = -(size / 2); x <= (size / 2); ++x) {
for (int y = -(size / 2); y <= (size / 2); ++y) {
chunks.put(CoordinatePacker.pack3IntsIntoLong(x, y, 0), new ChunkData(x, y, 0));
}
}
}
public ChunkData getChunk(int x, int y, int z) {
long key = CoordinatePacker.pack3IntsIntoLong(x, y, z);
return chunks.get(key);
}
public Collection<ChunkData> getChunks() {
return Collections.unmodifiableCollection(chunks.valueCollection());
}
}