Code archives/3D Graphics - Mesh/CSG Functions another edition

This code has been declared by its author to be Public Domain code.

Download source code

CSG Functions another edition by Markus Rauch2003
Rewritten Code Example from David Dawkins(Starfox)
Big thanks to him !!!
Now it don't loose UV coords and calculate new UVs !!!
And the output Mesh have multiple Surfaces :-)
02.11.2003
Repaint the new target Mesh
with use of GetSurfaceBrush BB =>V1.85

Only you need a test texture , look in CSGExample Function

; MR 02.11.2003 

;####################################################################################################

;Csg Engine 

;Thanks to David Dawkins(Starfox) for the bb code example ;-)

;MR 19.01.2003
;tuned and optimized , now by cube subtract sphere 2 seconds + :-)
;MR 25.01.2003
;add uvmapping :-) slow down but works
;MR 26.01.2003
;Multiple Surfaces :-)
;MR 02.11.2003
;Repaint target Surfaces , need BB 3D => V 1.85 !

;Features: Union, Subtraction, Intersect, Fast enough, Removes bad triangles
;Call the CSG(mesha,meshb,booltype) to do the actual functions

;Limits: Not to fast with many tris
;It works only if all Entity have no Pickmode !

;What CSG() returns is the result mesh, not the actual meshes you send to it
;so you might want to delete those.

