1891
Support / Streaming algorithm
« on: May 15, 2006, 01:16:10 am »
ehm, so i have an un-generatable memory leak somewhere, thanks for the gift :roll:
Code: [Select]
r a f t
This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.
r a f t
r a f t
The VM will do it for you before giving an out of memory error. If it still occurs, there must be a reference somewhere to the objects that should be collected.
r a f t
r a f t
r a f t
import com.threed.jpct.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
/**
* a simple area based mip mapper implementation.
* i.e: mip map level per polygon is decided according to ratio:
* area of polygon on screen / area of polygon on texture
*
* @author r a f t
*/
public class MipMapper {
/** the number of mipmap levels */
public static final int LEVELS = 8; // enough for mip mapping a 1024x1024 texture to 8x8
/** the minimum mipmap size (lower isn't possible, higher is) */
public static final int MIN_SIZE = 8;
/** original texture will be used dwon to this ratio (polygon area / texture area) */
public static final float FIRST_LEVEL = 1/2.5f;
/** debug switch for showing colored mip maps */
public static boolean colorMipmaps = false;
private final TextureManager tm = TextureManager.getInstance();
private final int[] visListData = new int[2];
private final BitSet changedObjects = new BitSet(256);
private final BitSet processedTextures = new BitSet(256);
private final Map<Integer, ObjectData> mipMapData = new HashMap<Integer, ObjectData>();
private final Map<String, Integer> textureIds = new HashMap<String, Integer>();
private final Set<String> createdTextures = new HashSet<String>();
public MipMapper() {}
/** adds the object to mipmap list */
public void addObject(Object3D object3d) {
mipMapData.put(object3d.getID(), new ObjectData(object3d));
}
/** removes the object from mipmap list */
public void removeObject(int id) {
mipMapData.remove(id);
}
/** creates mipmap textures if they arent already created */
private void createMipMaps(Object3D object3d) {
PolygonManager pm = object3d.getPolygonManager();
for (int polygonId = 0; polygonId < pm.getMaxPolygonID(); polygonId++) {
int textureId = pm.getPolygonTexture(polygonId);
if (!processedTextures.get(textureId)) {
Texture texture = tm.getTextureByID(textureId);
int owidth = texture.getWidth();
int oheight = texture.getHeight();
TexelGrabber tg = new TexelGrabber();
texture.setEffect(tg);
texture.applyEffect();
texture.removeEffect();
int[] texels = tg.texels;
BufferedImage origTextureImage = new BufferedImage(owidth, oheight, BufferedImage.TYPE_INT_RGB);
int[] pixels = ((DataBufferInt) ((BufferedImage) origTextureImage).getRaster().getDataBuffer()).getData();
Texture lastTexture = texture;
int lastTextureId = textureId;
for (int offset = 1; offset < LEVELS; offset++) {
int width = (int) ((float) owidth/Math.pow(2, offset));
int height = (int) ((float) oheight/Math.pow(2, offset));
width = Math.max(MIN_SIZE, width);
height = Math.max(MIN_SIZE, height);
if ((lastTexture.getWidth() != width) || (lastTexture.getHeight() != height)) {
if (colorMipmaps) {
int[] tmpy = new int[texels.length];
int col = 200<<(8*((offset-1)%3));
for (int i = 0; i < tmpy.length; i++) {
tmpy[i] = col;
}
texels = tmpy;
}
System.arraycopy(texels, 0, pixels, 0, owidth*oheight);
Image mipmapTextureImage = origTextureImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
Texture mipmapTexture = new Texture(mipmapTextureImage);
String textureName = getTextureName(textureId, offset);
if (tm.containsTexture(textureName))
tm.replaceTexture(textureName, mipmapTexture);
else tm.addTexture(textureName, mipmapTexture);
lastTexture = mipmapTexture;
lastTextureId = tm.getTextureID(textureName);
textureIds.put(textureName, lastTextureId);
createdTextures.add(textureName);
Logger.log("mip map texture created " + textureName + " " + width + "x" + height, Logger.MESSAGE);
} else {
// no need to create a new texture so use last one
String textureName = getTextureName(textureId, offset);
textureIds.put(textureName, lastTextureId);
Logger.log("re-used last mipmap texture " + textureName + " " + width + "x" + height, Logger.MESSAGE);
}
} // for offset
processedTextures.set(textureId);
} // if (!processedTextures.get(textureId)
} // for polygonId
}
private String getTextureName(int baseTextureId, int offset) {
return "mipmap/" + baseTextureId + "/" + offset;
}
/**
* Does the actual mipmapping based on the current visibility list.
* Has to be executed after/before rendering/drawing the scene, so it's always
* "one step behind" but if you don't move very fast, this shouldn't matter.
*/
public void mipMap(World world, FrameBuffer buffer) {
if (mipMapData.isEmpty())
return; // no mipMapped objects so return
VisList vl = world.getVisibilityList();
int size = vl.getSize();
int lastObjectId = -1;
ObjectData objectData = null;
changedObjects.clear();
for (int i = 0; i < size; i++) {
vl.getData(i, visListData);
if (lastObjectId != visListData[0]) {
lastObjectId = visListData[0];
objectData = mipMapData.get(visListData[0]);
}
if (objectData != null) {
SimpleVector v0, v1, v2;
int polygonId = visListData[1];
if ((v0 = Interact2D.project3D2D(world.getCamera(),
buffer, objectData.pm.getTransformedVertex(polygonId, 0))) == null)
continue;
if ((v1 = Interact2D.project3D2D(world.getCamera(),
buffer, objectData.pm.getTransformedVertex(polygonId, 1))) == null)
continue;
if ((v2 = Interact2D.project3D2D(world.getCamera(),
buffer, objectData.pm.getTransformedVertex(polygonId, 2))) == null)
continue;
float polygonArea = calculateArea(v0, v1, v2);
float ratio = polygonArea / objectData.textureAreas[polygonId];
int offset = -1; boolean done = false;
while (!done && (++offset < LEVELS - 1)) {
done = ratio > FIRST_LEVEL;
ratio *= 4f;
}
int targetTex = objectData.orgTex[polygonId][offset];
if (targetTex != objectData.curTex[polygonId]) {
changedObjects.set(lastObjectId);
objectData.curTex[polygonId] = targetTex;
objectData.pm.setPolygonTexture(polygonId, targetTex);
}
}
}
for (int i = changedObjects.nextSetBit(0); i >= 0; i = changedObjects.nextSetBit(i+1)) {
world.getObject(i).recreateTextureCoords();
}
}
/** resets all textures to their original states
* i.e. disables mip mapping */
public void reset() {
for (ObjectData objectData : mipMapData.values())
objectData.reset();
}
/**
* useful for cleanup purposes
* (i.e you dont want call TextureManager.flush() for whatever reason)
*/
public void dispose() {
for (String textureName : createdTextures) {
if (tm.containsTexture(textureName))
tm.replaceTexture(textureName, tm.getDummyTexture());
else Logger.log("couldnt find texture " + textureName, Logger.WARNING);
}
}
private float calculateArea(SimpleVector... v) {
float area = 0f;
for (int i = 0; i < v.length; i++) {
int j = (i + 1) % v.length;
area += v[i].x * v[j].y;
area -= v[i].y * v[j].x;
}
area /= 2.0;
return (area < 0) ? -area : area;
}
/** data structure to store polygon data per object */
private class ObjectData {
private final Object3D object3d;
private final PolygonManager pm;
private final int[][] orgTex;
private final int[] curTex;
private final float[] textureAreas;
private ObjectData(Object3D object3d) {
this.object3d = object3d;
pm = object3d.getPolygonManager();
orgTex = new int[pm.getMaxPolygonID()][LEVELS];
curTex = new int[pm.getMaxPolygonID()];
textureAreas = new float[pm.getMaxPolygonID()];
createMipMaps(object3d);
for (int i = 0; i < orgTex.length; i++) {
int textureId = pm.getPolygonTexture(i);
orgTex[i][0] = textureId;
for (int offset = 1; offset< LEVELS; offset++) {
orgTex[i][offset] = textureIds.get(getTextureName(textureId, offset));
}
curTex[i] = orgTex[i][0];
float uvArea = calculateArea(pm.getTextureUV(i, 0), pm.getTextureUV(i, 1), pm.getTextureUV(i, 2));
Texture texture = tm.getTextureByID(textureId);
textureAreas[i] = uvArea * texture.getWidth() * texture.getHeight();
}
}
private void reset() {
boolean changed = false;
for (int polygonId = 0; polygonId < orgTex.length; polygonId++) {
int originalTextureId = orgTex[polygonId][0];
if (originalTextureId != curTex[polygonId]) {
changed = true;
pm.setPolygonTexture(polygonId, originalTextureId);
curTex[polygonId] = originalTextureId;
}
}
if (changed)
object3d.recreateTextureCoords();
}
}
/**
* small helper class to access a texture's texels.
*/
private class TexelGrabber implements ITextureEffect {
private int[] texels = null;
public void init(Texture tex) {}
public void apply(int[] dest, int[] source) {
texels = source;
}
}
}
r a f t
r a f t
r a f t
r a f t
r a f t
r a f t
r a f t