Code archives/3D Graphics - Mesh/Autosmooth
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
The function takes a mesh and a smoothing angle, and creates a new mesh, with the same textures, colors etc. It allows for making a mesh totally flat and "explodable", all smooth and everything in between that. A cylinder can have flat end cappings, and still be smooth on the sides, a sphere can be flat-shaded or smooth etc. It also doubles as a mesh optimizer/weld function with threshold values. | |||||
;------------------------------------------------------------ ; ; Autosmooth by Peter Schuetz ; ; Function to give a mesh hard/soft edges ; Welds/explodes vertices ; ; Works on single meshes ; Check for children before calling! ; ; Version 1.0, 18 Martch 2002 ; Rev 1 30 Martch 2002 ; Changed defaults and description ; Rev 2 5 April 2002 ; Removed obsolete params in Showmodel() ; ; Public Domain, but try to give credit if used :) ; ;------------------------------------------------------------ ;Bank offsets for Vertices Const VertStructSize=80 Const C_X=0 Const C_Y=4 Const C_Z=8 Const C_NX=12 Const c_NY=16 Const C_NZ=20 Const C_R=24 Const C_G=28 Const C_B=32 Const C_U=36 Const C_V=40 Const C_W=44 Const C_AvgNX=48 Const C_AvgNY=52 Const C_AvgNZ=56 Const C_ISUSED=60 Const C_NewIndex=64 Const C_Qx=68 Const C_PREV=72 Const C_NEXT=76 ;Bank offsets for Triangles Const TriStructSize=12 Const C_V1=0 Const C_V2=4 Const C_V3=8 ; Parameters ; ; m = The Mesh to smooth ; sin_angle = The angle below witch triangles are to be smooth shaded ; If sin_angle is less than -1, all smooth everything welded ; If sin_angle is larger than 1, all flat, every triangle has its own set of vertices ; vThresh = weld distancee for vertices (Box compare) ; tThresh = weld distancee for texture coords (Box compare) ; cThresh = weld distancee for color values (Not tested, commented out) Function AutoSmooth(m,sin_angle#,vThresh#=.00001,tThresh#=.00001,cThresh#=5) Local maxVcount=0 Local maxTcount=0 ;Local vThresh#=0.00001 ; ajust this if you poly collapses or does not weld ;Local tThresh#=0.000001 ;Local cThresh#=.1 ; not testet! Local sc,k,vc,tc,b,i,v,t,v1,v2,v3,vOff,tOff Local surf Local B1,B2,B3,B4 Local qGrups Local minX#, xRange#,maxX#, xScalFactor# Local vprev,vnext Local Ax#,Ay#,Az#,Bx#,By#,Bz#,Cx#,Cy#,Cz#,vLen# Local vx#,vy#,vz#,tU#,tV#,tW#,vR#,vG#,vB# Local nx1#,ny1#,nz1#,nx2#,ny2#,nz2# Local OrgVOff1,OrgVOff2,OrgVOff3 ;calculate max size of banks sc=CountSurfaces(m) For k=1 To sc surf=GetSurface( m,k ) vc=CountVertices(surf) tc=CountTriangles(surf) If vc>maxVcount maxVcount=vc If tc>maxTcount maxTcount=tc Next qGroups=Int(maxVcount/10) ; maybe try different values for qGroups If qGroups<1 Then qGroups=1 ;allocate banks for the largest vertice and triangle counts B1=CreateBank (maxVcount*VertStructSize) ; bank for original vertices B2=CreateBank (maxTcount*TriStructSize) ; bank for triangles B3=CreateBank (maxTcount*VertStructSize*3) ; new seperated vertices B4=CreateBank (qGroups*4) ;entry point for group For k=1 To sc surf=GetSurface( m,k ) vc=CountVertices(surf) tc=CountTriangles(surf) maxX# = -10000000000000000 minX# = 10000000000000000 For v=0 To vc -1 b=v*VertStructSize x#=VertexX(surf,v) PokeFloat B1,b,VertexX(surf,v) PokeFloat B1,b+C_Y,VertexY(surf,v) PokeFloat B1,b+C_Z,VertexZ(surf,v) If x#> maxX# Then maxX#=x# If x#< minX# Then minX#=x# ; making new! ;PokeFloat B1,b+C_NX,VertexNX(surf,v) ;PokeFloat B1,b+C_NY,VertexNY(surf,v) ;PokeFloat B1,b+C_NZ,VertexNZ(surf,v) PokeFloat B1,b+C_R,VertexRed(surf,v) PokeFloat B1,b+C_G,VertexGreen(surf,v) PokeFloat B1,b+C_B,VertexBlue(surf,v) PokeFloat B1,b+C_U,VertexU(surf,v) PokeFloat B1,b+C_V,VertexV(surf,v) PokeFloat B1,b+C_W,VertexW(surf,v) Next ; *** calc quant qx xRange#=maxX#-minX# xScalFactor#=xRange#/(qGroups-1) For v=0 To vc-1 b=v*VertStructSize PokeInt B1,b+C_Qx,Int((PeekFloat( B1,b+C_X)-minX#)/xScalFactor#) Next ; init groups index For i=0 To qGroups -1 PokeInt B4,i*4,-1 Next ; create new vertices for all triange corners For t=0 To tc -1 b=t*TriStructSize ;offset in new vertice list: voff=t*3*VertStructSize v1=voff v2=voff+1*VertStructSize v3=voff+2*VertStructSize ; now copy original vertice data: OrgVOff1=TriangleVertex(surf,t,0)*VertStructSize CopyBank B1,OrgVOff1,B3,v1,VertStructSize OrgVOff2=TriangleVertex(surf,t,1)*VertStructSize CopyBank B1,OrgVOff2,B3,v2,VertStructSize OrgVOff3=TriangleVertex(surf,t,2)*VertStructSize CopyBank B1,OrgVOff3,B3,v3,VertStructSize ; new vertice offset in bank 3 PokeInt B2,b+C_V1,v1 ; no need to read original PokeInt B2,b+C_V2,v2 ; cause new verts are created for all tri-corners PokeInt B2,b+C_V3,v3 vx1#=PeekFloat (B3,v1+C_X) vy1#=PeekFloat (B3,v1+C_Y) vz1#=PeekFloat (B3,v1+C_Z) vx2#=PeekFloat (B3,v2+C_X) vy2#=PeekFloat (B3,v2+C_Y) vz2#=PeekFloat (B3,v2+C_Z) vx3#=PeekFloat (B3,v3+C_X) vy3#=PeekFloat (B3,v3+C_Y) vz3#=PeekFloat (B3,v3+C_Z) ;Add a normal ;get two vectors Ax#=vx2#-vx1# Ay#=vy2#-vy1# Az#=vz2#-vz1# Bx#=vx3#-vx1# By#=vy3#-vy1# Bz#=vz3#-vz1# ;crossproduct Cx# = Ay# * Bz# - By# * Az# Cy# = Az# * Bx# - Bz# * Ax# Cz# = Ax# * By# - Bx# * Ay# ;normalize: vLen#=Sqr#((Cx#*Cx#)+(Cy#*Cy#)+(Cz#*Cz#)) Cx#=Cx#/vLen# Cy#=Cy#/vLen# Cz#=Cz#/vLen# ; copy face normal to all 3 vertices PokeFloat B3,v1+C_NX,Cx# PokeFloat B3,v1+C_NY,Cy# PokeFloat B3,v1+C_NZ,Cz# PokeFloat B3,v2+C_NX,Cx# PokeFloat B3,v2+C_NY,Cy# PokeFloat B3,v2+C_NZ,Cz# PokeFloat B3,v3+C_NX,Cx# PokeFloat B3,v3+C_NY,Cy# PokeFloat B3,v3+C_NZ,Cz# ;init the average with face normal as shared normals will accumulate here ; PokeFloat B3,v1+C_AvgNX,Cx# ; PokeFloat B3,v1+C_AvgNY,Cy# ; PokeFloat B3,v1+C_AvgNZ,Cz# ; PokeFloat B3,v2+C_AvgNX,Cx# ; PokeFloat B3,v2+C_AvgNY,Cy# ; PokeFloat B3,v2+C_AvgNZ,Cz# ; PokeFloat B3,v3+C_AvgNX,Cx# ; PokeFloat B3,v3+C_AvgNY,Cy# ; PokeFloat B3,v3+C_AvgNZ,Cz# ;faster? CopyBank B3,v1+C_NX,B3,v1+C_AvgNX,12 CopyBank B3,v2+C_NX,B3,v2+C_AvgNX,12 CopyBank B3,v3+C_NX,B3,v3+C_AvgNX,12 Next ; t ClearSurface surf,True,True ;New vertexcount vc=3*tc ; Groups vertices into qGroups groups, based on quantizised x coord ; - allows for faster processing ; B3 doubles as qGroups linked lists. ; mark all new vertices in use, and make group lists: For v=0 To vc-1 b=v*VertStructSize ; mark all new vertices used PokeInt B3,v*VertStructSize+C_ISUSED,True ;make grup lists: vxgrp= PeekInt (B3,b+C_Qx) If PeekInt(B4,vxgrp*4)=-1 Then ; is this the first in the group? PokeInt B4,vxgrp*4,b ; store offset to first vertice in group PokeInt B3,b+C_PREV,-1 ; mark start of list PokeInt B3,b+C_NEXT,-1 ; mark end of list Else vgl=PeekInt (B4,vxgrp*4) ;read offset to last entry in list PokeInt B3,vgl+C_NEXT,b ;make v1 the next PokeInt B3,b+C_PREV,vgl PokeInt B3,b+C_NEXT,-1 ; mark end of list PokeInt B4,vxgrp*4,b ;store offset to *last* vertice in group EndIf Next ;process surface If sin_angle#<= 1 Then ; collapse vertices For g=0 To qGroups-1 vcmp=PeekInt(B4,g*4) ; get list entry (pointing to last entry ) ; replace with : While vcmp<>-1 ;is group empty? vx#=PeekFloat (B3,vcmp+C_X) vy#=PeekFloat (B3,vcmp+C_Y) vz#=PeekFloat (B3,vcmp+C_Z) nx1#=PeekFloat (B3,vcmp+C_NX) ny1#=PeekFloat (B3,vcmp+C_NY) nz1#=PeekFloat (B3,vcmp+C_NZ) tU#=PeekFloat (B3,vcmp+C_U) tV#=PeekFloat (B3,vcmp+C_V) tW#=PeekFloat (B3,vcmp+C_W) cR#=PeekFloat (B3,vcmp+C_R) cG#=PeekFloat (B3,vcmp+C_G) cB#=PeekFloat (B3,vcmp+C_B) v=PeekInt(B3,vcmp+C_PREV) ; now in the list look up previus ;walk list (list is walked from bottom to top, so vertices will come out in reverse order) While v<>-1 ; vprev=PeekInt(B3,v+C_PREV) vnext=PeekInt(B3,v+C_NEXT) If Abs(vx# - PeekFloat (B3,v+C_X))<=vThresh# Then If Abs(vy# - PeekFloat (B3,v+C_Y))<=vThresh# Then If Abs(vz# - PeekFloat (B3,v+C_Z))<=vThresh# Then ; comment if you dont want texture coord checking If Abs(tU# - PeekFloat (B3,v+C_U))<=tThresh# Then If Abs(tV# - PeekFloat (B3,v+C_V))<=tThresh# Then If Abs(tW# - PeekFloat (B3,v+C_W))<=tThresh# Then ; uncomment if you want vertex color checking ; If Abs(cR# - PeekFloat (B3,v+C_R))<=cTresh# Then ; If Abs(cG# - PeekFloat (B3,v+C_G))<=cTresh# Then ; If Abs(cB# - PeekFloat (B3,v+C_B))<=cTresh# Then nx2#=PeekFloat (B3,v+C_NX) ny2#=PeekFloat (B3,v+C_NY) nz2#=PeekFloat (B3,v+C_NZ) DotProduct#=nx1#*nx2#+ny1#*ny2#+nz1#*nz2# ; compare smoothing angle aginst face normal If DotProduct#>sin_Angle# Then ;maching vertex found, mark this one not used PokeInt B3,v+C_ISUSED,False i=Int(v/VertStructSize) tOff=Int(i/3) corner=(i Mod 3)*4 ; point triangle at the found vertex PokeInt B2,tOff*TriStructSize+C_V1+corner,vcmp ; add to the averaged normal PokeFloat B3,vcmp+C_AvgNX, PeekFloat (B3,vcmp+C_AvgNX)+PeekFloat (B3,v+C_NX) PokeFloat B3,vcmp+C_AvgNY, PeekFloat (B3,vcmp+C_AvgNY)+PeekFloat (B3,v+C_NY) PokeFloat B3,vcmp+C_AvgNZ, PeekFloat (B3,vcmp+C_AvgNZ)+PeekFloat (B3,v+C_NZ) ;remove vertex from list If vprev<>-1 Then PokeInt B3,vprev+C_NEXT,vnext ; copy next offset (could be -1 ) EndIf If vnext<>-1 Then PokeInt B3,vnext+C_PREV,vprev ; copy offset (could be -1 ) EndIf EndIf ; uncomment if you want vertex color checking ; EndIf ; EndIf ; EndIf ; comment if you dont want texture coord checking EndIf EndIf EndIf EndIf EndIf EndIf v=vprev Wend PokeInt B4,g*4,PeekInt(B3,vcmp+C_PREV) vcmp=PeekInt(B4,g*4) ; get list entry (pointing to last entry ) Wend Next EndIf ; calc the vertex new index newIndex=0 For v=0 To vc-1 ; been using offsets, now reverting to index PokeInt B3,v*VertStructSize+C_NewIndex,newIndex If PeekInt(B3,v*VertStructSize+C_ISUSED) Then newIndex=newIndex+1 Next ;Create new surface ;************************** For v=0 To vc -1 b=v*VertStructSize If PeekInt( B3,b+C_ISUSED)=True Then newV=AddVertex( surf,PeekFloat (B3,b),PeekFloat (B3,b+C_Y),PeekFloat (B3,b+C_Z), PeekFloat (B3,b+C_U),PeekFloat (B3,b+C_V),PeekFloat(B3,b+C_W)) ; normalize : Cx#=PeekFloat (B3,b+C_AvgNX) Cy#=PeekFloat (B3,b+C_AvgNY) Cz#=PeekFloat (B3,b+C_AvgNZ) vLen#=Sqr#((Cx#*Cx#)+(Cy#*Cy#)+(Cz#*Cz#)) Cx#=Cx#/vLen# Cy#=Cy#/vLen# Cz#=Cz#/vLen# VertexNormal surf,newV,Cx#,Cy#,Cz# VertexColor surf,newV,PeekFloat (B3,b+C_R),PeekFloat (B3,b+C_G),PeekFloat (B3,b+C_B) End If Next ;add the triangles For t=0 To tc -1 b=t*TriStructSize ; been using offsets, now reverting to index ; look up new index from original: newV1=PeekInt(B3,PeekInt(B2,b+C_V1)+C_NewIndex) newV2=PeekInt(B3,PeekInt(B2,b+C_V2)+C_NewIndex) newV3=PeekInt(B3,PeekInt(B2,b+C_V3)+C_NewIndex) AddTriangle surf,newV1,newV2,newV3 Next Next FreeBank B1 FreeBank B2 FreeBank B3 FreeBank B4 End Function ; Test program for Autosmooth ;************************************************ ;press 1 make flatter ;press 2 make smoother ;press 3 make explode all tri's ;press 4 to make all smooth ShowModel "C:\test.3ds" Function ShowModel( fil$) If Windowed3D() Graphics3D 640,480,0,2 Else Graphics3D 1024,768,0,1 EndIf SetBuffer BackBuffer() model=LoadMesh( fil$ ) If model FitMesh model,-1,-1,-1,2,2,2,True If model=0 RuntimeError "Unable to load 3D mesh:"+fil$ sc=CountSurfaces(model) For k=1 To sc vc=vc+CountVertices( GetSurface( model,k ) ) tc=tc+CountTriangles( GetSurface( model,k ) ) Next camera=CreateCamera() CameraClsColor camera,0,0,64 CameraRange camera,.01,10 xr#=0:yr#=0:z#=2.1 light=CreateLight() TurnEntity light,45,20,0 LightColor light,255,255,255 ca#=1 Repeat RotateEntity model,xr,yr,0 PositionEntity model,0,0,z UpdateWorld RenderWorld Text 0,0,"Triangles:"+tc+" Vertices:"+vc+" Surfaces:"+sc +" Cos Angle:"+ca# Flip key=False Repeat If KeyHit(1) End If KeyDown(200) xr=xr-3:key=True If KeyDown(208) xr=xr+3:key=True If KeyDown(203) yr=yr+3:key=True If KeyDown(205) yr=yr-3:key=True If KeyDown( 30) z=z-.1:key=True If KeyDown( 44) z=z+.1:key=True If KeyDown( 2) ca=ca+0.1 : doSmooth=True :key=True: ;press 1 make flatter If KeyDown( 3) ca=ca-0.1 : doSmooth=True :key=True: ;press 1 make smoother If KeyDown( 4) ca=1.1 : doSmooth=True :key=True: ;press 3 make explode all tri's If KeyDown( 5) ca=-1.1 : doSmooth=True :key=True: ;press 4 to make all smooth If doSmooth Then doSmooth=False AutoSmooth model ,ca# :key=True vc=0 tc=0 For k=1 To sc vc=vc+CountVertices( GetSurface( model,k ) ) tc=tc+CountTriangles( GetSurface( model,k ) ) Next EndIf If Not key WaitKey Until key Forever End Function |
Comments
None.
Code Archives Forum