WPF 어플리케이션 라이프 사이클(Application Life Cycle)


MSDN에 있는 WPF의 라이프 사이클에 대한 그림이다. 가운데 Application Object 상자 안이 코어 부분인데, 하나의 Application은 Run 매서드로 시작해서 Shutdown 매서드의 호출로 끝나게 된다. Shoutdown 매서드의 호출은 ShutdownMode의 값에 따라서 Application이 자동으로 호출해주는 경우와  사용자가 반드시 호출해주는 경우로 구분된다. 그리고 Activated, Deactivated, DispatcherUnhandledException, SessionEnding, Exit는 Application에 발생하는 이벤트이다. SessionEnding의 경우는 사용자가 OS를 Shutdown하거나 Logoff 시에 호출되는데 이 이벤트 안에서 OS의 종료를 취소시킬 수 있다. 또한 DispatcherUnhandledException 이벤트는 Application에서 처리되지 않는 예외가 발생했을 경우에 발생하는 범용 예외 처리가 가능한 곳으로 지정하지 않았을 경우 예외가 발생하면 Application은 자동으로 종료된다.


위의 그림은 WPF Window Application에 대한 또 다른 Life-Cycle이다. 모두 이벤트 명이다.

Singleton Pattern in C#

대다수의 패턴이 그런 것처럼 Singleton Pattern(이하 Singleton) 역시 언어에 독립적인 부분이 많기는 하지만 갑작스레 다소 익숙치 않은 C#으로 Singleton을 구현할 필요가 생겨 하나의 템플릿의 목적으로 작성해 놓는다.

public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
      }
   }
}

Singleton은 클라이언트 측에서 오직 하나의 Instance만을 생성하도록 제한해야 하므로 임으로 클라이언트가 생성하지 못하도록 생성자를 private로 선언하고 있음을 알 수 있다. 대신 Instance를 C#의 편리한 Property 문법을 사용해서 만들고 이를 통해 오직 하나의 Instance만을 만든다. Instance Propery의 get의 구현을 보면 static으로 선언된 자신의 클래스 타입인 instance 변수가 null인 경우, 즉 생성되지 않은 경우 단 한번 생성해 주고 반환하고 있음을 알 수 있다. 한번 생성된 경우라면 생성과정 없이 그저 반환만 이루어진다. 다른 Pattern에 비해 그 목적과 구현하는 것에 어려움이 없을 것이다. 하지만 한발작 더 접근해서 Multi Thread 환경에서 Singleton을 바라보면 오류의 근원이나 다름없다. 즉 여러개의 Thread에서 Singleton 클래스를 사용할 경우 충돌이 생긴다. 이유는 단 하나의 인스턴스 변수를 여러개의 스레드에서 사용하려 하기 때문이다. 고속화된 CPU나 Singleton 클래스가 무척 작은 코드조각이라면  충돌에의한 오류가 발생 확률이 다소 줄겠지만, 여전이 충돌 확율은 높다. 이에 대한 개선된 코드가 다음과 같다.

public class Singleton
{
   private static Singleton instance;
   private static object syncRoot = new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         lock (syncRoot) {
             if (instance == null) 
             { 
                 instance = new Singleton();

             }

             return instance;
          }
      }
   }
}

lock이라는 C#의 예약어를 사용함으로써 Critical Section을 만들어 주고 있다. 이 섹션 구간의 코드들은 원자성을 갖게 되어 오직 단 하나의 스레드만이 이 구간의 코드를 연속적으로 실행하는 것을 보장하게 된다.

Canon G7 오늘 주문..

2006년 계획 중의 하나였던 디지털카메라 구입을 드디어 실행시켰다. DSLR로 구입하려고 했으나, 내가 무슨 DSLR이냐라는 생각만 들어서 G 시리즈 중 최신인 G7을 주문했다.

Dip2K’s WPF 3D 입문 (3/3)

한때 3D의 꽃을 뽑으라면 Texture Mapping이였다. 물론 한때다. 지금 다시 뽑으라면 Shader가 되겠지만… 여하튼 오늘 잠시 WPF 3D 쪽으로 눈을 돌리면서 WPF 3D 입문에서 다소 부족했던 부분인, Texutre Mapping 부분을 정리하기로 하겠다.

먼저 Texture Mapping을 하기 위해서는 Texture Mapping 좌표가 필요하다. 이전에 했던 부분은 Texture Mapping 좌표를 제외한 Mesh의 정점과 정점 인덱스 그리고 법선 벡터 만을 지정하였다. 이제 Texture Mapping 좌표를 지정해보는 것을 살펴보자.

우리가 지금까지 구축해 왔던 것에서 시작해보자. Mesh의 정점과 인덱스 그리고 법선 벡터를 지정해 주는 부분을 포함하는 함수가 Windows1.xaml.cs 파일 안의 Window1 클래스에 대한 CreateTriangleModel 함수였다. 이제 이것이 아래와 같이 바뀐다. 파랑색의 코드 부분이 변경이나 추가된 부분이다.

