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