/*
* Copyright 2014 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.rendering.opengl;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.assets.AssetType;
import org.terasology.assets.ResourceUrn;
import org.terasology.engine.subsystem.lwjgl.LwjglGraphics;
import org.terasology.math.geom.Rect2f;
import org.terasology.math.geom.Rect2i;
import org.terasology.math.geom.Vector2i;
import org.terasology.rendering.assets.texture.Texture;
import org.terasology.rendering.assets.texture.TextureData;
import java.nio.ByteBuffer;
import java.util.List;
/**
*/
public class OpenGLTexture extends Texture {
private static final Logger logger = LoggerFactory.getLogger(OpenGLTexture.class);
private final TextureResources resources;
public OpenGLTexture(ResourceUrn urn, AssetType<?, TextureData> assetType, TextureData data, LwjglGraphics graphicsManager) {
super(urn, assetType);
this.resources = new TextureResources(graphicsManager);
getDisposalHook().setDisposeAction(resources);
reload(data);
}
public void setId(int id) {
resources.id = id;
}
@Override
protected void doReload(TextureData data) {
switch (data.getType()) {
// TODO: reconsider how 3D textures handled (probably separate asset implementation with common interface?
case TEXTURE3D:
if (data.getWidth() % data.getHeight() != 0 || data.getWidth() / data.getHeight() != data.getHeight()) {
throw new RuntimeException("3D texture must be cubic (height^3) - width must thus be a multiple of height");
}
int size = data.getHeight();
final int byteLength = 4 * 16 * 16 * 16;
final int strideX = 16 * 4;
final int strideY = 16 * 16 * 4;
final int strideZ = 4;
ByteBuffer alignedBuffer = ByteBuffer.allocateDirect(byteLength);
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
for (int z = 0; z < size; z++) {
final int index = x * strideX + z * strideZ + strideY * y;
alignedBuffer.put(data.getBuffers()[0].get(index));
alignedBuffer.put(data.getBuffers()[0].get(index + 1));
alignedBuffer.put(data.getBuffers()[0].get(index + 2));
alignedBuffer.put(data.getBuffers()[0].get(index + 3));
}
}
}
alignedBuffer.flip();
resources.loadedTextureInfo = new LoadedTextureInfo(size, size, size, data);
if (resources.id == 0) {
resources.graphicsManager.createTexture3D(alignedBuffer, getWrapMode(), getFilterMode(),
size, (newId) -> {
synchronized (this) {
if (resources.id != 0) {
resources.graphicsManager.disposeTexture(resources.id);
}
if (isDisposed()) {
resources.graphicsManager.disposeTexture(newId);
} else {
resources.id = newId;
logger.debug("Bound texture '{}' - {}", getUrn(), resources.id);
}
}
});
} else {
resources.graphicsManager.reloadTexture3D(resources.id, alignedBuffer, getWrapMode(), getFilterMode(), size);
}
break;
default:
int width = data.getWidth();
int height = data.getHeight();
resources.loadedTextureInfo = new LoadedTextureInfo(width, height, 1, data);
if (resources.id == 0) {
resources.graphicsManager.createTexture2D(data.getBuffers(), getWrapMode(), getFilterMode(), width, height, (newId) -> {
synchronized (this) {
if (resources.id != 0) {
resources.graphicsManager.disposeTexture(resources.id);
}
if (isDisposed()) {
resources.graphicsManager.disposeTexture(newId);
} else {
resources.id = newId;
logger.debug("Bound texture '{}' - {}", getUrn(), resources.id);
}
}
});
} else {
resources.graphicsManager.reloadTexture2D(resources.id, data.getBuffers(), getWrapMode(), getFilterMode(), width, height);
}
break;
}
}
@Override
public int getId() {
return resources.id;
}
@Override
public int getDepth() {
if (resources.loadedTextureInfo != null) {
return resources.loadedTextureInfo.getDepth();
}
return 0;
}
@Override
public int getWidth() {
if (resources.loadedTextureInfo != null) {
return resources.loadedTextureInfo.getWidth();
}
return 0;
}
@Override
public int getHeight() {
if (resources.loadedTextureInfo != null) {
return resources.loadedTextureInfo.getHeight();
}
return 0;
}
@Override
public Vector2i size() {
return new Vector2i(getWidth(), getHeight());
}
@Override
public Texture.WrapMode getWrapMode() {
return resources.loadedTextureInfo.getWrapMode();
}
@Override
public FilterMode getFilterMode() {
return resources.loadedTextureInfo.getFilterMode();
}
@Override
public TextureData getData() {
return new TextureData(resources.loadedTextureInfo.getTextureData());
}
@Override
public Texture getTexture() {
return this;
}
@Override
public Rect2f getRegion() {
return FULL_TEXTURE_REGION;
}
@Override
public Rect2i getPixelRegion() {
return Rect2i.createFromMinAndSize(0, 0, getWidth(), getHeight());
}
@Override
public synchronized void subscribeToDisposal(Runnable subscriber) {
resources.disposalSubscribers.add(subscriber);
}
@Override
public synchronized void unsubscribeToDisposal(Runnable subscriber) {
resources.disposalSubscribers.remove(subscriber);
}
@Override
public boolean isLoaded() {
return resources.id != 0;
}
private static class LoadedTextureInfo {
private final int width;
private final int height;
private final int depth;
private final Texture.WrapMode wrapMode;
private final Texture.FilterMode filterMode;
private final TextureData textureData;
LoadedTextureInfo(int width, int height, int depth, TextureData data) {
this.width = width;
this.height = height;
this.depth = depth;
this.wrapMode = data.getWrapMode();
this.filterMode = data.getFilterMode();
this.textureData = data;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getDepth() {
return depth;
}
public Texture.WrapMode getWrapMode() {
return wrapMode;
}
public Texture.FilterMode getFilterMode() {
return filterMode;
}
public TextureData getTextureData() {
return textureData;
}
}
private static class TextureResources implements Runnable {
private final LwjglGraphics graphicsManager;
private volatile int id;
private volatile LoadedTextureInfo loadedTextureInfo;
private final List<Runnable> disposalSubscribers = Lists.newArrayList();
TextureResources(LwjglGraphics graphicsManager) {
this.graphicsManager = graphicsManager;
}
@Override
public void run() {
if (loadedTextureInfo != null) {
disposalSubscribers.forEach(java.lang.Runnable::run);
graphicsManager.disposeTexture(id);
loadedTextureInfo = null;
id = 0;
}
}
}
}