[[Category RSC]] Work in progress. This page refers to .ob3, a custom format for 3D models created by Jagex. It is used by the RuneScape Classic engine since client version #74. For the earlier version of the format see [[OB2|OB2]].
public class OB3Model { private static final int num_seq = 0xbc614e; public int gouraud_shade[]; public int vert_count; public int vert_x[]; public int vert_y[]; public int vert_z[]; public int face_count; public int face_v_count[]; public int face_v[][]; public int face_back[]; public int face_front[]; public OB3Model(byte data[], int offset) { int vert_count = get_u_short(data, offset); offset += 2; int face_count = get_u_short(data, offset); offset += 2; vert_x = new int[vert_count]; vert_y = new int[vert_count]; vert_z = new int[vert_count]; face_v_count = new int[face_count]; faces = new int[face_count][]; face_back = new int[face_count]; face_front = new int[face_count]; gouraud_shade = new int[face_count]; for (int v = 0; v < vert_count; v++) { vert_x[v] = get_short(data, offset); offset += 2; } for (int v = 0; v < vert_count; v++) { vert_y[v] = get_short(data, offset); offset += 2; } for (int v = 0; v < vert_count; v++) { vert_z[v] = get_short(data, offset); offset += 2; } this.vert_count = vert_count; for (int f = 0; f < face_count; f++) face_v_count[f] = get_u_byte(data[offset++]); for (int f = 0; f < face_count; f++) { face_back[f] = get_short(data, offset); offset += 2; if (face_back[f] == 32767) face_back[f] = num_seq; } for (int f = 0; f < face_count; f++) { face_front[f] = get_short(data, offset); offset += 2; if (face_front[f] == 32767) face_front[f] = num_seq; } for (int f = 0; f < face_count; f++) { int i = get_u_byte(data[offset++]); if (i == 0) gouraud_shade[f] = 0; else gouraud_shade[f] = num_seq; } for (int f = 0; f < face_count; f++) { face_v[f] = new int[face_v_count[f]]; for (int fv = 0; fv < face_v_count[f]; fv++) { if (vert_count < 256) { face_v[f][fv] = get_u_byte(data[offset++]); } else { face_v[f][fv] = get_u_short(data, offset); offset += 2; } } } this.face_count = face_count; } public static int get_u_byte(byte b) { return (b & 0xff); } public static int get_u_short(byte b[], int start) { return ((b[start] & 0xff) << 8) + (b[start + 1] & 0xff); } public static int get_short(byte b[], int start) { int i = get_u_byte(b[start]) * 256 + get_u_byte(b[start + 1]); if (i > 32767) i -= 0x10000; return i; } }== '''Faces''' == A '''negative''' face_back or face_front value indicates a '''solid colour''', whereas a '''positive''' value indicates a '''texture'''. The texture is defined by its offset in the client's texture array. When converting to/from [http://en.wikipedia.org/wiki/Wavefront_.obj_file Wavefront OBJ] format, remember that the OB3 face values are one less than the OBJ face values.
public static int decode_colour(int i) { i = -(i + 1); int r = i >> 10 & 0x1f; int g = i >> 5 & 0x1f; int b = i & 0x1f; return (r << 19) + (g << 11) + (b << 3); }