;NOTE:AddMesh add no surfaces :-( , and with CopyMesh you became only 1 surface :-(

;####################################################################################################

;Here are the globals
Global csgscale#=1,csgpiv ;The world scale (1- Normal, 2-2x Normal)
Global csgmesh[2] ;Different surface holders for different opereations

;-----------------------

Type CSGVectorType
 Field x#
 Field y#
 Field z#

 Field u# ;Texture Coords !
 Field v#
 Field w#
End Type

;-----------------------

Type CSGTriangleTYPE

 Field id
 Field tarid            ;See Split
 Field del              ;Can be deleted
 Field mindex           ;Mesh Index (NOT a Handle)
 Field sindex						;Surface Index			
 Field tindex 					;Triangle Index

 Field v1x#           ;Edge Vertex 1
 Field v1y#
 Field v1z#
 Field u1#
 Field v1#
 Field w1#

 Field v2x#           ;Edge Vertex 2
 Field v2y#
 Field v2z#
 Field u2#
 Field v2#
 Field w2#

 Field v3x#           ;Edge Vertex 3
 Field v3y#
 Field v3z#
 Field u3#
 Field v3#
 Field w3#

 Field mix#   ;Middle Vector from all Edge Vertices
 Field miy# 
 Field miz#

 Field normx# ;Normal of Triangle
 Field normy# 
 Field normz# 

End Type

;###############################################################################################

.CSGTestIt

;Commend the two lines if you include this CSG file !

CSGExample()
End

;###############################################################################################

Function CSG(mesh1,mesh2,mode=2)

 ;MR 02.11.2003

 ;mode 1 - Union, mode 2 - Subtraction, mode 3 - Intersection

 Local x1#,y1#,z1#
 Local x2#,y2#,z2#
 Local x3#,y3#,z3#

 Local vi1,vi2,vi3    ;Only VertexIndex

 ;-------------------------------------------------------------------------------------- Memory Position and Angles

 Local ex#,ey#,ez#

 ex=EntityX(mesh1,1)
 ey=EntityY(mesh1,1)
 ez=EntityZ(mesh1,1)

 Local epitch#,eyaw#,eroll#

 epitch=EntityPitch(mesh1,1)
 eyaw  =EntityYaw  (mesh1,1)
 eroll =EntityRoll (mesh1,1)

 ;--------------------------------------------------------------------------------------

 Local curmesh
 Local mindex
 Local surf,sindex
 Local SurfaceBrush
 Local tindex
 Local idstate=0
 Local t.CSGTriangleTYPE
 Local t1.CSGTriangleTYPE
 Local t2.CSGTriangleTYPE

 Local ve1.CSGVectorType
 Local ve2.CSGVectorType
 Local ve3.CSGVectorType

 For mindex = 1 To 4

 If mindex = 1 Then curmesh = mesh1
 If mindex = 2 Then curmesh = mesh2
 If mindex = 3 Then curmesh = mesh1
 If mindex = 4 Then curmesh = mesh2

 ;Now copy all 4 Meshes to Buffer

 For sindex = 1 To CountSurfaces(curmesh)

  ;DebugLog "sindex "+sindex

	surf = GetSurface(curmesh,sindex)
	For tindex = 0 To CountTriangles(surf)-1
   
   ;---------------------------------------
  
	 vi1 = TriangleVertex(surf,tindex,0)
	 vi2 = TriangleVertex(surf,tindex,1)
	 vi3 = TriangleVertex(surf,tindex,2)

   ve1=CSGGetVertexRealPosition(curmesh,surf,vi1)
   ve2=CSGGetVertexRealPosition(curmesh,surf,vi2)
   ve3=CSGGetVertexRealPosition(curmesh,surf,vi3)

   t=CSGTriAdd(ve1,ve2,ve3)

	 t\mindex = mindex
	 t\sindex = sindex 
	 t\tindex = tindex
	 t\tarid = -1

   ;---------------------------------------

	Next ;Triangles
 Next ;Surfaces
 Next ;1-4

 CSGTriRemoveDel

 ;--------------------------------------------------------------------------------------

 ;Create the reuse objects
 csgmesh[0] = CreateMesh()
 surf = CreateSurface(csgmesh[0])
 vi1 = AddVertex(surf,-1,0,1)
 vi2 = AddVertex(surf,1,0,1)
 vi3 = AddVertex(surf,1,0,-1)
 AddTriangle(surf,vi1,vi2,vi3)

 ;----------------------------------------

 csgmesh[1] = CreateMesh()
 surf = CreateSurface(csgmesh[1])
 vi1 = AddVertex(surf,-1,0,1)
 vi2 = AddVertex(surf,1,0,1)
 vi3 = AddVertex(surf,1,0,-1)
 AddTriangle(surf,vi1,vi2,vi3)

 ;----------------------------------------

 csgmesh[2] = CreateCube()
 ScaleMesh csgmesh[2],100000*csgscale,0,100000*csgscale ;<--- mesh vertices must be in range of float !

 ;----------------------------------------

 ;Time to split the a polys
 For t1.CSGTriangleTYPE = Each CSGTriangleTYPE
 If t1\mindex = 2
	For t2.CSGTriangleTYPE = Each CSGTriangleTYPE
	If t2\mindex = 1 And t2\tarid <> t1\id
	 If t2\del = 0
	  If CSGTrisIntersect(t1,t2) = 1
	 	 CSGSplit t1,t2
	  EndIf
	 EndIf
	EndIf
	Next
 EndIf
 Next

 ;----------------------------------------

 ;Now split the b poly's
 For t1.CSGTriangleTYPE = Each CSGTriangleTYPE
 If t1\mindex = 3
	For t2.CSGTriangleTYPE = Each CSGTriangleTYPE
	If t2\mindex = 2 And t2\tarid <> t1\id
	 If t2\del = 0
	  If CSGTrisIntersect(t1,t2) = 1
		 CSGSplit t1,t2
	  EndIf
	 EndIf
	EndIf
	Next
 EndIf
 Next

 ;----------------------------------------

 ;Step 2 of reuse
 surf = GetSurface(csgmesh[0],1)
 AddTriangle(surf,2,1,0)

 FreeEntity csgmesh[1]
 FreeEntity csgmesh[2]

 csgpiv = CreatePivot()

 ;----------------------------------------

 ;Setting a flag to delete all the triangles without normals
 For t1.CSGTriangleTYPE = Each CSGTriangleTYPE
  If t1\normx = 0.0 And t1\normy = 0.0 And t1\normz = 0.0 ;And t1\del = 0
   ;DebugLog "NORM=0 : "+t1\normx+" "+t1\normy+" "+t1\normz+" MIndex="+t1\mindex
   Delete t1
  EndIf
 Next

 ;----------------------------------------

 Local epsilon#=0.000001

 ;MeshA
 For t1.CSGTriangleTYPE = Each CSGTriangleTYPE
 If t1\mindex = 1 And t1\del = 0
  cosangle# = -1
  inter = 0
  intermode = 0 ;1 - inside, 2 - shared, 3 - Not inside
  quickdist# = 100000

	For t2.CSGTriangleTYPE = Each CSGTriangleTYPE
	 If t2\mindex = 4
	  res = CSGRayIntersect(t2,t1)
	  If res = 1
	   intx# = PickedX()
	   inty# = PickedY()
	   intz# = PickedZ()
	   dist# = Sqr((intx-t1\mix)*(intx-t1\mix)+(inty-t1\miy)*(inty-t1\miy)+(intz-t1\miz)*(intz-t1\miz))
	   If dist < quickdist
	    quickdist = dist
	    inter = 1
	    cosangle# = (t1\normx*t2\normx)+(t1\normy*t2\normy)+(t1\normz*t2\normz)
	   EndIf
	  EndIf
	 EndIf
	Next
	
	If inter = 1
	 If cosangle > 0 Then intermode = 1
	 If cosangle < 0 Then intermode = 3
	 If quickdist < epsilon ;If the triangle is shared
	  intermode = 2
	 EndIf
	Else
	 intermode = 3
	EndIf

  Select mode
  Case  2
	 If intermode = 1 Or intermode = 2
	  t1\del = 1
	 EndIf
  Case 1
	 If intermode = 1 Or intermode = 2
	  t1\del = 1
	 EndIf
  Case  3
	 If intermode = 3 Or intermode = 2
	  t1\del = 1
	 EndIf
  End Select

  If t1\del=1 Then Delete t1

 EndIf ;Mesh A
 Next ;Tri

 ;----------------------------------------

 ;MeshB
 For t.CSGTriangleTYPE = Each CSGTriangleTYPE
 If t\mindex = 2 And t\del = 0
  cosangle = -1
  inter = 0
  intermode = 0;1 - inside, 2 - shared, 3 - not inside
  quickdist# = 100000
	For tr.CSGTriangleTYPE = Each CSGTriangleTYPE
	 If tr\mindex = 3
	  res = CSGRayIntersect(tr,t)
	  If res = 1
	   intx# = PickedX()
	   inty# = PickedY()
	   intz# = PickedZ()
	   dist# = Sqr((intx-t\mix)*(intx-t\mix)+(inty-t\miy)*(inty-t\miy)+(intz-t\miz)*(intz-t\miz))
	   If dist < quickdist
	    quickdist = dist
	    inter = 1
	    cosangle# = (t\normx*tr\normx)+(t\normy*tr\normy)+(t\normz*tr\normz)
	   EndIf
	  EndIf
	 EndIf
	Next
	
	If inter = 1
	 If cosangle > 0 Then intermode = 1
	 If cosangle < 0 Then intermode = 3
	 If quickdist < epsilon
	  intermode = 2
	 EndIf
	Else
	 intermode = 3
	EndIf
	
  If mode = 2
	 If intermode = 3 Or intermode = 2
	  t\del = 1
	 EndIf
  ElseIf mode = 1
	 If intermode = 1
	  t\del = 1
	 EndIf
  ElseIf mode = 3
	 If intermode = 3
	  t\del = 1
	 EndIf
  EndIf

  If t\del=1 Then Delete t

 EndIf
 Next

 ;----------------------------------------

 FreeEntity csgpiv
 FreeEntity csgmesh[0]

 ;----------------------------------------

 ;Now delete the polys that don't belong(loose polys)

 CSGClearlTris 1 
 CSGClearlTris 2

 ;----------------------------------------------- New Mesh

 Local mesh=CSGMakeNewMesh()
 
 CSGAddTheTriangles mesh,1

 ;Now add Mesh 2 to Mesh 1
 If mode = 2
  CSGAddTheTriangles mesh,2,True ;Flips all the triangles in a mesh
 Else
  CSGAddTheTriangles mesh,2,False
 EndIf

 ;----------------------------------------------- Repaint !

 CSGRepaint mesh,mesh1,mesh2

 ;----------------------------------------------- Reposition Mesh

 PositionMesh   mesh,-ex,-ey,-ez
 PositionEntity mesh, ex, ey, ez

 RotateMesh mesh,      0,-eyaw,0
 RotateMesh mesh,-epitch,    0,0
 RotateMesh mesh,      0,    0,-eroll

 RotateEntity mesh,epitch,eyaw,eroll

 ;-----------------------------------------------

 Delete Each CSGTriangleTYPE ;Free the whole Triangle Collection

 Return mesh

End Function

;###############################################################################################

Function CSGVec.CSGVectorType(x#,y#,z#,u#,v#,w#) 

 ;MR 25.01.2003 

 ;Simple Return a Vector as type :-)

 Local ve.CSGVectorType = New CSGVectorType

 ve\x=x#
 ve\y=y#
 ve\z=z#

 ve\u=u#
 ve\v=v#
 ve\w=w#

 Return ve

End Function

;###############################################################################################

Function CSGTriAdd.CSGTriangleTYPE(v1.CSGVectorType,v2.CSGVectorType,v3.CSGVectorType)

 ;MR 02.11.2003

 Local newid=0
 Local tr.CSGTriangleTYPE = Last CSGTriangleTYPE
 If tr<>Null Then newid = tr\id + 1

 ;------------------------------------------

  t.CSGTriangleTYPE = New CSGTriangleTYPE

  t\id = newid

 ;------------------------------------------ Copy Parameters V1,V2,V3

	t\v1x = v1\x
	t\v1y = v1\y
	t\v1z = v1\z

	t\v2x = v2\x
	t\v2y = v2\y
	t\v2z = v2\z

	t\v3x = v3\x
	t\v3y = v3\y
	t\v3z = v3\z

 ;------------------------------------------ Memory UVW for Vertex V1,V2,V3

	t\u1 = v1\u
	t\v1 = v1\v
	t\w1 = v1\w

	t\u2 = v2\u
	t\v2 = v2\v
	t\w2 = v2\w

	t\u3 = v3\u
	t\v3 = v3\v
	t\w3 = v3\w

 ;------------------------------------------ Middle

  t\mix = (t\v1x + t\v2x + t\v3x) / 3.0
  t\miy = (t\v1y + t\v2y + t\v3y) / 3.0
  t\miz = (t\v1z + t\v2z + t\v3z) / 3.0

 ;------------------------------------------ Normal

  Local ax#,ay#,az#
  Local bx#,by#,bz#

  ax#=t\v2x-t\v1x
  ay#=t\v2y-t\v1y
  az#=t\v2z-t\v1z
	
  bx#=t\v3x-t\v2x
  by#=t\v3y-t\v2y
  bz#=t\v3z-t\v2z

  t\normx=(ay#*bz#)-(az#*by#)
  t\normy=(az#*bx#)-(ax#*bz#)
  t\normz=(ax#*by#)-(ay#*bx#)

  Local normlen# = Sqr#((t\normx*t\normx)+(t\normy*t\normy)+(t\normz*t\normz))
  If normlen > 0.0
   t\normx = t\normx/normlen 
   t\normy = t\normy/normlen 
   t\normz = t\normz/normlen
  Else
   t\del = 1
  EndIf

 ;------------------------------------------

 Return t

End Function

;###############################################################################################
	
Function CSGTrisIntersect(t1.CSGTriangleTYPE,t2.CSGTriangleTYPE)

 ;MR 19.01.2003

 ;if one triangle in another triangle

 Local surf

 surf = GetSurface(csgmesh[0],1) ;Dummy Mesh with one triangle :-)

 VertexCoords(surf,0,t1\v1x,t1\v1y,t1\v1z)
 VertexCoords(surf,1,t1\v2x,t1\v2y,t1\v2z)
 VertexCoords(surf,2,t1\v3x,t1\v3y,t1\v3z)

 surf = GetSurface(csgmesh[1],1)

 VertexCoords(surf,0,t2\v1x,t2\v1y,t2\v1z)
 VertexCoords(surf,1,t2\v2x,t2\v2y,t2\v2z)
 VertexCoords(surf,2,t2\v3x,t2\v3y,t2\v3z)

 Return MeshesIntersect(csgmesh[0],csgmesh[1])

End Function

;###############################################################################################

Function CSGRayIntersect(t1.CSGTriangleTYPE,t2.CSGTriangleTYPE)

 ;MR 02.11.2003

 ;i think it used to find the triangles that can be deleted

 Local surf = GetSurface(csgmesh[0],1) ;Dummy Mesh with one triangle :-)

 VertexCoords(surf,0,t1\v1x,t1\v1y,t1\v1z)
 VertexCoords(surf,1,t1\v2x,t1\v2y,t1\v2z)
 VertexCoords(surf,2,t1\v3x,t1\v3y,t1\v3z)

 Local piv = csgpiv
 RotateEntity piv,0,0,0 ;!?
 PositionEntity(piv,t2\mix  ,t2\miy  ,t2\miz)
 AlignToVector (piv,t2\normx,t2\normy,t2\normz,3)
 MoveEntity piv,0,0,100000.0*csgscale ;<--- !

 EntityPickMode csgmesh[0],2

 Local distx# = EntityX(piv)-t2\mix
 Local disty# = EntityY(piv)-t2\miy
 Local distz# = EntityZ(piv)-t2\miz
 Local picked = LinePick(t2\mix,t2\miy,t2\miz,distx,disty,distz)

 EntityPickMode csgmesh[0],0

 If picked Then
  Return 1
 Else
  Return 0
 EndIf

End Function

;###############################################################################################

Function CSGSplit(t1.CSGTriangleTYPE,t2.CSGTriangleTYPE)

 ;MR 02.11.2003

 ;if CSGTrisIntersect = true than split it to new triangles

 ;now the new triangles are in menory triangle type collection !

 If t1 = Null Or t2 = Null Then Return 0

;------------------------------------------------------------

 Local newvx#[3]
 Local newvy#[3]
 Local newvz#[3]

 Local newu#[3]
 Local newv#[3]
 Local neww#[3]

 Local v.CSGVectorType 

 Local edge1,edge2,edge3

 Local epsilon#=0.000001 

;------------------------------------------------------------

 Local cube = csgmesh[2]
 EntityPickMode cube,2
 PositionEntity cube,t1\mix,t1\miy,t1\miz
 RotateEntity cube,0,0,0
 AlignToVector(cube,t1\normx,t1\normy,t1\normz,2)

;------------------------------------------------------------

 ;Edge1 to 2

 distx# = t2\v1x-t2\v2x
 disty# = t2\v1y-t2\v2y
 distz# = t2\v1z-t2\v2z

 picked = LinePick(t2\v2x,t2\v2y,t2\v2z,distx,disty,distz)
 If picked = cube
  newvx[1] = PickedX()
  newvy[1] = PickedY()
  newvz[1] = PickedZ()

  v=CSGPickedUVW(t2)

  newu[1]=v\u
  newv[1]=v\v
  neww[1]=v\w
 
  distx# = newvx[1] - t2\v2x
  disty# = newvy[1] - t2\v2y
  distz# = newvz[1] - t2\v2z
  cdist# = Sqr(distx*distx + disty*disty + distz*distz)
  If cdist <= epsilon
   edge1 = False
  Else
   edge1 = True
  EndIf

 EndIf

;------------------------------------------------------------

 ;Edge2 to 3

 distx# = t2\v2x-t2\v3x
 disty# = t2\v2y-t2\v3y
 distz# = t2\v2z-t2\v3z

 picked = LinePick(t2\v3x,t2\v3y,t2\v3z,distx,disty,distz)
 If picked = cube
  newvx[2] = PickedX()
  newvy[2] = PickedY()
  newvz[2] = PickedZ()

  v=CSGPickedUVW(t2)

  newu[2]=v\u
  newv[2]=v\v
  neww[2]=v\w

  distx# = newvx[2] - t2\v3x
  disty# = newvy[2] - t2\v3y
  distz# = newvz[2] - t2\v3z
  cdist# = Sqr(distx*distx + disty*disty + distz*distz)
  If cdist <= epsilon
   edge2 = False
  Else
   edge2 = True
  EndIf

 EndIf

;------------------------------------------------------------

 ;Edge3 to 1

 distx# = t2\v3x-t2\v1x
 disty# = t2\v3y-t2\v1y
 distz# = t2\v3z-t2\v1z

 picked = LinePick(t2\v1x,t2\v1y,t2\v1z,distx,disty,distz)
 If picked = cube
  newvx[3] = PickedX()
  newvy[3] = PickedY()
  newvz[3] = PickedZ()

  v=CSGPickedUVW(t2)

  newu[3]=v\u
  newv[3]=v\v
  neww[3]=v\w

  distx# = newvx[3] - t2\v1x
  disty# = newvy[3] - t2\v1y
  distz# = newvz[3] - t2\v1z
  cdist# = Sqr(distx*distx + disty*disty + distz*distz)
  If cdist <= epsilon
   edge3 = False
  Else
   edge3 = True
  EndIf

 EndIf

;------------------------------------------------------------

 EntityPickMode cube,0

 If edge1=0 And edge2=0 And edge3=0 Then Return 0

;------------------------------------------------------------

 Local v1.CSGVectorType = New CSGVectorType
 Local v2.CSGVectorType = New CSGVectorType
 Local v3.CSGVectorType = New CSGVectorType
 Local v4.CSGVectorType = New CSGVectorType
 Local v5.CSGVectorType = New CSGVectorType

 Local mindex = t2\mindex
 Local sindex = t2\sindex
 Local tm.CSGTriangleTYPE

;------------------------------------------------------------

 If edge1 And edge2

  t2\del = 1

  v1 = CSGVec(t2\v1x,t2\v1y,t2\v1z,t2\u1,t2\v1,t2\w1)
  v2 = CSGVec(newvx[1],newvy[1],newvz[1],newu[1],newv[1],neww[1])
  v3 = CSGVec(t2\v2x,t2\v2y,t2\v2z,t2\u2,t2\v2,t2\w2)
  v4 = CSGVec(newvx[2],newvy[2],newvz[2],newu[2],newv[2],neww[2])
  v5 = CSGVec(t2\v3x,t2\v3y,t2\v3z,t2\u3,t2\v3,t2\w3)

  tm = CSGTriAdd(v1,v2,v5)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v2,v4,v5)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v2,v3,v4)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

 ElseIf edge1 And edge3

  t2\del = 1

  v1 = CSGVec(t2\v1x,t2\v1y,t2\v1z,t2\u1,t2\v1,t2\w1)
  v2 = CSGVec(newvx[1],newvy[1],newvz[1],newu[1],newv[1],neww[1])
  v3 = CSGVec(t2\v2x,t2\v2y,t2\v2z,t2\u2,t2\v2,t2\w2)
  v4 = CSGVec(t2\v3x,t2\v3y,t2\v3z,t2\u3,t2\v3,t2\w3)
  v5 = CSGVec(newvx[3],newvy[3],newvz[3],newu[3],newv[3],neww[3])

  tm = CSGTriAdd(v1,v2,v5) ;1,2,5
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v2,v4,v5)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v2,v3,v4)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

 ElseIf edge2 And edge3

  t2\del = 1

  v1 = CSGVec(t2\v1x,t2\v1y,t2\v1z,t2\u1,t2\v1,t2\w1)
  v2 = CSGVec(t2\v2x,t2\v2y,t2\v2z,t2\u2,t2\v2,t2\w2)
  v3 = CSGVec(newvx[2],newvy[2],newvz[2],newu[2],newv[2],neww[2])
  v4 = CSGVec(t2\v3x,t2\v3y,t2\v3z,t2\u3,t2\v3,t2\w3)
  v5 = CSGVec(newvx[3],newvy[3],newvz[3],newu[3],newv[3],neww[3])

  tm = CSGTriAdd(v1,v2,v5)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v2,v3,v5)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v3,v4,v5)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

 ElseIf edge2

  t2\del = 1

  v1 = CSGVec(t2\v1x,t2\v1y,t2\v1z,t2\u1,t2\v1,t2\w1)
  v2 = CSGVec(t2\v2x,t2\v2y,t2\v2z,t2\u2,t2\v2,t2\w2)
  v3 = CSGVec(newvx[2],newvy[2],newvz[2],newu[2],newv[2],neww[2])
  v4 = CSGVec(t2\v3x,t2\v3y,t2\v3z,t2\u3,t2\v3,t2\w3)

  tm = CSGTriAdd(v1,v2,v3)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v1,v3,v4)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

 ElseIf edge1

  t2\del = 1

  v1 = CSGVec(t2\v1x,t2\v1y,t2\v1z,t2\u1,t2\v1,t2\w1)
  v2 = CSGVec(newvx[1],newvy[1],newvz[1],newu[1],newv[1],neww[1]) 
  v3 = CSGVec(t2\v2x,t2\v2y,t2\v2z,t2\u2,t2\v2,t2\w2)
  v4 = CSGVec(t2\v3x,t2\v3y,t2\v3z,t2\u3,t2\v3,t2\w3)

  tm = CSGTriAdd(v1,v2,v4)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v2,v3,v4)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

 ElseIf edge3

  t2\del = 1

  v1 = CSGVec(t2\v1x,t2\v1y,t2\v1z,t2\u1,t2\v1,t2\w1)
  v2 = CSGVec(t2\v2x,t2\v2y,t2\v2z,t2\u2,t2\v2,t2\w2)
  v3 = CSGVec(t2\v3x,t2\v3y,t2\v3z,t2\u3,t2\v3,t2\w3)
  v4 = CSGVec(newvx[3],newvy[3],newvz[3],newu[3],newv[3],neww[3])

  tm = CSGTriAdd(v1,v2,v4)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

  tm = CSGTriAdd(v2,v3,v4)
  tm\mindex= mindex
  tm\sindex= sindex
  tm\tarid = t1\id

 EndIf

 If t2\del=1 Then Delete t2

