git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10811 75d07b2b-3a1a-0410-a2c5-0572b91ccdcaexperimental
parent
592303181e
commit
eb7e7bbaad
@ -0,0 +1,184 @@ |
|||||||
|
package com.jme3.scene.plugins.blender.landscape; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.jme3.light.AmbientLight; |
||||||
|
import com.jme3.light.Light; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; |
||||||
|
import com.jme3.scene.plugins.blender.BlenderContext; |
||||||
|
import com.jme3.scene.plugins.blender.file.BlenderFileException; |
||||||
|
import com.jme3.scene.plugins.blender.file.Structure; |
||||||
|
import com.jme3.scene.plugins.blender.textures.ColorBand; |
||||||
|
import com.jme3.scene.plugins.blender.textures.CombinedTexture; |
||||||
|
import com.jme3.scene.plugins.blender.textures.ImageUtils; |
||||||
|
import com.jme3.scene.plugins.blender.textures.TextureHelper; |
||||||
|
import com.jme3.scene.plugins.blender.textures.TexturePixel; |
||||||
|
import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; |
||||||
|
import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.texture.TextureCubeMap; |
||||||
|
import com.jme3.util.SkyFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* The class that allows to load the following: <li>the ambient light of the scene <li>the sky of the scene (with or without texture) |
||||||
|
* |
||||||
|
* @author Marcin Roguski (Kaelthas) |
||||||
|
*/ |
||||||
|
public class LandscapeHelper extends AbstractBlenderHelper { |
||||||
|
private static final Logger LOGGER = Logger.getLogger(LandscapeHelper.class.getName()); |
||||||
|
|
||||||
|
private static final int SKYTYPE_BLEND = 1; |
||||||
|
private static final int SKYTYPE_REAL = 2; |
||||||
|
private static final int SKYTYPE_PAPER = 4; |
||||||
|
|
||||||
|
public LandscapeHelper(String blenderVersion, BlenderContext blenderContext) { |
||||||
|
super(blenderVersion, blenderContext); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Loads scene ambient light. |
||||||
|
* @param worldStructure |
||||||
|
* the world's blender structure |
||||||
|
* @return the scene's ambient light |
||||||
|
*/ |
||||||
|
public Light toAmbientLight(Structure worldStructure) { |
||||||
|
LOGGER.fine("Loading ambient light."); |
||||||
|
AmbientLight ambientLight = new AmbientLight(); |
||||||
|
float ambr = ((Number) worldStructure.getFieldValue("ambr")).floatValue(); |
||||||
|
float ambg = ((Number) worldStructure.getFieldValue("ambg")).floatValue(); |
||||||
|
float ambb = ((Number) worldStructure.getFieldValue("ambb")).floatValue(); |
||||||
|
ColorRGBA ambientLightColor = new ColorRGBA(ambr, ambg, ambb, 0.0f); |
||||||
|
ambientLight.setColor(ambientLightColor); |
||||||
|
LOGGER.log(Level.FINE, "Loaded ambient light: {0}.", ambientLightColor); |
||||||
|
return ambientLight; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Loads the background color. |
||||||
|
* @param worldStructure |
||||||
|
* the world's structure |
||||||
|
* @return the horizon color of the world which is used as a background color. |
||||||
|
*/ |
||||||
|
public ColorRGBA toBackgroundColor(Structure worldStructure) { |
||||||
|
float horr = ((Number) worldStructure.getFieldValue("horr")).floatValue(); |
||||||
|
float horg = ((Number) worldStructure.getFieldValue("horg")).floatValue(); |
||||||
|
float horb = ((Number) worldStructure.getFieldValue("horb")).floatValue(); |
||||||
|
return new ColorRGBA(horr, horg, horb, 1); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Loads scene's sky. Sky can be plain or textured. |
||||||
|
* If no sky type is selected in blender then no sky is loaded. |
||||||
|
* @param worldStructure |
||||||
|
* the world's structure |
||||||
|
* @return the scene's sky |
||||||
|
* @throws BlenderFileException |
||||||
|
* blender exception is thrown when problems with blender file occur |
||||||
|
*/ |
||||||
|
public Spatial toSky(Structure worldStructure) throws BlenderFileException { |
||||||
|
int skytype = ((Number) worldStructure.getFieldValue("skytype")).intValue(); |
||||||
|
if (skytype == 0) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
LOGGER.fine("Loading sky."); |
||||||
|
ColorRGBA horizontalColor = this.toBackgroundColor(worldStructure); |
||||||
|
|
||||||
|
float zenr = ((Number) worldStructure.getFieldValue("zenr")).floatValue(); |
||||||
|
float zeng = ((Number) worldStructure.getFieldValue("zeng")).floatValue(); |
||||||
|
float zenb = ((Number) worldStructure.getFieldValue("zenb")).floatValue(); |
||||||
|
ColorRGBA zenithColor = new ColorRGBA(zenr, zeng, zenb, 1); |
||||||
|
|
||||||
|
// jutr for this case load generated textures wheather user had set it or not because those might be needed to properly load the sky
|
||||||
|
boolean loadGeneratedTextures = blenderContext.getBlenderKey().isLoadGeneratedTextures(); |
||||||
|
blenderContext.getBlenderKey().setLoadGeneratedTextures(true); |
||||||
|
|
||||||
|
TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); |
||||||
|
Map<Number, CombinedTexture> loadedTextures = null; |
||||||
|
try { |
||||||
|
loadedTextures = textureHelper.readTextureData(worldStructure, new float[] { horizontalColor.r, horizontalColor.g, horizontalColor.b, horizontalColor.a }, true); |
||||||
|
} finally { |
||||||
|
blenderContext.getBlenderKey().setLoadGeneratedTextures(loadGeneratedTextures); |
||||||
|
} |
||||||
|
|
||||||
|
TextureCubeMap texture = null; |
||||||
|
if (loadedTextures != null && loadedTextures.size() > 0) { |
||||||
|
if (loadedTextures.size() > 1) { |
||||||
|
throw new IllegalStateException("There should be only one combined texture for sky!"); |
||||||
|
} |
||||||
|
CombinedTexture combinedTexture = loadedTextures.get(1); |
||||||
|
texture = combinedTexture.generateSkyTexture(horizontalColor, zenithColor, blenderContext); |
||||||
|
} else { |
||||||
|
LOGGER.fine("Preparing colors for colorband."); |
||||||
|
int colorbandType = ColorBand.IPO_CARDINAL; |
||||||
|
List<ColorRGBA> colorbandColors = new ArrayList<ColorRGBA>(3); |
||||||
|
colorbandColors.add(horizontalColor); |
||||||
|
if ((skytype & SKYTYPE_BLEND) != 0) { |
||||||
|
if ((skytype & SKYTYPE_PAPER) != 0) { |
||||||
|
colorbandType = ColorBand.IPO_LINEAR; |
||||||
|
} |
||||||
|
if ((skytype & SKYTYPE_REAL) != 0) { |
||||||
|
colorbandColors.add(0, zenithColor); |
||||||
|
} |
||||||
|
colorbandColors.add(zenithColor); |
||||||
|
} |
||||||
|
|
||||||
|
int size = blenderContext.getBlenderKey().getSkyGeneratedTextureSize(); |
||||||
|
|
||||||
|
List<Integer> positions = new ArrayList<Integer>(colorbandColors.size()); |
||||||
|
positions.add(0); |
||||||
|
if (colorbandColors.size() == 2) { |
||||||
|
positions.add(size - 1); |
||||||
|
} else if (colorbandColors.size() == 3) { |
||||||
|
positions.add(size / 2); |
||||||
|
positions.add(size - 1); |
||||||
|
} |
||||||
|
|
||||||
|
LOGGER.fine("Generating sky texture."); |
||||||
|
float[][] values = new ColorBand(colorbandType, colorbandColors, positions, size).computeValues(); |
||||||
|
|
||||||
|
Image image = ImageUtils.createEmptyImage(Format.RGB8, size, size, 6); |
||||||
|
PixelInputOutput pixelIO = PixelIOFactory.getPixelIO(image.getFormat()); |
||||||
|
TexturePixel pixel = new TexturePixel(); |
||||||
|
|
||||||
|
LOGGER.fine("Creating side textures."); |
||||||
|
int[] sideImagesIndexes = new int[] { 0, 1, 4, 5 }; |
||||||
|
for (int i : sideImagesIndexes) { |
||||||
|
for (int y = 0; y < size; ++y) { |
||||||
|
pixel.red = values[y][0]; |
||||||
|
pixel.green = values[y][1]; |
||||||
|
pixel.blue = values[y][2]; |
||||||
|
|
||||||
|
for (int x = 0; x < size; ++x) { |
||||||
|
pixelIO.write(image, i, pixel, x, y); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
LOGGER.fine("Creating top texture."); |
||||||
|
pixelIO.read(image, 0, pixel, 0, image.getHeight() - 1); |
||||||
|
for (int y = 0; y < size; ++y) { |
||||||
|
for (int x = 0; x < size; ++x) { |
||||||
|
pixelIO.write(image, 3, pixel, x, y); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
texture = new TextureCubeMap(image); |
||||||
|
} |
||||||
|
|
||||||
|
LOGGER.fine("Sky texture created. Creating sky."); |
||||||
|
return SkyFactory.createSky(blenderContext.getAssetManager(), texture, false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,471 @@ |
|||||||
|
package com.jme3.scene.plugins.blender.textures; |
||||||
|
|
||||||
|
import java.awt.color.ColorSpace; |
||||||
|
import java.awt.geom.AffineTransform; |
||||||
|
import java.awt.image.AffineTransformOp; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.awt.image.ColorConvertOp; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import jme3tools.converters.ImageToAwt; |
||||||
|
import jme3tools.converters.RGB565; |
||||||
|
|
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; |
||||||
|
import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* This utility class has the methods that deal with images. |
||||||
|
* |
||||||
|
* @author Marcin Roguski (Kaelthas) |
||||||
|
*/ |
||||||
|
public final class ImageUtils { |
||||||
|
/** |
||||||
|
* Creates an image of the given size and depth. |
||||||
|
* @param format |
||||||
|
* the image format |
||||||
|
* @param width |
||||||
|
* the image width |
||||||
|
* @param height |
||||||
|
* the image height |
||||||
|
* @param depth |
||||||
|
* the image depth |
||||||
|
* @return the new image instance |
||||||
|
*/ |
||||||
|
public static Image createEmptyImage(Format format, int width, int height, int depth) { |
||||||
|
int bufferSize = width * height * (format.getBitsPerPixel() >> 3); |
||||||
|
if (depth < 2) { |
||||||
|
return new Image(format, width, height, BufferUtils.createByteBuffer(bufferSize)); |
||||||
|
} |
||||||
|
ArrayList<ByteBuffer> data = new ArrayList<ByteBuffer>(depth); |
||||||
|
for (int i = 0; i < depth; ++i) { |
||||||
|
data.add(BufferUtils.createByteBuffer(bufferSize)); |
||||||
|
} |
||||||
|
return new Image(Format.RGB8, width, height, depth, data); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The method sets a color for the given pixel by merging the two given colors. |
||||||
|
* The lowIntensityColor will be most visible when the pixel has low intensity. |
||||||
|
* The highIntensityColor will be most visible when the pixel has high intensity. |
||||||
|
* |
||||||
|
* @param pixel |
||||||
|
* the pixel that will have the colors altered |
||||||
|
* @param lowIntensityColor |
||||||
|
* the low intensity color |
||||||
|
* @param highIntensityColor |
||||||
|
* the high intensity color |
||||||
|
* @return the altered pixel (the same instance) |
||||||
|
*/ |
||||||
|
public static TexturePixel color(TexturePixel pixel, ColorRGBA lowIntensityColor, ColorRGBA highIntensityColor) { |
||||||
|
float intensity = pixel.intensity; |
||||||
|
pixel.fromColor(lowIntensityColor); |
||||||
|
pixel.mult(1 - pixel.intensity); |
||||||
|
pixel.add(highIntensityColor.mult(intensity)); |
||||||
|
return pixel; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method merges two given images. The result is stored in the |
||||||
|
* 'target' image. |
||||||
|
* |
||||||
|
* @param targetImage |
||||||
|
* the target image |
||||||
|
* @param sourceImage |
||||||
|
* the source image |
||||||
|
*/ |
||||||
|
public static void merge(Image targetImage, Image sourceImage) { |
||||||
|
if (sourceImage.getDepth() != targetImage.getDepth()) { |
||||||
|
throw new IllegalArgumentException("The given images should have the same depth to merge them!"); |
||||||
|
} |
||||||
|
if (sourceImage.getWidth() != targetImage.getWidth()) { |
||||||
|
throw new IllegalArgumentException("The given images should have the same width to merge them!"); |
||||||
|
} |
||||||
|
if (sourceImage.getHeight() != targetImage.getHeight()) { |
||||||
|
throw new IllegalArgumentException("The given images should have the same height to merge them!"); |
||||||
|
} |
||||||
|
|
||||||
|
PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat()); |
||||||
|
PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat()); |
||||||
|
TexturePixel sourcePixel = new TexturePixel(); |
||||||
|
TexturePixel targetPixel = new TexturePixel(); |
||||||
|
int depth = targetImage.getDepth() == 0 ? 1 : targetImage.getDepth(); |
||||||
|
|
||||||
|
for (int layerIndex = 0; layerIndex < depth; ++layerIndex) { |
||||||
|
for (int x = 0; x < sourceImage.getWidth(); ++x) { |
||||||
|
for (int y = 0; y < sourceImage.getHeight(); ++y) { |
||||||
|
sourceIO.read(sourceImage, layerIndex, sourcePixel, x, y); |
||||||
|
targetIO.read(targetImage, layerIndex, targetPixel, x, y); |
||||||
|
targetPixel.merge(sourcePixel); |
||||||
|
targetIO.write(targetImage, layerIndex, targetPixel, x, y); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method merges two given images. The result is stored in the |
||||||
|
* 'target' image. |
||||||
|
* |
||||||
|
* @param targetImage |
||||||
|
* the target image |
||||||
|
* @param sourceImage |
||||||
|
* the source image |
||||||
|
*/ |
||||||
|
public static void mix(Image targetImage, Image sourceImage) { |
||||||
|
if (sourceImage.getDepth() != targetImage.getDepth()) { |
||||||
|
throw new IllegalArgumentException("The given images should have the same depth to merge them!"); |
||||||
|
} |
||||||
|
if (sourceImage.getWidth() != targetImage.getWidth()) { |
||||||
|
throw new IllegalArgumentException("The given images should have the same width to merge them!"); |
||||||
|
} |
||||||
|
if (sourceImage.getHeight() != targetImage.getHeight()) { |
||||||
|
throw new IllegalArgumentException("The given images should have the same height to merge them!"); |
||||||
|
} |
||||||
|
|
||||||
|
PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat()); |
||||||
|
PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat()); |
||||||
|
TexturePixel sourcePixel = new TexturePixel(); |
||||||
|
TexturePixel targetPixel = new TexturePixel(); |
||||||
|
int depth = targetImage.getDepth() == 0 ? 1 : targetImage.getDepth(); |
||||||
|
|
||||||
|
for (int layerIndex = 0; layerIndex < depth; ++layerIndex) { |
||||||
|
for (int x = 0; x < sourceImage.getWidth(); ++x) { |
||||||
|
for (int y = 0; y < sourceImage.getHeight(); ++y) { |
||||||
|
sourceIO.read(sourceImage, layerIndex, sourcePixel, x, y); |
||||||
|
targetIO.read(targetImage, layerIndex, targetPixel, x, y); |
||||||
|
targetPixel.mix(sourcePixel); |
||||||
|
targetIO.write(targetImage, layerIndex, targetPixel, x, y); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resizes the image to the given width and height. |
||||||
|
* @param source |
||||||
|
* the source image (this remains untouched, the new image instance is created) |
||||||
|
* @param width |
||||||
|
* the target image width |
||||||
|
* @param height |
||||||
|
* the target image height |
||||||
|
* @return the resized image |
||||||
|
*/ |
||||||
|
public static Image resizeTo(Image source, int width, int height) { |
||||||
|
BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0); |
||||||
|
|
||||||
|
double scaleX = width / (double) sourceImage.getWidth(); |
||||||
|
double scaleY = height / (double) sourceImage.getHeight(); |
||||||
|
AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); |
||||||
|
AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR); |
||||||
|
|
||||||
|
BufferedImage scaledImage = bilinearScaleOp.filter(sourceImage, new BufferedImage(width, height, sourceImage.getType())); |
||||||
|
return ImageUtils.toJmeImage(scaledImage, source.getFormat()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method converts the given texture into normal-map texture. |
||||||
|
* |
||||||
|
* @param source |
||||||
|
* the source texture |
||||||
|
* @param strengthFactor |
||||||
|
* the normal strength factor |
||||||
|
* @return normal-map texture |
||||||
|
*/ |
||||||
|
public static Image convertToNormalMapTexture(Image source, float strengthFactor) { |
||||||
|
BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0); |
||||||
|
|
||||||
|
BufferedImage heightMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB); |
||||||
|
BufferedImage bumpMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB); |
||||||
|
ColorConvertOp gscale = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null); |
||||||
|
gscale.filter(sourceImage, heightMap); |
||||||
|
|
||||||
|
Vector3f S = new Vector3f(); |
||||||
|
Vector3f T = new Vector3f(); |
||||||
|
Vector3f N = new Vector3f(); |
||||||
|
|
||||||
|
for (int x = 0; x < bumpMap.getWidth(); ++x) { |
||||||
|
for (int y = 0; y < bumpMap.getHeight(); ++y) { |
||||||
|
// generating bump pixel
|
||||||
|
S.x = 1; |
||||||
|
S.y = 0; |
||||||
|
S.z = strengthFactor * ImageUtils.getHeight(heightMap, x + 1, y) - strengthFactor * ImageUtils.getHeight(heightMap, x - 1, y); |
||||||
|
T.x = 0; |
||||||
|
T.y = 1; |
||||||
|
T.z = strengthFactor * ImageUtils.getHeight(heightMap, x, y + 1) - strengthFactor * ImageUtils.getHeight(heightMap, x, y - 1); |
||||||
|
|
||||||
|
float den = (float) Math.sqrt(S.z * S.z + T.z * T.z + 1); |
||||||
|
N.x = -S.z; |
||||||
|
N.y = -T.z; |
||||||
|
N.z = 1; |
||||||
|
N.divideLocal(den); |
||||||
|
|
||||||
|
// setting thge pixel in the result image
|
||||||
|
bumpMap.setRGB(x, y, ImageUtils.vectorToColor(N.x, N.y, N.z)); |
||||||
|
} |
||||||
|
} |
||||||
|
return ImageUtils.toJmeImage(bumpMap, source.getFormat()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method converts the given texture into black and whit (grayscale) texture. |
||||||
|
* |
||||||
|
* @param source |
||||||
|
* the source texture |
||||||
|
* @return grayscale texture |
||||||
|
*/ |
||||||
|
public static Image convertToGrayscaleTexture(Image source) { |
||||||
|
BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0); |
||||||
|
ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null); |
||||||
|
op.filter(sourceImage, sourceImage); |
||||||
|
return ImageUtils.toJmeImage(sourceImage, source.getFormat()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method decompresses the given image. If the given image is already |
||||||
|
* decompressed nothing happens and it is simply returned. |
||||||
|
* |
||||||
|
* @param image |
||||||
|
* the image to decompress |
||||||
|
* @return the decompressed image |
||||||
|
*/ |
||||||
|
public static Image decompress(Image image) { |
||||||
|
Format format = image.getFormat(); |
||||||
|
int depth = image.getDepth(); |
||||||
|
if (depth == 0) { |
||||||
|
depth = 1; |
||||||
|
} |
||||||
|
ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth); |
||||||
|
int[] sizes = image.getMipMapSizes() != null ? image.getMipMapSizes() : new int[1]; |
||||||
|
int[] newMipmapSizes = image.getMipMapSizes() != null ? new int[image.getMipMapSizes().length] : null; |
||||||
|
|
||||||
|
for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) { |
||||||
|
ByteBuffer data = image.getData(dataLayerIndex); |
||||||
|
data.rewind(); |
||||||
|
if (sizes.length == 1) { |
||||||
|
sizes[0] = data.remaining(); |
||||||
|
} |
||||||
|
float widthToHeightRatio = image.getWidth() / image.getHeight();// this should always be constant for each mipmap
|
||||||
|
List<DDSTexelData> texelDataList = new ArrayList<DDSTexelData>(sizes.length); |
||||||
|
int maxPosition = 0, resultSize = 0; |
||||||
|
|
||||||
|
for (int sizeIndex = 0; sizeIndex < sizes.length; ++sizeIndex) { |
||||||
|
maxPosition += sizes[sizeIndex]; |
||||||
|
DDSTexelData texelData = new DDSTexelData(sizes[sizeIndex], widthToHeightRatio, format); |
||||||
|
texelDataList.add(texelData); |
||||||
|
switch (format) { |
||||||
|
case DXT1:// BC1
|
||||||
|
case DXT1A: |
||||||
|
while (data.position() < maxPosition) { |
||||||
|
TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; |
||||||
|
short c0 = data.getShort(); |
||||||
|
short c1 = data.getShort(); |
||||||
|
int col0 = RGB565.RGB565_to_ARGB8(c0); |
||||||
|
int col1 = RGB565.RGB565_to_ARGB8(c1); |
||||||
|
colors[0].fromARGB8(col0); |
||||||
|
colors[1].fromARGB8(col1); |
||||||
|
|
||||||
|
if (col0 > col1) { |
||||||
|
// creating color2 = 2/3color0 + 1/3color1
|
||||||
|
colors[2].fromPixel(colors[0]); |
||||||
|
colors[2].mult(2); |
||||||
|
colors[2].add(colors[1]); |
||||||
|
colors[2].divide(3); |
||||||
|
|
||||||
|
// creating color3 = 1/3color0 + 2/3color1;
|
||||||
|
colors[3].fromPixel(colors[1]); |
||||||
|
colors[3].mult(2); |
||||||
|
colors[3].add(colors[0]); |
||||||
|
colors[3].divide(3); |
||||||
|
} else { |
||||||
|
// creating color2 = 1/2color0 + 1/2color1
|
||||||
|
colors[2].fromPixel(colors[0]); |
||||||
|
colors[2].add(colors[1]); |
||||||
|
colors[2].mult(0.5f); |
||||||
|
|
||||||
|
colors[3].fromARGB8(0); |
||||||
|
} |
||||||
|
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
||||||
|
texelData.add(colors, indexes); |
||||||
|
} |
||||||
|
break; |
||||||
|
case DXT3:// BC2
|
||||||
|
while (data.position() < maxPosition) { |
||||||
|
TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; |
||||||
|
long alpha = data.getLong(); |
||||||
|
float[] alphas = new float[16]; |
||||||
|
long alphasIndex = 0; |
||||||
|
for (int i = 0; i < 16; ++i) { |
||||||
|
alphasIndex |= i << i * 4; |
||||||
|
byte a = (byte) ((alpha >> i * 4 & 0x0F) << 4); |
||||||
|
alphas[i] = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f; |
||||||
|
} |
||||||
|
|
||||||
|
short c0 = data.getShort(); |
||||||
|
short c1 = data.getShort(); |
||||||
|
int col0 = RGB565.RGB565_to_ARGB8(c0); |
||||||
|
int col1 = RGB565.RGB565_to_ARGB8(c1); |
||||||
|
colors[0].fromARGB8(col0); |
||||||
|
colors[1].fromARGB8(col1); |
||||||
|
|
||||||
|
// creating color2 = 2/3color0 + 1/3color1
|
||||||
|
colors[2].fromPixel(colors[0]); |
||||||
|
colors[2].mult(2); |
||||||
|
colors[2].add(colors[1]); |
||||||
|
colors[2].divide(3); |
||||||
|
|
||||||
|
// creating color3 = 1/3color0 + 2/3color1;
|
||||||
|
colors[3].fromPixel(colors[1]); |
||||||
|
colors[3].mult(2); |
||||||
|
colors[3].add(colors[0]); |
||||||
|
colors[3].divide(3); |
||||||
|
|
||||||
|
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
||||||
|
texelData.add(colors, indexes, alphas, alphasIndex); |
||||||
|
} |
||||||
|
break; |
||||||
|
case DXT5:// BC3
|
||||||
|
float[] alphas = new float[8]; |
||||||
|
while (data.position() < maxPosition) { |
||||||
|
TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; |
||||||
|
alphas[0] = data.get() * 255.0f; |
||||||
|
alphas[1] = data.get() * 255.0f; |
||||||
|
long alphaIndices = data.get() | data.get() << 8 | data.get() << 16 | data.get() << 24 | data.get() << 32 | data.get() << 40; |
||||||
|
if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
|
||||||
|
alphas[2] = (6 * alphas[0] + alphas[1]) / 7; |
||||||
|
alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7; |
||||||
|
alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7; |
||||||
|
alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7; |
||||||
|
alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7; |
||||||
|
alphas[7] = (alphas[0] + 6 * alphas[1]) / 7; |
||||||
|
} else { |
||||||
|
alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f; |
||||||
|
alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f; |
||||||
|
alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f; |
||||||
|
alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f; |
||||||
|
alphas[6] = 0; |
||||||
|
alphas[7] = 1; |
||||||
|
} |
||||||
|
|
||||||
|
short c0 = data.getShort(); |
||||||
|
short c1 = data.getShort(); |
||||||
|
int col0 = RGB565.RGB565_to_ARGB8(c0); |
||||||
|
int col1 = RGB565.RGB565_to_ARGB8(c1); |
||||||
|
colors[0].fromARGB8(col0); |
||||||
|
colors[1].fromARGB8(col1); |
||||||
|
|
||||||
|
// creating color2 = 2/3color0 + 1/3color1
|
||||||
|
colors[2].fromPixel(colors[0]); |
||||||
|
colors[2].mult(2); |
||||||
|
colors[2].add(colors[1]); |
||||||
|
colors[2].divide(3); |
||||||
|
|
||||||
|
// creating color3 = 1/3color0 + 2/3color1;
|
||||||
|
colors[3].fromPixel(colors[1]); |
||||||
|
colors[3].mult(2); |
||||||
|
colors[3].add(colors[0]); |
||||||
|
colors[3].divide(3); |
||||||
|
|
||||||
|
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
||||||
|
texelData.add(colors, indexes, alphas, alphaIndices); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new IllegalStateException("Unknown compressed format: " + format); |
||||||
|
} |
||||||
|
newMipmapSizes[sizeIndex] = texelData.getSizeInBytes(); |
||||||
|
resultSize += texelData.getSizeInBytes(); |
||||||
|
} |
||||||
|
byte[] bytes = new byte[resultSize]; |
||||||
|
int offset = 0; |
||||||
|
byte[] pixelBytes = new byte[4]; |
||||||
|
for (DDSTexelData texelData : texelDataList) { |
||||||
|
for (int i = 0; i < texelData.getPixelWidth(); ++i) { |
||||||
|
for (int j = 0; j < texelData.getPixelHeight(); ++j) { |
||||||
|
if (texelData.getRGBA8(i, j, pixelBytes)) { |
||||||
|
bytes[offset + (j * texelData.getPixelWidth() + i) * 4] = pixelBytes[0]; |
||||||
|
bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 1] = pixelBytes[1]; |
||||||
|
bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 2] = pixelBytes[2]; |
||||||
|
bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 3] = pixelBytes[3]; |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
offset += texelData.getSizeInBytes(); |
||||||
|
} |
||||||
|
dataArray.add(BufferUtils.createByteBuffer(bytes)); |
||||||
|
} |
||||||
|
|
||||||
|
Image result = depth > 1 ? new Image(Format.RGBA8, image.getWidth(), image.getHeight(), depth, dataArray) : new Image(Format.RGBA8, image.getWidth(), image.getHeight(), dataArray.get(0)); |
||||||
|
if (newMipmapSizes != null) { |
||||||
|
result.setMipMapSizes(newMipmapSizes); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method returns the height represented by the specified pixel in the |
||||||
|
* given texture. The given texture should be a height-map. |
||||||
|
* |
||||||
|
* @param image |
||||||
|
* the height-map texture |
||||||
|
* @param x |
||||||
|
* pixel's X coordinate |
||||||
|
* @param y |
||||||
|
* pixel's Y coordinate |
||||||
|
* @return height reprezented by the given texture in the specified location |
||||||
|
*/ |
||||||
|
private static int getHeight(BufferedImage image, int x, int y) { |
||||||
|
if (x < 0) { |
||||||
|
x = 0; |
||||||
|
} else if (x >= image.getWidth()) { |
||||||
|
x = image.getWidth() - 1; |
||||||
|
} |
||||||
|
if (y < 0) { |
||||||
|
y = 0; |
||||||
|
} else if (y >= image.getHeight()) { |
||||||
|
y = image.getHeight() - 1; |
||||||
|
} |
||||||
|
return image.getRGB(x, y) & 0xff; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method transforms given vector's coordinates into ARGB color (A is |
||||||
|
* always = 255). |
||||||
|
* |
||||||
|
* @param x |
||||||
|
* X factor of the vector |
||||||
|
* @param y |
||||||
|
* Y factor of the vector |
||||||
|
* @param z |
||||||
|
* Z factor of the vector |
||||||
|
* @return color representation of the given vector |
||||||
|
*/ |
||||||
|
private static int vectorToColor(float x, float y, float z) { |
||||||
|
int r = Math.round(255 * (x + 1f) / 2f); |
||||||
|
int g = Math.round(255 * (y + 1f) / 2f); |
||||||
|
int b = Math.round(255 * (z + 1f) / 2f); |
||||||
|
return (255 << 24) + (r << 16) + (g << 8) + b; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts java awt image to jme image. |
||||||
|
* @param bufferedImage |
||||||
|
* the java awt image |
||||||
|
* @param format |
||||||
|
* the result image format |
||||||
|
* @return the jme image |
||||||
|
*/ |
||||||
|
private static Image toJmeImage(BufferedImage bufferedImage, Format format) { |
||||||
|
ByteBuffer byteBuffer = BufferUtils.createByteBuffer(bufferedImage.getWidth() * bufferedImage.getHeight() * 3); |
||||||
|
ImageToAwt.convert(bufferedImage, format, byteBuffer); |
||||||
|
return new Image(format, bufferedImage.getWidth(), bufferedImage.getHeight(), byteBuffer); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue