package com.vitco.core.world;
import com.threed.jpct.*;
import com.threed.jpct.util.Light;
import com.vitco.settings.DynamicSettings;
import com.vitco.settings.VitcoSettings;
import com.vitco.util.misc.SaveResourceLoader;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.HashMap;
/**
* Allows access to certain features of JPCT (e.g. textures)
*/
public final class WorldManager {
private final static TextureManager textureManager = TextureManager.getInstance();
// -------------------------------
// Manage efficient textures (they are updated without huge amount of data allocation)
// links textures to their effect
private static final HashMap<Texture, Effect> effectList = new HashMap<Texture, Effect>();
// an effect implementation to allow for efficient updating
private final static class Effect implements ITextureEffect {
private Texture texture = null;
@Override
public void init(Texture texture) {
this.texture = texture;
}
BufferedImage img = null;
private void setImage(BufferedImage img) {
this.img = img;
texture.applyEffect();
}
@Override
public void apply(int[] dest, int[] source) {
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();
System.arraycopy(pixels, 0, dest, 0, pixels.length);
int diff = dest.length - pixels.length;
System.arraycopy(pixels, pixels.length - diff, dest, pixels.length, diff);
// we don't need the reference anymore
// this is here in case the setImage call is async (!)
this.img = null;
}
@Override
public boolean containsAlpha() {
// needs to be true (otherwise there is a color flip happening)
return true;
}
}
// load a texture to the world (from image)
public static void loadEfficientTexture(String name, BufferedImage image, boolean useAlpha) {
// check if we can replace
Texture texture = !containsTexture(name) ? null : textureManager.getTexture(name);
if (texture == null ||
texture.getWidth() != image.getWidth() ||
texture.getHeight() != image.getHeight()) {
if (texture != null) {
// remove old
effectList.remove(texture);
}
// create texture with effect
texture = new Texture(image, useAlpha);
Effect effect = new Effect();
texture.setEffect(effect);
effectList.put(texture,effect);
// load the texture
loadTexture(name, texture);
} else {
Effect effect = effectList.get(texture);
effect.setImage(image);
}
}
public static boolean removeEfficientTexture(String name) {
if (containsTexture(name)) {
effectList.remove(textureManager.getTexture(name));
// remove the texture
textureManager.removeTexture(name);
return true;
}
return false;
}
// ------------------------------
// tiles are images that are placed on sides of voxels
public static final HashMap<String, Image> tileList = new HashMap<String, Image>();
// load a tile
public static void loadTile(String name, ImageIcon image) {
// this cast is necessary since pixel are stored differently in imageIcons
tileList.put(name, image.getImage());
}
// get a tile
public static Image getTile(String name) {
return tileList.get(name);
}
// remove a tile
public static boolean removeTile(String name) {
return tileList.remove(name) != null;
}
// ------------------------------
// load a texture to the world (from string)
public static void loadTexture(String name, String url, boolean useAlpha) {
Image image = new SaveResourceLoader(url).asImage();
loadTexture(name, image, useAlpha);
}
// load a texture to the world (from image)
public static void loadTexture(String name, Image image, boolean useAlpha) {
Texture texture = new Texture(image, useAlpha);
loadTexture(name, texture);
}
public static void loadTexture(String name, Texture texture) {
if (textureManager.containsTexture(name)) {
textureManager.replaceTexture(name, texture);
} else {
textureManager.addTexture(name, texture);
}
}
public static void loadTexture(String name, ImageIcon image, boolean useAlpha) {
// Create a texture from image
// this cast is necessary since pixel are stored differently in imageIcons ????
BufferedImage convertedImage = new BufferedImage(image.getIconWidth(), image.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
convertedImage.getGraphics().drawImage(image.getImage(), 0, 0, null);
loadTexture(name, convertedImage, useAlpha);
}
public static boolean containsTexture(String name) {
return textureManager.containsTexture(name);
}
public static boolean removeTexture(String name) {
if (containsTexture(name)) {
// remove the texture
textureManager.removeTexture(name);
return true;
}
return false;
}
public static int getTextureId(String name) {
return textureManager.getTextureID(name);
}
// ---------------------------
// static constructor
static {
// load texture for bounding box
WorldManager.loadTexture(
"__grid__",
new SaveResourceLoader("resource/tex/bounding_box_512.png").asImage(),
false
);
}
// add a light-source
public static Light addLight(World world, SimpleVector position, float strength) {
Light light = new Light(world);
light.setDiscardDistance(-1);
light.setIntensity(strength, strength, strength);
light.setPosition(position);
return light;
}
// add a box to the world
public static int addBox(World world, SimpleVector pos, float size, Color color) {
Object3D box = Primitives.getCube(size);
box.setAdditionalColor(color);
box.setEnvmapped(Object3D.ENVMAP_ENABLED);
box.setShadingMode(Object3D.SHADING_FAKED_FLAT);
box.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
box.setOrigin(pos);
box.rotateY((float) Math.PI / 4); // align correctly
box.build();
world.addObject(box);
return box.getID();
}
// add a plane to the world
// default rotation is parallel to xz-plane
public static int addPlane (World world, SimpleVector pos, SimpleVector rotation, Float size, Color color, Integer alpha) {
// add the world plane (ground)
Object3D plane = Primitives.getPlane(1, size);
plane.setCulling(false); //show from both sides
plane.setTransparency(alpha);
plane.setAdditionalColor(color);
plane.setOrigin(pos);
plane.rotateX(rotation.x + (float)Math.PI/2);
plane.rotateY(rotation.y);
plane.rotateZ(rotation.z);
world.addObject(plane);
return plane.getID();
}
// get the grid plane (the result depends on the current settings!)
public static Object3D getGridPlane() {
// create object (container)
Object3D box=new Object3D(12);
// add triangles
SimpleVector upperLeftFront=new SimpleVector(-1,-1,-1);
SimpleVector upperRightFront=new SimpleVector(1,-1,-1);
SimpleVector lowerLeftFront=new SimpleVector(-1,1,-1);
SimpleVector lowerRightFront=new SimpleVector(1,1,-1);
SimpleVector upperLeftBack = new SimpleVector( -1, -1, 1);
SimpleVector upperRightBack = new SimpleVector(1, -1, 1);
SimpleVector lowerLeftBack = new SimpleVector( -1, 1, 1);
SimpleVector lowerRightBack = new SimpleVector(1, 1, 1);
// scale
for (SimpleVector vec : new SimpleVector[] {
upperLeftFront, upperRightFront, lowerLeftFront, lowerRightFront,
upperLeftBack, upperRightBack, lowerLeftBack, lowerRightBack
}) {
vec.x *= DynamicSettings.VOXEL_PLANE_WORLD_SIZE_X /2;
vec.y *= DynamicSettings.VOXEL_PLANE_WORLD_SIZE_Y /2;
vec.z *= DynamicSettings.VOXEL_PLANE_WORLD_SIZE_Z /2;
}
float uvX = DynamicSettings.VOXEL_PLANE_SIZE_X/16f;
float uvY = DynamicSettings.VOXEL_PLANE_SIZE_Y/16f;
float uvZ = DynamicSettings.VOXEL_PLANE_SIZE_Z/16f;
// Front
box.addTriangle(upperLeftFront, uvX, uvY, lowerLeftFront, uvX, 0, upperRightFront, 0, uvY); // xy
box.addTriangle(upperRightFront, 0, uvY, lowerLeftFront, uvX, 0, lowerRightFront, 0, 0);
// Back
box.addTriangle(upperLeftBack, uvX, uvY, upperRightBack, 0, uvY, lowerLeftBack, uvX, 0);
box.addTriangle(upperRightBack, 0, uvY, lowerRightBack, 0, 0, lowerLeftBack, uvX, 0);
// Upper
box.addTriangle(upperLeftBack, uvX, uvZ, upperLeftFront, uvX, 0, upperRightBack, 0, uvZ); // xz
box.addTriangle(upperRightBack, 0, uvZ, upperLeftFront, uvX, 0, upperRightFront, 0, 0);
// Lower
box.addTriangle(lowerLeftBack, uvX, uvZ, lowerRightBack, 0, uvZ, lowerLeftFront, uvX, 0);
box.addTriangle(lowerRightBack, 0, uvZ, lowerRightFront, 0, 0, lowerLeftFront, uvX, 0);
// Left
box.addTriangle(upperLeftBack, uvY, uvZ, lowerLeftBack, 0, uvZ, upperLeftFront, uvY, 0); // yz
box.addTriangle(lowerLeftBack, 0, uvZ, lowerLeftFront, 0, 0, upperLeftFront, uvY, 0);
// Right
box.addTriangle(upperRightBack, uvY, uvZ, upperRightFront, uvY, 0, lowerRightBack, 0, uvZ);
box.addTriangle(lowerRightBack, 0, uvZ, upperRightFront, uvY, 0, lowerRightFront, 0, 0);
// set texture
box.setAdditionalColor(Color.WHITE);
box.setTransparency(0);
box.setTexture("__grid__");
// scale and place correctly
box.setOrigin(new SimpleVector(
DynamicSettings.VOXEL_PLANE_SIZE_X%2 == 0 ? -0.5f * VitcoSettings.VOXEL_SIZE : 0,
-DynamicSettings.VOXEL_PLANE_WORLD_SIZE_Y /2 + VitcoSettings.VOXEL_GROUND_DISTANCE,
DynamicSettings.VOXEL_PLANE_SIZE_Z%2 == 0 ? -0.5f * VitcoSettings.VOXEL_SIZE : 0
));
// make the "inside" visible
box.invertCulling(true);
box.build();
return box;
}
}