End Function

;###############################################################################################

Function CSGTriRemoveDel()

 ;MR 19.01.2003

 ;-----------------------------------------------

 For t.CSGTriangleTYPE = Each CSGTriangleTYPE
  If t\del=1 Then Delete t  
 Next

End Function

;###############################################################################################

Function CSGMakeNewMesh()

 ;MR 26.01.2003

 ;----------------------------------------------- New Mesh

 Local m=CreateMesh()

 ;----------------------------------------------- Find Max Surfaces for Mesh 1

 Local smax1=0

 For t.CSGTriangleTYPE = Each CSGTriangleTYPE
  If t\mindex=1 Then
   If t\sindex>smax1 Then smax1=t\sindex  
  EndIf
 Next

 ;----------------------------------------------- Find Max Surfaces for Mesh 2

 Local smax2=0

 For t.CSGTriangleTYPE = Each CSGTriangleTYPE
  If t\mindex=2 Then
   If t\sindex>smax2 Then smax2=t\sindex  
  EndIf
 Next

 ;----------------------------------------------- Add Max Surface Index from Mesh 1 to Mesh 2

 For t.CSGTriangleTYPE = Each CSGTriangleTYPE
  If t\mindex=2 Then
   t\sindex=t\sindex+smax1
  EndIf
 Next

 ;----------------------------------------------- Create all Surfaces

 Local smax=smax1+smax2 ;all needed surfaces

 Local si

 Local surf

 For si=1 To smax

  surf=CreateSurface(m)
 
 Next

 ;-----------------------------------------------

 Return m

