Menu
JAQForum Ver 19.10.27

Forum Index : Microcontroller and PC projects : Elite ships converted to full 3D by CLAUDE

Posted: 07:58am
30 Sep 2025
Copy link to clipboard
matherp
Guru

The following is I think extraordinary - I'll leave it to you to judge

https://elite.bbcelite.com/deep_dives/ship_blueprints.html
Has blueprints for all the ships used in the game ELITE as used in the 6502 source. Unfortunately, the way these are defined is incompatible with the MMBasic 3D engine so I thought that would be a good test for CLAUDE. I fed it the MMBasic manual for the Pico together with the 3D pdf manual from the download.
I then screen scraped one of the ships (VIPER) and saved it as a txt file on the PICO - see below.
I then asked CLAUDE to write a MMbasic program to convert this into a basic program that would read in the Elite model file and convert it to Basic statements to create the 3D model in MMBasic and then display it.
It took a small number of iterations  some Basic syntax, others the maths e.g.
Me
  Quote  I'm using viper as a simple example. There is something not right the image is corrupted

Claude
  Quote  The issue is likely that the face vertex ordering algorithm isn't working correctly. Let me look at the Elite data more carefully - the vertices need to be ordered clockwise when viewed from outside the ship, and my centroid-based sorting approach is too simplistic.

Let me create a better version that builds faces by following the edges in order:
Elite Ship to MMBasic Converter (Fixed)
Code ∙ Version 16

I've completely rewritten the face building algorithm. The key changes:

New approach:

   Follows edges in order - Instead of just collecting vertices randomly, it now traces around each face by following connected edges
   Maintains vertex ordering - Starts with the first edge and follows the chain of connected edges to build each face in the correct order
   Tracks used edges - Prevents using the same edge twice when building a face
   Stops at start vertex - Completes the face loop when it returns to the starting vertex

This should preserve the clockwise ordering that's implicit in Elite's edge data, which is critical for proper face culling in DRAW3D. The corruption you saw was likely due to vertices being in random order, causing faces to render incorrectly or inside-out.

Try running this on your Viper data - it should produce a clean, properly rendered ship model!


Here is the converter that Claude produced - note that I told it to use OPTION EXPLICIT and below that is the fully running file displaying the Viper ship in 3D rotating on the screen
' Elite Ship Blueprint to MMBasic DRAW3D Converter
' Reads Elite ship data files and generates MMBasic code

OPTION EXPLICIT
OPTION BASE 0

' Declare all variables
DIM filename$ LENGTH 50
DIM outfile$ LENGTH 50
DIM txt$ LENGTH 200
DIM rest$ LENGTH 200
DIM s$ LENGTH 200
DIM INTEGER nv, ne, nf, i, j, k, p1, p2, f1, f2, dotpos
DIM INTEGER totalidx, curr, nextv, found, startv
DIM FLOAT vx(100), vy(100), vz(100)
DIM INTEGER e1(100), e2(100), ef1(100), ef2(100)
DIM INTEGER facevert(100, 20)
DIM INTEGER facecount(100)
DIM INTEGER edgeused(100)
DIM INTEGER faceedges(50), nfaceedges

' Get filename from user
INPUT "Enter ship blueprint filename: ", filename$
IF filename$ = "" THEN END

' Create output filename (change .txt to .bas)
dotpos = INSTR(filename$, ".")
IF dotpos > 0 THEN
 outfile$ = LEFT$(filename$, dotpos-1) + ".bas"
ELSE
 outfile$ = filename$ + ".bas"
ENDIF

' Open and read the file
OPEN filename$ FOR INPUT AS #1

