비베닷넷이 되면서 완전히 객체지향 프로그래밍 언어로 탈바꿈했다. 객체지향프로그래밍의 핵심은 그 무엇보다도 객체를 만들어내는 설계도 같은 클래스라고 할 수 있다. 책 제목에서도 알 수 있듯이 이 클래스를 어떻게 효과적으로 설계할 것인가를 자세히 다루고 있다. 참 맛있게 읽은 책이다.
During object-oriented analysis and design, we identify the most important objects in our system, and consider how they relate to each other. But during object-oriented programming, we don't write 'objects'; we define classes to represent the behavior and attributes of objects. 객체지향 분석과 설계를 하는 동안에, 우리는 우리 시스템 안의 가장 중요한 객체를 규명하고, 서로간에 어떻게 관계하는지를 고려한다. 그러나 객체지향 프로그래밍을 하는 동안에는 '객체들'을 작성하지 않는다. 다만 객체들의 행위와 속성을 대표하는 클래스를 정의한다. (본문 중에서)
Visual Basic .NET에 대한 설명서가 잘 정리된 웹페이지를 소개한다. http://www.java2s.com/Tutorial/VB/CatalogVB.htm 키워드(keyword) 별로 어떻게 쓰이는지 알고 싶다면, 꼭 한 번 들를만 하다. 또한, 비주얼베이직 닷넷 말고 지구상에 존재하는 웬만한 프로그래밍 언어에 대한 설명서도 함께 있다. 여기서는 비주얼 베이직 닷넷만 연결해 놓은 것이다. 왜? 이 홈피의 주관심사여서. ^^
Public Function GetAzimuthRadian(ByVal x As Single, ByVal y As Single) As Single '천구상의 방위각을 계산한다. '이 때 방위각은 y축의 양의 방향을 기준으로 시계방향으로 잰각 '즉, 시각좌표계에서 사용해야 함. ' '1) 특수한 경우의 방위각 계산 If Math.Abs(x) < Single.Epsilon Then If y > 0 Then Return 0.0F '--> 예) (0,1)의 방위각은 0도 Else Return Math.PI '--> 예) (0,-1)의 방위각은 180도 End If End If ' If Math.Abs(y) < Single.Epsilon Then Return Math.Sign(x) * Math.PI / 2 '--> 예) (1,0)은 90도, (-1,0)은 -90도 End If ' '2) 일반적인 경우의 방위각 계산 Dim v1 As Single = Math.Atan(Math.Abs(x / y)) If y > 0 Then Return Math.Sign(x) * v1 '--> 예) (1,1)은 45도, (-1,1)은 -45도 Else Return Math.Sign(x) * (Math.PI - v1) '--> 예) (1,-1)은 135도, (-1,-1)은 -135도 End If ' End Function
If문에서 발생할 확률이 더 많은 것을 전진배치하면, 다음과 같이 된다.
Public Function GetAzimuthRadian(ByVal x As Single, ByVal y As Single) As Single '천구상의 방위각을 계산한다. '이 때 방위각은 y축의 양의 방향을 기준으로 시계방향으로 잰각 '즉, 시각좌표계에서 사용해야 함. ' If Math.Abs(x) >= Single.Epsilon Then If Math.Abs(y) >= Single.Epsilon Then '2) 일반적인 경우의 방위각 계산 Dim v1 As Single = Math.Atan(Math.Abs(x / y)) If y > 0 Then Return Math.Sign(x) * v1 '--> 예) (1,1)은 45도, (-1,1)은 -45도 Else Return Math.Sign(x) * (Math.PI - v1) '--> 예) (1,-1)은 135도, (-1,-1)은 -135도 End If Else '1) 특수한 경우의 방위각 계산 Return Math.Sign(x) * Math.PI / 2 '--> 예) (1,0)은 90도, (-1,0)은 -90도 End If Else '1) 특수한 경우의 방위각 계산 If y > 0 Then Return 0.0F '--> 예) (0,1)의 방위각은 0도 Else Return Math.PI '--> 예) (0,-1)의 방위각은 180도 End If End If ' End Function
이보다 더 간단히 정리하면 다음과 같다.
Public Function GetAzimuthRadian(ByVal x As Single, ByVal y As Single) As Single '천구상의 방위각을 계산한다. '이 때 방위각은 y축의 양의 방향을 기준으로 시계방향으로 잰각 '즉, 시각좌표계에서 사용해야 함. ' Return Math.Atan2(x, y) ' End Function
3차원 벡터(또는 점)에 대한 구조체(structure)를 아래와 같이 작성할 수 있다. <특이점> 1) 벡터의 성분은 X, Y, Z로 명백하므로 그냥 Public 변수로 설정함 2) 배열로 선언된 벡터 성분과도 호환되도록 함 - VB.NET의 Default Property를 이용함 - 사용예 :
Dim v1,v2 As Vector3D Dim innerProduct As Single = v1(0)*v2(0) + v1(1)*v2(1) + v1(2)*v2(2)
구조체 소스코드
Public Structure Vector3D Public X, Y, Z As Single Public Shared ReadOnly ZeroVector As Vector3D = New Vector3D(0, 0, 0)
#Region "Constructors" 'Private Sub New() 'Nothing -> No default initialization 'End Sub Public Sub New(ByVal x As Single, ByVal y As Single, ByVal z As Single) Me.X = x Me.Y = y Me.Z = z End Sub Public Sub New(ByVal v As Vector3D) Me.New(v.X, v.Y, v.Z) 'copy constructor End Sub #End Region
#Region "Properties" ''' <summary> ''' 배열로 선언된 벡터 성분과 호환이 되도록 작성함 ''' </summary> ''' <param name="index">성분의 위치를 지정하는 값 0:x, 1:y, 2:z</param> ''' <value>해당성분의 값</value> ''' <returns>해당성분의 값</returns> ''' <remarks> ''' 왜 호환되게 했나고요? ''' c/c++로 짠 코드를 변환하다 보면, 배열로 간단하게 해결한 것들이 많아서요. ''' </remarks> Default Public Property Elements(ByVal index As Short) As Single Get Dim i As Short = index Mod 3'실수를 방지하기 위해서 Select Case i Case 0 Return X Case 1 Return Y Case 2 Return Z End Select End Get Set(ByVal value As Single) Dim i As Short = index Mod 3 Select Case i Case 0 X = value Case 1 Y = value Case 2 Z = value End Select End Set End Property #End Region ''각종 Property와 Operator 등은 생략 ^^ End Class
광선과 AABB가 교차하는지를 평가하는 함수이다. 이 함수는 Stefan Zerbst의 책 "3D Game Engine Programming"의 138~140쪽에 나오는 ZFXRay::Intersects 함수를 VB.NET으로 변환한 것이다. 이 함수의 원천적인 알고리즘은 Andrew Woo의 알고리즘에서 비롯한 것이다.
Function IntersectionAABBonText(ByVal RayDirection As Vector3D, ByVal RayOrigin As Vector3D) As Boolean Dim RayInsideAABB As Boolean = True Dim HitPoint As Vector3D Dim MaxT As New Vector3D(-1.0F, -1.0F, -1.0F) Dim Epsilon As Single = 0.00001F ' 'Find the x component If (RayOrigin.X < Me.m_Min.X) Then HitPoint.X = Me.m_Min.X RayInsideAABB = False If RayDirection.X <> 0.0F Then MaxT.X = (Me.m_Min.X - RayOrigin.X) / RayDirection.X End If ElseIf (RayOrigin.X > Me.m_Max.X) Then HitPoint.X = Me.m_Max.X RayInsideAABB = False If RayDirection.X <> 0.0F Then MaxT.X = (Me.m_Max.X - RayOrigin.X) / RayDirection.X End If End If 'Find the y component If (RayOrigin.Y < Me.m_Min.Y) Then HitPoint.Y = Me.m_Min.Y RayInsideAABB = False If RayDirection.Y <> 0.0F Then MaxT.Y = (Me.m_Min.Y - RayOrigin.Y) / RayDirection.Y End If ElseIf (RayOrigin.Y > Me.m_Max.Y) Then HitPoint.Y = Me.m_Max.Y RayInsideAABB = False If RayDirection.Y <> 0.0F Then MaxT.Y = (Me.m_Max.Y - RayOrigin.Y) / RayDirection.Y End If End If 'Find the z component If (RayOrigin.Z < Me.m_Min.Z) Then HitPoint.Z = Me.m_Min.Z RayInsideAABB = False If RayDirection.Z <> 0.0F Then MaxT.Z = (Me.m_Min.Z - RayOrigin.Z) / RayDirection.Z End If ElseIf (RayOrigin.Z > Me.m_Max.Z) Then HitPoint.Z = Me.m_Max.Z RayInsideAABB = False If RayDirection.Z <> 0.0F Then MaxT.Z = (Me.m_Max.Z - RayOrigin.Z) / RayDirection.Z End If End If ' '---- Ray origin inside bounding box '광선의 시점이 박스 안에 있다. 이 판단이 내 프로그램에서 유용할 것인가? '내 프로그램은 기본적으로 3D Face이기 때문에 정확한 판단이 아닐 수도 있다. If RayInsideAABB Then HitPoint = RayOrigin Return True End If ' '---- Get largest of the maxT's for final choice of intersection '최대값 구하기 Dim nPlane As Integer = 0 If (MaxT.Y > MaxT(nPlane)) Then nPlane = 1 If (MaxT.Z > MaxT(nPlane)) Then nPlane = 2 ' '---- Check final candidate actually inside box If (MaxT(nPlane) < 0.0F) Then Return False If nPlane <> 0 Then HitPoint.X = RayOrigin.X + MaxT.X * RayDirection.X If (HitPoint.X < Me.m_Min.X - Epsilon) OrElse (HitPoint.X > Me.m_Max.X + Epsilon) Then Return False End If End If If nPlane <> 1 Then HitPoint.Y = RayOrigin.Y + MaxT.Y * RayDirection.Y If (HitPoint.Y < Me.m_Min.Y - Epsilon) OrElse (HitPoint.Y > Me.m_Max.Y + Epsilon) Then Return False End If End If If nPlane <> 2 Then HitPoint.Z = RayOrigin.Z + MaxT.Z * RayDirection.Z If (HitPoint.Z < Me.m_Min.Z - Epsilon) OrElse (HitPoint.Z > Me.m_Max.Z + Epsilon) Then Return False End If End If Return True End Function