End Function

;###############################################################################################

Function CSGAddTheTriangles(m,mindex,FlipTriangles=0)

 ;MR 02.11.2003

 ;----------------------------------------------- Bring Triangle to Surface

 Local v1,v2,v3 ;VertexIndex

 Local surf=0

 Local si

 ;-----------------------------------------------

 For si=1 To CountSurfaces(m)

  For t.CSGTriangleTYPE = Each CSGTriangleTYPE
   If t\del=0 And t\mindex=mindex And t\sindex=si Then

    surf=GetSurface(m,si)  

    v1=AddVertex(surf,t\v1x,t\v1y,t\v1z)
    v2=AddVertex(surf,t\v2x,t\v2y,t\v2z)
    v3=AddVertex(surf,t\v3x,t\v3y,t\v3z)

    VertexNormal surf,v1,t\normx,t\normy,t\normz 
    VertexNormal surf,v2,t\normx,t\normy,t\normz 
    VertexNormal surf,v3,t\normx,t\normy,t\normz 

    VertexTexCoords surf,v1,t\u1,t\v1,t\w1 ;Set UV Coords !
    VertexTexCoords surf,v2,t\u2,t\v2,t\w2 
    VertexTexCoords surf,v3,t\u3,t\v3,t\w3 

    If FlipTriangles=0 Then
     AddTriangle surf,v1,v2,v3
    Else
     AddTriangle surf,v3,v2,v1
    EndIf

    Delete t ;Remove the Triangle from Collection !

   EndIf  
  Next

 Next ;All Surfaces

 ;-----------------------------------------------

 UpdateNormals m