private GeometryModel3D CreateTriangleModel(Point3D p0, Point3D p1, 
    Point3D p2, Point p0t, Point p1t, Point p2t)
{
    MeshGeometry3D mesh = new MeshGeometry3D();
            
    mesh.Positions.Add(p0);
    mesh.Positions.Add(p1);
    mesh.Positions.Add(p2);

    mesh.TriangleIndices.Add(0);
    mesh.TriangleIndices.Add(1);
    mesh.TriangleIndices.Add(2);

    mesh.TextureCoordinates.Add(p0t);
    mesh.TextureCoordinates.Add(p1t);
    mesh.TextureCoordinates.Add(p2t);

    Vector3D normal = CalculateNormal(p0, p1, p2);
            
    mesh.Normals.Add(normal);
    mesh.Normals.Add(normal);
    mesh.Normals.Add(normal);

    BitmapImage bi = new BitmapImage();
    bi.BeginInit();
    bi.UriSource = new Uri(@"y:/face.PNG", UriKind.RelativeOrAbsolute);
    bi.EndInit();

    Brush brush = new ImageBrush(bi);
    Material material = new DiffuseMaterial(brush);
    GeometryModel3D model = new GeometryModel3D(mesh, material);

    return model;
}

Texture Mapping 좌표와 Texture Image로 사용될 그림을 재질(Material)로 지정하였다.  CreateTriangleModel 함수가 변경되었으니 이 함수를 사용하는 부분도 변경되어야 하지 않겠는가? 그 부분에 대한 코드는 ClickCubeButton 함수 안이며 변경된 코드를 파란색으로 나타내면 다음과 같다.

private void ClickCubeButton(object Sender, RoutedEventArgs e)
{
    if (model != null) return;

    Model3DGroup cube = new Model3DGroup();

    Point3D p0 = new Point3D(-1, -1, -1);
    Point3D p1 = new Point3D(1, -1, -1);
    Point3D p2 = new Point3D(1, -1, 1);
    Point3D p3 = new Point3D(-1, -1, 1);
    Point3D p4 = new Point3D(-1, 1, -1);
    Point3D p5 = new Point3D(1, 1, -1);
    Point3D p6 = new Point3D(1, 1, 1);
    Point3D p7 = new Point3D(-1, 1, 1);

    Point t00 = new Point(0, 0);
    Point t01 = new Point(0, 1);
    Point t10 = new Point(1, 0);
    Point t11 = new Point(1, 1);

    //front side triangles
    cube.Children.Add(CreateTriangleModel(p3, p2, p6, t00, t10, t11));
    cube.Children.Add(CreateTriangleModel(p3, p6, p7, t00, t11, t01));

    //right side triangles
    cube.Children.Add(CreateTriangleModel(p2, p1, p5, t00, t10, t11));
    cube.Children.Add(CreateTriangleModel(p2, p5, p6, t00, t11, t01));

    //back side triangles
    cube.Children.Add(CreateTriangleModel(p1, p0, p4, t10, t00, t01));
    cube.Children.Add(CreateTriangleModel(p1, p4, p5, t10, t01, t11));

    //left side triangles
    cube.Children.Add(CreateTriangleModel(p0, p3, p7, t10, t00, t01));
    cube.Children.Add(CreateTriangleModel(p0, p7, p4, t10, t01, t11));

    //top side triangles
    cube.Children.Add(CreateTriangleModel(p7, p6, p5, t00, t10, t11));
    cube.Children.Add(CreateTriangleModel(p7, p5, p4, t00, t11, t01));

    //bottom side triangles
    cube.Children.Add(CreateTriangleModel(p2, p3, p0, t10, t00, t01));
    cube.Children.Add(CreateTriangleModel(p2, p0, p1, t10, t01, t11));

    model = new ModelVisual3D();
    model.Content = cube;

    mainViewport.Children.Add(model);
}

Texture Mapping 좌표에 대한 설명은 이곳 OpenGL의 Texture Mapping에 대한 강좌에 동일하니 그곳을 참고하길 바란다. 그 실행 결과는 다음과 같다.
참고로 텍스쳐 이미지의 크기는 과거의 2의 자승이여야 한다는 제약이 더 이상 적용하지 않으며 동영상(AVI, 동영상 GIF 등)도 쉽게 지원하며 이미지의 경로를 http 프로토콜을 통해서도 쉽게 받아들일 수 있다.

이상으로 WPF를 이용한 간단한 3D 그래픽에 대한 글의 정리를 끝내겠다. 추후에 지속적으로 WPF에 대한 Article을 실제 업무에 적용하면서 정리하는 예가 많아지길 스스로에게 기대한다.