Code archives/3D Graphics - Mesh/UpdateNormalsAngle
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| This is an enhanced UpdateNormals function allowing to set an angle, to which two faces are smoothed. The regular UpdateNormals just smoothes everything, even when a clear edge is desired, so especially mechanical models aren't that well-shaded. There is already a code for flat shading in the archives, but with that one curved parts are flattened too, so it's not a solution for a complex mesh with angular and curved parts. Using the UpdateNormalsAngle function it is now possible to set the normals for a smooth shading on curved parts and a hard shading on angular parts. The parameter "Angle" is compared with the delta angle between two vertex normals. So the maximum value is 180°, which means the normals are exactly opposite. In this case the function works like the regular UpdateNormals, every face is smoothed. With the default of 89° square edges stay, obtuse ones get smoothed. Just try this example to get a feeling how to use this function: http://www.mein-murks.de/quellcode/UpdateNormals.zip | |||||
;by Robert Hierl / www.mein-murks.de / 30.12.2007
Type tVertexVector
Field vertex
Field x#
Field y#
Field z#
End Type
Type tVertexTree
Field x#
Field y#
Field z#
Field vertices.tVertexVector[50]
Field octree.tVertexTree[7]
End Type
Function UpdateNormalsAngle( pMesh, pAngle# = 89 )
;This function is used much like the regular UpdateNormals in B3D. Unlike the original function,
;it doesn't just smooth all edges, but provides an option to set an angle, to which two faces are smoothed.
;So it's a very easy way to improve shading on a mesh, without setting up each normal manually.
;Author Robert Hierl / www.mein-murks.de
Local surf, numSurface
Local vert, numVertex
Local vertexTree.tVertexTree
Local triNormal.tVertexVector
Local v1.tVertexVector, v2.tVertexVector, v3.tVertexVector
;handle special ones
If pAngle# = 0 Then UpdateNormalsFlat( pMesh ) : Return
;regular UpdateNormals works only, when mesh was modified, so line is commented for this example
;If pAngle# >= 180 Then UpdateNormals( pMesh ) : Return
For numSurface = 1 To CountSurfaces( pMesh )
surf = GetSurface( pMesh, numSurface )
Delete Each tVertexTree
vertexTree = New tVertexTree
;gather all possible vertex coordinates with their triangle normal
For numTriangle = 0 To CountTriangles( surf ) - 1
v1 = GetVertexVector(surf, TriangleVertex( surf, numTriangle, 0 ))
v2 = GetVertexVector(surf, TriangleVertex( surf, numTriangle, 1 ))
v3 = GetVertexVector(surf, TriangleVertex( surf, numTriangle, 2 ))
;calculate triangle normal
triNormal = GetTriangleNormal(surf, v1\vertex, v2\vertex, v3\vertex)
;add each vertex with calculated normal
AddVertex2Tree(vertexTree, v1, triNormal)
AddVertex2Tree(vertexTree, v2, triNormal)
AddVertex2Tree(vertexTree, v3, triNormal)
;clean up
Delete triNormal
Delete v1
Delete v2
Delete v3
Next
;calculate and set new vertex normals
For vertexTree = Each tVertexTree
SetNormalsMulti(vertexTree, surf, pAngle#)
Next
;clean up
Delete Each tVertexTree
Delete Each tVertexVector
Next
End Function
Function UpdateNormalsFlat(mesh)
;This simple function is used to disable smoothing on a mesh. It is faster than UpdateNormalsAngle with value 0.
Local surf, numSurface, numTriangle
Local v1, v2, v3
Local triNormal.tVertexVector
For numSurface = 1 To CountSurfaces(mesh)
surf = GetSurface(mesh, numSurface)
For numTriangle = 0 To CountTriangles( surf ) - 1
;calculate normal for each triangle
v1 = TriangleVertex(surf, numTriangle, 0)
v2 = TriangleVertex(surf, numTriangle, 1)
v3 = TriangleVertex(surf, numTriangle, 2)
triNormal = GetTriangleNormal(surf, v1, v2, v3)
;set normals for vertex
VertexNormal surf, v1, triNormal\x, triNormal\y, triNormal\z
;when using EntityFX 4, only the first vertex normal is relevant, in this case just comment the following two lines
VertexNormal surf, v2, triNormal\x, triNormal\y, triNormal\z
VertexNormal surf, v3, triNormal\x, triNormal\y, triNormal\z
Next
Next
End Function
Function SetNormalsMulti(vertexTree.tVertexTree, surf, pAngle#)
;calculate new normals
Local ax#, ay#, az#
Local lx#, ly#, lz#
Local nx#, ny#, nz#
Local factor#, merged
Local diffAngle#, vertex.tVertexVector
Local i, l
Repeat
merged = False
For i = 0 To 50
If vertexTree\vertices[i] = Null Then Exit
vertexcount = 0
nx# = 0
ny# = 0
nz# = 0
For l = 0 To 50
If vertexTree\vertices[l] = Null Then Exit
diffAngle# = VectorAngle#(vertexTree\vertices[l], vertexTree\vertices[i])
If diffAngle# <= pAngle# Then
If diffAngle# > 0 Then merged = True;
vertex.tVertexVector = vertexTree\vertices[l]
vertexcount = vertexcount + 1
nx# = nx# + vertex\x
ny# = ny# + vertex\y
nz# = nz# + vertex\z
EndIf
Next
nx# = nx# / vertexcount
ny# = ny# / vertexcount
nz# = nz# / vertexcount
;normalize result
factor# = Sqr((nx# * nx#)+(ny# * ny#)+(nz# * nz#))
nx# = nx# / factor#
ny# = ny# / factor#
nz# = nz# / factor#
VertexNormal surf, vertexTree\vertices[i]\vertex, nx#, ny#, nz#
Next
If Not merged Then Exit
For i = 0 To 50
If vertexTree\vertices[i] = Null Then Exit
vertex = vertexTree\vertices[i]
vertex\x = VertexNX(surf, vertex\vertex)
vertex\y = VertexNY(surf, vertex\vertex)
vertex\z = VertexNZ(surf, vertex\vertex)
Next
Forever
End Function
Function GetVertexVector.tVertexVector(pSurface, pVertex)
;this one provides the coordinate of a given vertex as vector
Return VertexVector(VertexX#(pSurface, pVertex), VertexY#(pSurface, pVertex), VertexZ#(pSurface, pVertex), pVertex)
End Function
Function AddVertex2Tree( pNode.tVertexTree, pVertex.tVertexVector, pNormal.tVertexVector)
;adds a vertex to our octree
Local i, treePosition
;if our coordinate matches, we just add the given vertex normal to the list
If pNode\x = pVertex\x And pNode\y = pVertex\y And pNode\z = pVertex\z Then
For i = 0 To 50
If pNode\vertices[i] = Null Then
pNode\vertices[i] = VertexVector(pNormal\x#, pNormal\y#, pNormal\z#, pVertex\vertex)
Return
EndIf
Next
Else
If pNode\x >= pVertex\x Then treePosition = treePosition Or 1
If pNode\y >= pVertex\y Then treePosition = treePosition Or 2
If pNode\z >= pVertex\z Then treePosition = treePosition Or 4
If pNode\octree[treePosition] = Null Then
pNode\octree[treePosition] = New tVertexTree
pNode\octree[treePosition]\x# = pVertex\x#
pNode\octree[treePosition]\y# = pVertex\y#
pNode\octree[treePosition]\z# = pVertex\z#
pNode\octree[treePosition]\vertices[0] = VertexVector(pNormal\x#, pNormal\y#, pNormal\z#, pVertex\vertex)
Else
AddVertex2Tree( pNode\octree[treePosition], pVertex, pNormal)
EndIf
EndIf
End Function
Function GetTriangleNormal.tVertexVector(pSurface, v1, v2, v3)
;return normal of given triangle as vector
Local factor#
;v1 to v2 as vector
Local lx# = VertexX#(pSurface,v1) - VertexX#(pSurface,v2)
Local ly# = VertexY#(pSurface,v1) - VertexY#(pSurface,v2)
Local lz# = VertexZ#(pSurface,v1) - VertexZ#(pSurface,v2)
;v1 to v3 as vector
Local ax# = VertexX#(pSurface,v1) - VertexX#(pSurface,v3)
Local ay# = VertexY#(pSurface,v1) - VertexY#(pSurface,v3)
Local az# = VertexZ#(pSurface,v1) - VertexZ#(pSurface,v3)
;cross product of these two vectors
Local nx# = (ly# * az#)-(lz# * ay#)
Local ny# = (lz# * ax#)-(lx# * az#)
Local nz# = (lx# * ay#)-(ly# * ax#)
;normalize result ( set vector length to 1 )
factor# = Sqr((nx# * nx#)+(ny# * ny#)+(nz# * nz#))
nx# = nx# / factor#
ny# = ny# / factor#
nz# = nz# / factor#
Return VertexVector(nx#, ny#, nz#)
End Function
Function VertexVector.tVertexVector(x#, y#, z#, pVertex = -1)
;creates a VertexVector type, storing coordinates and related mesh vertex
Local Vector.tVertexVector
Vector = New tVertexVector
Vector\x# = x#
Vector\y# = y#
Vector\z# = z#
Vector\vertex = pVertex
Return Vector
End Function
Function VectorAngle#(v1.tVertexVector,v2.tVertexVector)
;returns angle between two normalized vectors
;dot product is converted to integer and back to avoid some weird float issues
;(as a matter of fact I don't know what the problem is exactly, maybe rounding differences, maybe NaN)
Local dot = ((v1\X * v2\X) + ( v1\Y * v2\Y) + (v1\Z*v2\Z)) * 10000
Return ACos#( dot / 10000.0 )
End Function |
Comments
None.
Code Archives Forum