End Function

;###############################################################################################

Function CSGRepaint(meshnew,mesh1,mesh2)

 ;MR 02.11.2003

 ;Repaint all Surfaces :-)

 ;----------------------------------------------------------------------------------------------

  Local cc1=0 ;New SurfaceCount !

  Local c1

  ;Paint New Surfaces from Mesh A  

  If CountSurfaces (mesh1)=>1 Then 
   For c1=1 To CountSurfaces(mesh1)
    cc1=cc1+1
   
    If CountSurfaces(meshnew)=>cc1 Then 
     PaintSurface GetSurface(meshnew,cc1),GetSurfaceBrush(GetSurface(mesh1,c1))
    EndIf 

   Next
  EndIf ;CountSurfaces=>1

 ;----------------------------------------------------------------------------------------------

  ;Paint New Surfaces from Mesh B  

  If CountSurfaces(mesh2)=>1 Then 
   For c1=1 To CountSurfaces(mesh2)
    cc1=cc1+1
   
    If CountSurfaces(meshnew)=>cc1 Then 
     PaintSurface GetSurface(meshnew,cc1),GetSurfaceBrush(GetSurface(mesh2,c1))
    EndIf 

   Next
  EndIf ;CountSurfaces=>1

 ;----------------------------------------------------------------------------------------------

