Elite spaceship "ADDER" and AI

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Andrew Davie

This is the spaceship "ADDER" from the game Elite (1984).  I've colourised it and installed it in my Atari 2600 3D engine as it currently stands.


This is an interesting, if a bit scary, example of the use of modern AI systems to assist programming. Firstly, thanks to Thomas Jentzsch for the copy of the data. It was a fairly straightforward format, but with a twist that was beyond my math...

REM Format
REM : (a colon)
REM  shape1_name
REM  front_laser_vertex_number
REM  qty_vertices, qty_faces
REM "vertices"
REM  vertex1_x, vertex1_y, vertex1_z
REM  vertex2_x, vettex2_y, vertex2_z
REM    ......et al ....
REM  "faces"
REM  face1_colour,face1_normalx,face1_normaly,face1_normalz,
REM  face1_vertexqty,face1_1st_vertex_number,face1_2nd_vertex_number ...

REM  face2_colour,face2_normalx,face2_normaly,face2_normalz,
REM  face2_vertexqty,face2_1st_vertex_number,face2_2nd_vertex_number ...

REM    ......et al ....

REM  "hue-lig-sat"
REM  15 colour definitions

REM  shape2_name .....
REM  ...
REM  Note: &xx denotes a hex number

:
 ADDER
 &00
 &12,&0E
 vertices
 -&12,&00,&28
 &12,&00,&28
 &1E,&00,-&18
 &1E,&00,-&28
 &12,-&07,-&28
 -&12,-&07,-&28
 -&1E,&00,-&28
 -&1E,&00,-&18
 -&12,&07,-&28
 &12,&07,-&28
 -&12,&07,&0D
 &12,&07,&0D
 -&12,-&07,&0D
 &12,-&07,&0D
 -&0B,&03,&1D
 &0B,&03,&1D
 &0B,&04,&18
 -&0B,&04,&18
 faces
 &00,&00,&27,&0A,4,11,10,0,1
 &00,&00,-&27,&0A,4,13,12,0,1
 &03,&45,&32,&0D,3,2,11,1
 &02,&45,-&32,&0D,3,2,13,1
 &04,&1E,&34,&00,4,9,11,2,3
 &05,&1E,-&34,&00,4,4,13,2,3
 &06,&00,&00,-&A0,6,3,4,5,6,8,9
 &04,-&1E,&34,&00,4,10,8,6,7
 &05,-&1E,-&34,&00,4,12,5,6,7
 &03,-&45,&32,&0D,3,10,7,0
 &02,-&45,-&32,&0D,3,12,7,0
 &0B,&00,&1C,&00,4,10,11,9,8
 &0B,&00,-&1C,&00,4,12,13,4,5
 &0D,&00,&27,&0A,4,17,14,15,16
 hue-lig-sat
 &2D,&02,&07
 &00,&00,&07
 &20,&02,&0F
 &1D,&05,&09
 &25,&04,&0A
 &0F,&02,&0F
 &00,&07,&0F
 &00,&00,&07
 &00,&00,&07
 &00,&00,&07
 &00,&00,&07
 &3C,&03,&07
 &00,&00,&00
 &0F,&07,&0F
 &00,&00,&07

