/*
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.util.Utilities.round;
import jpcsp.Memory;
import jpcsp.graphics.RE.software.PixelColor;
/**
* @author gid15
*
*/
public class VertexInfoReaderTemplate {
public static boolean isLogTraceEnabled;
public static boolean transform2D;
public static int skinningWeightCount;
public static int morphingVertexCount;
public static int texture;
public static int color;
public static int normal;
public static int position;
public static int weight;
public static int index;
public static int vtype;
public static boolean readTexture;
public static int vertexSize;
public static int oneVertexSize;
public static int textureOffset;
public static int colorOffset;
public static int normalOffset;
public static int positionOffset;
public static int alignmentSize;
public void processType(VertexInfo vinfo) {
vinfo.transform2D = transform2D;
vinfo.skinningWeightCount = skinningWeightCount;
vinfo.morphingVertexCount = morphingVertexCount;
vinfo.texture = texture;
vinfo.color = color;
vinfo.normal = normal;
vinfo.position = position;
vinfo.weight = weight;
vinfo.index = index;
vinfo.vtype = vtype;
vinfo.readTexture = readTexture;
vinfo.vertexSize = vertexSize;
vinfo.oneVertexSize = oneVertexSize;
vinfo.textureOffset = textureOffset;
vinfo.colorOffset = colorOffset;
vinfo.normalOffset = normalOffset;
vinfo.positionOffset = positionOffset;
vinfo.alignmentSize = alignmentSize;
}
public void readVertex(Memory mem, int addr, VertexState v, float[] morphWeights) {
doReadVertex(mem, addr, v, morphWeights);
}
private static void doReadVertex(Memory mem, int addr, VertexState v, float[] morphWeights) {
int startAddr = addr;
//
// Weight
//
if (weight != 0) {
// Align first weight
if (weight == 2) {
addr = (addr + 1) & ~1;
} else if (weight == 3) {
addr = (addr + 3) & ~3;
}
for (int i = 0; i < skinningWeightCount; i++) {
switch (weight) {
case 1:
// Unsigned 8 bit, mapped to [0..2]
v.boneWeights[i] = mem.read8(addr++);
v.boneWeights[i] /= 0x80;
break;
case 2:
// Unsigned 16 bit, mapped to [0..2]
v.boneWeights[i] = mem.read16(addr);
v.boneWeights[i] /= 0x8000;
addr += 2;
break;
case 3:
v.boneWeights[i] = Float.intBitsToFloat(mem.read32(addr));
addr += 4;
break;
}
}
}
if (morphingVertexCount > 1 && !transform2D) {
//
// Read vertex with morphing
//
if (readTexture) {
//
// Texture with morphing
//
v.t[0] = v.t[1] = 0f;
switch (texture) {
case 1:
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + textureOffset;
// Unsigned 8 bit
float tu = mem.read8(addr);
float tv = mem.read8(addr + 1);
// To be mapped to [0..2] for 3D
tu /= 0x80;
tv /= 0x80;
v.t[0] += tu * morphWeights[morphCounter];
v.t[1] += tv * morphWeights[morphCounter];
}
break;
case 2:
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + textureOffset;
// Unsigned 16 bit
float tu = mem.read16(addr);
float tv = mem.read16(addr + 2);
// To be mapped to [0..2] for 3D
tu /= 0x8000;
tv /= 0x8000;
v.t[0] += tu * morphWeights[morphCounter];
v.t[1] += tv * morphWeights[morphCounter];
}
break;
case 3:
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + textureOffset;
float tu = Float.intBitsToFloat(mem.read32(addr));
float tv = Float.intBitsToFloat(mem.read32(addr + 4));
v.t[0] += tu * morphWeights[morphCounter];
v.t[1] += tv * morphWeights[morphCounter];
}
break;
}
}
if (color != 0) {
//
// Color with morphing
//
v.c[0] = v.c[1] = v.c[2] = v.c[3] = 0f;
switch (color) {
case 1:
case 2:
case 3:
VideoEngine.log.error(String.format("Unknown color type %d", color));
break;
case 4: { // GU_COLOR_5650
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + colorOffset;
int packed = mem.read16(addr);
// All components checked on PSP
//
// 5650 format: BBBBBGGGGGGRRRRR
// 4321054321043210
// transformed into
// 8888 format: 11111111BBBBBBBBGGGGGGGGRRRRRRRR
// 432104325432105443210432
//
int rBits = (packed) & 0x1F;
int gBits = (packed >> 5) & 0x3F;
int bBits = (packed >> 11) & 0x1F;
float r = ((rBits << 3) | (rBits >> 2)) / 255.0f;
float g = ((gBits << 2) | (gBits >> 4)) / 255.0f;
float b = ((bBits << 3) | (bBits >> 2)) / 255.0f;
v.c[0] += r * morphWeights[morphCounter];
v.c[1] += g * morphWeights[morphCounter];
v.c[2] += b * morphWeights[morphCounter];
v.c[3] += morphWeights[morphCounter];
}
break;
}
case 5: { // GU_COLOR_5551
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + colorOffset;
int packed = mem.read16(addr);
// All components checked on PSP
//
// 5551 format: ABBBBBGGGGGRRRRR
// 432104321043210
// transformed into
// 8888 format: AAAAAAAABBBBBBBBGGGGGGGGRRRRRRRR
// 432104324321043243210432
//
int rBits = (packed) & 0x1F;
int gBits = (packed >> 5) & 0x1F;
int bBits = (packed >> 10) & 0x1F;
float r = ((rBits << 3) | (rBits >> 2)) / 255.0f;
float g = ((gBits << 3) | (gBits >> 2)) / 255.0f;
float b = ((bBits << 3) | (bBits >> 2)) / 255.0f;
float a = ((packed >> 15) & 0x01) / 1.0f;
v.c[0] += r * morphWeights[morphCounter];
v.c[1] += g * morphWeights[morphCounter];
v.c[2] += b * morphWeights[morphCounter];
v.c[3] += a * morphWeights[morphCounter];
}
break;
}
case 6: { // GU_COLOR_4444
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + colorOffset;
int packed = mem.read16(addr);
// All components checked on PSP
//
// 4444 format: AAAABBBBGGGGRRRR
// 3210321032103210
// transformed into
// 8888 format: AAAAAAAABBBBBBBBGGGGGGGGRRRRRRRR
// 32103210321032103210321032103210
//
int rBits = (packed) & 0x0F;
int gBits = (packed >> 4) & 0x0F;
int bBits = (packed >> 8) & 0x0F;
int aBits = (packed >> 12) & 0x0F;
float r = ((rBits << 4) | rBits) / 255.0f;
float g = ((gBits << 4) | gBits) / 255.0f;
float b = ((bBits << 4) | bBits) / 255.0f;
float a = ((aBits << 4) | aBits) / 255.0f;
v.c[0] += r * morphWeights[morphCounter];
v.c[1] += g * morphWeights[morphCounter];
v.c[2] += b * morphWeights[morphCounter];
v.c[3] += a * morphWeights[morphCounter];
}
break;
}
case 7: { // GU_COLOR_8888
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + colorOffset;
int packed = mem.read32(addr);
float r = ((packed) & 0xff) / 255.0f;
float g = ((packed >> 8) & 0xff) / 255.0f;
float b = ((packed >> 16) & 0xff) / 255.0f;
float a = ((packed >> 24) & 0xff) / 255.0f;
v.c[0] += r * morphWeights[morphCounter];
v.c[1] += g * morphWeights[morphCounter];
v.c[2] += b * morphWeights[morphCounter];
v.c[3] += a * morphWeights[morphCounter];
}
break;
}
}
}
if (normal != 0) {
//
// Normal with morphing
//
v.n[0] = v.n[1] = v.n[2] = 0f;
switch (normal) {
case 1:
// TODO Check if this value is signed like position or unsigned like texture
// Signed 8 bit
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + normalOffset;
float x = (byte) mem.read8(addr);
float y = (byte) mem.read8(addr + 1);
float z = (byte) mem.read8(addr + 2);
// To be mapped to [-1..1] for 3D
x /= 0x7f;
y /= 0x7f;
z /= 0x7f;
v.n[0] += x * morphWeights[morphCounter];
v.n[1] += y * morphWeights[morphCounter];
v.n[2] += z * morphWeights[morphCounter];
}
break;
case 2:
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + normalOffset;
// TODO Check if this value is signed like position or unsigned like texture
// Signed 16 bit
float x = (short) mem.read16(addr);
float y = (short) mem.read16(addr + 2);
float z = (short) mem.read16(addr + 4);
// To be mapped to [-1..1] for 3D
x /= 0x7fff;
y /= 0x7fff;
z /= 0x7fff;
v.n[0] += x * morphWeights[morphCounter];
v.n[1] += y * morphWeights[morphCounter];
v.n[2] += z * morphWeights[morphCounter];
}
break;
case 3:
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + normalOffset;
float x = Float.intBitsToFloat(mem.read32(addr));
float y = Float.intBitsToFloat(mem.read32(addr + 4));
float z = Float.intBitsToFloat(mem.read32(addr + 8));
v.n[0] += x * morphWeights[morphCounter];
v.n[1] += y * morphWeights[morphCounter];
v.n[2] += z * morphWeights[morphCounter];
}
break;
}
}
if (position != 0) {
//
// Position with morphing
//
v.p[0] = v.p[1] = v.p[2] = 0f;
switch (position) {
case 1:
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + positionOffset;
// Signed 8 bit, to be mapped to [-1..1] for 3D
float x = ((byte) mem.read8(addr)) / 127f;
float y = ((byte) mem.read8(addr + 1)) / 127f;
float z = ((byte) mem.read8(addr + 2)) / 127f;
v.p[0] += x * morphWeights[morphCounter];
v.p[1] += y * morphWeights[morphCounter];
v.p[2] += z * morphWeights[morphCounter];
}
break;
case 2:
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + positionOffset;
// Signed 16 bit, to be mapped to [-1..1] for 3D
float x = ((short) mem.read16(addr)) / 32767f;
float y = ((short) mem.read16(addr + 2)) / 32767f;
float z = ((short) mem.read16(addr + 4)) / 32767f;
v.p[0] += x * morphWeights[morphCounter];
v.p[1] += y * morphWeights[morphCounter];
v.p[2] += z * morphWeights[morphCounter];
}
break;
case 3: // GU_VERTEX_32BITF
for (int morphCounter = 0; morphCounter < morphingVertexCount; morphCounter++) {
addr = startAddr + (morphCounter * oneVertexSize) + positionOffset;
float x = Float.intBitsToFloat(mem.read32(addr));
float y = Float.intBitsToFloat(mem.read32(addr + 4));
float z = Float.intBitsToFloat(mem.read32(addr + 8));
v.p[0] += x * morphWeights[morphCounter];
v.p[1] += y * morphWeights[morphCounter];
v.p[2] += z * morphWeights[morphCounter];
}
break;
}
}
} else {
//
// Read Vertex without morphing
//
if (readTexture) {
//
// Texture
//
switch (texture) {
case 1: {
// Unsigned 8 bit
float tu = mem.read8(addr++);
float tv = mem.read8(addr++);
if (!transform2D) {
// To be mapped to [0..2] for 3D
tu /= 0x80;
tv /= 0x80;
}
v.t[0] = tu;
v.t[1] = tv;
break;
}
case 2: {
addr = (addr + 1) & ~1;
// Unsigned 16 bit
float tu = mem.read16(addr);
float tv = mem.read16(addr + 2);
addr += 4;
if (!transform2D) {
// To be mapped to [0..2] for 3D
tu /= 0x8000;
tv /= 0x8000;
}
v.t[0] = tu;
v.t[1] = tv;
break;
}
case 3: {
addr = (addr + 3) & ~3;
v.t[0] = Float.intBitsToFloat(mem.read32(addr));
v.t[1] = Float.intBitsToFloat(mem.read32(addr + 4));
addr += 8;
break;
}
}
} else if (texture != 0) {
// Skip unread texture
addr += colorOffset - textureOffset;
}
if (color != 0) {
//
// Color
//
switch (color) {
case 1:
case 2:
case 3:
VideoEngine.log.error(String.format("Unknown color type %d", color));
break;
case 4: { // GU_COLOR_5650
addr = (addr + 1) & ~1;
int packed = mem.read16(addr);
addr += 2;
// All components checked on PSP
//
// 5650 format: BBBBBGGGGGGRRRRR
// 4321054321043210
// transformed into
// 8888 format: 11111111BBBBBBBBGGGGGGGGRRRRRRRR
// 432104325432105443210432
//
int rBits = packed & 0x1F;
int gBits = (packed >> 5) & 0x3F;
int bBits = (packed >> 11) & 0x1F;
v.c[0] = ((rBits << 3) | (rBits >> 2)) / 255f;
v.c[1] = ((gBits << 2) | (gBits >> 4)) / 255f;
v.c[2] = ((bBits << 3) | (bBits >> 2)) / 255f;
v.c[3] = 1f;
break;
}
case 5: { // GU_COLOR_5551
addr = (addr + 1) & ~1;
int packed = mem.read16(addr);
addr += 2;
// All components checked on PSP
//
// 5551 format: ABBBBBGGGGGRRRRR
// 432104321043210
// transformed into
// 8888 format: AAAAAAAABBBBBBBBGGGGGGGGRRRRRRRR
// 432104324321043243210432
//
int rBits = (packed) & 0x1F;
int gBits = (packed >> 5) & 0x1F;
int bBits = (packed >> 10) & 0x1F;
v.c[0] = ((rBits << 3) | (rBits >> 2)) / 255f;
v.c[1] = ((gBits << 3) | (gBits >> 2)) / 255f;
v.c[2] = ((bBits << 3) | (bBits >> 2)) / 255f;
v.c[3] = ((packed >> 15) & 0x01) / 1f;
break;
}
case 6: { // GU_COLOR_4444
addr = (addr + 1) & ~1;
int packed = mem.read16(addr);
addr += 2;
// All components checked on PSP
//
// 4444 format: AAAABBBBGGGGRRRR
// 3210321032103210
// transformed into
// 8888 format: AAAAAAAABBBBBBBBGGGGGGGGRRRRRRRR
// 32103210321032103210321032103210
//
int rBits = (packed) & 0x0F;
int gBits = (packed >> 4) & 0x0F;
int bBits = (packed >> 8) & 0x0F;
int aBits = (packed >> 12) & 0x0F;
v.c[0] = ((rBits << 4) | rBits) / 255f;
v.c[1] = ((gBits << 4) | gBits) / 255f;
v.c[2] = ((bBits << 4) | bBits) / 255f;
v.c[3] = ((aBits << 4) | aBits) / 255f;
break;
}
case 7: { // GU_COLOR_8888
addr = (addr + 3) & ~3;
int packed = mem.read32(addr);
addr += 4;
v.c[0] = ((packed) & 0xff) / 255f;
v.c[1] = ((packed >> 8) & 0xff) / 255f;
v.c[2] = ((packed >> 16) & 0xff) / 255f;
v.c[3] = ((packed >> 24) & 0xff) / 255f;
break;
}
}
}
if (normal != 0) {
//
// Normal
//
switch (normal) {
case 1: {
float x = (byte) mem.read8(addr++);
float y = (byte) mem.read8(addr++);
float z = (byte) mem.read8(addr++);
if (!transform2D) {
// To be mapped to [-1..1] for 3D
x /= 0x7f;
y /= 0x7f;
z /= 0x7f;
}
v.n[0] = x;
v.n[1] = y;
v.n[2] = z;
break;
}
case 2: {
addr = (addr + 1) & ~1;
// TODO Check if this value is signed like position or unsigned like texture
// Signed 16 bit
float x = (short) mem.read16(addr);
float y = (short) mem.read16(addr + 2);
float z = (short) mem.read16(addr + 4);
addr += 6;
if (!transform2D) {
// To be mapped to [-1..1] for 3D
x /= 0x7fff;
y /= 0x7fff;
z /= 0x7fff;
}
v.n[0] = x;
v.n[1] = y;
v.n[2] = z;
break;
}
case 3: {
addr = (addr + 3) & ~3;
v.n[0] = Float.intBitsToFloat(mem.read32(addr));
v.n[1] = Float.intBitsToFloat(mem.read32(addr + 4));
v.n[2] = Float.intBitsToFloat(mem.read32(addr + 8));
addr += 12;
break;
}
}
}
if (position != 0) {
//
// Position
//
switch (position) {
case 1: {
if (transform2D) {
// X and Y are signed 8 bit, Z is unsigned 8 bit
v.p[0] = (byte) mem.read8(addr++);
v.p[1] = (byte) mem.read8(addr++);
v.p[2] = mem.read8(addr++);
} else {
// Signed 8 bit, to be mapped to [-1..1] for 3D
v.p[0] = ((byte) mem.read8(addr++)) / 127f;
v.p[1] = ((byte) mem.read8(addr++)) / 127f;
v.p[2] = ((byte) mem.read8(addr++)) / 127f;
}
break;
}
case 2: {
addr = (addr + 1) & ~1;
if (transform2D) {
// X and Y are signed 16 bit, Z is unsigned 16 bit
v.p[0] = (short) mem.read16(addr);
v.p[1] = (short) mem.read16(addr + 2);
v.p[2] = mem.read16(addr + 4);
} else {
// Signed 16 bit, to be mapped to [-1..1] for 3D
v.p[0] = ((short) mem.read16(addr)) / 32767f;
v.p[1] = ((short) mem.read16(addr + 2)) / 32767f;
v.p[2] = ((short) mem.read16(addr + 4)) / 32767f;
}
addr += 6;
break;
}
case 3: { // GU_VERTEX_32BITF
addr = (addr + 3) & ~3;
float x = Float.intBitsToFloat(mem.read32(addr));
float y = Float.intBitsToFloat(mem.read32(addr + 4));
float z = Float.intBitsToFloat(mem.read32(addr + 8));
addr += 12;
if (transform2D) {
// Z is an integer value clamped between 0 and 65535
if (z < 0f) {
z = 0f;
} else if (z > 65535f) {
z = 65535f;
} else {
// 2D positions are always integer values
z = (int) z;
}
}
v.p[0] = x;
v.p[1] = y;
v.p[2] = z;
break;
}
}
}
}
//
// Tracing
//
if (isLogTraceEnabled) {
VideoEngine.log.trace(String.format("Reading vertex at 0x%08X %s", startAddr, VertexInfo.toString(texture, color, normal, position, weight, skinningWeightCount, morphingVertexCount, index, transform2D, vertexSize)));
if (weight != 0) {
VideoEngine.log.trace(String.format("Weight(%d) %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f", skinningWeightCount, v.boneWeights[0], v.boneWeights[1], v.boneWeights[2], v.boneWeights[3], v.boneWeights[4], v.boneWeights[5], v.boneWeights[6], v.boneWeights[7]));
}
if (texture != 0) {
if (transform2D) {
VideoEngine.log.trace(String.format("texture type %d %d, %d", texture, round(v.t[0]), round(v.t[1])));
} else {
VideoEngine.log.trace(String.format("texture type %d %f, %f", texture, v.t[0], v.t[1]));
}
}
if (color != 0) {
VideoEngine.log.trace(String.format("color type %d 0x%08X", color, PixelColor.getColor(v.c)));
}
if (normal != 0) {
VideoEngine.log.trace(String.format("normal type %d %f, %f, %f", normal, v.n[0], v.n[1], v.n[2]));
}
if (position != 0) {
VideoEngine.log.trace(String.format("vertex type %d %f, %f, %f", position, v.p[0], v.p[1], v.p[2]));
}
if (morphingVertexCount > 1) {
VideoEngine.log.trace(String.format("Morphing oneVertexSize=%d, textureOffset=%d, colorOffset=%d, normalOffset=%d, positionOffset=%d", oneVertexSize, textureOffset, colorOffset, normalOffset, positionOffset));
}
}
}
}