End Function

;###############################################################################################

Function CSGClearLTris(mindex)

 ;MR 26.01.2003

 ;Clears the triangles that have no partners
 Local epsilon#=0.000001 

 Local count=0
 Local full
 Local car

 Local distx#
 Local disty#
 Local distz#
 Local cdist#

 Local vax#
 Local vay#
 Local vaz#

 Local vbx#
 Local vby#
 Local vbz#

 For t.CSGTriangleTYPE = Each CSGTriangleTYPE
  If t\mindex = mindex And t\del = 0
   count=0
   For full = 0 To 2
    Select full
    Case 0
     vax#=t\v1x
     vay#=t\v1y
     vaz#=t\v1z
    Case 1
     vax#=t\v2x
     vay#=t\v2y
     vaz#=t\v2z
    Case 2
     vax#=t\v3x
     vay#=t\v3y
     vaz#=t\v3z
    End Select

 	  For tom.CSGTriangleTYPE = Each CSGTriangleTYPE
 	   If t\id<>tom\id And tom\del = 0 And tom\mindex = mindex
	    cdist = 0
	    For car = 0 To 2
       Select car
       Case 0
        vbx#=tom\v1x
        vby#=tom\v1y
        vbz#=tom\v1z
       Case 1
        vbx#=tom\v2x
        vby#=tom\v2y
        vbz#=tom\v2z
       Case 2
        vbx#=tom\v3x
        vby#=tom\v3y
        vbz#=tom\v3z
       End Select
		   distx# = vbx - vax
		   disty# = vby - vay
		   distz# = vbz - vaz 
		   cdist# = Sqr(distx*distx + disty*disty + distz*distz)
		   If cdist <= epsilon Then Exit
	    Next
 	    If cdist >= 0.0 Then count = count + 1:Exit
	   EndIf
	  Next
   Next

   If count < 3
    Delete t
   EndIf
  EndIf
 Next

End Function

;############################################################################################################

Function CSGGetVertexRealPosition.CSGVectorType(entity,surf,vindex)

 Local pivot1=CreatePivot()
 Local pivot2=CreatePivot(pivot1)

 Local vreal.CSGVectorType =New CSGVectorType

 PositionEntity pivot1,0,0,0
 PositionEntity pivot2,VertexX(surf,vindex),VertexY(surf,vindex),VertexZ(surf,vindex)
 RotateEntity pivot1,EntityPitch(entity,True),EntityYaw(entity,True),EntityRoll(entity,True)
 
 vreal\x=EntityX(pivot2,True)+EntityX(entity,True)
 vreal\y=EntityY(pivot2,True)+EntityY(entity,True)
 vreal\z=EntityZ(pivot2,True)+EntityZ(entity,True)

 vreal\u=VertexU(surf,vindex)
 vreal\v=VertexV(surf,vindex)
 vreal\w=VertexW(surf,vindex)

 FreeEntity pivot2
 FreeEntity pivot1

 Return vreal

End Function

;#################################################################################################### UVW

Function CSGPickedUVW.CSGVectorType(t2.CSGTriangleTYPE)

  ;MR 25.01.2003

  Local ve.CSGVectorType = New CSGVectorType

  Local px#=PickedX()
  Local py#=PickedY()
  Local pz#=PickedZ() 

  Local pnx#=t2\normx
  Local pny#=t2\normy
  Local pnz#=t2\normz

  ;DebugLog "Picked XYZ "+px+" "+py+" "+pz
		
		; Select which component of xyz coordinates to ignore
		Local coords = 3

		If Abs(PNX) > Abs(PNY)
			If Abs(PNX)>Abs(PNZ) Then coords = 1
		Else
			If Abs(PNY)>Abs(PNZ) Then coords = 2
		EndIf

    ;DebugLog "coords "+coords
		
		Local a0#,a1#,b0#,b1#,c0#,c1#
		
		If (coords = 3)

 		  ;xy components

			; edge 1
			a0# = t2\v2x - t2\v1x
			a1# = t2\v2y - t2\v1y
		
			; edge 2
			b0# = t2\v3x - t2\v1x
			b1# = t2\v3y - t2\v1y

			; picked offset from triangle vertex 1
			c0# = px - t2\v1x
			c1# = py - t2\v1y
		Else		
			If (coords = 2)
				; xz components

				; edge 1
				a0# = t2\v2x - t2\v1x
				a1# = t2\v2z - t2\v1z
		
				; edge 2
				b0# = t2\v3x - t2\v1x
				b1# = t2\v3z - t2\v1z

				; picked offset from triangle vertex 1
				c0# = px - t2\v1x
				c1# = pz - t2\v1z
			Else
				; yz components

				; edge 1
				a0# = t2\v2y - t2\v1y
				a1# = t2\v2z - t2\v1z
		
				; edge 2
				b0# = t2\v3y - t2\v1y
				b1# = t2\v3z - t2\v1z

				; picked offset from triangle vertex 1
				c0# = py - t2\v1y
				c1# = pz - t2\v1z
			End If
		End If
						
		;
		; u and v are offsets from vertex 0 along edge 0 and edge 1
		; using these it is possible to calculate the Texture UVW coordinates
		; of the picked XYZ location
		;
		; a0*u + b0*v = c0
		; a1*u + b1*v = c1
		;
		; solve equation (standard equation with 2 unknown quantities)
		; check a math book to see why the following is true
		;
		Local u# = (c0*b1 - b0*c1) / (a0*b1 - b0*a1)
		Local v# = (a0*c1 - c0*a1) / (a0*b1 - b0*a1)

    ;DebugLog "U "+u+" V "+v
				
		; Calculate picked uvw's 
		ve\u = (t2\u1 + ((t2\u2 - t2\u1) * u) + ((t2\u3 - t2\u1) * v))
		ve\v = (t2\v1 + ((t2\v2 - t2\v1) * u) + ((t2\v3 - t2\v1) * v))
		ve\w = (t2\w1 + ((t2\w2 - t2\w1) * u) + ((t2\w3 - t2\w1) * v))
		
    ;DebugLog "U "+ve\u+" V "+ve\v

 Return ve
		
