Thanks Fredborg but I think your way of doing the calculations is simply a shorthand version of the way I was doing them (so I'll be using your way from now on ;) ). So, the resulting normal will still not have a length of one. However, I've since discovered a way of unifying the normals using the calc:
nl# = sqr(nx*nx + ny*ny + nz*nz)
nx = nx / nl
ny = ny / nl
nz = nz / nl
This seems to work fine in setting the new normals to a length of one, unfortunantely it doesn't seemed to have solved my problem of incorrect lighting. Please see demo:
Graphics3D 800,600,16
WireFrame 0
AntiAlias 0
SeedRnd MilliSecs()
Type tri_linkT
Field prev.tri_linkT
Field surf
Field tri
Field dx#,dy#,dz#
Field pitch#,yaw#,roll#
End Type
Type explode_ctrlT
Field tri_list.tri_linkT
Field mesh
Field life%
Field fader#
Field fade_start%
Field exploding%
End Type
fps_timer = CreateTimer(60)
Global cam = CreateCamera()
PositionEntity cam,0,0,-7
light = CreateLight()
TurnEntity light,0,75,0
; Scratch pad mesh used by copy_tri_explode() for sub-division.
Global sd_mesh = CreateMesh()
Global sd_surf = CreateSurface(sd_mesh)
HideEntity sd_mesh
BB_prim1 = CreateCone(16)
BB_prim2 = CopyEntity(BB_prim1)
SD_prim1.explode_ctrlT = copy_mesh_explode(BB_prim1,200,1)
SD_prim2.explode_ctrlT = copy_mesh_explode(BB_prim1,200,1)
PositionEntity BB_prim1,-3,-2,0
PositionEntity BB_prim2,-3,2,0
PositionEntity SD_prim1\mesh,3,-2,0
PositionEntity SD_prim2\mesh,3,2,0
EntityFX BB_prim1,4
EntityFX SD_prim1\mesh,4
; Main loop
While Not KeyHit(1)
TurnEntity BB_prim1,1,0,.5
TurnEntity BB_prim2,1,0,.5
TurnEntity SD_prim1\mesh,1,0,.5
TurnEntity SD_prim2\mesh,1,0,.5
UpdateWorld
RenderWorld
Line 400,0,400,600
Text 400,20," B3D Primitives Sub-divided Primitives",1
Text 400,280,"Smooth Shaded Smooth Shaded",1
Text 400,530,"Flat Shaded Flat Shaded",1
WaitTimer(fps_timer)
Flip(1)
Wend
ClearWorld
End
;
; Creates a subdivided (optional), unwelded copy of an existing mesh.
; Explode info for all tris in the new mesh are kept in a separate linked list
; contained in the control variable returned to the caller.
; Note: the source mesh being copied remains completely unaltered.
;
; Params:
; s_mesh - Source mesh to be copied.
; life - Duration of explosion animation (in frames) to use when exploded.
; divs - Number of times to perform x4 sub-division on each source tri.
; keep_surfs - True to keep the same amount of surfaces in
; the copy as in the source mesh.
; False (Default) to copy all tris in the source mesh
; to a single surface in the copy.
;
; Returns:
; Pointer to the control variable (of type explode_ctrlT) for the
; new explodable mesh created.
;
Function copy_mesh_explode.explode_ctrlT(s_mesh,life%,divs=0,keep_surfs%=False)
tri_list.tri_linkT = Null
d_mesh = CreateMesh()
For sno = 1 To CountSurfaces(s_mesh)
s_surf = GetSurface(s_mesh,sno)
nt = CountTriangles(s_surf)
If sno = 1 Or keep_surfs Then d_surf = CreateSurface(d_mesh)
For tno = 0 To nt-1
tri_list = copy_tri_explode(s_surf,tno,d_surf,divs,tri_list)
Next
Next
; Create and return this explodable mesh's control variable.
ctrl.explode_ctrlT = New explode_ctrlT
ctrl\tri_list = tri_list
ctrl\mesh = d_mesh
ctrl\life = life
ctrl\fade_start = Ceil(Float(life)*FADE_START#)
ctrl\fader = 1.0/(ctrl\fade_start+1)
ctrl\exploding = False
Return ctrl
End Function
;
; Adds an unwelded copy of a tri from the source surface to the destination
; surface, performing any sub-division requested.
;
; Sub-division is done by calling this function recursively, splitting the
; source tri into 4 unwelded tris each time, like so:
;
; v1
; /\
; /____\
; / \ / \
; /_____\/_____\
; v0 v2
;
; Params:
; s_surf - Source surface containing tri to be copied.
; tri - Index of tri to be copied.
; d_surf - Destination surface to copy the source tri to.
; divs - Number of times to perform x4 sub-division on the source tri.
; tri_list - Current top item in the linked list.
; reset - True (default) to clear scratch pad mesh.
; False to not clear scratch pad mesh (used internally only!).
;
; Returns:
; The current top item in the linked list.
;
Function copy_tri_explode.tri_linkT(s_surf,tri,d_surf,divs,tri_list.tri_linkT,reset%=True)
If reset Then ClearSurface(sd_surf)
sv0 = TriangleVertex(s_surf,tri,0)
sv1 = TriangleVertex(s_surf,tri,1)
sv2 = TriangleVertex(s_surf,tri,2)
; Get coords of all 3 vertices of source tri.
x0# = VertexX(s_surf,sv0)
y0# = VertexY(s_surf,sv0)
z0# = VertexZ(s_surf,sv0)
x1# = VertexX(s_surf,sv1)
y1# = VertexY(s_surf,sv1)
z1# = VertexZ(s_surf,sv1)
x2# = VertexX(s_surf,sv2)
y2# = VertexY(s_surf,sv2)
z2# = VertexZ(s_surf,sv2)
; Get normals of all 3 vertices of source tri.
nx0# = VertexNX(s_surf,sv0)
ny0# = VertexNY(s_surf,sv0)
nz0# = VertexNZ(s_surf,sv0)
nx1# = VertexNX(s_surf,sv1)
ny1# = VertexNY(s_surf,sv1)
nz1# = VertexNZ(s_surf,sv1)
nx2# = VertexNX(s_surf,sv2)
ny2# = VertexNY(s_surf,sv2)
nz2# = VertexNZ(s_surf,sv2)
; Get tex coords of all 3 vertices of source tri.
u0# = VertexU(s_surf,sv0)
v0# = VertexV(s_surf,sv0)
w0# = VertexW(s_surf,sv0)
u1# = VertexU(s_surf,sv1)
v1# = VertexV(s_surf,sv1)
w1# = VertexW(s_surf,sv1)
u2# = VertexU(s_surf,sv2)
v2# = VertexV(s_surf,sv2)
w2# = VertexW(s_surf,sv2)
; Get colour of all 3 vertices of source tri.
r0# = VertexRed(s_surf,sv0)
g0# = VertexGreen(s_surf,sv0)
b0# = VertexBlue(s_surf,sv0)
r1# = VertexRed(s_surf,sv1)
g1# = VertexGreen(s_surf,sv1)
b1# = VertexBlue(s_surf,sv1)
r2# = VertexRed(s_surf,sv2)
g2# = VertexGreen(s_surf,sv2)
b2# = VertexBlue(s_surf,sv2)
If divs
; Calculate coords of the 3 discrete vertices used by sub-division.
x3# = (x1+x0)/2.0
y3# = (y1+y0)/2.0
z3# = (z1+z0)/2.0
x4# = (x2+x1)/2.0
y4# = (y2+y1)/2.0
z4# = (z2+z1)/2.0
x5# = (x0+x2)/2.0
y5# = (y0+y2)/2.0
z5# = (z0+z2)/2.0
; Calculate normals of the 3 discrete vertices used by sub-division.
nx3# = (nx1+nx0)/2.0
ny3# = (ny1+ny0)/2.0
nz3# = (nz1+nz0)/2.0
nx4# = (nx2+nx1)/2.0
ny4# = (ny2+ny1)/2.0
nz4# = (nz2+nz1)/2.0
nx5# = (nx0+nx2)/2.0
ny5# = (ny0+ny2)/2.0
nz5# = (nz0+nz2)/2.0
; Unify.
nl# = Sqr(nx3*nx3 + ny3*ny3 + nz3*nz3)
nx3 = nx3 / nl
ny3 = ny3 / nl
nz3 = nz3 / nl
nl# = Sqr(nx4*nx4 + ny4*ny4 + nz4*nz4)
nx4 = nx4 / nl
ny4 = ny4 / nl
nz4 = nz4 / nl
nl# = Sqr(nx5*nx5 + ny5*ny5 + nz5*nz5)
nx5 = nx5 / nl
ny5 = ny5 / nl
nz5 = nz5 / nl
; Calculate tex coords of the 3 discrete vertices used by sub-division.
u3# = (u1+u0)/2.0
v3# = (v1+v0)/2.0
w3# = (w1+w0)/2.0
u4# = (u2+u1)/2.0
v4# = (v2+v1)/2.0
w4# = (w2+w1)/2.0
u5# = (u0+u2)/2.0
v5# = (v0+v2)/2.0
w5# = (w0+w2)/2.0
; Calculate colour of the 3 discrete vertices used by sub-division.
r3# = (r1+r0)/2.0
g3# = (g1+g0)/2.0
b3# = (b1+b0)/2.0
r4# = (r2+r1)/2.0
g4# = (g2+g1)/2.0
b4# = (b2+b1)/2.0
r5# = (r0+r2)/2.0
g5# = (g0+g2)/2.0
b5# = (b0+b2)/2.0
; Add the 4 unwelded tris comprising the sub-division to the
; temporary, scratch pad mesh surface.
tv0 = AddVertex(sd_surf,x0,y0,z0)
tv3 = AddVertex(sd_surf,x3,y3,z3)
tv5 = AddVertex(sd_surf,x5,y5,z5)
tri0 = AddTriangle(sd_surf,tv0,tv3,tv5)
VertexNormal sd_surf,tv0,nx0,ny0,nz0
VertexNormal sd_surf,tv3,nx3,ny3,nz3
VertexNormal sd_surf,tv5,nx5,ny5,nz5
VertexColor sd_surf,tv0,r0,g0,b0
VertexColor sd_surf,tv3,r3,g3,b3
VertexColor sd_surf,tv5,r5,g5,b5
VertexTexCoords sd_surf,tv0,u0,v0,w0
VertexTexCoords sd_surf,tv3,u3,v3,w3
VertexTexCoords sd_surf,tv5,u5,v5,w5
tv1 = AddVertex(sd_surf,x1,y1,z1)
tv4 = AddVertex(sd_surf,x4,y4,z4)
tv3b = AddVertex(sd_surf,x3,y3,z3)
tri1 = AddTriangle(sd_surf,tv1,tv4,tv3b)
VertexNormal sd_surf,tv1,nx1,ny1,nz1
VertexNormal sd_surf,tv4,nx4,ny4,nz4
VertexNormal sd_surf,tv3b,nx3,ny3,nz3
VertexColor sd_surf,tv1,r1,g1,b1
VertexColor sd_surf,tv4,r4,g4,b4
VertexColor sd_surf,tv3b,r3,g3,b3
VertexTexCoords sd_surf,tv1,u1,v1,w1
VertexTexCoords sd_surf,tv4,u4,v4,w4
VertexTexCoords sd_surf,tv3b,u3,v3,w3
tv2 = AddVertex(sd_surf,x2,y2,z2)
tv5b = AddVertex(sd_surf,x5,y5,z5)
tv4b = AddVertex(sd_surf,x4,y4,z4)
tri2 = AddTriangle(sd_surf,tv2,tv5b,tv4b)
VertexNormal sd_surf,tv2,nx2,ny2,nz2
VertexNormal sd_surf,tv5b,nx5,ny5,nz5
VertexNormal sd_surf,tv4b,nx4,ny4,nz4
VertexColor sd_surf,tv2,r2,g2,b2
VertexColor sd_surf,tv5b,r5,g5,b5
VertexColor sd_surf,tv4b,r4,g4,b4
VertexTexCoords sd_surf,tv2,u2,v2,w2
VertexTexCoords sd_surf,tv5b,u5,v5,w5
VertexTexCoords sd_surf,tv4b,u4,v4,w4
tv3c = AddVertex(sd_surf,x3,y3,z3)
tv4c = AddVertex(sd_surf,x4,y4,z4)
tv5c = AddVertex(sd_surf,x5,y5,z5)
tri3 = AddTriangle(sd_surf,tv3c,tv4c,tv5c)
VertexNormal sd_surf,tv3c,nx3,ny3,nz3
VertexNormal sd_surf,tv4c,nx4,ny4,nz4
VertexNormal sd_surf,tv5c,nx5,ny5,nz5
VertexColor sd_surf,tv3c,r3,g3,b3
VertexColor sd_surf,tv4c,r4,g4,b4
VertexColor sd_surf,tv5c,r5,g5,b5
VertexTexCoords sd_surf,tv3c,u3,v3,w3
VertexTexCoords sd_surf,tv4c,u4,v4,w4
VertexTexCoords sd_surf,tv5c,u5,v5,w5
divs = divs - 1
tri_list = copy_tri_explode(sd_surf,tri0,d_surf,divs,tri_list,False)
tri_list = copy_tri_explode(sd_surf,tri1,d_surf,divs,tri_list,False)
tri_list = copy_tri_explode(sd_surf,tri2,d_surf,divs,tri_list,False)
tri_list = copy_tri_explode(sd_surf,tri3,d_surf,divs,tri_list,False)
Else
dv0 = AddVertex(d_surf,x0,y0,z0)
dv1 = AddVertex(d_surf,x1,y1,z1)
dv2 = AddVertex(d_surf,x2,y2,z2)
; Calculate and add a lone 'centre of tri'
; vertex (needed for per-tri rotations).
tx# = (x2+x1)/2.0
ty# = (y2+y1)/2.0
tz# = (z2+z1)/2.0
cvx# = tx - ((tx-x0)/3.0)
cvy# = ty - ((ty-y0)/3.0)
cvz# = tz - ((tz-z0)/3.0)
AddVertex(d_surf,cvx,cvy,cvz)
real_tri = AddTriangle(d_surf,dv0,dv1,dv2)
VertexNormal d_surf,dv0,nx0,ny0,nz0
VertexNormal d_surf,dv1,nx1,ny1,nz1
VertexNormal d_surf,dv2,nx2,ny2,nz2
VertexColor d_surf,dv0,r0,g0,b0
VertexColor d_surf,dv1,r1,g1,b1
VertexColor d_surf,dv2,r2,g2,b2
VertexTexCoords d_surf,dv0,u0,v0,w0
VertexTexCoords d_surf,dv1,u1,v1,w1
VertexTexCoords d_surf,dv2,u2,v2,w2
; Add this tri to the linked list.
link.tri_linkT = New tri_linkT
link\prev = tri_list
link\surf = d_surf
link\tri = real_tri
r# = Rnd(10,80)
link\dx = cvx/r
link\dy = cvy/r
link\dz = cvz/r
link\pitch = Rnd(-10,10)
link\yaw = Rnd(-10,10)
link\roll = Rnd(-10,10)
tri_list = link
EndIf
Return tri_list
End Function
;
; Free all mem used by an explodable mesh created with copy_mesh_explode().
;
; Params:
; ctrl - Control variable of the explodable mesh to be freed.
;
Function free_mesh_explode(ctrl.explode_ctrlT)
this.tri_linkT = ctrl\tri_list
While this <> Null
delme.tri_linkT = this
this = delme\prev
Delete delme
Wend
FreeEntity ctrl\mesh
Delete ctrl
End Function
Firstly, there seems to be some bizarre shading going on with the bottom of the cone. And in the flat shaded one, I don't understand why each individual tri is being lit differently, even when they're a part of the same facet.
Again, any help much appreciated to stop me going insane! :)
Thanks again.