Refactored texture loading: added caching and allowed loading from RAM
This commit is contained in:
parent
e8cec807bc
commit
16205909ed
@ -1,167 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* 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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -29,24 +29,30 @@ class Pixels {
|
||||
private final int bufferWidth;
|
||||
private final int bufferHeight;
|
||||
|
||||
private final boolean filtered;
|
||||
private final TextureSettings settings;
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public Pixels(
|
||||
ByteBuffer data,
|
||||
int bufferWidth, int bufferHeight,
|
||||
boolean filtered
|
||||
int width, int height,
|
||||
TextureSettings settings
|
||||
) {
|
||||
this.data = data;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.bufferWidth = bufferWidth;
|
||||
this.bufferHeight = bufferHeight;
|
||||
this.filtered = filtered;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public int load() {
|
||||
int handle = glGenTextures();
|
||||
glBindTexture(GL_TEXTURE_2D, handle);
|
||||
|
||||
if (filtered) {
|
||||
if (settings.isFiltered()) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
} else {
|
||||
@ -74,4 +80,28 @@ class Pixels {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public ByteBuffer getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public int getBufferWidth() {
|
||||
return bufferWidth;
|
||||
}
|
||||
|
||||
public int getBufferHeight() {
|
||||
return bufferHeight;
|
||||
}
|
||||
|
||||
public int getContentWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getContentHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public TextureSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,109 @@
|
||||
package ru.windcorp.optica.client.graphics.texture;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Hashtable;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import ru.windcorp.optica.common.util.BinUtil;
|
||||
|
||||
class PngLoader {
|
||||
|
||||
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);
|
||||
|
||||
public static Pixels loadPngImage(
|
||||
InputStream pngStream,
|
||||
TextureSettings settings
|
||||
) throws IOException {
|
||||
|
||||
BufferedImage readResult = ImageIO.read(pngStream);
|
||||
|
||||
int width = readResult.getWidth();
|
||||
int height = readResult.getHeight();
|
||||
|
||||
int bufferWidth = BinUtil.roundToGreaterPowerOf2(width);
|
||||
int bufferHeight = BinUtil.roundToGreaterPowerOf2(height);
|
||||
|
||||
WritableRaster raster = createRaster(bufferWidth, bufferHeight);
|
||||
|
||||
BufferedImage canvas = createCanvas(raster);
|
||||
|
||||
Graphics2D g = canvas.createGraphics();
|
||||
try {
|
||||
g.setColor(CANVAS_BACKGROUND);
|
||||
g.fillRect(0, 0, width, height);
|
||||
g.drawImage(
|
||||
readResult,
|
||||
0, 0, width, height,
|
||||
0, height, width, 0, // Flip the image
|
||||
null
|
||||
);
|
||||
} finally {
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
return new Pixels(
|
||||
extractBytes(raster),
|
||||
width, height,
|
||||
bufferWidth, bufferHeight,
|
||||
settings
|
||||
);
|
||||
}
|
||||
|
||||
private static WritableRaster createRaster(
|
||||
int bufferWidth,
|
||||
int bufferHeight
|
||||
) {
|
||||
return Raster.createInterleavedRaster(
|
||||
DataBuffer.TYPE_BYTE, // Storage model
|
||||
bufferWidth, // Buffer width
|
||||
bufferHeight, // Buffer height
|
||||
4, // RGBA
|
||||
null // Location (here (0; 0))
|
||||
);
|
||||
}
|
||||
|
||||
private static BufferedImage createCanvas(WritableRaster raster) {
|
||||
return new BufferedImage(
|
||||
COLOR_MODEL, // Color model
|
||||
raster, // Backing raster
|
||||
false, // Raster is not premultipied
|
||||
CANVAS_PROPERTIES // Properties
|
||||
);
|
||||
}
|
||||
|
||||
private static ByteBuffer extractBytes(WritableRaster raster) {
|
||||
byte[] data = (
|
||||
(DataBufferByte) raster.getDataBuffer()
|
||||
).getData();
|
||||
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(data.length);
|
||||
buffer.put(data);
|
||||
buffer.flip();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
@ -17,128 +17,202 @@
|
||||
*******************************************************************************/
|
||||
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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Hashtable;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue;
|
||||
import ru.windcorp.optica.common.resource.Resource;
|
||||
import ru.windcorp.optica.common.resource.ResourceManager;
|
||||
import ru.windcorp.optica.common.util.ByteBufferInputStream;
|
||||
|
||||
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 final Map<String, Pixels> LOADED_PIXELS =
|
||||
new HashMap<>();
|
||||
|
||||
private static final Map<String, TexturePrimitive> LOADED_PRIMITIVES =
|
||||
new HashMap<>();
|
||||
|
||||
private static Pixels getCachedPixels(String name) {
|
||||
return LOADED_PIXELS.get(name);
|
||||
}
|
||||
|
||||
private static Pixels getCachedPixels(Resource resource) {
|
||||
return getCachedPixels(resource.getName());
|
||||
}
|
||||
|
||||
private static Pixels cachePixels(Pixels pixels, String name) {
|
||||
LOADED_PIXELS.put(name, pixels);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
private static Pixels cachePixels(Pixels pixels, Resource resource) {
|
||||
return cachePixels(pixels, resource.getName());
|
||||
}
|
||||
|
||||
private static TexturePrimitive getCachedPrimitive(String name) {
|
||||
return LOADED_PRIMITIVES.get(name);
|
||||
}
|
||||
|
||||
private static TexturePrimitive getCachedPrimitive(Resource resource) {
|
||||
return getCachedPrimitive(resource.getName());
|
||||
}
|
||||
|
||||
private static TexturePrimitive cachePrimitive(
|
||||
TexturePrimitive primitive,
|
||||
String name
|
||||
) {
|
||||
LOADED_PRIMITIVES.put(name, primitive);
|
||||
return primitive;
|
||||
}
|
||||
|
||||
private static TexturePrimitive cachePrimitive(
|
||||
TexturePrimitive primitive,
|
||||
Resource resource
|
||||
) {
|
||||
return cachePrimitive(primitive, resource.getName());
|
||||
}
|
||||
|
||||
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
|
||||
public static Pixels createOpenGLBuffer(
|
||||
InputStream stream,
|
||||
TextureSettings settings
|
||||
) {
|
||||
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);
|
||||
return PngLoader.loadPngImage(stream, settings);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("u stupid. refresh ur project");
|
||||
}
|
||||
}
|
||||
|
||||
private static int toPowerOf2(int i) {
|
||||
|
||||
// TODO optimize
|
||||
|
||||
int result = 1;
|
||||
do {
|
||||
result *= 2;
|
||||
} while (result < i);
|
||||
public static Pixels createOpenGLBuffer(
|
||||
Resource resource,
|
||||
TextureSettings settings
|
||||
) {
|
||||
Pixels cache = getCachedPixels(resource);
|
||||
if (cache != null) return cache;
|
||||
return cachePixels(
|
||||
createOpenGLBuffer(resource.getInputStream(), settings),
|
||||
resource
|
||||
);
|
||||
}
|
||||
|
||||
public static Pixels createOpenGLBuffer(
|
||||
String textureName,
|
||||
TextureSettings settings
|
||||
) {
|
||||
Pixels cache = getCachedPixels(textureName);
|
||||
if (cache != null) return cache;
|
||||
return cachePixels(
|
||||
createOpenGLBuffer(getResource(textureName), settings),
|
||||
textureName
|
||||
);
|
||||
}
|
||||
|
||||
public static Pixels createOpenGLBuffer(
|
||||
ByteBuffer bytes,
|
||||
TextureSettings settings
|
||||
) {
|
||||
bytes.mark();
|
||||
try {
|
||||
return createOpenGLBuffer(
|
||||
new ByteBufferInputStream(bytes), settings
|
||||
);
|
||||
} finally {
|
||||
bytes.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public static TexturePrimitive createTexturePrimitive(
|
||||
InputStream stream,
|
||||
TextureSettings settings
|
||||
) {
|
||||
Pixels pixels = createOpenGLBuffer(stream, settings);
|
||||
TexturePrimitive result = new TexturePrimitive(pixels);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static TexturePrimitive createTexturePrimitive(
|
||||
Resource resource,
|
||||
TextureSettings settings
|
||||
) {
|
||||
TexturePrimitive primitive = getCachedPrimitive(resource);
|
||||
if (primitive != null) return primitive;
|
||||
return cachePrimitive(
|
||||
createTexturePrimitive(resource.getInputStream(), settings),
|
||||
resource
|
||||
);
|
||||
}
|
||||
|
||||
public static TexturePrimitive createTexturePrimitive(
|
||||
String textureName,
|
||||
TextureSettings settings
|
||||
) {
|
||||
TexturePrimitive primitive = getCachedPrimitive(textureName);
|
||||
if (primitive != null) return primitive;
|
||||
return cachePrimitive(
|
||||
createTexturePrimitive(getResource(textureName), settings),
|
||||
textureName
|
||||
);
|
||||
}
|
||||
|
||||
public static TexturePrimitive createTexturePrimitive(
|
||||
ByteBuffer bytes,
|
||||
TextureSettings settings
|
||||
) {
|
||||
bytes.mark();
|
||||
try {
|
||||
return createTexturePrimitive(
|
||||
new ByteBufferInputStream(bytes), settings
|
||||
);
|
||||
} finally {
|
||||
bytes.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public static TexturePrimitive load(
|
||||
InputStream stream,
|
||||
TextureSettings settings
|
||||
) {
|
||||
TexturePrimitive result = createTexturePrimitive(stream, settings);
|
||||
if (!result.isLoaded()) RenderTaskQueue.invokeLater(result::load);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static TexturePrimitive load(
|
||||
Resource resource,
|
||||
TextureSettings settings
|
||||
) {
|
||||
return load(resource.getInputStream(), settings);
|
||||
}
|
||||
|
||||
public static TexturePrimitive load(
|
||||
String textureName,
|
||||
TextureSettings settings
|
||||
) {
|
||||
return load(getResource(textureName), settings);
|
||||
}
|
||||
|
||||
public static TexturePrimitive load(
|
||||
ByteBuffer bytes,
|
||||
TextureSettings settings
|
||||
) {
|
||||
bytes.mark();
|
||||
try {
|
||||
return load(new ByteBufferInputStream(bytes), settings);
|
||||
} finally {
|
||||
bytes.reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -28,43 +28,28 @@ public class TexturePrimitive implements OpenGLDeletable {
|
||||
private static final int NOT_LOADED = -1;
|
||||
|
||||
private int handle = NOT_LOADED;
|
||||
private Pixels pixelsToLoad;
|
||||
private Pixels pixels;
|
||||
|
||||
public TexturePrimitive(Pixels pixels) {
|
||||
this.pixels = pixels;
|
||||
}
|
||||
|
||||
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;
|
||||
return pixels.getBufferWidth();
|
||||
}
|
||||
|
||||
public int getBufferHeight() {
|
||||
return bufferHeight;
|
||||
return pixels.getBufferHeight();
|
||||
}
|
||||
|
||||
|
||||
public int getWidth() {
|
||||
return pixels.getContentWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return pixels.getContentHeight();
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return handle != NOT_LOADED;
|
||||
}
|
||||
@ -83,13 +68,12 @@ public class TexturePrimitive implements OpenGLDeletable {
|
||||
protected void load() {
|
||||
if (isLoaded()) return;
|
||||
|
||||
handle = pixelsToLoad.load();
|
||||
handle = pixels.load();
|
||||
OpenGLObjectTracker.register(this);
|
||||
|
||||
if (handle < 0) {
|
||||
throw new RuntimeException("oops");
|
||||
}
|
||||
|
||||
pixelsToLoad = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,15 @@
|
||||
package ru.windcorp.optica.client.graphics.texture;
|
||||
|
||||
public class TextureSettings {
|
||||
|
||||
private final boolean isFiltered;
|
||||
|
||||
public TextureSettings(boolean isFiltered) {
|
||||
this.isFiltered = isFiltered;
|
||||
}
|
||||
|
||||
public boolean isFiltered() {
|
||||
return isFiltered;
|
||||
}
|
||||
|
||||
}
|
@ -24,18 +24,22 @@ 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.texture.TextureSettings;
|
||||
import ru.windcorp.optica.client.world.renders.bro.BlockRenderOpaqueCube;
|
||||
|
||||
public class BlockRenders {
|
||||
|
||||
private static final Map<String, BlockRender> BLOCK_RENDERS =
|
||||
new HashMap<>();
|
||||
|
||||
private static final TextureSettings TEXTURE_SETTINGS =
|
||||
new TextureSettings(false);
|
||||
|
||||
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() {}
|
||||
|
||||
@ -59,7 +63,9 @@ public class BlockRenders {
|
||||
}
|
||||
|
||||
private static Texture qtex(String name) {
|
||||
return new SimpleTexture(new Sprite(TextureManager.load(name, false)));
|
||||
return new SimpleTexture(new Sprite(
|
||||
TextureManager.load(name, TEXTURE_SETTINGS)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
18
src/main/java/ru/windcorp/optica/common/util/BinUtil.java
Normal file
18
src/main/java/ru/windcorp/optica/common/util/BinUtil.java
Normal file
@ -0,0 +1,18 @@
|
||||
package ru.windcorp.optica.common.util;
|
||||
|
||||
public class BinUtil {
|
||||
|
||||
public static int closestGreaterPowerOf2(int x) {
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
public static int roundToGreaterPowerOf2(int x) {
|
||||
return closestGreaterPowerOf2(x - 1);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package ru.windcorp.optica.common.util;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author <a href="https://stackoverflow.com/users/37416/mike-houston">Mike
|
||||
* Houston</a>, adapted by Javapony
|
||||
*/
|
||||
public class ByteBufferInputStream extends InputStream {
|
||||
|
||||
private final ByteBuffer buffer;
|
||||
|
||||
public ByteBufferInputStream(ByteBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public int read() {
|
||||
if (!buffer.hasRemaining()) {
|
||||
return -1;
|
||||
}
|
||||
return buffer.get() & 0xFF;
|
||||
}
|
||||
|
||||
public int read(byte[] bytes, int off, int len) {
|
||||
if (!buffer.hasRemaining()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = Math.min(len, buffer.remaining());
|
||||
buffer.get(bytes, off, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package ru.windcorp.optica.common.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ByteBufferOutputStream extends OutputStream {
|
||||
|
||||
private final ByteBuffer buffer;
|
||||
|
||||
public ByteBufferOutputStream(ByteBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
try {
|
||||
buffer.put((byte) b);
|
||||
} catch (BufferOverflowException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(byte[] bytes, int off, int len) throws IOException {
|
||||
try {
|
||||
buffer.put(bytes, off, len);
|
||||
} catch (BufferOverflowException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
58
src/test/java/ru/windcorp/optica/util/BinUtilTest.java
Normal file
58
src/test/java/ru/windcorp/optica/util/BinUtilTest.java
Normal file
@ -0,0 +1,58 @@
|
||||
package ru.windcorp.optica.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ru.windcorp.optica.common.util.BinUtil;
|
||||
|
||||
public class BinUtilTest {
|
||||
|
||||
@Test
|
||||
public void cornerCases() {
|
||||
test(1);
|
||||
test(2);
|
||||
test(3);
|
||||
test(4);
|
||||
test(7);
|
||||
test(8);
|
||||
test(9);
|
||||
|
||||
test(1023);
|
||||
test(1024);
|
||||
test(1025);
|
||||
|
||||
test((1 << 16) - 1);
|
||||
test(1 << 16);
|
||||
test((1 << 16) + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void random() {
|
||||
Random random = new Random(0);
|
||||
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
test(random.nextInt((1 << 30) - 2) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void test(int x) {
|
||||
assertEquals("Round, x = " + x, referenceRound(x), BinUtil.roundToGreaterPowerOf2(x));
|
||||
assertEquals("Greater, x = " + x, referenceGreater(x), BinUtil.closestGreaterPowerOf2(x));
|
||||
}
|
||||
|
||||
int referenceGreater(int x) {
|
||||
int p;
|
||||
for (p = 1; p <= x; p *= 2);
|
||||
return p;
|
||||
}
|
||||
|
||||
int referenceRound(int x) {
|
||||
int p;
|
||||
for (p = 1; p < x; p *= 2);
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user