Ok finally here's a combination of the ICU method and the linepick method.
First it uses the ICU detection method that is pretty fast. But then there are still some enclosed Triangles left that should not be there. So I additionally use the Linepick method to get rid of them. It's a bit less accurate than the Linepick only version. However, the speed is around 9ms, compared to the linepickversion (230ms), the coldet version (50ms) and the plain ICU method(ca. 6ms, tho not accurate enough).
I have also seen as soon as I use smaller triangles (like a sphere with more segments), the rouding errors show up more oftenly. So I'd say: this may be useful for simple, rough geometry only.
Additionally there's the bitter fact that animated meshes cannot make use of this because VertexX etc. will ignore Animations and return the initial pose.
;create shadow volume with a combination of Linepick and the ICU method:
;-----------------------------------------------------------------------
; works kind of, tho meshes with lots of little triangles will produce more rounding errors
Graphics3D 640,480,32,2
SetBuffer BackBuffer()
Global helper=CreatePivot()
Global helper2=CreatePivot()
Global icucam=CreateCamera()
CameraProjMode icucam,0
Dim icu_seen(0)
Global infi#=50 ;20 ; length shadow volume
Global pick_radius#=0.00007;0.1
Global pick_back#=50 ;10 ; pick from n behind light
Global divi#=3.0
lidis#=4
Global plane=CreatePlane() ; map
TranslateEntity plane,0,-5,0
EntityColor plane,0,255,0
Global camera=CreateCamera()
TranslateEntity camera,0,0,-12
light=CreateLight(2)
RotateEntity light,45,45,45
cube=CreateSphere(5) ;CreateCube() ; object to cast shadow
;cube=CreateCube() ; object to cast shadow
;cube=CreateCone(10)
RotateEntity cube,45,45,0
;RotateEntity cube,245,245,0
EntityPickMode cube,2
;TranslateEntity cube,1.2,0.5,1.3
lime=CreateSphere() ; position of light
ScaleEntity lime,0.1,0.1,0.1
PositionEntity lime,4,4,0
a#=90
While KeyDown(1)=0
a=(a +2) Mod 360
PositionEntity lime,Sin(a)*lidis,lidis,Cos(a)*lidis
; PositionEntity light,Sin(a)*lidis,lidis,Cos(a)*lidis
; TurnEntity cube,1,2,3
If vol<>0 Then FreeEntity vol
t1=MilliSecs()
vol=make_volume(cube,lime)
t2=MilliSecs()
RenderWorld()
Text 0,0,"tris "+TrisRendered()
Text 0,16,"ms "+(t2-t1)
Text 0,32,"space=pause"
Flip
While KeyDown(57)
Delay 1
Wend
Wend
End
Function make_volume(source,lite)
; for every triangle create 2 tris in the shadow volume, where one is
; transposed by "infi" in the direction of lightrays
tri_count=0
TFormPoint EntityX(lite),EntityY(lite),EntityZ(lite),0,source
PositionEntity helper2,TFormedX(),TFormedY(),TFormedZ()
litemesh=lite
lite=helper2
vol=CreateMesh()
su2=CreateSurface(vol)
For s=1 To CountSurfaces(source)
su=GetSurface(source,s)
For tri=0 To CountTriangles(su)-1
vx0#=VertexX(su,TriangleVertex(su,tri,0))
vy0#=VertexY(su,TriangleVertex(su,tri,0))
vz0#=VertexZ(su,TriangleVertex(su,tri,0))
vx1#=VertexX(su,TriangleVertex(su,tri,1))
vy1#=VertexY(su,TriangleVertex(su,tri,1))
vz1#=VertexZ(su,TriangleVertex(su,tri,1))
vx2#=VertexX(su,TriangleVertex(su,tri,2))
vy2#=VertexY(su,TriangleVertex(su,tri,2))
vz2#=VertexZ(su,TriangleVertex(su,tri,2))
; create projected vertex coords
PositionEntity helper,vx0,vy0,vz0
PointEntity helper,lite
MoveEntity helper,0,0,-infi
vx0_b#=EntityX(helper)
vy0_b#=EntityY(helper)
vz0_b#=EntityZ(helper)
PositionEntity helper,vx1,vy1,vz1
PointEntity helper,lite
MoveEntity helper,0,0,-infi
vx1_b#=EntityX(helper)
vy1_b#=EntityY(helper)
vz1_b#=EntityZ(helper)
PositionEntity helper,vx2,vy2,vz2
PointEntity helper,lite
MoveEntity helper,0,0,-infi
vx2_b#=EntityX(helper)
vy2_b#=EntityY(helper)
vz2_b#=EntityZ(helper)
; add all potential shadow volume tris and set their ID color
;- AddTriangle su2, v0 ,v1 ,v1_b
vv0=AddVertex(su2,vx0,vy0,vz0)
vv1=AddVertex(su2,vx1,vy1,vz1)
vv2=AddVertex(su2,vx1_b,vy1_b,vz1_b)
icu_code=(tri_count+10)+0
icu_red=(icu_code And $FF0000) Shr 16
icu_green=(icu_code And $FF00) Shr 8
icu_blue=(icu_code And $FF)
VertexColor su2,vv0,icu_red,icu_green,icu_blue
VertexColor su2,vv1,icu_red,icu_green,icu_blue
VertexColor su2,vv2,icu_red,icu_green,icu_blue
AddTriangle su2, vv0 ,vv1 ,vv2
;- AddTriangle su2, v0_b,v0 ,v1_b
vv0=AddVertex(su2,vx0_b,vy0_b,vz0_b)
vv1=AddVertex(su2,vx0,vy0,vz0)
vv2=AddVertex(su2,vx1_b,vy1_b,vz1_b)
icu_code=(tri_count+10)+1
icu_red=(icu_code And $FF0000) Shr 16
icu_green=(icu_code And $FF00) Shr 8
icu_blue=(icu_code And $FF)
VertexColor su2,vv0,icu_red,icu_green,icu_blue
VertexColor su2,vv1,icu_red,icu_green,icu_blue
VertexColor su2,vv2,icu_red,icu_green,icu_blue
AddTriangle su2, vv0 ,vv1 ,vv2
;- AddTriangle su2, v1_b,v1,v2_b
vv0=AddVertex(su2,vx1_b,vy1_b,vz1_b)
vv1=AddVertex(su2,vx1,vy1,vz1)
vv2=AddVertex(su2,vx2_b,vy2_b,vz2_b)
icu_code=(tri_count+10)+2
icu_red=(icu_code And $FF0000) Shr 16
icu_green=(icu_code And $FF00) Shr 8
icu_blue=(icu_code And $FF)
VertexColor su2,vv0,icu_red,icu_green,icu_blue
VertexColor su2,vv1,icu_red,icu_green,icu_blue
VertexColor su2,vv2,icu_red,icu_green,icu_blue
AddTriangle su2, vv0 ,vv1 ,vv2
;- AddTriangle su2, v1,v2,v2_b
vv0=AddVertex(su2,vx1,vy1,vz1)
vv1=AddVertex(su2,vx2,vy2,vz2)
vv2=AddVertex(su2,vx2_b,vy2_b,vz2_b)
icu_code=(tri_count+10)+3
icu_red=(icu_code And $FF0000) Shr 16
icu_green=(icu_code And $FF00) Shr 8
icu_blue=(icu_code And $FF)
VertexColor su2,vv0,icu_red,icu_green,icu_blue
VertexColor su2,vv1,icu_red,icu_green,icu_blue
VertexColor su2,vv2,icu_red,icu_green,icu_blue
AddTriangle su2, vv0 ,vv1 ,vv2
;- AddTriangle su2, v2,v0_b,v2_b
vv0=AddVertex(su2,vx2,vy2,vz2)
vv1=AddVertex(su2,vx0_b,vy0_b,vz0_b)
vv2=AddVertex(su2,vx2_b,vy2_b,vz2_b)
icu_code=(tri_count+10)+4
icu_red=(icu_code And $FF0000) Shr 16
icu_green=(icu_code And $FF00) Shr 8
icu_blue=(icu_code And $FF)
VertexColor su2,vv0,icu_red,icu_green,icu_blue
VertexColor su2,vv1,icu_red,icu_green,icu_blue
VertexColor su2,vv2,icu_red,icu_green,icu_blue
AddTriangle su2, vv0 ,vv1 ,vv2
;- AddTriangle su2, v2,v0,v0_b
vv0=AddVertex(su2,vx2,vy2,vz2)
vv1=AddVertex(su2,vx0,vy0,vz0)
vv2=AddVertex(su2,vx0_b,vy0_b,vz0_b)
icu_code=(tri_count+10)+5
icu_red=(icu_code And $FF0000) Shr 16
icu_green=(icu_code And $FF00) Shr 8
icu_blue=(icu_code And $FF)
VertexColor su2,vv0,icu_red,icu_green,icu_blue
VertexColor su2,vv1,icu_red,icu_green,icu_blue
VertexColor su2,vv2,icu_red,icu_green,icu_blue
AddTriangle su2, vv0 ,vv1 ,vv2
tri_count=tri_count+6
Next
Next
EntityPickMode vol,2
EntityFX vol,1 Or 2
EntityColor vol,0,0,255
RotateEntity vol, EntityPitch(source),EntityYaw(source),EntityRoll(source),1
PositionEntity vol,EntityX(source),EntityY(source),EntityZ(source)
; vol is now a shadow volume, containing the silhouette and a lot of enclosed, hidden tris.
; -------------------------------
; create a copy without the enclosed tris:
vol3=CreateMesh()
su3=CreateSurface(vol3)
PositionEntity helper ,EntityX(lite),EntityY(lite),EntityZ(lite),1
PointEntity helper,source,0
MoveEntity helper,0,0,-pick_back
PositionEntity icucam ,EntityX(litemesh),EntityY(litemesh),EntityZ(litemesh),1
PointEntity icucam,source
MoveEntity icucam,0,0,-pick_back
Dim icu_seen(tri_count)
; prepare scene for ICU scan
sz=64;127
; work around rounding Z errors...
remx#=EntityX(source)
remy#=EntityY(source)
remz#=EntityZ(source)
rempitch#=EntityPitch(source)
remyaw#=EntityYaw(source)
remroll#=EntityRoll(source)
PointEntity source,litemesh
MoveEntity source,0,0,.1
RotateEntity source, rempitch,remyaw,remroll
; eo work around
EntityColor plane,0,0,0
EntityColor source,0,0,0
HideEntity litemesh
CameraViewport icucam,0,0,sz+1,sz+1
;CameraProjMode icucam,1;2 ; better? guess not
;CameraRange icucam,10,5000
;CameraZoom icucam,10
CameraProjMode icucam,2
CameraRange icucam,0.0002,100
CameraZoom icucam,.0001
CameraProjMode camera,0
; create ICU render and scan it for valid ID colors
RenderWorld()
SetBuffer BackBuffer()
LockBuffer()
For j=0 To sz
For i=0 To sz
rgb=ReadPixelFast(i,j) And $FFFFFF
If( rgb > 0) And (rgb<=tri_count+10)
icu_index=rgb-10
icu_seen(icu_index)=icu_seen(icu_index)+1
EndIf
Next
Next
UnlockBuffer()
;PositionEntity source, remx,remy,remz
EntityColor plane,0,255,0
EntityColor source,255,255,255
ShowEntity litemesh
CameraProjMode icucam,0
CameraProjMode camera,1
;Flip 0 ; use this for visual control of the ICU render
;WaitKey()
; create a new volume mesh containing the triangles with detected ICU ID colors (visible tris)
For i=0 To tri_count-1
If icu_seen(i)>0
x0#=VertexX(su2,TriangleVertex(su2,i,0))
y0#=VertexY(su2,TriangleVertex(su2,i,0))
z0#=VertexZ(su2,TriangleVertex(su2,i,0))
x1#=VertexX(su2,TriangleVertex(su2,i,1))
y1#=VertexY(su2,TriangleVertex(su2,i,1))
z1#=VertexZ(su2,TriangleVertex(su2,i,1))
x2#=VertexX(su2,TriangleVertex(su2,i,2))
y2#=VertexY(su2,TriangleVertex(su2,i,2))
z2#=VertexZ(su2,TriangleVertex(su2,i,2))
v0=AddVertex(su3,x0,y0,z0) ; yes, then it must be a silhouette tris
v1=AddVertex(su3,x1,y1,z1)
v2=AddVertex(su3,x2,y2,z2)
AddTriangle(su3,v0,v1,v2)
EndIf
Next
; Goto skip_combi ; use this to see the ICU product only!
FreeEntity vol ; release old volume
vol=vol3
UpdateNormals vol
EntityPickMode vol,2
EntityFX vol,1 Or 2
EntityColor vol,0,0,255
RotateEntity vol, EntityPitch(source),EntityYaw(source),EntityRoll(source),1
PositionEntity vol,EntityX(source),EntityY(source),EntityZ(source)
; now we create a new mesh and use the linepick method to scan trough the
; imperfect shadow volume mesh we just created using the ICU method.
; this Linepick method is more accurate, but much slower. Thanks to the
; ICU method we only have to scan a fraction of the first volume mesh.
PositionEntity helper ,EntityX(litemesh),EntityY(litemesh),EntityZ(litemesh),1
PointEntity helper,source,0
MoveEntity helper,0,0,-pick_back
vol3=CreateMesh()
su3=CreateSurface(vol3)
su2=GetSurface(vol,1)
tri_count=CountTriangles(su2)
For i=0 To tri_count-1
x0#=VertexX(su2,TriangleVertex(su2,i,0))
y0#=VertexY(su2,TriangleVertex(su2,i,0))
z0#=VertexZ(su2,TriangleVertex(su2,i,0))
x1#=VertexX(su2,TriangleVertex(su2,i,1))
y1#=VertexY(su2,TriangleVertex(su2,i,1))
z1#=VertexZ(su2,TriangleVertex(su2,i,1))
x2#=VertexX(su2,TriangleVertex(su2,i,2))
y2#=VertexY(su2,TriangleVertex(su2,i,2))
z2#=VertexZ(su2,TriangleVertex(su2,i,2))
x#=(x0+x1+x2)/divi ; get a triangles interpolated center point
y#=(y0+y1+y2)/divi
z#=(z0+z1+z2)/divi
TFormPoint(x,y,z,vol,0)
he=helper
p=LinePick(EntityX#(he),EntityY#(he),EntityZ#(he) ,TFormedX#()-EntityX#(he),TFormedY#()-EntityY#(he),TFormedZ#()-EntityZ#(he),pick_radius#)
If (p=vol)
If PickedTriangle()=i ; did we pick the tris of the corresponding center point?
v0=AddVertex(su3,x0,y0,z0) ; yes, then it must be a silhouette tris
v1=AddVertex(su3,x1,y1,z1)
v2=AddVertex(su3,x2,y2,z2)
AddTriangle(su3,v0,v1,v2)
EndIf
EndIf
Next
.skip_combi
; (undo cosmetics)
PositionEntity source, remx,remy,remz
FreeEntity vol ; release old volume
vol=vol3
EntityColor vol,255,0,0
EntityFX vol,1 Or 16
EntityAlpha vol,0.5
RotateEntity vol, EntityPitch(source),EntityYaw(source),EntityRoll(source),1
Return vol
;so we first created a mesh containig every potential triangle required for the shadow volume mesh.
;then we used the ICU (Identification by Color Uniqueness) Method to make a rough copy of the
;silhouette triangles (= removing enclosed Triangles). This is not very accurate: there are
;still some triangles copied that should not be there.
;so finally we scan this rough silhouette mesh and create a third mesh, containing only the
;picked Silhouette Triangles.
End Function
Well right now this is the best I can do. Maybe I'll try it again from scratch one day, with a completely diffrent method, preferably the official math way and not cheats like these.