End Function

;###############################################################################################

Function CSGExample()

 ;MR 02.11.2003

 AppTitle "CSG Test"

 Graphics3D 640,480

 ;---------------------------------------------------------------------------------------

 Local texture=LoadTexture("ch1.bmp")
 Local brush=CreateBrush(255,255,255)

 BrushTexture brush,texture

 ;cube =CSGAddMyMeshCubeX(   0,0,0,0,20, 4,20,1,1,1,brush,brush,brush,brush,brush,brush)
 ;cube =CSGAddMyMeshCubeX(cube,0,0,0,10,10,10,1,1,1,brush,brush,brush,brush,brush,brush)

 cube = CreateCube()
 ScaleMesh cube,10,2,10
 For s=1 To CountSurfaces(cube)
  PaintSurface GetSurface(cube,s),brush
 Next 
 EntityFX cube,16

 ;TurnEntity cube,90,0,0
 ;MoveEntity cube,0,5,0

 ;---------------------------------------------------------------------------------------

 Local tex2=CreateTexture(64,64)
 SetBuffer TextureBuffer(tex2) 
 Color 128,0,0
 Rect 0,0,64,64 
 Color 255,0,0
 Rect 0,0,32,32
 Rect 32,32,32,32
 SetBuffer BackBuffer()
 Local br2=CreateBrush()
 BrushTexture br2,tex2
 ScaleTexture tex2,.25,.25

 ;cube2 = CreateSphere(8)
 ;cube2 = CreateCube()
 cube2 = CreateCylinder(16)
 
 ScaleMesh cube2,6,6,6
 For s=1 To CountSurfaces(cube2)
  PaintSurface GetSurface(cube2,s),br2
 Next 

 ;---------------------------------------------------------------------------------------

 cam = CreateCamera()
 MoveEntity cam,0,10,-20
 PointEntity cam,cube2
 CameraClsColor cam,0,0,80 
 CameraRange cam,1,2000

 ;---------------------------------------------------------------------------------------

 Local light=CreateLight()
 TurnEntity light,45,1,0
 LightRange light,50
 PositionEntity light,0,50,-50

 ;---------------------------------------------------------------------------------------

 UpdateNormals cube
 UpdateNormals cube2
 EntityAlpha cube2,.5

 ;---------------------------------------------------------------------------------------

 Local wire=0
 Local Mode=2

 Color 255,255,255
 While Not KeyHit(1) ;ESC

  If KeyHit(2) Then Mode=1 ;1
  If KeyHit(3) Then Mode=2 ;2
  If KeyHit(4) Then Mode=3 ;3

  If KeyDown(205) Then TranslateEntity cube2,.1,0,0
  If KeyDown(203) Then TranslateEntity cube2,-.1,0,0

  If KeyDown(30)  Then TranslateEntity cube2,0,0,.1
  If KeyDown(44)  Then TranslateEntity cube2,0,0,-.1 ;Y German

  If KeyDown(200) Then TranslateEntity cube2,0,.1,0
  If KeyDown(208) Then TranslateEntity cube2,0,-.1,0

  If KeyDown(37) Then ;K
   ;ClearSurface GetSurface(cube,7),True,True ;TEST
  EndIf

  If KeyDown(18) Then ;E
   TurnEntity cube,-1,0,0   
  EndIf

  If KeyDown(19) Then ;R
   TurnEntity cube,0,-1,0   
  EndIf

  If KeyDown(20) Then ;T
   TurnEntity cube,0,0,-1   
  EndIf

  If KeyHit(21) Then ;Z    German
   TurnEntity cube2,-45,0,0   
  EndIf

  If KeyHit(31) Then ;S = Screenshot
   SaveBuffer FrontBuffer(),"screenshot.bmp"
  EndIf

  If KeyHit(17) ;W
   wire = 1 - wire
   WireFrame wire
  EndIf
 
  If KeyHit(57) ;Space
   Cls
   RenderWorld
   Text 0,0,"Wait ..."
   Flip 
   e = MilliSecs()
   man = csg(cube,cube2,Mode) ;1 Add , 2 Sub , 3 Diff
   e = MilliSecs() -e
   FreeEntity cube
   cube = man
   ;PaintEntity cube,brush
   EntityFX cube,16
   FlushKeys
  EndIf

  UpdateWorld
  RenderWorld
  Text 0,0,"Time: "+e+" ms"
  Text 0,15*1,"Arrows to Move Left/Right/Up/Down"
  Text 0,15*2,"A/Z to move Forward and Back"
  Text 0,15*3,"Tris All : "+TrisRendered()
  Text 0,15*4,"Tris 1 : "+CSGCountAllTriangles(cube )+" Surfaces : " +CountSurfaces(cube)
  Text 0,15*5,"Tris 2 : "+CSGCountAllTriangles(cube2)
  Text 0,15*6,"Mode: "+Mode
  Text 0,15*7,"E,R,T Rotate Obj. 1"
  Text 0,15*8,"Y Rotate Obj. 2"
  Text 0,15*9,"S Screenshot"
  Flip
 Wend

End Function

;#################################################################################################### 

Function CSGCountAllTriangles(mesh) ;only for example

 ;without child entitys

 Local si

 If mesh=0 Then Return 0

 Local c=0

 For si=1 To CountSurfaces(mesh)
  c=c+CountTriangles(GetSurface(mesh,si))
 Next 

 Return c

End Function

;#################################################################################################### 