The conversion of the vector and face arrays were mostly straightforward. I can ignore the normals - my engine effectively calculates normals (well, it doesn't kinda need them) and the faces must be defined in counter-clockwise format to allow for the backface culling/removal to work. Unfortunately, the above data just has x/y/z normals for each face, but the ordering of the vertices in the faces is... unknown. To me, anyway.

So when I first put up the ship it was a mess. Faces facing you were absent, faces away from you were showing. You could see a vague ship shape, but somehow I had to figure out which faces were defined clockwise (wrong) and flip 'em.  I had no idea how to go from x/y/z normals to "is this face defined counter-clockwise".

So I asked ChatGPT (4.0) to figure it out for me.  Here's my query...

i'm doing a 3d renderer - wireframe and solid faces. I have data that give x,y,z surface normals for faces, but the vertices are not ordered counter-clockwise. given the normals, can I determine if the vertices are correctly oriented?

From that, and then the above data, ChatGPT wrote a python program for me that correctly identified which of the 14 faces needed to be flipped. I flipped 'em - and aside from the still unknown problem in the back/engine area... it got it totally right. The ship looked like the video (but uncoloured of course) the very first go. It's pretty astounding.

Here's the Python program the AI wrote to solve the problem (remember, from that original prompt + the data)...

import numpy as np

def hex_to_dec(hex_str):
    hex_str = hex_str.replace('&', '')  # Remove '&' character
    return int(hex_str, 16) if hex_str[0] != '-' else -int(hex_str[1:], 16)

# Parse vertices
vertices_hex = [
    "-&12,&00,&28", "&12,&00,&28", "&1E,&00,-&18", "&1E,&00,-&28", "&12,-&07,-&28",
    "-&12,-&07,-&28", "-&1E,&00,-&28", "-&1E,&00,-&18", "-&12,&07,-&28", "&12,&07,-&28",
    "-&12,&07,&0D", "&12,&07,&0D", "-&12,-&07,&0D", "&12,-&07,&0D", "-&0B,&03,&1D",
    "&0B,&03,&1D", "&0B,&04,&18", "-&0B,&04,&18"
]

vertices = []
for vertex in vertices_hex:
    x, y, z = vertex.split(',')
    vertices.append((hex_to_dec(x), hex_to_dec(y), hex_to_dec(z)))

# Parse faces
faces_hex = [
    "&00,&00,&27,&0A,4,11,10,0,1", "&00,&00,-&27,&0A,4,13,12,0,1", "&03,&45,&32,&0D,3,2,11,1",
    "&02,&45,-&32,&0D,3,2,13,1", "&04,&1E,&34,&00,4,9,11,2,3", "&05,&1E,-&34,&00,4,4,13,2,3",
    "&06,&00,&00,-&A0,6,3,4,5,6,8,9", "&04,-&1E,&34,&00,4,10,8,6,7", "&05,-&1E,-&34,&00,4,12,5,6,7",
    "&03,-&45,&32,&0D,3,10,7,0", "&02,-&45,-&32,&0D,3,12,7,0", "&0B,&00,&1C,&00,4,10,11,9,8",
    "&0B,&00,-&1C,&00,4,12,13,4,5", "&0D,&00,&27,&0A,4,17,14,15,16"
]

def parse_face(face_str):
    components = face_str.split(',')
    normal = [hex_to_dec(components[1]), hex_to_dec(components[2]), hex_to_dec(components[3])]
    vertex_count = int(components[4])
    vertex_indices = [int(components[i + 5]) for i in range(vertex_count)]
    return normal, vertex_indices

faces = [parse_face(face) for face in faces_hex]

# Function to check if face is clockwise
def is_face_clockwise(vertices, normal, vertex_indices):
    v1 = np.array(vertices[vertex_indices[0]])
    v2 = np.array(vertices[vertex_indices[1]])
    v3 = np.array(vertices[vertex_indices[2]])
   
    # Calculate the normal using cross product
    edge1 = v2 - v1
    edge2 = v3 - v1
    face_normal = np.cross(edge1, edge2)
   
    # Normalize the face normal
    face_normal = face_normal / np.linalg.norm(face_normal)
   
    # Compare the calculated normal with the provided normal
    dot_product = np.dot(face_normal, normal)
   
    return dot_product < 0

# Check each face
clockwise_faces = []
for i, (normal, vertex_indices) in enumerate(faces):
    if is_face_clockwise(vertices, normal, vertex_indices):
        clockwise_faces.append(i)

# Print the results
print("Clockwise-oriented faces:", clockwise_faces)


I then basically said "run it" and it came back with the following...

The faces that have clockwise-oriented vertices are at the following indices:

Face 1
Face 3
Face 5
Face 8
Face 10
Face 12


It's pretty incredible. This is kinda scary, but amazingly useful at the same time.


I'm not doing Elite. Not yet, anyway. This was just an attempt to see if the spaceships could be rendered and recognisable on the '2600. An emphatic YES.


Andrew Davie

ICC pics...


Andrew Davie

#2
First video was ChronoColour (CC). This one is Interleaved ChronoColour (iCC)


What's a space game without blowing up stuff?  In the attached binary you can blow up the ship (hard to avoid, really) if you keep the cursor on it for a while.

Update: when you shoot the ship it gets pushed away just a little

Andrew Davie

In this video I change the spaceship palette when you press fire whilst your target cursor is over the spaceship. I fixed/resolved the prior issue with the missing engine-bay poly;  that was due to the original data using a multi-vertex poly (as opposed to a tri or quad) for that area. I "pulse" that particular face now, trying to make it look like an active engine. There's still one minor bug - a missing outline in one of the "pod bays". But overall, it's pretty good now.