[[Category RSC]] 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; // 12345678
    public int vertex_count;
    public int vertices_x[];
    public int vertices_y[];
    public int vertices_z[];
    public int face_count;
    public int face_vertex_count[];
    public int face_vertices[][];
    public int face_fill_back[];
    public int face_fill_front[];
    public int face_gouraud[];

    public OB3Model(byte data[], int offset) {
        int vertex_count = get_uint16(data, offset);
        offset += 2;
        int face_count = get_uint16(data, offset);
        offset += 2;

        vertices_x = new int[vertex_count];
        vertices_y = new int[vertex_count];
        vertices_z = new int[vertex_count];
        face_vertex_count = new int[face_count];
        face_vertices = new int[face_count][];
        face_fill_back = new int[face_count];
        face_fill_front = new int[face_count];
        face_gouraud = new int[face_count];

        for (int v = 0; v < vertex_count; v++) {
            vertices_x[v] = get_int16b(data, offset);
            offset += 2;
        }

        for (int v = 0; v < vertex_count; v++) {
            vertices_y[v] = get_int16b(data, offset);
            offset += 2;
        }

        for (int v = 0; v < vertex_count; v++) {
            vertices_z[v] = get_int16b(data, offset);
            offset += 2;
        }

        this.vertex_count = vertex_count;
        for (int f = 0; f < face_count; f++)
            face_vertex_count[f] = get_ubyte(data[offset++]);

        for (int f = 0; f < face_count; f++) {
            face_fill_back[f] = get_int16b(data, offset);
            offset += 2;
            if (face_fill_back[f] == 32767)
                face_fill_back[f] = num_seq;
        }

        for (int f = 0; f < face_count; f++) {
            face_fill_front[f] = get_int16b(data, offset);
            offset += 2;
            if (face_fill_front[f] == 32767)
                face_fill_front[f] = num_seq;
        }

        for (int f = 0; f < face_count; f++) {
            int i = get_ubyte(data[offset++]);
            if (i == 0)
                face_gouraud[f] = 0;
            else
                face_gouraud[f] = num_seq;
        }

        for (int f = 0; f < face_count; f++) {
            face_vertices[f] = new int[face_vertex_count[f]];
            for (int fv = 0; fv < face_vertex_count[f]; fv++) {
                if (vertex_count < 256) {
                    face_vertices[f][fv] = get_ubyte(data[offset++]);
                } else {
                    face_vertices[f][fv] = get_uint16(data, offset);
                    offset += 2;
                }
            }
        }

        this.face_count = face_count;
    }

    private static int get_ubyte(byte b) {
        return (b & 0xff);
    }

    private static int get_uint16(byte b[], int start) {
        return (get_ubyte(b[start]) << 8) + get_ubyte(b[start + 1]);
    }

    private static int get_int16b(byte b[], int start) {
        int i = get_ubyte(b[start]) * 256 + get_ubyte(b[start + 1]);
        if (i > 32767)
            i -= 0x10000;
        return i;
    }
}
== '''Faces''' == A '''negative''' face_fill_back or face_fill_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 vertices are one less than the OBJ face vertices.
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);
}
public static int encode_colour(int r, int g, int b) {
    return -1 - (r / 8) * 1024 - (g / 8) * 32 - b / 8;
}