Function CSGAddMyMeshCubeX(m,x1#,y1#,z1#,x2#,y2#,z2#,cx,cy,cz,br_top,br_bottom,br_left,br_right,br_front,br_back,o_top=1,o_bottom=1,o_left=1,o_right=1,o_front=1,o_back=1)

 ;MR 31.10.2002

 ;Create a Cube centered

 ;m       =Entity Handle 0=Create a new one :-)
 ;x1,x2   =From X1 To X2
 ;cx,cy,cy=Center  
 ;br_     =Brush  Handle
 ;o_      =Optional 1=Create 0=No Create  
 
  Local w#,h#,d#

 ;w#=witdh  (X)
 ;h#=height (Y)
 ;d#=depth  (Z)

  w#=x2#-x1#
  h#=y2#-y1#
  d#=z2#-z1#

  ;--------------------- Center ?

  If cx=1 Then 
   x1=x1-w/2.0
   x2=x2-w/2.0
  EndIf

  If cy=1 Then 
   y1=y1-h/2.0
   y2=y2-h/2.0
  EndIf

  If cz=1 Then 
   z1=z1-d/2.0
   z2=z2-d/2.0
  EndIf

  ;-----------------------------

  If m=0 Then
	 m=CreateMesh()
  EndIf

  ;-----------------------------

	;top face
	If o_top=1 Then 
	 s=CreateSurface( m , Br_Top)
	 AddVertex s,x1,y2,z2,0,1:AddVertex s,x2,y2,z2,0,0
	 AddVertex s,x2,y2,z1,1,0:AddVertex s,x1,y2,z1,1,1
	 AddTriangle s,0,1,2:AddTriangle s,0,2,3
  EndIf

	;bottom face	
  If o_bottom=1 Then
	 s=CreateSurface( m , Br_Bottom)
	 AddVertex s,x1,y1,z1,1,0:AddVertex s,x2,y1,z1,1,1
	 AddVertex s,x2,y1,z2,0,1:AddVertex s,x1,y1,z2,0,0
	 AddTriangle s,0,1,2:AddTriangle s,0,2,3
  EndIf

	;left face
  If o_left=1 Then
	 s=CreateSurface( m , Br_Left)
	 AddVertex s,x1,y2,z2,0,0:AddVertex s,x1,y2,z1,1,0
	 AddVertex s,x1,y1,z1,1,1:AddVertex s,x1,y1,z2,0,1
	 AddTriangle s,0,1,2:AddTriangle s,0,2,3
  EndIf 

	;right face
  If o_right=1 Then
	 s=CreateSurface( m , Br_Right)
	 AddVertex s,x2,y2,z1,0,0:AddVertex s,x2,y2,z2,1,0
	 AddVertex s,x2,y1,z2,1,1:AddVertex s,x2,y1,z1,0,1
	 AddTriangle s,0,1,2:AddTriangle s,0,2,3
  EndIf

	;front face
  If o_front=1 Then
	 s=CreateSurface( m , Br_Front)
	 AddVertex s,x1,y2,z1,0,0:AddVertex s,x2,y2,z1,1,0
	 AddVertex s,x2,y1,z1,1,1:AddVertex s,x1,y1,z1,0,1
	 AddTriangle s,0,1,2:AddTriangle s,0,2,3
  EndIf

	;back face
  If o_back=1 Then
	 s=CreateSurface( m , Br_Back)
	 AddVertex s,x2,y2,z2,0,0:AddVertex s,x1,y2,z2,1,0
	 AddVertex s,x1,y1,z2,1,1:AddVertex s,x2,y1,z2,0,1
	 AddTriangle s,0,1,2:AddTriangle s,0,2,3
  EndIf

  UpdateNormals m
	  
  ;EntityPickMode m,2 ;Poly

  Return m  

End Function

;####################################################################################################

Comments

Picklesworth2005
Wow.
Thank you. You are David's Co-god.


Pao Hang2005
Good god man... This is great!!!!


John Blackledge2005
This is brilliant!
Such an obvious way to work.

Just wish I had time to build an editor around this code, but my guess is that someone's already working on it :-)

Thanks for sharing.


KimoTech2006
but when you carve a cube inside the already carved mesh, then there are some polygons that are being deleted, and when you carve a mesh a couple of times, the polycount is increased largely.. is there a way to fix this? :)


Warren2006
That's what CSG does. Carving shapes creates more polygons. Just the way it is.


jfk EO-111102006
It may always be a good idea to use a polygon reduction tool that will at least optimize the polycount. CSG tends to fragment things unneccessarily.


KimoTech2006
I mean this:


If u carve a cube two times in the already carved place, it removes some polys that makes holes in the carved object..


Boiled Sweets2006
Nice, but what's the point of this?


Rook Zimbabwe2006
Well... it is basically RHINO 3D... albeit an early version... You have the beggining of a 3D Model Creation program here. How to write what you created into a reckognizable format (B3D, X, 3DS etc.) is the hard part.


kevin80842006
Rhino3d is excellent. Milkshape would be much better if it had boolean operations as well. Makes it a snap to create complex models that would otherwise make you go through contortions.
Thank you for the CSG Engine code!


Leon Drake2007
i noticed when you try to subtract a shape along the wall or floor of a mesh it has a tendancy to remove parts of the wall and floor making gaps in the mesh.. which this CSG function is great for making levels if you keep meshes seperate instead of carving out of one giant cube. But i noticed when i commented out this section of code under MESHB

If mode = 2
	 If intermode = 3 ;Or intermode = 2<----------
	  t\del = 1
	 EndIf
  ElseIf mode = 1
	 If intermode = 1
	  t\del = 1
	 EndIf
  ElseIf mode = 3
	 If intermode = 3
	  t\del = 1
	 EndIf
  EndIf

It doesnt carve out gaps in the walls and floor. which actually makes this CSG lib work better for indoor level carving.

Although that means on a shared edge on the outside of the shape its not going to be deleted either.. which well doesnt bother me.. i can sinply move the brush over to be intersected on the outside opposed to shared to open a hole.

I know shared edges are tricky.. shoot i cant really completely grasp CSG maths but i can tell they are tricky from some of the tuts ive read


_332007
Genius piece of code right here. This is code that I find much much more useful than marching cubes, definately. This should be on a golden disk with inscriptions: Keep in a safe place.


Trader35642007
Lol, thats cool! Now we can make it so you can blow holes in walls with bazooka!!

All we then need is a class to define what is CSG'able and what is not


Code Archives Forum