' Skip to vertices section
DO WHILE NOT EOF(#1)
 LINE INPUT #1, txt$
 IF INSTR(txt$, "_VERTICES") > 0 THEN EXIT
LOOP

' Count vertices and read vertex data
nv = 0
DO WHILE NOT EOF(#1)
 LINE INPUT #1, txt$
 txt$ = Trim$(txt$, " " + CHR$(9))
 
 ' Stop at edges section
 IF INSTR(txt$, "_EDGES") > 0 THEN EXIT
 
 ' Parse VERTEX lines
 IF LEFT$(txt$, 6) = "VERTEX" THEN
   ' Extract x, y, z coordinates
   p1 = INSTR(txt$, " ")
   rest$ = Trim$(MID$(txt$, p1), " " + CHR$(9))
   
   ' Get x coordinate
   p2 = INSTR(rest$, ",")
   vx(nv) = VAL(LEFT$(rest$, p2-1))
   rest$ = Trim$(MID$(rest$, p2+1), " " + CHR$(9))
   
   ' Get y coordinate
   p2 = INSTR(rest$, ",")
   vy(nv) = VAL(LEFT$(rest$, p2-1))
   rest$ = Trim$(MID$(rest$, p2+1), " " + CHR$(9))
   
   ' Get z coordinate
   p2 = INSTR(rest$, ",")
   vz(nv) = VAL(LEFT$(rest$, p2-1))
   
   nv = nv + 1
 ENDIF
LOOP

' Count edges and read edge data
ne = 0
DO WHILE NOT EOF(#1)
 LINE INPUT #1, txt$
 txt$ = Trim$(txt$, " " + CHR$(9))
 
 ' Stop at faces section
 IF INSTR(txt$, "_FACES") > 0 THEN EXIT
 
 ' Parse EDGE lines
 IF LEFT$(txt$, 4) = "EDGE" THEN
   ' Extract vertex1, vertex2, face1, face2
   p1 = INSTR(txt$, " ")
   rest$ = Trim$(MID$(txt$, p1), " " + CHR$(9))
   
   ' Get vertex1
   p2 = INSTR(rest$, ",")
   e1(ne) = VAL(LEFT$(rest$, p2-1))
   rest$ = Trim$(MID$(rest$, p2+1), " " + CHR$(9))
   
   ' Get vertex2
   p2 = INSTR(rest$, ",")
   e2(ne) = VAL(LEFT$(rest$, p2-1))
   rest$ = Trim$(MID$(rest$, p2+1), " " + CHR$(9))
   
   ' Get face1
   p2 = INSTR(rest$, ",")
   ef1(ne) = VAL(LEFT$(rest$, p2-1))
   rest$ = Trim$(MID$(rest$, p2+1), " " + CHR$(9))
   
   ' Get face2
   p2 = INSTR(rest$, ",")
   ef2(ne) = VAL(LEFT$(rest$, p2-1))
   
   ne = ne + 1
 ENDIF
LOOP

' Count faces
nf = 0
DO WHILE NOT EOF(#1)
 LINE INPUT #1, txt$
 txt$ = Trim$(txt$, " " + CHR$(9))
 IF LEFT$(txt$, 4) = "FACE" THEN nf = nf + 1
LOOP

CLOSE #1

PRINT "Found"; nv; "vertices,"; ne; "edges,"; nf; "faces"
PRINT "Writing output to: "; outfile$
PRINT

' Open output file
OPEN outfile$ FOR OUTPUT AS #2

' Initialize face data
FOR i = 0 TO nf-1
 facecount(i) = 0
NEXT i

' Build faces by following edges in order
FOR f1 = 0 TO nf-1
 ' Clear edge used flags for this face
 FOR i = 0 TO ne-1
   edgeused(i) = 0
 NEXT i
 
 ' Collect all edges that belong to this face
 nfaceedges = 0
 FOR i = 0 TO ne-1
   IF ef1(i) = f1 OR ef2(i) = f1 THEN
     faceedges(nfaceedges) = i
     nfaceedges = nfaceedges + 1
   ENDIF
 NEXT i
 
 IF nfaceedges = 0 THEN CONTINUE FOR
 
 ' Start with first edge
 facevert(f1, 0) = e1(faceedges(0))
 facevert(f1, 1) = e2(faceedges(0))
 facecount(f1) = 2
 edgeused(faceedges(0)) = 1
 startv = e1(faceedges(0))
 curr = e2(faceedges(0))
 
 ' Follow edges to build complete face
 DO WHILE curr <> startv AND facecount(f1) < 20
   found = 0
   FOR j = 0 TO nfaceedges-1
     i = faceedges(j)
     IF edgeused(i) = 0 THEN
       IF e1(i) = curr THEN
         nextv = e2(i)
         found = 1
       ELSEIF e2(i) = curr THEN
         nextv = e1(i)
         found = 1
       ENDIF
       
       IF found = 1 THEN
         IF nextv <> startv THEN
           facevert(f1, facecount(f1)) = nextv
           facecount(f1) = facecount(f1) + 1
         ENDIF
         edgeused(i) = 1
         curr = nextv
         EXIT FOR
       ENDIF
     ENDIF
   NEXT j
   
   IF found = 0 THEN EXIT DO
 LOOP
NEXT f1

' Calculate total face indices needed
totalidx = 0
FOR i = 0 TO nf-1
 totalidx = totalidx + facecount(i)
NEXT i

' Generate MMBasic code to file
PRINT #2, "' Generated from Elite ship blueprint: "; filename$
PRINT #2, "OPTION BASE 0"
PRINT #2, ""
PRINT #2, "DIM n = 1"
PRINT #2, "DIM nv ="; nv
PRINT #2, "DIM nf ="; nf
PRINT #2, "DIM camera = 1"
PRINT #2, ""
PRINT #2, "' Vertex data"
PRINT #2, "DIM FLOAT vertices(2,"; nv-1; ")"
PRINT #2, "FOR i = 0 TO"; nv-1
PRINT #2, "  READ vertices(0,i), vertices(1,i), vertices(2,i)"
PRINT #2, "NEXT i"
PRINT #2, ""

' Output vertex data
FOR i = 0 TO nv-1
 PRINT #2, "DATA"; vx(i); ","; vy(i); ","; vz(i)
NEXT i

PRINT #2, ""
PRINT #2, "MATH SCALE vertices(), 2.0, vertices()"
PRINT #2, ""
PRINT #2, "' Face counts"
PRINT #2, "DIM INTEGER facecount("; nf-1; ")"
PRINT #2, "FOR i = 0 TO"; nf-1
PRINT #2, "  READ facecount(i)"
PRINT #2, "NEXT i"
PRINT #2, ""

' Output face counts
FOR i = 0 TO nf-1 STEP 10
 s$ = "DATA "
 FOR j = i TO MIN(i+9, nf-1)
   s$ = s$ + STR$(facecount(j))
   IF j < MIN(i+9, nf-1) THEN s$ = s$ + ","
 NEXT j
 PRINT #2, s$
NEXT i

PRINT #2, ""
PRINT #2, "' Face vertex indices ("; totalidx; "total)"
PRINT #2, "DIM INTEGER faces("; totalidx-1; ")"
PRINT #2, "FOR i = 0 TO"; totalidx-1
PRINT #2, "  READ faces(i)"
PRINT #2, "NEXT i"
PRINT #2, ""

' Output face vertex data
FOR i = 0 TO nf-1
 s$ = "DATA "
 FOR j = 0 TO facecount(i)-1
   s$ = s$ + STR$(facevert(i,j))
   IF j < facecount(i)-1 THEN s$ = s$ + ","
 NEXT j
 PRINT #2, s$
NEXT i

PRINT #2, ""
PRINT #2, "' Colors"
PRINT #2, "DIM INTEGER colours(4)"
PRINT #2, "colours(0) = RGB(180,180,200)"
PRINT #2, "colours(1) = RGB(100,100,120)"
PRINT #2, "colours(2) = RGB(200,50,50)"
PRINT #2, "colours(3) = RGB(60,60,80)"
PRINT #2, "colours(4) = RGB(150,150,170)"
PRINT #2, ""
PRINT #2, "DIM INTEGER edge("; nf-1; ")"
PRINT #2, "FOR i = 0 TO"; nf-1
PRINT #2, "  edge(i) = 3"
PRINT #2, "NEXT i"
PRINT #2, ""
PRINT #2, "DIM INTEGER fill("; nf-1; ")"
PRINT #2, "FOR i = 0 TO"; nf-1
PRINT #2, "  fill(i) = i MOD 5"
PRINT #2, "NEXT i"
PRINT #2, ""
PRINT #2, "DRAW3D CREATE n, nv, nf, camera, vertices(), facecount(), faces(), colours(), edge(), fill()"
PRINT #2, ""
PRINT #2, "' Setup camera"
PRINT #2, "DIM INTEGER viewplane = 600"
PRINT #2, "DRAW3D CAMERA camera, viewplane, 0, 0"
PRINT #2, ""
PRINT #2, "' Animation variables"
PRINT #2, "DIM FLOAT yaw = 0, pitch = 0, roll = 0"
PRINT #2, "DIM FLOAT q1(4)"
PRINT #2, ""
PRINT #2, "CLS"
PRINT #2, ""
PRINT #2, "' Main display loop"
PRINT #2, "DO"
PRINT #2, "  MATH Q_EULER yaw, pitch, roll, q1()"
PRINT #2, "  INC yaw, RAD(1)"
PRINT #2, "  INC pitch, RAD(2)"
PRINT #2, "  INC roll, RAD(0.5)"
PRINT #2, "  DRAW3D ROTATE q1(), 1"
PRINT #2, "  DRAW3D SHOW 1, 0, 0, 1000, 1"
PRINT #2, "  PAUSE 100"
PRINT #2, "LOOP"

CLOSE #2

PRINT "Conversion complete!"
PRINT "Output written to: "; outfile$

END

' Custom Trim function (like manual example)
FUNCTION Trim$(s$, c$) AS STRING
 LOCAL result$ LENGTH 200
 result$ = s$
 ' Trim from left
 DO WHILE INSTR(c$, LEFT$(result$, 1)) > 0 AND LEN(result$) > 0
   result$ = MID$(result$, 2)
 LOOP
 ' Trim from right
 DO WHILE INSTR(c$, RIGHT$(result$, 1)) > 0 AND LEN(result$) > 0
   result$ = LEFT$(result$, LEN(result$) - 1)
 LOOP
 Trim$ = result$
END FUNCTION


' Generated from Elite ship blueprint: viper.txt
Option BASE 0

Dim n = 1
Dim nv = 15
Dim nf = 7
Dim camera = 1

' Vertex data
Dim FLOAT vertices(2, 14)
For i = 0 To 14
 Read vertices(0,i), vertices(1,i), vertices(2,i)
Next i

Data 0, 0, 72
Data 0, 16, 24
Data 0,-16, 24
Data 48, 0,-24
Data -48, 0,-24
Data 24,-16,-24
Data -24,-16,-24
Data 24, 16,-24
Data -24, 16,-24
Data -32, 0,-24
Data 32, 0,-24
Data 8, 8,-24
Data -8, 8,-24
Data -8,-8,-24
Data 8,-8,-24

Math SCALE vertices(), 2.0, vertices()

' Face counts
Dim INTEGER facecount( 6)
For i = 0 To 6
 Read facecount(i)
Next i

Data 3,4,4,4,4,3,6

' Face vertex indices ( 28total)
Dim INTEGER faces( 27)
For i = 0 To 27
 Read faces(i)
Next i

Data 1,7,8
Data 0,1,8,4
Data 0,3,7,1
Data 0,2,6,4
Data 0,3,5,2
Data 2,5,6
Data 7,8,4,6,5,3

' Colors
Dim INTEGER colours(4)
colours(0) = RGB(180,180,200)
colours(1) = RGB(100,100,120)
colours(2) = RGB(200,50,50)
colours(3) = RGB(60,60,80)
colours(4) = RGB(150,150,170)

Dim INTEGER edge( 6)
For i = 0 To 6
 edge(i) = 3
Next i

Dim INTEGER fill( 6)
For i = 0 To 6
 fill(i) = i Mod 5
Next i

Draw3D CREATE n, nv, nf, camera, vertices(), facecount(), faces(), colours(), edge(), fill()

' Setup camera
Dim INTEGER viewplane = 600
Draw3D CAMERA camera, viewplane, 0, 0

' Animation variables
Dim FLOAT yaw = 0, pitch = 0, roll = 0
Dim FLOAT q1(4)

CLS

' Main display loop
Do
 Math Q_EULER yaw, pitch, roll, q1()
 Inc yaw, Rad(1)
 Inc pitch, Rad(2)
 Inc roll, Rad(0.5)
 Draw3D ROTATE q1(), 1
 Draw3D SHOW 1, 0, 0, 1000, 1
 Pause 100
Loop

.SHIP_VIPER

EQUB 0                 \ Max. canisters on demise = 0
EQUW 75 * 75           \ Targetable area          = 75 * 75

EQUB LO(SHIP_VIPER_EDGES - SHIP_VIPER)            \ Edges data offset (low)
EQUB LO(SHIP_VIPER_FACES - SHIP_VIPER)            \ Faces data offset (low)

EQUB 77                \ Max. edge count          = (77 - 1) / 4 = 19
EQUB 0                 \ Gun vertex               = 0
EQUB 42                \ Explosion count          = 9, as (4 * n) + 6 = 42
EQUB 90                \ Number of vertices       = 90 / 6 = 15
EQUB 20                \ Number of edges          = 20
EQUW 0                 \ Bounty                   = 0
EQUB 28                \ Number of faces          = 28 / 4 = 7
EQUB 23                \ Visibility distance      = 23
EQUB 120               \ Max. energy              = 120
EQUB 32                \ Max. speed               = 32

EQUB HI(SHIP_VIPER_EDGES - SHIP_VIPER)            \ Edges data offset (high)
EQUB HI(SHIP_VIPER_FACES - SHIP_VIPER)            \ Faces data offset (high)

EQUB 1                 \ Normals are scaled by    = 2^1 = 2
EQUB %00010001         \ Laser power              = 2
                       \ Missiles                 = 1

.SHIP_VIPER_VERTICES

     \    x,    y,    z, face1, face2, face3, face4, visibility
VERTEX    0,    0,   72,     1,      2,    3,     4,         31    \ Vertex 0
VERTEX    0,   16,   24,     0,      1,    2,     2,         30    \ Vertex 1
VERTEX    0,  -16,   24,     3,      4,    5,     5,         30    \ Vertex 2
VERTEX   48,    0,  -24,     2,      4,    6,     6,         31    \ Vertex 3
VERTEX  -48,    0,  -24,     1,      3,    6,     6,         31    \ Vertex 4
VERTEX   24,  -16,  -24,     4,      5,    6,     6,         30    \ Vertex 5
VERTEX  -24,  -16,  -24,     5,      3,    6,     6,         30    \ Vertex 6
VERTEX   24,   16,  -24,     0,      2,    6,     6,         31    \ Vertex 7
VERTEX  -24,   16,  -24,     0,      1,    6,     6,         31    \ Vertex 8
VERTEX  -32,    0,  -24,     6,      6,    6,     6,         19    \ Vertex 9
VERTEX   32,    0,  -24,     6,      6,    6,     6,         19    \ Vertex 10
VERTEX    8,    8,  -24,     6,      6,    6,     6,         19    \ Vertex 11
VERTEX   -8,    8,  -24,     6,      6,    6,     6,         19    \ Vertex 12
VERTEX   -8,   -8,  -24,     6,      6,    6,     6,         18    \ Vertex 13
VERTEX    8,   -8,  -24,     6,      6,    6,     6,         18    \ Vertex 14

.SHIP_VIPER_EDGES

   \ vertex1, vertex2, face1, face2, visibility
EDGE       0,       3,     2,     4,         31    \ Edge 0
EDGE       0,       1,     1,     2,         30    \ Edge 1
EDGE       0,       2,     3,     4,         30    \ Edge 2
EDGE       0,       4,     1,     3,         31    \ Edge 3
EDGE       1,       7,     0,     2,         30    \ Edge 4
EDGE       1,       8,     0,     1,         30    \ Edge 5
EDGE       2,       5,     4,     5,         30    \ Edge 6
EDGE       2,       6,     3,     5,         30    \ Edge 7
EDGE       7,       8,     0,     6,         31    \ Edge 8
EDGE       5,       6,     5,     6,         30    \ Edge 9
EDGE       4,       8,     1,     6,         31    \ Edge 10
EDGE       4,       6,     3,     6,         30    \ Edge 11
EDGE       3,       7,     2,     6,         31    \ Edge 12
EDGE       3,       5,     6,     4,         30    \ Edge 13
EDGE       9,      12,     6,     6,         19    \ Edge 14
EDGE       9,      13,     6,     6,         18    \ Edge 15
EDGE      10,      11,     6,     6,         19    \ Edge 16
EDGE      10,      14,     6,     6,         18    \ Edge 17
EDGE      11,      14,     6,     6,         16    \ Edge 18
EDGE      12,      13,     6,     6,         16    \ Edge 19

.SHIP_VIPER_FACES

   \ normal_x, normal_y, normal_z, visibility
FACE        0,       32,        0,         31      \ Face 0
FACE      -22,       33,       11,         31      \ Face 1
FACE       22,       33,       11,         31      \ Face 2
FACE      -22,      -33,       11,         31      \ Face 3
FACE       22,      -33,       11,         31      \ Face 4
FACE        0,      -32,        0,         31      \ Face 5
FACE        0,        0,      -48,         31      \ Face 6

Edited 2025-09-30 18:46 by matherp
 
Posted: 09:11am
30 Sep 2025
Copy link to clipboard
Volhout
Guru

Wauw,

About a year ago I tried to manually convert (using the 6502 data) a Cobra to PicoMite, controlling it with a nunchuck (3 axis sensors). I never got it right, there was always some surface transparent, or a rib sticking out. So I never published it.

Now we have a way to convert all elite spacecraft to MMbasic format. Great...

Thanks,

Volhout

P.S. I added use of framebuffer F to avoid glitches.
Edited 2025-09-30 19:19 by Volhout
 
Posted: 10:28am
30 Sep 2025
Copy link to clipboard
dddns
Guru

Many thanks for this! Very nice entry point to look in to.
And fantastic MMBasic as the processing speed is overwhelming.
The example above doesn't look that attractive, this shows the beauty better:

Option BASE 0
MODE 2
FRAMEBUFFER create
FRAMEBUFFER write f
Dim n = 1
Dim nv = 15
Dim nf = 7
Dim camera = 1

' Vertex data
Dim FLOAT vertices(2, 14)
For i = 0 To 14
Read vertices(0,i), vertices(1,i), vertices(2,i)
Next i

Data 0, 0, 72
Data 0, 16, 24
Data 0,-16, 24
Data 48, 0,-24
Data -48, 0,-24
Data 24,-16,-24
Data -24,-16,-24
Data 24, 16,-24
Data -24, 16,-24
Data -32, 0,-24
Data 32, 0,-24
Data 8, 8,-24
Data -8, 8,-24
Data -8,-8,-24
Data 8,-8,-24

Math SCALE vertices(), 2.0, vertices()

' Face counts
Dim INTEGER facecount( 6)
For i = 0 To 6
Read facecount(i)
Next i

Data 3,4,4,4,4,3,6

' Face vertex indices ( 28total)
Dim INTEGER faces( 27)
For i = 0 To 27
Read faces(i)
Next i

Data 1,7,8
Data 0,1,8,4
Data 0,3,7,1
Data 0,2,6,4
Data 0,3,5,2
Data 2,5,6
Data 7,8,4,6,5,3

' Colors
Dim INTEGER colours(5)
colours(0) = RGB(red)
colours(1) = RGB(blue)
colours(2) = RGB(green)
colours(3) = RGB(pink)
colours(4) = RGB(brown)
colours(5) = RGB(white)
Dim INTEGER edge( 6)
For i = 0 To 6
edge(i) = 3
Next i

Dim INTEGER fill( 6)
For i = 0 To 6
fill(i) = i Mod 5
Next i

Draw3D CREATE n, nv, nf, camera, vertices(), facecount(), faces(), colours(), edge(), fill()

' Setup camera
Dim INTEGER viewplane = 600
Draw3D CAMERA camera, viewplane, 0, 0

' Animation variables
Dim FLOAT yaw = 0, pitch = 0, roll = 0
Dim FLOAT q1(4)

CLS
Colour RGB(white),RGB(white)
' Main display loop
Do
Math Q_EULER yaw, pitch, roll, q1()
Inc yaw, Rad(1)
Inc pitch, Rad(2)
Inc roll, Rad(0.5)
Draw3D ROTATE q1(), 1
Draw3D SHOW 1, 0, 0, 1000, 1
Pause 20
FRAMEBUFFER copy f,n
Loop

.SHIP_VIPER

EQUB 0                 \ Max. canisters on demise = 0
EQUW 75 * 75           \ Targetable area          = 75 * 75

EQUB LO(SHIP_VIPER_EDGES - SHIP_VIPER)            \ Edges data offset(low)
EQUB LO(SHIP_VIPER_FACES - SHIP_VIPER)            \ Faces data offset(low)

EQUB 77                \ Max. edge count          = (77 - 1) / 4 = 19
EQUB 0                 \ Gun vertex               = 0
EQUB 42                \ Explosion count          = 9, As (4 * n) + 6 = 42
EQUB 90                \ Number of vertices       = 90 / 6 = 15
EQUB 20                \ Number of edges          = 20
EQUW 0                 \ Bounty                   = 0
EQUB 28                \ Number of faces          = 28 / 4 = 7
EQUB 23                \ Visibility distance      = 23
EQUB 120               \ Max. energy              = 120
EQUB 32                \ Max. speed               = 32

EQUB HI(SHIP_VIPER_EDGES - SHIP_VIPER)            \ Edges data offset(high)
EQUB HI(SHIP_VIPER_FACES - SHIP_VIPER)            \ Faces data offset(high)

EQUB 1                 \ Normals are scaled by    = 2^1 = 2
EQUB %00010001         \ Laser power              = 2
                      \ Missiles                 = 1

.SHIP_VIPER_VERTICES

    \    x,    y,    z, face1, face2, face3, face4, visibility
VERTEX    0,    0,   72,     1,      2,    3,     4,         31    \ Vertex 0
VERTEX    0,   16,   24,     0,      1,    2,     2,         30    \ Vertex 1
VERTEX    0,  -16,   24,     3,      4,    5,     5,         30    \ Vertex 2
VERTEX   48,    0,  -24,     2,      4,    6,     6,         31    \ Vertex 3
VERTEX  -48,    0,  -24,     1,      3,    6,     6,         31    \ Vertex 4
VERTEX   24,  -16,  -24,     4,      5,    6,     6,         30    \ Vertex 5
VERTEX  -24,  -16,  -24,     5,      3,    6,     6,         30    \ Vertex 6
VERTEX   24,   16,  -24,     0,      2,    6,     6,         31    \ Vertex 7
VERTEX  -24,   16,  -24,     0,      1,    6,     6,         31    \ Vertex 8
VERTEX  -32,    0,  -24,     6,      6,    6,     6,         19    \ Vertex 9
VERTEX   32,    0,  -24,     6,      6,    6,     6,         19    \ Vertex 10
VERTEX    8,    8,  -24,     6,      6,    6,     6,         19    \ Vertex 11
VERTEX   -8,    8,  -24,     6,      6,    6,     6,         19    \ Vertex 12
VERTEX   -8,   -8,  -24,     6,      6,    6,     6,         18    \ Vertex 13
VERTEX    8,   -8,  -24,     6,      6,    6,     6,         18    \ Vertex 14

.SHIP_VIPER_EDGES

  \ vertex1, vertex2, face1, face2, visibility
EDGE       0,       3,     2,     4,         31    \ Edge 0
EDGE       0,       1,     1,     2,         30    \ Edge 1
EDGE       0,       2,     3,     4,         30    \ Edge 2
EDGE       0,       4,     1,     3,         31    \ Edge 3
EDGE       1,       7,     0,     2,         30    \ Edge 4
EDGE       1,       8,     0,     1,         30    \ Edge 5
EDGE       2,       5,     4,     5,         30    \ Edge 6
EDGE       2,       6,     3,     5,         30    \ Edge 7
EDGE       7,       8,     0,     6,         31    \ Edge 8
EDGE       5,       6,     5,     6,         30    \ Edge 9
EDGE       4,       8,     1,     6,         31    \ Edge 10
EDGE       4,       6,     3,     6,         30    \ Edge 11
EDGE       3,       7,     2,     6,         31    \ Edge 12
EDGE       3,       5,     6,     4,         30    \ Edge 13
EDGE       9,      12,     6,     6,         19    \ Edge 14
EDGE       9,      13,     6,     6,         18    \ Edge 15
EDGE      10,      11,     6,     6,         19    \ Edge 16
EDGE      10,      14,     6,     6,         18    \ Edge 17
EDGE      11,      14,     6,     6,         16    \ Edge 18
EDGE      12,      13,     6,     6,         16    \ Edge 19

.SHIP_VIPER_FACES

  \ normal_x, normal_y, normal_z, visibility
FACE        0,       32,        0,         31      \ Face 0
FACE      -22,       33,       11,         31      \ Face 1
FACE       22,       33,       11,         31      \ Face 2
FACE      -22,      -33,       11,         31      \ Face 3
FACE       22,      -33,       11,         31      \ Face 4
FACE        0,      -32,        0,         31      \ Face 5
FACE        0,        0,      -48,         31      \ Face 6
 
Posted: 10:31am
30 Sep 2025
Copy link to clipboard
dddns
Guru

I tried to set the background colour of the 3d drawing from black to white without looking into the manual and failed. Is there a way?

p.s in MODE 3 with PSRAM it looks breathtaking nice!!
Edited 2025-09-30 20:44 by dddns
 
Posted: 10:55am
30 Sep 2025
Copy link to clipboard
dddns
Guru

Sorry, don't want to spoil your thread, I made a mistake.
This is all you need:
Option BASE 0
MODE 2
FRAMEBUFFER create
FRAMEBUFFER write f
Dim n = 1
Dim nv = 15
Dim nf = 7
Dim camera = 1

' Vertex data
Dim FLOAT vertices(2, 14)
For i = 0 To 14
Read vertices(0,i), vertices(1,i), vertices(2,i)
Next i

Data 0, 0, 72
Data 0, 16, 24
Data 0,-16, 24
Data 48, 0,-24
Data -48, 0,-24
Data 24,-16,-24
Data -24,-16,-24
Data 24, 16,-24
Data -24, 16,-24
Data -32, 0,-24
Data 32, 0,-24
Data 8, 8,-24
Data -8, 8,-24
Data -8,-8,-24
Data 8,-8,-24

Math SCALE vertices(), 2.0, vertices()

' Face counts
Dim INTEGER facecount( 6)
For i = 0 To 6
Read facecount(i)
Next i

Data 3,4,4,4,4,3,6

' Face vertex indices ( 28total)
Dim INTEGER faces( 27)
For i = 0 To 27
Read faces(i)
Next i

Data 1,7,8
Data 0,1,8,4
Data 0,3,7,1
Data 0,2,6,4
Data 0,3,5,2
Data 2,5,6
Data 7,8,4,6,5,3

' Colors
Dim INTEGER colours(4)
colours(0) = RGB(red)
colours(1) = RGB(blue)
colours(2) = RGB(green)
colours(3) = RGB(pink)
colours(4) = RGB(brown)
Dim INTEGER edge( 6)
For i = 0 To 6
edge(i) = 3
Next i

Dim INTEGER fill( 6)
For i = 0 To 6
fill(i) = i Mod 5
Next i

Draw3D CREATE n, nv, nf, camera, vertices(), facecount(), faces(), colours(), edge(), fill()

' Setup camera
Dim INTEGER viewplane = 600
Draw3D CAMERA camera, viewplane, 0, 0

' Animation variables
Dim FLOAT yaw = 0, pitch = 0, roll = 0
Dim FLOAT q1(4)

CLS
Colour RGB(white),RGB(white)
' Main display loop
Do
Math Q_EULER yaw, pitch, roll, q1()
Inc yaw, Rad(1)
Inc pitch, Rad(2)
Inc roll, Rad(0.5)
Draw3D ROTATE q1(), 1
Draw3D SHOW 1, 0, 0, 1000, 1
Pause 20
FRAMEBUFFER copy f,n
Loop
 
Posted: 11:05am
30 Sep 2025
Copy link to clipboard
matherp
Guru

I'm still working with Claude on this. There is a bug/feature in the ELITE definitions that causes some models not to convert properly. This is work-in-progress to fix but I've run out of credit on Claude for the moment. The issue is that some of the models have coincident vertices so there are an unbalanced number of vertices and edges which is impossible. Cobra Mk3 is an example of this so the 3d rendering isn't correct.
 
Posted: 11:17am
30 Sep 2025
Copy link to clipboard
dddns
Guru

Thank you for the development, having a "nice looking future"!
 
Posted: 01:12pm
30 Sep 2025
Copy link to clipboard
matherp
Guru

Still not getting there

  Quote  The conversion runs correctly but we are back to the rendering issue with hidden faces showing and top faces missing exactly like before. As before viper renders correctly. The issue is in your interpretation of the cobra blueprint. The difference is that cobra has co-planer faces overlapping the back face representing guns and jets. This isn't being handled correctly

  Quote  
Ah! Now I understand the real issue. The Cobra has co-planar faces - like the back face representing the engine section, and then overlapping smaller faces on that same plane representing guns and jets/exhausts.

When edges are shared between two faces that are on the SAME face (ef1 and ef2 are the same), those are NOT outline edges - they're internal edges that divide one large face into smaller sections.

The current code only uses edges where ef1(i) <> ef2(i) (outline edges). But for co-planar overlapping faces, the dividing edges between them will have ef1(i) = ef2(i) because they're both part of the same logical face from the game's perspective.

Looking at Face 9 which had 19 vertices but only 7 edges - that's probably the large back face. The 19 vertices include vertices from all the sub-faces (guns, jets) on that same plane. The 7 edges are just the outline of the whole back face.

The smaller faces (guns, jets) need to be built separately, and they'll use edges where BOTH faces reference face 9 (the back face).

So the logic needs to be:

   For each face, use ALL edges that reference it (not just outline edges)
   Build multiple polygons per face if needed
   Handle both outline edges (ef1 != ef2) and internal edges (ef1 = ef2 = this face)

Let me revise the approach to handle co-planar overlapping faces correctly.

Ah! Now I understand the actual problem. Co-planar overlapping faces like guns and jets on the back of the Cobra!

When edges have ef1 = ef2 = 9 (both faces the same), those are internal dividing edges that separate the large back face from the smaller gun/jet faces that sit on the same plane. The current code ignores these because it only uses edges where ef1 != ef2.

Face 9 has 19 vertices (including all the gun/jet vertices) but only 7 outline edges. The gun and jet faces need to be built using edges where both face references are 9.

Let me fix this to handle co-planar overlapping faces:
Elite Ship Converter v2.6 (Fixed)
Code ∙ Version 6

I've created version 2.9 that handles co-planar overlapping faces. The key insight is:

When ef1 = ef2 = 9 (both face references are the same), those edges divide Face 9 into multiple separate closed loops - the outer boundary of the back face, plus the interior gun and jet details.

The converter now:

   Finds ALL edges for each face (outline where ef1 ≠ ef2 AND internal where ef1 = ef2)
   Traces multiple closed loops from these edges
   Outputs each loop as a separate face to DRAW3D

This should split the problematic Face 9 into multiple separate polygons - the outer back face and the interior gun/jet cutouts - so they render correctly without hiding each other.

Run this and let me know what the output shows for Face 9!
 
Posted: 06:57pm
30 Sep 2025
Copy link to clipboard
matherp
Guru

That has been a genuinely interesting day. There is precisely NO WAY I could have solved this by myself - pretty humbling.

To make it work a modification to the MMBasic firmware has been needed to add an new feature. Rendering of the 3D object works by Painters algorithm. This works by sorting the faces and drawing those further away first. In addition the MMBasic firmware supports "back face culling". This doesn't draw faces that are pointing away from the camera. In order to sort the faces the current MMbasic code uses the Z axis position of the face centroid. This works in many situations but in some 3D models it is better to use the furthest vertex and sort on that. I've added that as an option to DRAW3D SHOW and DRAW3D WRITE
DRAW3D SHOW n, x, y, z [,nocull][,usefurthest]
DRAW3D WRITE n, x, y, z [,nocull][,usefurthest]

set 1 to use the furthest vertex and zero (default) to use the centroid.

The model conversion code is now as below. I've also attached the demo of the Cobra ship. You will need an update to the firmware to run this - attached for the RP2350 PicoMite. I'll add this to all the versions in the next beta.
Have a play with the conversion program and try other models as in the elite source - let me know how you get on.

Demo video here

PicoMite.zip

This code was written by the convert program below
' Elite Ship - Converter v2.9.7 (Origin-based normals)
Option BASE 0
Dim n=1,nv= 28,nf= 17,camera=1
Dim FLOAT vertices(2, 27)
For i=0 To 27
 Read vertices(0,i),vertices(1,i),vertices(2,i)
Next i

Data 32, 0, 76
Data -32, 0, 76
Data 0, 26, 24
Data -120,-3,-8
Data 120,-3,-8
Data -88, 16,-40
Data 88, 16,-40
Data 128,-8,-40
Data -128,-8,-40
Data 0, 26,-40
Data -32,-24,-40
Data 32,-24,-40
Data -36, 8,-40
Data -8, 12,-40
Data 8, 12,-40
Data 36, 8,-40
Data 36,-12,-40
Data 8,-16,-40
Data -8,-16,-40
Data -36,-12,-40
Data 0, 0, 76
Data 0, 0, 90
Data -80,-6,-40
Data -80, 6,-40
Data -88, 0,-40
Data 80, 6,-40
Data 88, 0,-40
Data 80,-6,-40

Math SCALE vertices(),2.0,vertices()
Dim INTEGER facecount( 16)
For i=0 To 16
 Read facecount(i)
Next i

Data 3,3,3,3,3,3,3,3,3,7
Data 4,4,3,3,4,4,4

Dim INTEGER faces( 59)
For i=0 To 59
 Read faces(i)
Next i

Data 2,1,0
Data 2,5,1
Data 0,6,2
Data 5,3,1
Data 0,4,6
Data 2,9,5
Data 6,9,2
Data 5,8,3
Data 4,7,6
Data 6,7,11,10,8,5,9
Data 12,13,18,19
Data 14,15,16,17
Data 22,24,23
Data 25,26,27
Data 1,3,8,10
Data 0,1,10,11
Data 11,7,4,0

Dim INTEGER colours(4),edge( 16),fill( 16)
colours(0)=RGB(180,180,200):colours(1)=RGB(100,100,120)
colours(2)=RGB(200,50,50):colours(3)=RGB(60,60,80):colours(4)=RGB(150,150,170)
For i=0 To 16
 edge(i)=3:fill(i)=i Mod 5
Next i
Draw3D CREATE n,nv,nf,camera,vertices(),facecount(),faces(),colours(),edge(),fill()
Dim INTEGER viewplane=600
Draw3D CAMERA camera,viewplane,0,0
Dim FLOAT yaw=0,pitch=0,roll=0,q1(4)
CLS
Do
 Math Q_EULER yaw,pitch,roll,q1()
 Inc yaw,Rad(1):Inc pitch,Rad(2):Inc roll,Rad(0.5)
 Draw3D ROTATE q1(),1
 Draw3D SHOW 1,0,0,1000,0,1
 Pause 100
Loop


This is the COBRA model from https://elite.bbcelite.com/deep_dives/ship_blueprints.html
.SHIP_COBRA_MK_3

EQUB 3                 \ Max. canisters on demise = 3
EQUW 95 * 95           \ Targetable area          = 95 * 95

EQUB LO(SHIP_COBRA_MK_3_EDGES - SHIP_COBRA_MK_3)  \ Edges data offset (low)
EQUB LO(SHIP_COBRA_MK_3_FACES - SHIP_COBRA_MK_3)  \ Faces data offset (low)

EQUB 153               \ Max. edge count          = (153 - 1) / 4 = 38
EQUB 84                \ Gun vertex               = 84 / 4 = 21
EQUB 42                \ Explosion count          = 9, as (4 * n) + 6 = 42
EQUB 168               \ Number of vertices       = 168 / 6 = 28
EQUB 38                \ Number of edges          = 38
EQUW 0                 \ Bounty                   = 0
EQUB 52                \ Number of faces          = 52 / 4 = 13
EQUB 50                \ Visibility distance      = 50
EQUB 150               \ Max. energy              = 150
EQUB 28                \ Max. speed               = 28

EQUB HI(SHIP_COBRA_MK_3_EDGES - SHIP_COBRA_MK_3)  \ Edges data offset (low)
EQUB HI(SHIP_COBRA_MK_3_FACES - SHIP_COBRA_MK_3)  \ Faces data offset (low)

EQUB 1                 \ Normals are scaled by    = 2^1 = 2
EQUB %00010011         \ Laser power              = 2
                       \ Missiles                 = 3

.SHIP_COBRA_MK_3_VERTICES

     \    x,    y,    z, face1, face2, face3, face4, visibility
VERTEX   32,    0,   76,    15,     15,   15,    15,         31    \ Vertex 0
VERTEX  -32,    0,   76,    15,     15,   15,    15,         31    \ Vertex 1
VERTEX    0,   26,   24,    15,     15,   15,    15,         31    \ Vertex 2
VERTEX -120,   -3,   -8,     3,      7,   10,    10,         31    \ Vertex 3
VERTEX  120,   -3,   -8,     4,      8,   12,    12,         31    \ Vertex 4
VERTEX  -88,   16,  -40,    15,     15,   15,    15,         31    \ Vertex 5
VERTEX   88,   16,  -40,    15,     15,   15,    15,         31    \ Vertex 6
VERTEX  128,   -8,  -40,     8,      9,   12,    12,         31    \ Vertex 7
VERTEX -128,   -8,  -40,     7,      9,   10,    10,         31    \ Vertex 8
VERTEX    0,   26,  -40,     5,      6,    9,     9,         31    \ Vertex 9
VERTEX  -32,  -24,  -40,     9,     10,   11,    11,         31    \ Vertex 10
VERTEX   32,  -24,  -40,     9,     11,   12,    12,         31    \ Vertex 11
VERTEX  -36,    8,  -40,     9,      9,    9,     9,         20    \ Vertex 12
VERTEX   -8,   12,  -40,     9,      9,    9,     9,         20    \ Vertex 13
VERTEX    8,   12,  -40,     9,      9,    9,     9,         20    \ Vertex 14
VERTEX   36,    8,  -40,     9,      9,    9,     9,         20    \ Vertex 15
VERTEX   36,  -12,  -40,     9,      9,    9,     9,         20    \ Vertex 16
VERTEX    8,  -16,  -40,     9,      9,    9,     9,         20    \ Vertex 17
VERTEX   -8,  -16,  -40,     9,      9,    9,     9,         20    \ Vertex 18
VERTEX  -36,  -12,  -40,     9,      9,    9,     9,         20    \ Vertex 19
VERTEX    0,    0,   76,     0,     11,   11,    11,          6    \ Vertex 20
VERTEX    0,    0,   90,     0,     11,   11,    11,         31    \ Vertex 21
VERTEX  -80,   -6,  -40,     9,      9,    9,     9,          8    \ Vertex 22
VERTEX  -80,    6,  -40,     9,      9,    9,     9,          8    \ Vertex 23
VERTEX  -88,    0,  -40,     9,      9,    9,     9,          6    \ Vertex 24
VERTEX   80,    6,  -40,     9,      9,    9,     9,          8    \ Vertex 25
VERTEX   88,    0,  -40,     9,      9,    9,     9,          6    \ Vertex 26
VERTEX   80,   -6,  -40,     9,      9,    9,     9,          8    \ Vertex 27

.SHIP_COBRA_MK_3_EDGES

   \ vertex1, vertex2, face1, face2, visibility
EDGE       0,       1,     0,    11,         31    \ Edge 0
EDGE       0,       4,     4,    12,         31    \ Edge 1
EDGE       1,       3,     3,    10,         31    \ Edge 2
EDGE       3,       8,     7,    10,         31    \ Edge 3
EDGE       4,       7,     8,    12,         31    \ Edge 4
EDGE       6,       7,     8,     9,         31    \ Edge 5
EDGE       6,       9,     6,     9,         31    \ Edge 6
EDGE       5,       9,     5,     9,         31    \ Edge 7
EDGE       5,       8,     7,     9,         31    \ Edge 8
EDGE       2,       5,     1,     5,         31    \ Edge 9
EDGE       2,       6,     2,     6,         31    \ Edge 10
EDGE       3,       5,     3,     7,         31    \ Edge 11
EDGE       4,       6,     4,     8,         31    \ Edge 12
EDGE       1,       2,     0,     1,         31    \ Edge 13
EDGE       0,       2,     0,     2,         31    \ Edge 14
EDGE       8,      10,     9,    10,         31    \ Edge 15
EDGE      10,      11,     9,    11,         31    \ Edge 16
EDGE       7,      11,     9,    12,         31    \ Edge 17
EDGE       1,      10,    10,    11,         31    \ Edge 18
EDGE       0,      11,    11,    12,         31    \ Edge 19
EDGE       1,       5,     1,     3,         29    \ Edge 20
EDGE       0,       6,     2,     4,         29    \ Edge 21
EDGE      20,      21,     0,    11,          6    \ Edge 22
EDGE      12,      13,     9,     9,         20    \ Edge 23
EDGE      18,      19,     9,     9,         20    \ Edge 24
EDGE      14,      15,     9,     9,         20    \ Edge 25
EDGE      16,      17,     9,     9,         20    \ Edge 26
EDGE      15,      16,     9,     9,         19    \ Edge 27
EDGE      14,      17,     9,     9,         17    \ Edge 28
EDGE      13,      18,     9,     9,         19    \ Edge 29
EDGE      12,      19,     9,     9,         19    \ Edge 30
EDGE       2,       9,     5,     6,         30    \ Edge 31
EDGE      22,      24,     9,     9,          6    \ Edge 32
EDGE      23,      24,     9,     9,          6    \ Edge 33
EDGE      22,      23,     9,     9,          8    \ Edge 34
EDGE      25,      26,     9,     9,          6    \ Edge 35
EDGE      26,      27,     9,     9,          6    \ Edge 36
EDGE      25,      27,     9,     9,          8    \ Edge 37

.SHIP_COBRA_MK_3_FACES

   \ normal_x, normal_y, normal_z, visibility
FACE        0,       62,       31,         31      \ Face 0
FACE      -18,       55,       16,         31      \ Face 1
FACE       18,       55,       16,         31      \ Face 2
FACE      -16,       52,       14,         31      \ Face 3
FACE       16,       52,       14,         31      \ Face 4
FACE      -14,       47,        0,         31      \ Face 5
FACE       14,       47,        0,         31      \ Face 6
FACE      -61,      102,        0,         31      \ Face 7
FACE       61,      102,        0,         31      \ Face 8
FACE        0,        0,      -80,         31      \ Face 9
FACE       -7,      -42,        9,         31      \ Face 10
FACE        0,      -30,        6,         31      \ Face 11
FACE        7,      -42,        9,         31      \ Face 12


And this is the conversion program written by CLAUDE. There are all sort of sophisticated elements in this to handle coplanar faces, to correct the face normal vector to be pointing away from the origin and just to read the source above. It really is very impressive
' Elite Ship Blueprint to MMBasic DRAW3D Converter
' Version 2.9.7 - FIXED: Normals face outward from origin
Option EXPLICIT
Option BASE 0
' === ALL VARIABLE DECLARATIONS ===
Dim filename$ LENGTH 50, outfile$ LENGTH 50
Dim txt$ LENGTH 200, rest$ LENGTH 200, s$ LENGTH 200
Dim INTEGER nv, ne, nf, i, j, k, p1, p2, f1, f2, dotpos
Dim INTEGER totalidx, curr, nextv, found, startv, temp, mergeCount
Dim INTEGER nTotalSubfaces, totalSubfaceVerts, maxIter
Dim FLOAT vx(100), vy(100), vz(100)
Dim INTEGER vf1(100), vf2(100), vf3(100), vf4(100)
Dim FLOAT fnx(100), fny(100), fnz(100)
Dim INTEGER e1(100), e2(100), ef1(100), ef2(100)
Dim INTEGER facevert(100, 20), facecount(100)
Dim INTEGER edgeused(100), faceedges(50), nfaceedges
Dim INTEGER allSubfaceVerts(500), allSubfaceCount(200)
Dim INTEGER nOutline, nInternal, useGeometric, hasInternal
Dim FLOAT faceZ, zTolerance
Dim INTEGER hasZ, zCount, filteredCount, includeEdge
Dim INTEGER loopVerts(20), nLoopVerts, loopsFound
Dim INTEGER reversedCount, vIdx
Dim INTEGER v0wind, v1wind, v2wind
Dim FLOAT v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z
Dim FLOAT e1x,e1y,e1z,e2x,e2y,e2z,nx,ny,nz,dot
Dim FLOAT fcx, fcy, fcz
Dim INTEGER v0chk, face9Start, face9Count
Input "Enter ship blueprint filename: ", filename$
If filename$ = "" Then End
dotpos = Instr(filename$, ".")
If dotpos > 0 Then
 outfile$ = Left$(filename$, dotpos-1) + ".bas"
Else
 outfile$ = filename$ + ".bas"
EndIf
Open filename$ For INPUT As #1
Do While Not Eof(#1)
 Line Input #1, txt$
 If Instr(txt$, "_VERTICES") > 0 Then Exit
Loop
nv = 0
Do While Not Eof(#1)
 Line Input #1, txt$
 txt$ = Trim$(txt$, " " + Chr$(9))
 If Instr(txt$, "_EDGES") > 0 Then Exit
 If Left$(txt$, 6) = "VERTEX" Then
   p1 = Instr(txt$, " ")
   rest$ = Trim$(Mid$(txt$, p1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   vx(nv) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   vy(nv) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   vz(nv) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   vf1(nv) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   vf2(nv) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   vf3(nv) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   vf4(nv) = Val(Left$(rest$, p2-1))
   nv = nv + 1
 EndIf
Loop
ne = 0
Do While Not Eof(#1)
 Line Input #1, txt$
 txt$ = Trim$(txt$, " " + Chr$(9))
 If Instr(txt$, "_FACES") > 0 Then Exit
 If Left$(txt$, 4) = "EDGE" Then
   p1 = Instr(txt$, " ")
   rest$ = Trim$(Mid$(txt$, p1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   e1(ne) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   e2(ne) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   ef1(ne) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   ef2(ne) = Val(Left$(rest$, p2-1))
   ne = ne + 1
 EndIf
Loop
nf = 0
Do While Not Eof(#1)
 Line Input #1, txt$
 txt$ = Trim$(txt$, " " + Chr$(9))
 If Left$(txt$, 4) = "FACE" Then
   p1 = Instr(txt$, " ")
   rest$ = Trim$(Mid$(txt$, p1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   fnx(nf) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   fny(nf) = Val(Left$(rest$, p2-1))
   rest$ = Trim$(Mid$(rest$, p2+1), " " + Chr$(9))
   p2 = Instr(rest$, ",")
   fnz(nf) = Val(Left$(rest$, p2-1))
   nf = nf + 1
 EndIf
Loop
Close #1
Print "Elite Ship Converter v2.9.7"
Print "Found"; nv; "vertices,"; ne; "edges,"; nf; "faces"
Print
For i = 0 To nf-1
 facecount(i) = 0
Next i
nTotalSubfaces = 0
totalSubfaceVerts = 0
Print "Building faces (hybrid: on-plane outline + internal edges)..."
For f1 = 0 To nf-1
 For i = 0 To ne-1
   edgeused(i) = 0
 Next i
 useGeometric = 0
 hasZ = 0
 zTolerance = 2.0
 hasInternal = 0
 For i = 0 To ne-1
   If ef1(i) = f1 And ef2(i) = f1 Then
     hasInternal = 1
     Exit For
   EndIf
 Next i
 If hasInternal = 1 Then
   zCount = 0
   faceZ = 0
   For i = 0 To ne-1
     If (ef1(i) = f1 Or ef2(i) = f1) Then
       If hasZ = 0 Then
         faceZ = vz(e1(i))
         hasZ = 1
       EndIf
       If Abs(vz(e1(i)) - faceZ) < zTolerance Then zCount = zCount + 1
       If Abs(vz(e2(i)) - faceZ) < zTolerance Then zCount = zCount + 1
     EndIf
   Next i
   If zCount > 10 Then useGeometric = 1
 EndIf
 nfaceedges = 0
 nOutline = 0
 nInternal = 0
 For i = 0 To ne-1
   includeEdge = 0
   If (ef1(i) = f1 Or ef2(i) = f1) Then
     If hasInternal = 1 Then
       If ef1(i) = f1 And ef2(i) = f1 Then
         includeEdge = 1
         nInternal = nInternal + 1
       ElseIf Abs(vz(e1(i)) - faceZ) < zTolerance And Abs(vz(e2(i)) - faceZ) < zTolerance Then
         includeEdge = 1
         nOutline = nOutline + 1
       Else
         nOutline = nOutline + 1
       EndIf
     Else
       includeEdge = 1
       If ef1(i) <> ef2(i) Then
         nOutline = nOutline + 1
       Else
         nInternal = nInternal + 1
       EndIf
     EndIf
   EndIf
   If includeEdge = 1 Then
     faceedges(nfaceedges) = i
     nfaceedges = nfaceedges + 1
   EndIf
 Next i
 Print "  Face"; f1; ":"; nOutline; "outline,"; nInternal; "internal edges";
 If hasInternal = 1 Then
   Print " (hybrid)";
 EndIf
 If nfaceedges = 0 Then
   Print " -> EMPTY"
   Continue For
 EndIf
 loopsFound = 0
 Do While loopsFound < 10
   startv = -1
   For i = 0 To nfaceedges-1
     If edgeused(faceedges(i)) = 0 Then
       startv = e1(faceedges(i))
       curr = e2(faceedges(i))
       edgeused(faceedges(i)) = 1
       Exit For
     EndIf
   Next i
   If startv = -1 Then Exit Do
   loopVerts(0) = startv
   loopVerts(1) = curr
   nLoopVerts = 2
   maxIter = 0
   Do While curr <> startv And nLoopVerts < 20
     maxIter = maxIter + 1
     If maxIter > nfaceedges + 5 Then Exit Do
     found = 0
     For j = 0 To nfaceedges-1
       i = faceedges(j)
       If edgeused(i) = 0 Then
         If e1(i) = curr Then
           nextv = e2(i)
           found = 1
         ElseIf e2(i) = curr Then
           nextv = e1(i)
           found = 1
         EndIf
         If found = 1 Then
           If nextv <> startv Then
             loopVerts(nLoopVerts) = nextv
             nLoopVerts = nLoopVerts + 1
           EndIf
           edgeused(i) = 1
           curr = nextv
           Exit For
         EndIf
       EndIf
     Next j
     If found = 0 Then Exit Do
   Loop
   If curr = startv And nLoopVerts >= 3 Then
     allSubfaceCount(nTotalSubfaces) = nLoopVerts
     For i = 0 To nLoopVerts-1
       allSubfaceVerts(totalSubfaceVerts) = loopVerts(i)
       totalSubfaceVerts = totalSubfaceVerts + 1
     Next i
     nTotalSubfaces = nTotalSubfaces + 1
     loopsFound = loopsFound + 1
   EndIf
 Loop
 If loopsFound > 1 Then
   Print " ->"; loopsFound; "subfaces"
 ElseIf loopsFound = 1 Then
   Print " -> OK"
 Else
   Print " -> FAILED"
 EndIf
Next f1
Print
Print "Total subfaces built:"; nTotalSubfaces
Print "Total vertices:"; totalSubfaceVerts
Print
Print "Checking winding direction (outward from origin)..."
reversedCount = 0
vIdx = 0
For i = 0 To nTotalSubfaces-1
 If allSubfaceCount(i) >= 3 Then
   v0wind = allSubfaceVerts(vIdx)
   v1wind = allSubfaceVerts(vIdx+1)
   v2wind = allSubfaceVerts(vIdx+2)
   ' Get first 3 vertices
   v0x=vx(v0wind):v0y=vy(v0wind):v0z=vz(v0wind)
   v1x=vx(v1wind):v1y=vy(v1wind):v1z=vz(v1wind)
   v2x=vx(v2wind):v2y=vy(v2wind):v2z=vz(v2wind)
   ' Calculate face center (average of all verts in this subface)
   fcx = 0: fcy = 0: fcz = 0
   For j = 0 To allSubfaceCount(i)-1
     fcx = fcx + vx(allSubfaceVerts(vIdx+j))
     fcy = fcy + vy(allSubfaceVerts(vIdx+j))
     fcz = fcz + vz(allSubfaceVerts(vIdx+j))
   Next j
   fcx = fcx / allSubfaceCount(i)
   fcy = fcy / allSubfaceCount(i)
   fcz = fcz / allSubfaceCount(i)
   ' Compute winding normal
   e1x=v1x-v0x:e1y=v1y-v0y:e1z=v1z-v0z
   e2x=v2x-v0x:e2y=v2y-v0y:e2z=v2z-v0z
   nx=e1y*e2z-e1z*e2y:ny=e1z*e2x-e1x*e2z:nz=e1x*e2y-e1y*e2x
   ' Check if normal points away from origin (dot with face center)
   dot=nx*fcx+ny*fcy+nz*fcz
   If dot < 0 Then
     ' Normal points inward, reverse winding
     reversedCount = reversedCount + 1
     For j=0 To allSubfaceCount(i)\2-1
       k=allSubfaceCount(i)-1-j
       temp=allSubfaceVerts(vIdx+j)
       allSubfaceVerts(vIdx+j)=allSubfaceVerts(vIdx+k)
       allSubfaceVerts(vIdx+k)=temp
     Next j
   EndIf
   vIdx = vIdx + allSubfaceCount(i)
 Else
   vIdx = vIdx + allSubfaceCount(i)
 EndIf
Next i
Print "Reversed"; reversedCount; "subfaces to face outward"
Print
Print "Writing to: "; outfile$
Open outfile$ For OUTPUT As #2
Print #2, "' Elite Ship - Converter v2.9.7 (Origin-based normals)"
Print #2, "OPTION BASE 0"
Print #2, "DIM n=1,nv=";nv;",nf=";nTotalSubfaces;",camera=1"
Print #2, "DIM FLOAT vertices(2,";nv-1;")"
Print #2, "FOR i=0 TO";nv-1
Print #2, "  READ vertices(0,i),vertices(1,i),vertices(2,i)"
Print #2, "NEXT i"
Print #2, ""
For i = 0 To nv-1
 Print #2, "DATA";vx(i);",";vy(i);",";vz(i)
Next i
Print #2, ""
Print #2, "MATH SCALE vertices(),2.0,vertices()"
Print #2, "DIM INTEGER facecount(";nTotalSubfaces-1;")"
Print #2, "FOR i=0 TO";nTotalSubfaces-1
Print #2, "  READ facecount(i)"
Print #2, "NEXT i"
Print #2, ""
For i = 0 To nTotalSubfaces-1 Step 10
 s$ = "DATA "
 For j = i To Min(i+9, nTotalSubfaces-1)
   s$ = s$ + Str$(allSubfaceCount(j))
   If j < Min(i+9, nTotalSubfaces-1) Then s$ = s$ + ","
 Next j
 Print #2, s$
Next i
Print #2, ""
Print #2, "DIM INTEGER faces(";totalSubfaceVerts-1;")"
Print #2, "FOR i=0 TO";totalSubfaceVerts-1
Print #2, "  READ faces(i)"
Print #2, "NEXT i"
Print #2, ""
vIdx = 0
For i = 0 To nTotalSubfaces-1
 If allSubfaceCount(i) > 0 Then
   s$ = "DATA "
   For j = 0 To allSubfaceCount(i)-1
     s$ = s$ + Str$(allSubfaceVerts(vIdx))
     vIdx = vIdx + 1
     If j < allSubfaceCount(i)-1 Then s$ = s$ + ","
   Next j
   Print #2, s$
 EndIf
Next i
Print #2, ""
Print #2, "DIM INTEGER colours(4),edge(";nTotalSubfaces-1;"),fill(";nTotalSubfaces-1;")"
Print #2, "colours(0)=RGB(180,180,200):colours(1)=RGB(100,100,120)"
Print #2, "colours(2)=RGB(200,50,50):colours(3)=RGB(60,60,80):colours(4)=RGB(150,150,170)"
Print #2, "FOR i=0 TO";nTotalSubfaces-1
Print #2, "  edge(i)=3:fill(i)=i MOD 5"
Print #2, "NEXT i"
Print #2, "DRAW3D CREATE n,nv,nf,camera,vertices(),facecount(),faces(),colours(),edge(),fill()"
Print #2, "DIM INTEGER viewplane=600"
Print #2, "DRAW3D CAMERA camera,viewplane,0,0"
Print #2, "DIM FLOAT yaw=0,pitch=0,roll=0,q1(4)"
Print #2, "CLS"
Print #2, "DO"
Print #2, "  MATH Q_EULER yaw,pitch,roll,q1()"
Print #2, "  INC yaw,RAD(1):INC pitch,RAD(2):INC roll,RAD(0.5)"
Print #2, "  DRAW3D ROTATE q1(),1"
Print #2, "  DRAW3D SHOW 1,0,0,1000,0,1"
Print #2, "  PAUSE 100"
Print #2, "LOOP"
Close #2
Print "Conversion complete!"
End
Function Trim$(s$,c$) As STRING
 Local result$ LENGTH 200
 result$=s$
 Do While Instr(c$,Left$(result$,1))>0 And Len(result$)>0
   result$=Mid$(result$,2)
 Loop
 Do While Instr(c$,Right$(result$,1))>0 And Len(result$)>0
   result$=Left$(result$,Len(result$)-1)
 Loop
 Trim$=result$
End Function

 
Posted: 04:43pm
01 Oct 2025
Copy link to clipboard
LeoNicolas
Guru


matherp, will this change be added to the MMB4W?
 
Posted: 10:38pm
01 Oct 2025
Copy link to clipboard
vegipete
Guru


And CCM2, please?
 


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025