'LightWave 3d Object files loader by Relsoft 'Supports Quads and tris in object. 'Supports Textures 'Loades with or without texture 'All Lightwave 3d models made by DR. DAVIDSTIEN (Thanks!!!) 'SetVideoSeg by PLASMA 'Orig Poly algo by CGI JOE 'I don't have that much of an experience with 3d OBJ files but 'my friend DR. DAVIDSTIEN says this will work with all OBJ files 'made by any 3d modeller as long as they import OBJ files. 'I only tested this with all the models packaged with this article 'as I don't have a 3d card. 'I only made this loader because of one post by the good doctor at 'Qbasicnews.com. 'Bug: Quad textures are sometimes screwed. :*( If you can fix that, it 'would be great and I would be happy to hear from you. 'BTW: I RENAMED ALL .OBJ FILES TO .L3D SO AS NOT TO CONFUSE WITH OBJ FILES 'GENERATED BY QB. 'BE SURE TO SET the PATH$ variable 'Relsoft 2004 'Rel.Betterwebber.com DECLARE SUB Rel.LoadBMP (File$, SwitchPal%) DECLARE SUB TextureTri (ox1%, oy1%, ou1%, ov1%, ox2%, oy2%, ou2%, ov2%, ox3%, oy3%, ou3%, ov3%, TSEG%, TOFF%) DECLARE SUB FindCentroid (Model() AS ANY) DECLARE SUB ScaleModel (M() AS ANY, Scale!) DECLARE SUB LoadLightWaveQ (M() AS ANY, P() AS ANY, File$, TSIZE%) DECLARE SUB DrawModelG (Model() AS ANY, Poly() AS ANY) DECLARE SUB DrawModelT (Model() AS ANY, Poly() AS ANY) DECLARE SUB SortPolys (Model() AS ANY, Poly() AS ANY) DECLARE SUB ShellSort (Poly() AS ANY, Min%, Max%) DECLARE SUB SetPolyBaseColor (Poly() AS ANY) DECLARE SUB GouraudTri (ox1%, oy1%, oc1%, ox2%, oy2%, oc2%, ox3%, oy3%, oc3%) DECLARE SUB GradColor (Col1%, r1%, g1%, b1%, Col2%, r2%, g2%, b2%) DECLARE SUB CalcNormals (Model() AS ANY, Poly() AS ANY, v() AS ANY, v2() AS ANY) DECLARE SUB SetVideoSeg (Segment%) DECLARE SUB RotateAndProject (Model() AS ANY, AngleX%, AngleY%, AngleZ%) DECLARE SUB RotNormals (v() AS ANY, v2() AS ANY, AngleX%, AngleY%, AngleZ%) DEFINT A-Z REM $DYNAMIC TYPE BMPHeaderType 'All values are for 320*200*256 bmp ID AS STRING * 2 '"BM" size AS LONG 'width*heigth+1078= 65078 RSV1 AS INTEGER '0 Reserved RSV2 AS INTEGER '0 Reserved offset AS LONG '1078 First Pixel(Scanline order) HORZ AS LONG '40 WID AS LONG '320 HEI AS LONG '200 PLANES AS INTEGER '1 num of Planes BPP AS INTEGER '8 Bits per Plane COMPRESSION AS LONG '0 IMAGESIZE AS LONG '64000 Width *Height XRES AS LONG '3790 X pels YRES AS LONG '3780 Y pels CLRUSED AS LONG '0 Colors used CLRIMPORT AS LONG '0 Colors Important Pal AS STRING * 1024 'Order=Blue*4, Green*4, Red*4, 0 * 4 END TYPE TYPE Point3d x AS SINGLE 'Normal 3d coords y AS SINGLE z AS SINGLE xr AS SINGLE 'Rotated coords yr AS SINGLE zr AS SINGLE scrx AS INTEGER 'Translated and projected scry AS INTEGER '2d Coords END TYPE TYPE PolyType p1 AS INTEGER 'vertex p2 AS INTEGER p3 AS INTEGER u1 AS INTEGER v1 AS INTEGER u2 AS INTEGER v2 AS INTEGER u3 AS INTEGER v3 AS INTEGER clr AS INTEGER 'Basecolor for translucency idx AS INTEGER zcenter AS INTEGER END TYPE TYPE VectorType 'Normals are vectors x AS SINGLE y AS SINGLE z AS SINGLE END TYPE CONST LENS = 256 'Z CONST XCENTER = 160 '?? CONST YCENTER = 100 '?? CONST TSIZE% = 128 'TextureSize 'Needed by the Loader!!! CONST PI = 3.14151693# CONST FALSE = 0, TRUE = NOT FALSE REDIM SHARED Vpage(32009) AS INTEGER 'Virtual page DIM SHARED Lcos(359) AS SINGLE 'Lookup tables for speed DIM SHARED Lsin(359) AS SINGLE REDIM SHARED Model(1) AS Point3d REDIM SHARED Poly(1) AS PolyType REDIM SHARED FaceNormal(1) AS VectorType 'Orig Face normal REDIM SHARED VTXNormal(1) AS VectorType 'Orig Vertex normal REDIM SHARED VTXNormal2(1) AS VectorType 'Rotated Vertex normal DIM SHARED ThetaX, ThetaY, ThetaZ 'angle of rotation DIM SHARED LightNormal AS VectorType 'our light normal DIM SHARED camx%, camy%, camz% 'camera offset DIM SHARED Path$ size% = ((TSIZE% * TSIZE%) + 4) \ 2 'Calc array big enough for 64*64 texture DIM SHARED Texture%(size%) DIM SHARED TSEG%, TOFF% 'Segment:offset of textures(needed by 'TextureTri sub 'PreCalc sin and cos lookuptable FOR i = 0 TO 359 A! = i * PI / 180 Lcos(i) = COS(A!) Lsin(i) = SIN(A!) NEXT i LightNormal.x = 0 'Light normal LightNormal.y = 0 LightNormal.z = -.6 Path$ = "C:\Qbasic\3dTute\3dObj\" CLS SCREEN 0 WIDTH 80 '/============Loader parameters======================================= 'Setup model including normals Scale! = 20 'Scaling Textured% = FALSE 'If the model has textures FILES Path$ + "*.L3d" INPUT "Type in the name of the file to load(default=Ship.L3d):", File$ IF File$ = "" THEN File$ = "Ship.L3d" IF INSTR(File$, ".") = 0 THEN File$ = File$ + ".L3d" PRINT PRINT PRINT "1. Textured" PRINT "2. Non-Textured(Default)" K$ = INPUT$(1) SELECT CASE ASC(K$) CASE 49 Textured% = TRUE CASE 50 Textured% = FALSE CASE ELSE Textured% = FALSE END SELECT j1: 'Load LoadLightWaveQ Model(), Poly(), Path$ + File$, TSIZE% ScaleModel Model(), Scale! 'scale FindCentroid Model() 'center model '===============end loader================================================== CLS SCREEN 13 RANDOMIZE TIMER 'If not textured then calculate normals for gouraud shading IF NOT Textured% THEN 'Face normals REDIM FaceNormal(1 TO UBOUND(Poly)) AS VectorType 'Vertex normals REDIM VTXNormal(1 TO UBOUND(Model)) AS VectorType REDIM VTXNormal2(1 TO UBOUND(Model)) AS VectorType RotateAndProject Model(), 0, 0, 0 'may not be needed CalcNormals Model(), Poly(), FaceNormal(), VTXNormal() 'Set basecolor for each poly SetPolyBaseColor Poly() 'Read ending grad colors and set gradient pal RESTORE RGB FOR i = 0 TO 15 READ R%, g%, B% GradColor i * 16, 0, 0, 0, (i * 16) + 15, R%, g%, B% NEXT i ELSE 'Generate texture 'Load bmp as Texture Rel.LoadBMP Path$ + "Anya256.bmp", TRUE C$ = INPUT$(1) 'Get our texture GET (0, 0)-(TSIZE% - 1, TSIZE% - 1), Texture% 'Set up texture variables for easy referencing TSEG% = VARSEG(Texture%(0)) TOFF% = VARPTR(Texture%(0)) END IF ThetaX = INT(RND * 360) ThetaY = INT(RND * 360) ThetaZ = INT(RND * 360) camx% = 0 'camera camy% = 0 camz% = 128 Vpage(6) = 2560 'set up buffer Vpage(7) = 200 Layer = VARSEG(Vpage(0)) + 1 SetVideoSeg Layer DO 'Increment angles ThetaX = (ThetaX + 1) MOD 360 ThetaY = (ThetaY + 1) MOD 360 ThetaZ = (ThetaZ + 1) MOD 360 'Rotate model RotateAndProject Model(), ThetaX, ThetaY, ThetaZ SortPolys Model(), Poly() 'Set draw to buffer SetVideoSeg Layer LINE (0, 0)-(319, 199), 0, BF 'cls 'Draw IF NOT Textured% THEN 'Gouraud RotNormals VTXNormal(), VTXNormal2(), ThetaX, ThetaY, ThetaZ DrawModelG Model(), Poly() ELSE 'Texture DrawModelT Model(), Poly() END IF 'Set draw to screen SetVideoSeg &HA000 PUT (0, 0), Vpage(6), PSET 'Pcopy LOOP UNTIL INKEY$ <> "" CLS SCREEN 0 WIDTH 80 END 'RGB colors for our Grad RGB: DATA 63,63,63 : 'WHITE DATA 63,0,0 : 'RED DATA 0,63,0 : 'GREEN DATA 0,0,63 : 'BLUE DATA 0,63,63 : 'BLUE/GREEN DATA 63,63,0 : 'RED/GREEN DATA 63,0,63 : 'RED/BLUE DATA 32,25,63 : ' DATA 63,0,45 : ' DATA 16,63,20 : ' DATA 45,45,63 : ' DATA 63,45,20 : ' DATA 25,63,45 : ' DATA 56,63,34 : ' DATA 45,25,50 : ' DATA 63,25,11 : ' REM $STATIC SUB CalcNormals (Model() AS Point3d, Poly() AS PolyType, v() AS VectorType, v2() AS VectorType) 'Calculates the face and vertex normals of all the polygons of our model 'Face normals FOR i = 1 TO UBOUND(v) p1 = Poly(i).p1 'vertex p2 = Poly(i).p2 p3 = Poly(i).p3 x1 = Model(p1).x 'coords x2 = Model(p2).x x3 = Model(p3).x y1 = Model(p1).y y2 = Model(p2).y y3 = Model(p3).y Z1 = Model(p1).z Z2 = Model(p2).z Z3 = Model(p3).z ax! = x2 - x1 'vectors bx! = x3 - x2 ay! = y2 - y1 by! = y3 - y2 az! = Z2 - Z1 bz! = Z3 - Z2 'Cross product xnormal! = ay! * bz! - az! * by! ynormal! = az! * bx! - ax! * bz! znormal! = ax! * by! - ay! * bx! 'Normalize Mag! = SQR(xnormal! ^ 2 + ynormal! ^ 2 + znormal! ^ 2) IF Mag! <> 0 THEN xnormal! = xnormal! / Mag! ynormal! = ynormal! / Mag! znormal! = znormal! / Mag! END IF v(i).x = xnormal! 'final face normal v(i).y = ynormal! v(i).z = znormal! NEXT i 'VertexNormals 'Algo: since we cannot find a normal to a point(doh?!!!) we find adjacent 'planes(faces) of the polyhedra that a vertex is located then adding 'all the facenormals of all the faces that a particular vertex is 'located. FOR i = 1 TO UBOUND(Model) xnormal! = 0 ynormal! = 0 znormal! = 0 FaceFound = 0 FOR j = 1 TO UBOUND(Poly) IF Poly(j).p1 = i OR Poly(j).p2 = i OR Poly(j).p3 = i THEN xnormal! = xnormal! + v(j).x ynormal! = ynormal! + v(j).y znormal! = znormal! + v(j).z FaceFound = FaceFound + 1 END IF NEXT j xnormal! = xnormal! / FaceFound ynormal! = ynormal! / FaceFound znormal! = znormal! / FaceFound 'Normalize Mag! = SQR(xnormal! ^ 2 + ynormal! ^ 2 + znormal! ^ 2) IF Mag! <> 0 THEN xnormal! = xnormal! / Mag! ynormal! = ynormal! / Mag! znormal! = znormal! / Mag! END IF v2(i).x = xnormal! 'Vertex normals v2(i).y = ynormal! v2(i).z = znormal! NEXT i END SUB SUB DrawModelG (Model() AS Point3d, Poly() AS PolyType) STATIC FOR i = 1 TO UBOUND(Poly) j = Poly(i).idx x1 = Model(Poly(j).p1).scrx 'Get triangles from "projected" x2 = Model(Poly(j).p2).scrx 'X and Y coords since Znormal x3 = Model(Poly(j).p3).scrx 'Does not require a Z coord y1 = Model(Poly(j).p1).scry 'V1= Point1 connected to V2 then y2 = Model(Poly(j).p2).scry 'V2 to V3 and so on... y3 = Model(Poly(j).p3).scry znormal = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) IF znormal < 0 THEN nx1! = VTXNormal2(Poly(j).p1).x 'Vertex1 ny1! = VTXNormal2(Poly(j).p1).y nz1! = VTXNormal2(Poly(j).p1).z nx2! = VTXNormal2(Poly(j).p2).x 'Vertex2 ny2! = VTXNormal2(Poly(j).p2).y nz2! = VTXNormal2(Poly(j).p2).z nx3! = VTXNormal2(Poly(j).p3).x 'Vertex3 ny3! = VTXNormal2(Poly(j).p3).y nz3! = VTXNormal2(Poly(j).p3).z Lx! = LightNormal.x ly! = LightNormal.y lz! = LightNormal.z 'Calculate dot-products of vertex normals Dot1! = (nx1! * Lx!) + (ny1! * ly!) + (nz1! * lz!) IF Dot1! < 0 THEN 'Limit Dot1! = 0 ELSEIF Dot1! > 1 THEN Dot1! = 1 END IF Dot2! = (nx2! * Lx!) + (ny2! * ly!) + (nz2! * lz!) IF Dot2! < 0 THEN Dot2! = 0 ELSEIF Dot2! > 1 THEN Dot2! = 1 END IF Dot3! = (nx3! * Lx!) + (ny3! * ly!) + (nz3! * lz!) IF Dot3! < 0 THEN Dot3! = 0 ELSEIF Dot3! > 1 THEN Dot3! = 1 END IF 'multiply by color range Clr1 = (Dot1! * 16) + Poly(j).clr '16 color grad Clr2 = (Dot2! * 16) + Poly(j).clr Clr3 = (Dot3! * 16) + Poly(j).clr GouraudTri x1, y1, Clr1, x2, y2, Clr2, x3, y3, Clr3 END IF NEXT i END SUB SUB DrawModelT (Model() AS Point3d, Poly() AS PolyType) STATIC FOR i = 1 TO UBOUND(Poly) j = Poly(i).idx x1 = Model(Poly(j).p1).scrx 'Get triangles from "projected" x2 = Model(Poly(j).p2).scrx 'X and Y coords since Znormal x3 = Model(Poly(j).p3).scrx 'Does not require a Z coord y1 = Model(Poly(j).p1).scry 'V1= Point1 connected to V2 then y2 = Model(Poly(j).p2).scry 'V2 to V3 and so on... y3 = Model(Poly(j).p3).scry znormal = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) IF znormal < 0 THEN u1 = Poly(j).u1 'Texture Coords v1 = Poly(j).v1 u2 = Poly(j).u2 v2 = Poly(j).v2 u3 = Poly(j).u3 v3 = Poly(j).v3 TextureTri x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, TSEG%, TOFF% END IF NEXT i END SUB SUB FindCentroid (Model() AS Point3d) 'Centers the model at (0,0,0) NP = UBOUND(Model) x! = 0 y! = 0 z! = 0 FOR i = 1 TO NP x! = x! + Model(i).x y! = y! + Model(i).y z! = z! + Model(i).z NEXT i xc! = x! / NP yc! = y! / NP zc! = z! / NP FOR i = 1 TO NP Model(i).x = Model(i).x - xc! Model(i).y = Model(i).y - yc! Model(i).z = Model(i).z - zc! NEXT i END SUB SUB GouraudTri (ox1%, oy1%, oc1%, ox2%, oy2%, oc2%, ox3%, oy3%, oc3%) ' ' ' / ' d1 / | ' / | ' \ | ' \ |d3 ' d2 \ | ' \ | ' \| 'This implementation of the Gouraud triangle routine is 'the the fast FIXPOINT point version. 'Almost the same as a Flat Triangle routine with an added interpolation 'of the color gradients. Note the comments with '***. It means 'Addition from the Flat filler code to make it a gouraud filler code. CONST FIXPOINT = 65536 DIM x1 AS INTEGER, y1 AS INTEGER DIM x2 AS INTEGER, y2 AS INTEGER DIM x3 AS INTEGER, y3 AS INTEGER DIM c1 AS INTEGER '***colors DIM c2 AS INTEGER DIM c3 AS INTEGER DIM dx1 AS INTEGER, dy1 AS INTEGER, dc1 AS INTEGER '*** DIM dx2 AS INTEGER, dy2 AS INTEGER, dc2 AS INTEGER '*** DIM dx3 AS INTEGER, dy3 AS INTEGER, dc3 AS INTEGER '*** DIM delta1&, delta2&, delta3& DIM CDelta1&, CDelta2&, CDelta3& '*** DIM Lx&, Rx& DIM Lc&, Rc& '*** x1 = ox1% y1 = oy1% c1 = oc1% '*** x2 = ox2% y2 = oy2% c2 = oc2% '*** x3 = ox3% y3 = oy3% c3 = oc3% '*** IF y2 < y1 THEN SWAP y1, y2 SWAP x1, x2 SWAP c1, c2 '*** END IF IF y3 < y1 THEN SWAP y3, y1 SWAP x3, x1 SWAP c3, c1 '*** END IF IF y3 < y2 THEN SWAP y3, y2 SWAP x3, x2 SWAP c3, c2 '*** END IF dx1 = x2 - x1 dy1 = y2 - y1 dc1 = c2 - c1 '*** IF dy1 <> 0 THEN delta1& = dx1 * FIXPOINT \ dy1 CDelta1& = dc1 * FIXPOINT \ dy1 '*** ELSE delta1& = 0 CDelta1& = 0 '*** END IF dx2 = x3 - x2 dy2 = y3 - y2 dc2 = c3 - c2 '*** IF dy2 <> 0 THEN delta2& = dx2 * FIXPOINT \ dy2 CDelta2& = dc2 * FIXPOINT \ dy2 '*** ELSE delta2& = 0 CDelta2& = 0 '*** END IF dx3 = x1 - x3 dy3 = y1 - y3 dc3 = c1 - c3 '*** IF dy3 <> 0 THEN delta3& = dx3 * FIXPOINT \ dy3 CDelta3& = dc3 * FIXPOINT \ dy3 '*** ELSE delta3& = 0 CDelta3& = 0 '*** END IF 'Flat bottom 'Tup part of triangle Lx& = x1 * FIXPOINT Rx& = Lx& Lc& = c1 * FIXPOINT '***Left color Rc& = Lc& '***Right Color FOR y% = y1 TO y2 - 1 Tx1% = Lx& \ FIXPOINT '\ '*** Tx2% = Rx& \ FIXPOINT ' \ Parameters for GourHline subroutine Col1& = Lc& ' / '*** Col2& = Rc& '/ '*** GOSUB GourHline '*** Lx& = Lx& + delta1& Rx& = Rx& + delta3& Lc& = Lc& + CDelta1& '*** DDA the color grad Rc& = Rc& + CDelta3& '*** NEXT y% 'Flat top 'Lower part of triangle Lx& = x2 * FIXPOINT Lc& = c2 * FIXPOINT '*** FOR y% = y2 TO y3 Tx1% = Lx& \ FIXPOINT '*** Tx2% = Rx& \ FIXPOINT '*** Col1& = Lc& '*** Col2& = Rc& '*** GOSUB GourHline '*** Lx& = Lx& + delta2& Rx& = Rx& + delta3& Lc& = Lc& + CDelta2& '*** Rc& = Rc& + CDelta3& '*** NEXT y% EXIT SUB '************************************************************************** 'Draws a color gradiated horizontal line interpolated from col1 to col2 'Needed variables. ' Tx1% = integer x1 coordinate ' Tx2% = integer x2 coordinate ' y% = integer y coordinate ' Col1& = Long int of color1 *Fixpoint. In this case 2^16 ' Col2& = Ditto. '************************************************************************** GourHline: Gx1% = Tx1% 'Save values to be safe Gx2% = Tx2% yy% = y% Clr1& = Col1& Clr2& = Col2& IF Gx1% > Gx2% THEN 'Sort values SWAP Gx1%, Gx2% SWAP Clr1&, Clr2& END IF Gdx% = (Gx2% - Gx1%) + 1 'Get Xdelta(+1) for the Div by 0 error Cdx& = Clr2& - Clr1& 'Color delta deltac& = Cdx& \ Gdx% 'Interpolate Col& = Clr1& 'save orig color to be safe FOR l% = Gx1% TO Gx2% 'Rasterizer loop PSET (l%, yy%), (Col& \ FIXPOINT) 'Use poke for speed Col& = Col& + deltac& 'DDA NEXT l% RETURN END SUB SUB GradColor (Col1, r1, g1, b1, Col2, r2, g2, b2) 'Makes a gradient color by interpolating the RGB values of the first 'color index (col1) and col2 by the number of cols. 'Only use this in screen 13 R! = r1 g! = g1 B! = b1 cols = (Col2 - Col1 + 1) Rstep! = (r2 - r1 + 1) / cols Gstep! = (g2 - g1 + 1) / cols Bstep! = (b2 - b1 + 1) / cols FOR Col = Col1 TO Col2 R! = R! + Rstep! g! = g! + Gstep! B! = B! + Bstep! IF R! > 63 THEN R! = 63 IF R! < 0 THEN R! = 0 IF g! > 63 THEN g! = 63 IF g! < 0 THEN g! = 0 IF B! > 63 THEN B! = 63 IF B! < 0 THEN B! = 0 OUT &H3C8, Col OUT &H3C9, FIX(R!) OUT &H3C9, FIX(g!) OUT &H3C9, FIX(B!) NEXT Col END SUB SUB LoadLightWaveQ (M() AS Point3d, P() AS PolyType, File$, TSIZE%) Textured = 0 DIM utx!(1 TO 4) DIM vtx!(1 TO 4) NumTris = 0 NumPoints = 0 currentVert = 0 currentFace = 0 f = FREEFILE OPEN File$ FOR INPUT AS #f DO WHILE NOT EOF(f) LINE INPUT #f, Temp$ SELECT CASE MID$(Temp$, 1, 1) CASE "f" NumTris = NumTris + 1 GOSUB CountVertsQ CASE "#" GOSUB GetHeaderQ CASE ELSE END SELECT LOOP CLOSE #f REDIM P(1 TO NumTris) AS PolyType f = FREEFILE OPEN File$ FOR INPUT AS #f DO WHILE NOT EOF(f) LINE INPUT #f, Temp$ GOSUB RemodelQ SELECT CASE LTRIM$(RTRIM$(MID$(Temp$, 1, 2))) CASE "v" GOSUB GetVertexQ CASE "vt" vtxcounter = vtxcounter + 1 GOSUB GetUV CASE "f" IF NOT Textured THEN GOSUB GetFacesQ ELSE GOSUB GetFacesQT END IF UVcounter = 0 CASE ELSE END SELECT LOOP CLOSE #f EXIT SUB '========================Header======================================= GetHeaderQ: '///Vertex Vnum = INSTR(Temp$, "Vertices:") IF Vnum <> 0 THEN Vnum = Vnum + LEN("Vertices:") PRINT Vnum NumPoints = VAL(MID$(Temp$, Vnum + 1, LEN(Temp$) - Vnum)) PRINT "Numverts:"; NumPoints C$ = INPUT$(1) REDIM M(1 TO NumPoints) AS Point3d END IF '///Polys Tnum = INSTR(Temp$, "Faces:") IF Tnum <> 0 THEN Tnum = Tnum + LEN("Faces:") 'PRINT Tnum '''NumTris = VAL(MID$(Temp$, Tnum + 1, LEN(Temp$) - Tnum)) PRINT "NumFace:"; NumTris C$ = INPUT$(1) ''REDIM P(1 TO NumTris) AS PolyType END IF RETURN '=========================Vertex==================================== GetVertexQ: currentVert = currentVert + 1 spc1 = INSTR(Temp$, " ") + 1 '1st space spc2 = INSTR(spc1, Temp$, " ") + 1 '2nd spc3 = INSTR(spc2, Temp$, " ") + 1 '3rd Leng1 = (spc2 - spc1) 'length of value Leng2 = (spc3 - spc2) Leng3 = (LEN(Temp$) - spc3) + 2 M(currentVert).x = VAL(MID$(Temp$, spc1, Leng1)) M(currentVert).y = VAL(MID$(Temp$, spc2, Leng2)) M(currentVert).z = -VAL(MID$(Temp$, spc3, Leng3)) RETURN '=========================Faces====================================== GetFacesQ: spacecnt = 0 FOR t = 1 TO LEN(Temp$) IF MID$(Temp$, t, 1) = " " THEN spacecnt = spacecnt + 1 END IF NEXT t IF spacecnt < 4 THEN currentFace = currentFace + 1 spc1 = INSTR(Temp$, " ") + 1 '1st space spc2 = INSTR(spc1, Temp$, " ") + 1 '2nd spc3 = INSTR(spc2, Temp$, " ") + 1 '3rd Leng1 = (spc2 - spc1) 'length of value Leng2 = (spc3 - spc2) Leng3 = (LEN(Temp$) - spc3) + 2 P(currentFace).p1 = VAL(MID$(Temp$, spc1, Leng1)) 'value of Face 1 P(currentFace).p2 = VAL(MID$(Temp$, spc2, Leng2)) 'ditto P(currentFace).p3 = VAL(MID$(Temp$, spc3, Leng3)) P(currentFace).idx = currentFace P(currentFace).clr = 20 + INT(RND * 128) ELSE spc1 = INSTR(Temp$, " ") + 1 '1st space spc2 = INSTR(spc1, Temp$, " ") + 1 '2nd spc3 = INSTR(spc2, Temp$, " ") + 1 '3rd spc4 = INSTR(spc3, Temp$, " ") + 1 '3rd Leng1 = (spc2 - spc1) 'length of value Leng2 = (spc3 - spc2) Leng3 = (spc4 - spc3) Leng4 = (LEN(Temp$) - spc4) + 2 p1 = VAL(MID$(Temp$, spc1, Leng1)) 'value of Face 1 p2 = VAL(MID$(Temp$, spc2, Leng2)) 'ditto p3 = VAL(MID$(Temp$, spc3, Leng3)) p4 = VAL(MID$(Temp$, spc4, Leng4)) currentFace = currentFace + 1 P(currentFace).p1 = p1 P(currentFace).p2 = p2 P(currentFace).p3 = p4 P(currentFace).idx = currentFace P(currentFace).clr = 20 + INT(RND * 128) currentFace = currentFace + 1 P(currentFace).p1 = p2 P(currentFace).p2 = p3 P(currentFace).p3 = p4 P(currentFace).idx = currentFace P(currentFace).clr = 20 + INT(RND * 128) END IF RETURN '=========================Faces and UV============================ GetFacesQT: spacecnt = 0 FOR t = 1 TO LEN(Temp$) IF MID$(Temp$, t, 1) = " " THEN spacecnt = spacecnt + 1 END IF NEXT t IF spacecnt < 4 THEN currentFace = currentFace + 1 spc1 = INSTR(Temp$, " ") + 1 '1st space spc2 = INSTR(spc1, Temp$, " ") + 1 '2nd spc3 = INSTR(spc2, Temp$, " ") + 1 '3rd Leng1 = (spc2 - spc1) 'length of value Leng2 = (spc3 - spc2) Leng3 = (LEN(Temp$) - spc3) + 2 Char1$ = MID$(Temp$, spc1, Leng1) Char2$ = MID$(Temp$, spc2, Leng2) Char3$ = MID$(Temp$, spc3, Leng3) Spc1x = INSTR(Char1$, "/") - 1 '1st slash Spc2x = INSTR(Char2$, "/") - 1 '1st slash Spc3x = INSTR(Char3$, "/") - 1 '1st slash Face1$ = LEFT$(Char1$, Spc1x) Face2$ = LEFT$(Char2$, Spc2x) Face3$ = LEFT$(Char3$, Spc3x) vtx1$ = RIGHT$(Char1$, Leng1 - Spc1x - 1) vtx2$ = RIGHT$(Char2$, Leng2 - Spc2x - 1) vtx3$ = RIGHT$(Char3$, Leng3 - Spc3x - 2) vtx1 = ABS(VAL(vtx1$)) vtx2 = ABS(VAL(vtx2$)) vtx3 = ABS(VAL(vtx3$)) P(currentFace).p1 = VAL(Face1$) 'value of Face 1 P(currentFace).p2 = VAL(Face2$) P(currentFace).p3 = VAL(Face3$) P(currentFace).idx = currentFace P(currentFace).clr = 20 + INT(RND * 128) P(currentFace).u1 = utx!(1) * TSIZE% P(currentFace).v1 = vtx!(1) * TSIZE% P(currentFace).u2 = utx!(2) * TSIZE% P(currentFace).v2 = vtx!(2) * TSIZE% P(currentFace).u3 = utx!(3) * TSIZE% P(currentFace).v3 = vtx!(3) * TSIZE% ELSE spc1 = INSTR(Temp$, " ") + 1 '1st space spc2 = INSTR(spc1, Temp$, " ") + 1 '2nd spc3 = INSTR(spc2, Temp$, " ") + 1 '3rd spc4 = INSTR(spc3, Temp$, " ") + 1 '4th Leng1 = (spc2 - spc1) 'length of value Leng2 = (spc3 - spc2) Leng3 = (spc4 - spc3) Leng4 = (LEN(Temp$) - spc4) + 2 Char1$ = MID$(Temp$, spc1, Leng1) Char2$ = MID$(Temp$, spc2, Leng2) Char3$ = MID$(Temp$, spc3, Leng3) Char4$ = MID$(Temp$, spc4, Leng4) Spc1x = INSTR(Char1$, "/") - 1 '1st slash Spc2x = INSTR(Char2$, "/") - 1 '1st slash Spc3x = INSTR(Char3$, "/") - 1 '1st slash Spc4x = INSTR(Char4$, "/") - 1 '1st slash Face1$ = LEFT$(Char1$, Spc1x) Face2$ = LEFT$(Char2$, Spc2x) Face3$ = LEFT$(Char3$, Spc3x) Face4$ = LEFT$(Char4$, Spc4x) vtx1$ = RIGHT$(Char1$, Leng1 - Spc1x - 1) vtx2$ = RIGHT$(Char2$, Leng2 - Spc2x - 1) vtx3$ = RIGHT$(Char3$, Leng3 - Spc3x - 1) vtx4$ = RIGHT$(Char4$, Leng4 - Spc4x - 2) vtx1 = ABS(VAL(vtx1$)) vtx2 = ABS(VAL(vtx2$)) vtx3 = ABS(VAL(vtx3$)) vtx4 = ABS(VAL(vtx4$)) p1 = VAL(Face1$) 'value of Face 1 p2 = VAL(Face2$) p3 = VAL(Face3$) p4 = VAL(Face4$) 'First tri currentFace = currentFace + 1 P(currentFace).p1 = p1 P(currentFace).p2 = p2 P(currentFace).p3 = p4 P(currentFace).idx = currentFace P(currentFace).clr = 20 + INT(RND * 128) P(currentFace).u1 = utx!(1) * TSIZE% P(currentFace).v1 = vtx!(1) * TSIZE% P(currentFace).u2 = utx!(2) * TSIZE% P(currentFace).v2 = vtx!(2) * TSIZE% P(currentFace).u3 = utx!(4) * TSIZE% P(currentFace).v3 = vtx!(4) * TSIZE% 'Second tri currentFace = currentFace + 1 P(currentFace).p1 = p2 P(currentFace).p2 = p3 P(currentFace).p3 = p4 P(currentFace).idx = currentFace P(currentFace).clr = 20 + INT(RND * 128) P(currentFace).u1 = utx!(2) * TSIZE% P(currentFace).v1 = vtx!(2) * TSIZE% P(currentFace).u2 = utx!(3) * TSIZE% P(currentFace).v2 = vtx!(3) * TSIZE% P(currentFace).u3 = utx!(4) * TSIZE% P(currentFace).v3 = vtx!(4) * TSIZE% END IF RETURN '//==============================Remodel========================== RemodelQ: Temp2$ = "" Leng = LEN(Temp$) FOR i = 1 TO Leng Char$ = MID$(Temp$, i, 2) IF Char$ = "- " THEN Temp2$ = Temp2$ + LEFT$(Char$, 1) i = i + 1 ELSE Temp2$ = Temp2$ + LEFT$(Char$, 1) END IF NEXT i Temp$ = Temp2$ RETURN CountVertsQ: spacecnt = 0 FOR t = 1 TO LEN(Temp$) IF MID$(Temp$, t, 1) = " " THEN spacecnt = spacecnt + 1 END IF IF MID$(Temp$, t, 1) = "/" THEN Textured = -1 END IF NEXT t IF spacecnt > 3 THEN NumTris = NumTris + 1 RETURN '//==============================UV============================= GetUV: UVcounter = UVcounter + 1 'Vertex U,V spc1 = INSTR(Temp2$, " ") + 1 '1st space spc2 = INSTR(spc1, Temp2$, " ") + 1 '2nd Leng1 = (spc2 - spc1) 'length of value Leng2 = (LEN(Temp2$) - spc2) + 2 u$ = MID$(Temp2$, spc1, Leng1) v$ = MID$(Temp2$, spc2, Leng2) UText! = VAL(u$) VText! = VAL(v$) utx!(UVcounter) = UText! vtx!(UVcounter) = VText! RETURN END SUB SUB Rel.LoadBMP (File$, SwitchPal%) STATIC 'Loads a BMP to a layer or directly to the screen 'if you want it to be on screen pass &HA000 as DestSeg% 'only supports 256 color BMPs. DIM BMP AS BMPHeaderType f% = FREEFILE 'Get free filenum OPEN File$ FOR BINARY AS #f% 'Binary read GET #f%, , BMP 'Get header 'Our File Pointer points to 55 byte seek 55, first byte 'Pal should be 1024 in length Pall$ = BMP.Pal IF SwitchPal% THEN 'if we switch to pal then IF LEN(Pall$) = 1024 THEN OUT &H3C8, 0 'color zero start FOR i% = 1 TO 1024 STEP 4 B% = ASC(MID$(Pall$, i%, 1)) \ 4 'div by 4 g% = ASC(MID$(Pall$, i% + 1, 1)) \ 4 'div by 4 R% = ASC(MID$(Pall$, i% + 2, 1)) \ 4 'div by 4 'Byte 4 unused. Just for padding 32 bit regs OUT &H3C9, R% OUT &H3C9, g% OUT &H3C9, B% NEXT i% END IF END IF 'Read and Write time!!! 'Notes: Bad MS(I don't get it why they stored it backwards?) Byte$ = SPACE$(BMP.WID) DEF SEG = DestSeg% Wide% = BMP.WID - 1 'Sub 1 since we start at zero Hite% = BMP.HEI - 1 FOR y% = Hite% TO 0 STEP -1 GET #f%, , Byte$ FOR x% = 0 TO Wide% C% = ASC(MID$(Byte$, x% + 1, 1)) PSET (x%, y%), C% NEXT x% NEXT y% END SUB SUB RotateAndProject (Model() AS Point3d, AngleX, AngleY, AngleZ) STATIC ''Right handed system ''x=goes right ''y=up ''z=goes into you(out of the screen) '''rotation: counter-clockwise of each axis ''ei. make yourself perpenicular to the axis ''wave your hand from the center of your body to the left. ''or nake a fist and touch your thumb to your nose, the fingers ''curl counter clocwise. ''That's how it rotates. ;*) 'Precalculate the SIN and COS of each angle cx! = Lcos(AngleX) sx! = Lsin(AngleX) cy! = Lcos(AngleY) sy! = Lsin(AngleY) cz! = Lcos(AngleZ) sz! = Lsin(AngleZ) '''After2 hours of work, I was able to weed out the constants from '''Rotate and project N to reduce my muls to 9 instead of 12. woot!!!! xx! = cy! * cz! xy! = sx! * sy! * cz! - cx! * sz! xz! = cx! * sy! * cz! + sx! * sz! yx! = cy! * sz! yy! = cx! * cz! + sx! * sy! * sz! yz! = -sx! * cz! + cx! * sy! * sz! zx! = -sy! zy! = sx! * cy! zz! = cx! * cy! FOR i = 1 TO UBOUND(Model) x! = Model(i).x y! = Model(i).y z! = Model(i).z RotX! = (x! * xx! + y! * xy! + z! * xz!) - camx% RotY! = (x! * yx! + y! * yy! + z! * yz!) - camy% RotZ! = (x! * zx! + y! * zy! + z! * zz!) - camz% Model(i).xr = RotX! Model(i).yr = RotY! Model(i).zr = RotZ! 'Project Distance% = (LENS - RotZ!) IF Distance% THEN Model(i).scrx = XCENTER + (LENS * RotX! / Distance%) Model(i).scry = YCENTER - (LENS * RotY! / Distance%) ELSE END IF NEXT i END SUB SUB RotNormals (v() AS VectorType, v2() AS VectorType, AngleX, AngleY, AngleZ) 'We don't have to calculate normals in real time but we could instead 'rotate them just as we would rotate our points. 'Precalculate the SIN and COS of each angle cx! = Lcos(AngleX) sx! = Lsin(AngleX) cy! = Lcos(AngleY) sy! = Lsin(AngleY) cz! = Lcos(AngleZ) sz! = Lsin(AngleZ) '''After2 hours of work, I was able to weed out the constants from '''Rotate and project N to reduce my muls to 9 instead of 12. woot!!!! xx! = cy! * cz! xy! = sx! * sy! * cz! - cx! * sz! xz! = cx! * sy! * cz! + sx! * sz! yx! = cy! * sz! yy! = cx! * cz! + sx! * sy! * sz! yz! = -sx! * cz! + cx! * sy! * sz! zx! = -sy! zy! = sx! * cy! zz! = cx! * cy! FOR i = 1 TO UBOUND(v) x! = v(i).x 'Load Original normals y! = v(i).y z! = v(i).z RotX! = (x! * xx! + y! * xy! + z! * xz!) RotY! = (x! * yx! + y! * yy! + z! * yz!) RotZ! = (x! * zx! + y! * zy! + z! * zz!) v2(i).x = RotX! 'Rotated normals v2(i).y = RotY! v2(i).z = RotZ! NEXT i END SUB SUB ScaleModel (M() AS Point3d, Scale!) FOR i = 1 TO UBOUND(M) M(i).x = M(i).x * Scale! M(i).y = M(i).y * Scale! M(i).z = M(i).z * Scale! NEXT i END SUB SUB SetPolyBaseColor (Poly() AS PolyType) 'Sets the basecolor for each poly for use in translucency FOR i% = 1 TO UBOUND(Poly) C% = ((C% MOD 15) + 1) * 16 Poly(i%).clr = C% i% = i% + 1 'Unrem these 2 lines ;*) Poly(i%).clr = C% NEXT i% END SUB SUB SetVideoSeg (Segment) STATIC DEF SEG IF VideoAddrOff& = 0 THEN ' First time the sub is called ' We need to find the location of b$AddrC, which holds the graphics ' offset (b$OffC) and segment (b$SegC). Since b$AddrC is in the default ' segment, we can find it by setting it to a certain value, and then ' searching for that value. SCREEN 13 ' Set b$SegC to A000 (00A0 in memory) PSET (160, 100), 0 ' Set b$OffC to 7DA0 (not needed in the IDE) FOR offset& = 0 TO 32764 ' Search for b$AddrC, which is IF PEEK(offset&) = &HA0 THEN ' in the default segment and IF PEEK(offset& + 1) = &H7D THEN ' should have a value of IF PEEK(offset& + 2) = &H0 THEN ' A0 7D 00 A0. IF PEEK(offset& + 3) = &HA0 THEN VideoAddrOff& = offset& + 2 ' If we found it, record the EXIT FOR ' offset of b$SegC and quit END IF ' looking. (Oddly, changing END IF ' the b$OffC doesn't seem to END IF ' do anything, so this is why END IF ' this sub only changes b$SegC) NEXT END IF ' Change b$SegC to the specified Segment POKE VideoAddrOff&, Segment AND &HFF POKE VideoAddrOff& + 1, (Segment AND &HFF00&) \ &H100 END SUB SUB ShellSort (Poly() AS PolyType, Min, Max) 'Shell sort Algorithm ' Set comparison offset to half the number of records. offset = Max \ 2 ' Loop until offset gets to zero. DO WHILE offset > 0 Limit = Max - offset DO ' Assume no switches at this offset. Switch = FALSE ' Compare elements for the specified field and switch ' any that are out of order. FOR i = Min TO Limit - 1 Ti = Poly(i).zcenter Tj = Poly(i + offset).zcenter IF Ti > Tj THEN SWAP Poly(i).idx, Poly(i + offset).idx SWAP Poly(i).zcenter, Poly(i + offset).zcenter Switch = i END IF NEXT i ' Sort on next pass only to location where last switch was made. Limit = Switch LOOP WHILE Switch ' No switches at last offset. Try an offset half as big. offset = offset \ 2 LOOP END SUB SUB SortPolys (Model() AS Point3d, Poly() AS PolyType) FOR i% = 1 TO UBOUND(Poly) Poly(i%).zcenter = Model(Poly(i%).p1).zr + Model(Poly(i%).p2).zr + Model(Poly(i%).p3).zr Poly(i%).idx = i% NEXT i% ShellSort Poly(), 1, UBOUND(Poly) + 1 END SUB SUB TextureTri (ox1%, oy1%, ou1%, ov1%, ox2%, oy2%, ou2%, ov2%, ox3%, oy3%, ou3%, ov3%, TSEG%, TOFF%) ' ' ' / ' d1 / | ' / | ' \ | ' \ |d3 ' d2 \ | ' \ | ' \| 'This implementation of the Textured triangle routine is 'the the fast FIXPOINT point version. 'Almost the same as a Flat Triangle routine with an added interpolation 'of the U and V Texture coordinates. Note the comments with '***. It means 'Addition from the Flat filler code to make it a Texture filler code. CONST FIXPOINT = 65536 DIM x1 AS INTEGER, y1 AS INTEGER DIM x2 AS INTEGER, y2 AS INTEGER DIM x3 AS INTEGER, y3 AS INTEGER DIM u1 AS INTEGER, v1 AS INTEGER '***texture coords DIM u2 AS INTEGER, v2 AS INTEGER DIM u3 AS INTEGER, v3 AS INTEGER DIM dx1 AS INTEGER, dy1 AS INTEGER, du1 AS INTEGER, dv1 AS INTEGER '*** DIM dx2 AS INTEGER, dy2 AS INTEGER, du2 AS INTEGER, dv2 AS INTEGER '*** DIM dx3 AS INTEGER, dy3 AS INTEGER, du3 AS INTEGER, dv3 AS INTEGER '*** DIM delta1&, delta2&, delta3& DIM UDelta1&, UDelta2&, UDelta3& '*** DIM VDelta1&, VDelta2&, VDelta3& '*** DIM Lx&, Rx& DIM Lu&, Ru& '*** DIM Lv&, Rv& '*** x1 = ox1% y1 = oy1% u1 = ou1% '*** v1 = ov1% '*** x2 = ox2% y2 = oy2% u2 = ou2% '*** v2 = ov2% '*** x3 = ox3% y3 = oy3% u3 = ou3% '*** v3 = ov3% '*** DEF SEG = TSEG% TEXTUREWID% = (PEEK(TOFF%) + PEEK(TOFF% + 1) * 256) \ 8 TWM1% = TEXTUREWID% - 1 IF y2 < y1 THEN SWAP y1, y2 SWAP x1, x2 SWAP u1, u2 SWAP v1, v2 END IF IF y3 < y1 THEN SWAP y3, y1 SWAP x3, x1 SWAP u3, u1 SWAP v3, v1 END IF IF y3 < y2 THEN SWAP y3, y2 SWAP x3, x2 SWAP u3, u2 SWAP v3, v2 END IF dx1 = x2 - x1 dy1 = y2 - y1 du1 = u2 - u1 '*** dv1 = v2 - v1 IF dy1 <> 0 THEN delta1& = dx1 * FIXPOINT \ dy1 UDelta1& = du1 * FIXPOINT \ dy1 '*** VDelta1& = dv1 * FIXPOINT \ dy1 '*** ELSE delta1& = 0 UDelta1& = 0 '*** VDelta1& = 0 '*** END IF dx2 = x3 - x2 dy2 = y3 - y2 du2 = u3 - u2 '*** dv2 = v3 - v2 IF dy2 <> 0 THEN delta2& = dx2 * FIXPOINT \ dy2 UDelta2& = du2 * FIXPOINT \ dy2 '*** VDelta2& = dv2 * FIXPOINT \ dy2 '*** ELSE delta2& = 0 UDelta2& = 0 '*** VDelta2& = 0 '*** END IF dx3 = x1 - x3 dy3 = y1 - y3 du3 = u1 - u3 '*** dv3 = v1 - v3 IF dy3 <> 0 THEN delta3& = dx3 * FIXPOINT \ dy3 UDelta3& = du3 * FIXPOINT \ dy3 '*** VDelta3& = dv3 * FIXPOINT \ dy3 '*** ELSE delta3& = 0 UDelta3& = 0 '*** VDelta3& = 0 '*** END IF 'Flat bottom 'Tup part of triangle Lx& = x1 * FIXPOINT Rx& = Lx& Lu& = u1 * FIXPOINT '***Left U Ru& = Lu& '***Right U Lv& = v1 * FIXPOINT '***Left V Rv& = Lv& '***Right V FOR y% = y1 TO y2 - 1 Tx1% = Lx& \ FIXPOINT '\ '*** Tx2% = Rx& \ FIXPOINT ' \ Parameters for TextureHline subroutine TLu1& = Lu& ' / '*** TLu2& = Ru& '/ TLv1& = Lv& '/ '*** TLv2& = Rv& GOSUB TextureHline '*** Lx& = Lx& + delta1& Rx& = Rx& + delta3& Lu& = Lu& + UDelta1& '*** Ru& = Ru& + UDelta3& '*** Lv& = Lv& + VDelta1& '*** Rv& = Rv& + VDelta3& '*** NEXT y% 'Flat top 'Lower part of triangle Lx& = x2 * FIXPOINT Lu& = u2 * FIXPOINT '*** Lv& = v2 * FIXPOINT '*** FOR y% = y2 TO y3 Tx1% = Lx& \ FIXPOINT '\ '*** Tx2% = Rx& \ FIXPOINT ' \ Parameters for TextureHline subroutine TLu1& = Lu& ' / '*** TLu2& = Ru& '/ TLv1& = Lv& '/ '*** TLv2& = Rv& GOSUB TextureHline '*** Lx& = Lx& + delta2& Rx& = Rx& + delta3& Lu& = Lu& + UDelta2& '*** Ru& = Ru& + UDelta3& '*** Lv& = Lv& + VDelta2& '*** Rv& = Rv& + VDelta3& '*** NEXT y% EXIT SUB '************************************************************************** 'Draws a Textured horizontal line interpolated from u1 to u2, v1 to v2 'Needed variables. ' Tx1% = integer x1 coordinate ' Tx2% = integer x2 coordinate ' y% = integer y coordinate ' u1& = Long int of u1 *Fixpoint. In this case 2^16 ' u2& = Ditto. ' v1& = Long int of v1 *Fixpoint. In this case 2^16 ' v2& = Ditto. '************************************************************************** TextureHline: Gx1% = Tx1% 'Save values to be safe Gx2% = Tx2% yy% = y% Tu1& = TLu1& Tu2& = TLu2& Tv1& = TLv1& Tv2& = TLv2& IF Gx1% > Gx2% THEN 'Sort values SWAP Gx1%, Gx2% SWAP Tu1&, Tu2& SWAP Tv1&, Tv2& END IF Gdx% = (Gx2% - Gx1%) + 1 'Get Xdelta(+1) for the Div by 0 error Udx& = Tu2& - Tu1& 'U delta deltaU& = Udx& \ Gdx% 'Interpolate Vdx& = Tv2& - Tv1& 'V delta deltaV& = Vdx& \ Gdx% 'Interpolate u& = Tu1& 'save values to be safe v& = Tv1& FOR l% = Gx1% TO Gx2% 'Rasterizer loop u% = (u& \ FIXPOINT) AND TWM1% v% = (v& \ FIXPOINT) AND TWM1% Pix = PEEK(4 + u% + v% * TEXTUREWID%) PSET (l%, yy%), Pix 'Use poke for speed u& = u& + deltaU& 'DDA v& = v& + deltaV& 'DDA NEXT l% RETURN END SUB