/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.graphics; import static jpcsp.graphics.RE.IRenderingEngine.RE_BYTE; import static jpcsp.graphics.RE.IRenderingEngine.RE_FLOAT; import static jpcsp.graphics.RE.IRenderingEngine.RE_INT; import static jpcsp.graphics.RE.IRenderingEngine.RE_SHORT; import static jpcsp.graphics.RE.IRenderingEngine.RE_UNSIGNED_BYTE; import static jpcsp.graphics.RE.IRenderingEngine.RE_UNSIGNED_SHORT; import java.nio.Buffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import org.apache.log4j.Logger; import jpcsp.memory.BufferedMemoryReader; import jpcsp.memory.ImageReader; /** * @author gid15 * */ public class VertexInfoReader { private static Logger log = VideoEngine.log; private VideoEngine videoEngine; private BufferedMemoryReader memoryReader; private VertexInfo vertexInfo; private IVertexDataBuffer vertexDataBuffer; private int weightOffset; private int weightType; private int weightNumberValues; private int textureOffset; private int textureType; private int textureNumberValues; private int colorOffset; private int colorType; private int colorNumberValues; private int normalOffset; private int normalType; private int normalNumberValues; private int positionOffset; private int positionType; private int positionNumberValues; private int stride; private boolean weightNative; private boolean textureNative; private boolean colorNative; private boolean normalNative; private boolean positionNative; private float[] boneWeights = new float[8]; private float[] normal = new float[3]; private float[] position = new float[3]; private boolean canAllNativeVertexInfo; private final static int typeNone = -1; // Readers skipping the padding at the end of a vertex element, // indexed by the alignment (1 = byte-aligned, 2 = short-aligned, 4 = int-aligned) private final IVertexInfoReader[] paddingReaders = new IVertexInfoReader[] { new NotImplementedReader("Padding 0") , new NopReader() // Alignment on 8 bit boundary , new AlignShortReader() // Alignment on 16 bit boundary , new NotImplementedReader("Padding 3") , new AlignIntReader() // Alignment on 32 bit boundary }; // Readers skipping 2 elements, indexed by the element type (1 = byte, 2 = short, 3 = float) private final IVertexInfoReader[] skip2Readers = new IVertexInfoReader[] { new NopReader() , new Skip2BytesReader() , new Skip2ShortsReader() , new Skip2FloatsReader() }; // Readers skipping 3 elements, indexed by the element type (1 = byte, 2 = short, 3 = float) private final IVertexInfoReader[] skip3Readers = new IVertexInfoReader[] { new NopReader() , new Skip3BytesReader() , new Skip3ShortsReader() , new Skip3FloatsReader() }; // Readers skipping a color element, indexed by the color type private final IVertexInfoReader[] skipColorReaders = new IVertexInfoReader[] { new NopReader() , new NotImplementedReader("Color 1") // Color type 1 is unknown , new NotImplementedReader("Color 2") // Color type 2 is unknown , new NotImplementedReader("Color 3") // Color type 3 is unknown , new Skip1ShortReader() // GU_COLOR_5650 , new Skip1ShortReader() // GU_COLOR_5551 , new Skip1ShortReader() // GU_COLOR_4444 , new Skip1IntReader() // GU_COLOR_8888 }; // Readers reading a texture element, indexed by the texture type private final IVertexInfoReader[] textureReaders = new IVertexInfoReader[] { new NopReader() , new Texture1Reader() , new Texture2Reader() , new Texture3Reader() }; // Readers reading a color element, indexed by the color type private final IVertexInfoReader[] colorReaders = new IVertexInfoReader[] { new NopReader() , new NotImplementedReader("Color 1") // Color type 1 is unknown , new NotImplementedReader("Color 2") // Color type 2 is unknown , new NotImplementedReader("Color 3") // Color type 3 is unknown , new Color4Reader() // GU_COLOR_5650 , new Color5Reader() // GU_COLOR_5551 , new Color6Reader() // GU_COLOR_4444 , new Color7Reader() // GU_COLOR_8888 }; // Readers reading a normal element, indexed by the normal type private final IVertexInfoReader[] normalReaders = new IVertexInfoReader[] { new NopReader() , new Normal1Reader() , new Normal2Reader() , new Normal3Reader() }; // Readers reading a position (vertex) element, indexed by the position type private final IVertexInfoReader[] positionReaders = new IVertexInfoReader[] { new NopReader() , new Position1Reader() , new Position2Reader() , new Position3Reader() }; // Readers reading a weight element, indexed by the weight type private final IVertexInfoReader[] weightReaders = new IVertexInfoReader[] { new NopReader() , new Weight1Reader() , new Weight2Reader() , new Weight3Reader() }; // Readers skipping a weight element, indexed by the weight type private final IVertexInfoReader[] skipWeightReaders = new IVertexInfoReader[] { new NopReader() , new SkipWeight1Reader() , new SkipWeight2Reader() , new SkipWeight3Reader() }; public VertexInfoReader() { } private void setAddress(int address) { memoryReader = new BufferedMemoryReader(address); } /** * Sets the "native", "type" and "offset" attributes for all the vertex elements * (texture, color, normal and position). * Computes the stride. */ private void update() { stride = 0; // Weight IVertexInfoReader weightReader = getWeightReader(false); weightNative = weightReader.isNative(); weightType = weightReader.type(); weightOffset = 0; weightNumberValues = weightReader.numberValues(); int weightSize = weightReader.size(); stride += weightSize; // Texture IVertexInfoReader textureReader = getTextureReader(false); textureNative = textureReader.isNative(); textureType = textureReader.type(); textureOffset = (textureNative ? vertexInfo.textureOffset : stride); textureNumberValues = textureReader.numberValues(); int textureSize = textureReader.size(); stride += textureSize; // Color IVertexInfoReader colorReader = getColorReader(false); colorNative = colorReader.isNative(); colorType = colorReader.type(); colorOffset = (colorNative ? vertexInfo.colorOffset : stride); colorNumberValues = colorReader.numberValues(); int colorSize = colorReader.size(); stride += colorSize; // Normal IVertexInfoReader normalReader = getNormalReader(false); normalNative = normalReader.isNative(); normalType = normalReader.type(); normalOffset = (normalNative ? vertexInfo.normalOffset : stride); normalNumberValues = normalReader.numberValues(); int normalSize = normalReader.size(); stride += normalSize; // Position IVertexInfoReader positionReader = getPositionReader(false); positionNative = positionReader.isNative(); positionType = positionReader.type(); positionOffset = (positionNative ? vertexInfo.positionOffset : stride); positionNumberValues = positionReader.numberValues(); int positionSize = positionReader.size(); stride += positionSize; } public void addNativeOffset(int offset) { if (offset != 0) { if (weightNative) { weightOffset += offset; } if (textureNative) { textureOffset += offset; } if (colorNative) { colorOffset += offset; } if (normalNative) { normalOffset += offset; } if (positionNative) { positionOffset += offset; } } } public int getWeightOffset() { return weightOffset; } public int getWeightType() { return weightType; } public int getWeightNumberValues() { return weightNumberValues; } public int getTextureOffset() { return textureOffset; } public int getTextureType() { return textureType; } public int getTextureNumberValues() { return textureNumberValues; } public int getColorOffset() { return colorOffset; } public int getColorType() { return colorType; } public int getColorNumberValues() { return colorNumberValues; } public int getNormalOffset() { return normalOffset; } public int getNormalType() { return normalType; } public int getNormalNumberValues() { return normalNumberValues; } public int getPositionOffset() { return positionOffset; } public int getPositionType() { return positionType; } public int getPositionNumberValues() { return positionNumberValues; } public int getStride() { return stride; } public boolean isWeightNative() { return weightNative; } public boolean isTextureNative() { return textureNative; } public boolean isColorNative() { return colorNative; } public boolean isNormalNative() { return normalNative; } public boolean isPositionNative() { return positionNative; } /** * @return true if the vertex has at least one native element, false otherwise. */ public boolean hasNative() { if (textureNative && vertexInfo.texture != 0) { return true; } if (colorNative && vertexInfo.color != 0) { return true; } if (normalNative && vertexInfo.normal != 0) { return true; } if (positionNative && vertexInfo.position != 0) { return true; } return false; } /** * @return true if the vertex has only native element, false otherwise. */ public boolean isAllNative() { return textureNative && colorNative && normalNative && positionNative; } /** * Reads a sequence of VertexInfo structures. * * @param vertexInfo The VertexInfo prepared by the command list * @param address The start address of the structures * @param numberOfVertex The number of VertexInfo to read * @return A Buffer containing all the non-native VertexInfo * elements. The native elements are not included. * Returns "null" if all the elements are native. */ public Buffer read(VertexInfo vertexInfo, int address, int firstVertex, int numberOfVertex, boolean canAllNativeVertexInfo) { videoEngine = VideoEngine.getInstance(); this.vertexInfo = vertexInfo; this.canAllNativeVertexInfo = canAllNativeVertexInfo; update(); // Don't need to read the vertex data if all elements are native if (isAllNative()) { if (log.isDebugEnabled()) { log.debug(String.format("Not reading Vertex, all native at 0x%08X", address)); } return null; } // Display debug information on non-native elements if (log.isDebugEnabled()) { log.debug(String.format("Reading %d Vertex at 0x%08X", numberOfVertex, address + firstVertex * vertexInfo.vertexSize)); if (!textureNative) { log.debug("Texture non-native " + vertexInfo.toString()); } if (!colorNative) { log.debug("Color non-native " + vertexInfo.toString()); } if (!normalNative) { log.debug("Normal non-native " + vertexInfo.toString()); } if (!positionNative) { log.debug("Position non-native " + vertexInfo.toString()); } } setAddress(address + firstVertex * vertexInfo.vertexSize); createVertexDataBuffer(numberOfVertex); // Prepare all the element readers IVertexInfoReader weightReader = getWeightReader(weightNative); IVertexInfoReader textureReader = getTextureReader(textureNative); IVertexInfoReader colorReader = getColorReader(colorNative); IVertexInfoReader normalReader = getNormalReader(normalNative); IVertexInfoReader positionReader = getPositionReader(positionNative); IVertexInfoReader padReader = getPaddingReader(vertexInfo.alignmentSize); // Read all the VertexInfo in sequence for (int i = 0; i < numberOfVertex; i++) { weightReader.read(); textureReader.read(); colorReader.read(); normalReader.read(); positionReader.read(); padReader.read(); } return vertexDataBuffer.getBuffer(); } private IVertexInfoReader getWeightReader(boolean isNative) { return (isNative ? skipWeightReaders[vertexInfo.weight] : weightReaders[vertexInfo.weight]); } private IVertexInfoReader getTextureReader(boolean isNative) { return (isNative ? skip2Readers[vertexInfo.texture] : textureReaders[vertexInfo.texture]); } private IVertexInfoReader getColorReader(boolean isNative) { return (isNative ? skipColorReaders[vertexInfo.color] : colorReaders[vertexInfo.color]); } private IVertexInfoReader getNormalReader(boolean isNative) { return (isNative ? skip3Readers[vertexInfo.normal] : normalReaders[vertexInfo.normal]); } private IVertexInfoReader getPositionReader(boolean isNative) { return (isNative ? skip3Readers[vertexInfo.position] : positionReaders[vertexInfo.position]); } private IVertexInfoReader getPaddingReader(int size) { return paddingReaders[size]; } /** * Create the VertexDataBuffer for storing all the non-native elements. * An "int"-based or a "float"-based buffer can be created, trying to find * the best performance by avoiding conversions. * The current decision is * - GU_TRANSFORM_2D: use "int"-based buffer * - GU_TRANSFORM_3D: use "float"-based buffer * * @param numberOfVertex The number of VertexInfo to read */ private void createVertexDataBuffer(int numberOfVertex) { boolean intBufferType = false; // Decide which buffer type is better (for performance) if (vertexInfo.transform2D) { intBufferType = true; } if (intBufferType) { vertexDataBuffer = new IntVertexDataBuffer(stride * numberOfVertex); } else { vertexDataBuffer = new FloatVertexDataBuffer(stride * numberOfVertex); } } /** * Interface for all VertexDataBuffer classes * */ private interface IVertexDataBuffer { public Buffer getBuffer(); public void put(int data); public void put(float data); } /** * VertexDataBuffer based on "int" values * */ private static class IntVertexDataBuffer implements IVertexDataBuffer { private int[] buffer; private int index; public IntVertexDataBuffer(int sizeInBytes) { buffer = new int[(sizeInBytes + 3) / 4]; index = 0; } @Override public Buffer getBuffer() { return IntBuffer.wrap(buffer); } @Override public void put(int data) { buffer[index] = data; index++; } @Override public void put(float data) { buffer[index] = Float.floatToRawIntBits(data); index++; } } /** * VertexDataBuffer based on "float" values * */ private static class FloatVertexDataBuffer implements IVertexDataBuffer { private float[] buffer; private int index; public FloatVertexDataBuffer(int sizeInBytes) { buffer = new float[(sizeInBytes + 3) / 4]; index = 0; } @Override public Buffer getBuffer() { return FloatBuffer.wrap(buffer); } @Override public void put(int data) { buffer[index] = Float.intBitsToFloat(data); index++; } @Override public void put(float data) { buffer[index] = data; index++; } } /** * Interface for all readers * */ private interface IVertexInfoReader { /** * Reads the vertex data from the memory and stores them into the vertex data buffer */ public void read(); /** * Returns the number of bytes stored into the vertex data buffer by one read() call */ public int size(); /** * Returns the Rendering Engine type of the values put into the vertex data buffer */ public int type(); /** * Returns the number of values for the element type */ public int numberValues(); /** * Returns if the vertex data can be used directly by the RE, without conversion */ public boolean isNative(); } /** * Abstract Reader for all native readers * */ private abstract class AbstractNativeReader implements IVertexInfoReader { @Override public void read() { // Raise error VideoEngine.getInstance().error("This vertex information is always native! " + vertexInfo.toString()); } @Override public boolean isNative() { return true; } @Override public int size() { return 0; } } /** * Abstract Reader for all skip readers * */ private abstract class AbstractSkipReader implements IVertexInfoReader { @Override public boolean isNative() { return true; } @Override public int size() { return 0; } @Override public int type() { return typeNone; } @Override public int numberValues() { return 0; } } /** * Reader displaying "Not Implemented" error * */ private class NotImplementedReader extends AbstractSkipReader { private String comment; public NotImplementedReader(String comment) { this.comment = comment; } @Override public void read() { // Raise error VideoEngine.getInstance().error(String.format("Unsupported Vertex Information %s for %s", comment, vertexInfo.toString())); } } /** * Reader doing nothing * */ private class NopReader extends AbstractSkipReader { @Override public void read() { // Nothing to do } } /** * Reader aligning on a 16 bit boundary * */ private class AlignShortReader extends AbstractSkipReader { @Override public void read() { // Align on a short boundary memoryReader.align16(); } } /** * Reader aligning on a 32 bit boundary * */ private class AlignIntReader extends AbstractSkipReader { @Override public void read() { // Align on an int boundary memoryReader.align32(); } } /** * Reader skipping 2 bytes (2 x 8 bit) * */ private class Skip2BytesReader extends AbstractSkipReader { @Override public void read() { // Skip 2 Bytes memoryReader.skipNext8(); memoryReader.skipNext8(); } } /** * Reader skipping 3 bytes (3 x 8 bit) * */ private class Skip3BytesReader extends AbstractSkipReader { @Override public void read() { // Skip 3 Bytes memoryReader.skipNext8(); memoryReader.skipNext8(); memoryReader.skipNext8(); } } /** * Reader skipping 1 short (1 x 16 bit) * */ private class Skip1ShortReader extends AbstractSkipReader { @Override public void read() { // Skip 1 Shorts memoryReader.skipNext16(); } } /** * Reader skipping 2 shorts (2 x 16 bit) * */ private class Skip2ShortsReader extends AbstractSkipReader { @Override public void read() { // Skip 2 Shorts memoryReader.skipNext16(); memoryReader.skipNext16(); } } /** * Reader skipping 3 shorts (3 x 16 bit) * */ private class Skip3ShortsReader extends AbstractSkipReader { @Override public void read() { // Skip 3 Shorts memoryReader.skipNext16(); memoryReader.skipNext16(); memoryReader.skipNext16(); } } /** * Reader skipping 1 integer (1 x 32 bit) * */ private class Skip1IntReader extends AbstractSkipReader { @Override public void read() { // Skip 1 Int memoryReader.skipNext32(); } } /** * Reader skipping 2 floats (2 x 32 bit) * */ private class Skip2FloatsReader extends AbstractSkipReader { @Override public void read() { // Skip 2 Floats memoryReader.skipNext32(2); } } /** * Reader skipping 3 floats (3 x 32 bit) * */ private class Skip3FloatsReader extends AbstractSkipReader { @Override public void read() { // Skip 3 Floats memoryReader.skipNext32(3); } } /** * Reader for Texture type 1 (GU_TEXTURE_8BIT) * */ private class Texture1Reader implements IVertexInfoReader { @Override public void read() { // Unsigned 8 bit int texture1 = memoryReader.readNext8(); int texture2 = memoryReader.readNext8(); if (vertexInfo.transform2D) { // Transform 2 unsigned 8 bit into 2 signed 16 bit vertexDataBuffer.put(texture1 | (texture2 << 16)); } else { // To be mapped to [0..2] for 3D vertexDataBuffer.put(texture1 / 128f); vertexDataBuffer.put(texture2 / 128f); } } @Override public boolean isNative() { // Unsigned byte is not available as a native texture coordinate value return canAllNativeVertexInfo; } @Override public int size() { return (isNative() ? 0 : (vertexInfo.transform2D ? 4 : 8)); } @Override public int type() { return (isNative() ? RE_UNSIGNED_BYTE : (vertexInfo.transform2D ? RE_SHORT : RE_FLOAT)); } @Override public int numberValues() { return 2; } } /** * Reader for Texture type 2 (GU_TEXTURE_16BIT) * */ private class Texture2Reader implements IVertexInfoReader { @Override public void read() { // Unsigned 16 bit int texture1 = memoryReader.readNext16(); int texture2 = memoryReader.readNext16(); // To be mapped to [0..2] for 3D vertexDataBuffer.put(texture1 / 32768f); vertexDataBuffer.put(texture2 / 32768f); } @Override public boolean isNative() { return canAllNativeVertexInfo || vertexInfo.transform2D; } @Override public int size() { return (isNative() ? 0 : 8); } @Override public int type() { // Use signed Int16 because unsigned Int16 is not allowed for glTexCoordPointer return (canAllNativeVertexInfo ? RE_UNSIGNED_SHORT : (isNative() ? RE_SHORT : RE_FLOAT)); } @Override public int numberValues() { return 2; } } /** * Reader for Texture type 3 (GU_TEXTURE_32BITF) * */ private class Texture3Reader extends AbstractNativeReader { @Override public int type() { return RE_FLOAT; } @Override public int numberValues() { return 2; } } /** * Reader for Color type 4 (GU_COLOR_5650) * */ private class Color4Reader implements IVertexInfoReader { @Override public void read() { vertexDataBuffer.put(ImageReader.color565to8888(memoryReader.readNext16())); } @Override public boolean isNative() { return canAllNativeVertexInfo; } @Override public int size() { return (isNative() ? 0 : 4); } @Override public int type() { return (isNative() ? RE_UNSIGNED_SHORT : RE_UNSIGNED_BYTE); } @Override public int numberValues() { return (isNative() ? 1 : 4); } } /** * Reader for Color type 5 (GU_COLOR_5551) * */ private class Color5Reader implements IVertexInfoReader { @Override public void read() { vertexDataBuffer.put(ImageReader.color5551to8888(memoryReader.readNext16())); } @Override public boolean isNative() { return canAllNativeVertexInfo; } @Override public int size() { return (isNative() ? 0 : 4); } @Override public int type() { return (isNative() ? RE_UNSIGNED_SHORT : RE_UNSIGNED_BYTE); } @Override public int numberValues() { return (isNative() ? 1 : 4); } } /** * Reader for Color type 6 (GU_COLOR_4444) * */ private class Color6Reader implements IVertexInfoReader { @Override public void read() { vertexDataBuffer.put(ImageReader.color4444to8888(memoryReader.readNext16())); } @Override public boolean isNative() { return canAllNativeVertexInfo; } @Override public int size() { return (isNative() ? 0 : 4); } @Override public int type() { return (isNative() ? RE_UNSIGNED_SHORT : RE_UNSIGNED_BYTE); } @Override public int numberValues() { return (isNative() ? 1 : 4); } } /** * Reader for Color type 7 (GU_COLOR_8888) * */ private class Color7Reader extends AbstractNativeReader { @Override public int type() { return RE_UNSIGNED_BYTE; } @Override public int numberValues() { return 4; } } /** * Reader for Normal type 1 (GU_NORMAL_8BIT) * */ private class Normal1Reader implements IVertexInfoReader { @Override public void read() { // TODO Check if this value is signed like position or unsigned like texture // Signed 8 bit // To be mapped to [-1..1] for 3D normal[0] = ((byte) memoryReader.readNext8()) / 127f; normal[1] = ((byte) memoryReader.readNext8()) / 127f; normal[2] = ((byte) memoryReader.readNext8()) / 127f; if (vertexInfo.weight != 0) { videoEngine.doNormalSkinning(vertexInfo, boneWeights, normal); } vertexDataBuffer.put(normal[0]); vertexDataBuffer.put(normal[1]); vertexDataBuffer.put(normal[2]); } @Override public boolean isNative() { return canAllNativeVertexInfo || vertexInfo.transform2D; } @Override public int size() { return (isNative() ? 0 : 12); } @Override public int type() { return (isNative() ? RE_BYTE : RE_FLOAT); } @Override public int numberValues() { return 3; } } /** * Reader for Normal type 2 (GU_NORMAL_16BIT) * */ private class Normal2Reader implements IVertexInfoReader { @Override public void read() { // TODO Check if this value is signed like position or unsigned like texture // Signed 16 bit // To be mapped to [-1..1] for 3D normal[0] = ((short) memoryReader.readNext16()) / 32767f; normal[1] = ((short) memoryReader.readNext16()) / 32767f; normal[2] = ((short) memoryReader.readNext16()) / 32767f; if (vertexInfo.weight != 0) { videoEngine.doNormalSkinning(vertexInfo, boneWeights, normal); } vertexDataBuffer.put(normal[0]); vertexDataBuffer.put(normal[1]); vertexDataBuffer.put(normal[2]); } @Override public boolean isNative() { return canAllNativeVertexInfo || vertexInfo.transform2D; } @Override public int size() { return (isNative() ? 0 : 12); } @Override public int type() { return (isNative() ? RE_SHORT : RE_FLOAT); } @Override public int numberValues() { return 3; } } /** * Reader for Normal type 3 (GU_NORMAL_32BITF) * */ private class Normal3Reader implements IVertexInfoReader { @Override public void read() { normal[0] = memoryReader.readNextFloat(); normal[1] = memoryReader.readNextFloat(); normal[2] = memoryReader.readNextFloat(); videoEngine.doNormalSkinning(vertexInfo, boneWeights, normal); vertexDataBuffer.put(normal[0]); vertexDataBuffer.put(normal[1]); vertexDataBuffer.put(normal[2]); } @Override public boolean isNative() { return canAllNativeVertexInfo || vertexInfo.weight == 0; } @Override public int size() { return (isNative() ? 0 : 12); } @Override public int type() { return RE_FLOAT; } @Override public int numberValues() { return 3; } } /** * Reader for Position (Vertex) type 1 (GU_VERTEX_8BIT) * */ private class Position1Reader implements IVertexInfoReader { @Override public void read() { if (vertexInfo.transform2D) { // X and Y are signed 8 bit, Z is unsigned 8 bit vertexDataBuffer.put((byte) memoryReader.readNext8()); vertexDataBuffer.put((byte) memoryReader.readNext8()); vertexDataBuffer.put( memoryReader.readNext8()); } else { // Signed 8 bit, to be mapped to [-1..1] for 3D position[0] = ((byte) memoryReader.readNext8()) / 127f; position[1] = ((byte) memoryReader.readNext8()) / 127f; position[2] = ((byte) memoryReader.readNext8()) / 127f; if (vertexInfo.weight != 0) { videoEngine.doPositionSkinning(vertexInfo, boneWeights, position); } vertexDataBuffer.put(position[0]); vertexDataBuffer.put(position[1]); vertexDataBuffer.put(position[2]); } } @Override public boolean isNative() { // Cannot be native because X and Y are signed and Z is unsigned return canAllNativeVertexInfo; } @Override public int size() { return (isNative() ? 0 : 12); } @Override public int type() { return (isNative() ? RE_BYTE : (vertexInfo.transform2D ? RE_INT : RE_FLOAT)); } @Override public int numberValues() { return 3; } } /** * Reader for Position (Vertex) type 2 (GU_VERTEX_16BIT) * */ private class Position2Reader implements IVertexInfoReader { @Override public void read() { if (vertexInfo.transform2D) { // X and Y are signed 16 bit, Z is unsigned 16 bit vertexDataBuffer.put((short) memoryReader.readNext16()); vertexDataBuffer.put((short) memoryReader.readNext16()); vertexDataBuffer.put( memoryReader.readNext16()); } else { // Signed 16 bit, to be mapped to [-1..1] for 3D position[0] = ((short) memoryReader.readNext16()) / 32767f; position[1] = ((short) memoryReader.readNext16()) / 32767f; position[2] = ((short) memoryReader.readNext16()) / 32767f; if (vertexInfo.weight != 0) { videoEngine.doPositionSkinning(vertexInfo, boneWeights, position); } vertexDataBuffer.put(position[0]); vertexDataBuffer.put(position[1]); vertexDataBuffer.put(position[2]); } } @Override public boolean isNative() { // Cannot be native in 2D because X and Y are signed and Z is unsigned return canAllNativeVertexInfo; } @Override public int size() { return (isNative() ? 0 : 12); } @Override public int type() { return (isNative() ? RE_SHORT : (vertexInfo.transform2D ? RE_INT : RE_FLOAT)); } @Override public int numberValues() { return 3; } } /** * Reader for Position (Vertex) type 3 (GU_VERTEX_32BITF) * */ private class Position3Reader implements IVertexInfoReader { @Override public void read() { position[0] = memoryReader.readNextFloat(); position[1] = memoryReader.readNextFloat(); position[2] = memoryReader.readNextFloat(); if (vertexInfo.weight != 0) { videoEngine.doPositionSkinning(vertexInfo, boneWeights, position); } if (vertexInfo.transform2D) { // Z is an integer value clamped between 0 and 65535 if (position[2] < 0f) { position[2] = 0f; } else if (position[2] > 65535f) { position[2] = 65535f; } else { // 2D positions are always integer values: truncate float value position[2] = (int) position[2]; } } vertexDataBuffer.put(position[0]); vertexDataBuffer.put(position[1]); vertexDataBuffer.put(position[2]); } @Override public boolean isNative() { // 2D needs some corrections of the Z coord. return canAllNativeVertexInfo || !vertexInfo.transform2D; } @Override public int size() { return (isNative() ? 0 : 12); } @Override public int type() { return RE_FLOAT; } @Override public int numberValues() { return 3; } } /** * Reader for Weight type 1 (GU_WEIGHT_8BIT) * */ public class Weight1Reader implements IVertexInfoReader { @Override public void read() { for (int i = 0; i < vertexInfo.skinningWeightCount; i++) { // Unsigned 8 bit, mapped to [0..2] boneWeights[i] = memoryReader.readNext8() / 128f; } } @Override public boolean isNative() { return true; } @Override public int size() { return 0; } @Override public int type() { return RE_UNSIGNED_BYTE; } @Override public int numberValues() { return vertexInfo.skinningWeightCount; } } /** * Reader for Weight type 2 (GU_WEIGHT_16BIT) * */ public class Weight2Reader implements IVertexInfoReader { @Override public void read() { for (int i = 0; i < vertexInfo.skinningWeightCount; i++) { // Unsigned 16 bit, mapped to [0..2] boneWeights[i] = memoryReader.readNext16() / 32768f; } } @Override public boolean isNative() { return true; } @Override public int size() { return 0; } @Override public int type() { return RE_UNSIGNED_SHORT; } @Override public int numberValues() { return vertexInfo.skinningWeightCount; } } /** * Reader for Weight type 3 (GU_WEIGHT_32BITF) * */ public class Weight3Reader implements IVertexInfoReader { @Override public void read() { for (int i = 0; i < vertexInfo.skinningWeightCount; i++) { // Float value boneWeights[i] = memoryReader.readNextFloat(); } } @Override public boolean isNative() { return true; } @Override public int size() { return 0; } @Override public int type() { return RE_FLOAT; } @Override public int numberValues() { return vertexInfo.skinningWeightCount; } } /** * Reader skipping the weight bytes (N x 8 bit) * */ private class SkipWeight1Reader extends AbstractSkipReader { @Override public void read() { // Skip Weight Bytes for (int i = 0; i < vertexInfo.skinningWeightCount; i++) { memoryReader.skipNext8(); } } } /** * Reader skipping the weight shorts (N x 16 bit) * */ private class SkipWeight2Reader extends AbstractSkipReader { @Override public void read() { // Skip Weight Shorts for (int i = 0; i < vertexInfo.skinningWeightCount; i++) { memoryReader.skipNext16(); } } } /** * Reader skipping the weight floats (N x 32 bit) * */ private class SkipWeight3Reader extends AbstractSkipReader { @Override public void read() { // Skip Weight Floats memoryReader.skipNext32(vertexInfo.skinningWeightCount); } } }