GL 공부를 다시 하던 중, x축이 화면 왼쪽을 가리키고 있는 기묘한 현상을 발견했다. 와인딩, 좌표 모두 문제 없었고 y,z는 오른손좌표계 그대로인데, x만 뒤집혀 있었던 것이다.


원인은 glFrustum함수의 left, right가 바뀌어있었던 것 때문이었다.


프러스텀이 +z에서 -z를 바라보고 있어야 했는데 left가 0.5, right가 -0.5인 상태였다. 정상으로 바꾸자 문제는 해결되었다.


프러스텀 설정에 문제가 있는걸 찾기까지 몇시간을 허비했다.

어떤 카메라든 각 카메라의 절두체 안에 들어와 다음 프레임에 화면에 표시 될 경우,

OnBacameVisible이 호출된다.

반대로 어떤 카메라로부터도 보이지 않을 경우 OnBacameInvisible이 호출된다.

각자는 Renderer컴포넌트가 GameObject에 붙어있을 때에만 호출된다.

Tag // unity3D
D1에서는 아래와 같은 C스타일 함수포인터 선언이 가능했다.
typedef int (*functionName) (int a, int b);


D2부터는 C스타일의 함수 포인터 선언이 불가능해지고 D스타일 함수 포인터만을 사용해야 한다. (D2에서 typedef가 폐기된 영향으로 추측된다) 선언 형식은 다음과 같다.

alias [반환형] function ([매개변수 목록]) [이름];

대리자 포인터의 경우 function 대신 delegate를 사용하면 된다. 선언한 함수 포인터 변수에는 일반적인 함수의 주소, 또는 함수 리터럴을 대입할 수 있다. 함수 포인터 변수에 대리자 리터럴을 담을 수는 없다.

 
alias int function(string b) func;

int foo(string b)
{
	writeln(b);
	
	return 0;
}

void main()
{
	func f;
	
	f = &foo;
	
	f("hello D!");
	
	f = function(string b)
	{
		writeln(b);
		return 1;
	};
	
	f("hello D!");
}

D의 모든 내장 형식은 .init 속성에 기본값을 가지고 있으며 변수가 선언되면 메모리가 기본값으로 초기화된다. 이는 변수를 초기화하지 않고 사용하더라도 런타임에 문제가 발생하지 않도록 돕는 장치이다. 다음 코드를 보자. (표준 라이브러리를 사용하기 위한 import선언은 배제한다.)

void main()
{
   int a;
   writeln(a);
}

위 코드에서는 a가 int.init, 즉 0 으로 초기화되어 0이 출력된다. 그렇다면 a에 void를 대입하면 어떻게 될까.

void main()
{
   int a = void;
   writeln(a);
}

위 코드의 실행 결과 a는 쓰레기 값이 된다. 변수의 자동 초기화는 초기화되지 않은 변수가 부주의하게 사용되는 경우를 대비한 D의 배려이므로, 만약 변수가 생성당시 초기화되지 않았더라도 사용되기 전에 어떤 값으로 초기화되는 것이 보장되는 경우에는 불필요한 오버헤드가 된다. 이 때 변수에 void를 대입함으로서 자동 초기화 기능을 변수 생성 과정에서 배제할 수 있다. 이 기능은 단일한 내장 형식 뿐 아니라 배열, 구조체에도 동일하게 적용된다.

//배열의 경우
int[3] getarray()
{
	int[3] array = void;
	
	foreach(i, ref a; array)
		a = i;
	
	return array;
}

//구조체의 경우
struct S { int a; bool b; }

void getByRef(ref S s)
{
	s.a = 123;
	s.b = true;
}

void main()
{
	S s = void;
	getByRef(s);
	writeln(s.a, s.b);
}

C에서의 typedef는 단지 특정 형식에 대해 별칭을 만드는 것 뿐이었다. 따라서 다음과 같은 코드는 컴파일 에러가 발생했다.

typedef int HANDLE;

int foo(int);
int foo(HANDLE);

하지만 D에는 C의 typedef에 해당하는 alias라는 키워드가 존재한다. 물론 완전히 같은 기능은 아니다. alias는 타입에 대한 것 뿐 아니라 네임스페이스, 변수 등 모든 심볼에 대해 사용이 가능하다. 우선 C와 D의 typedef 키워드가 어떻게 다른지 명백하게 보여주는 샘플 코드를 보기로 하자.

import std.stdio;

typedef int INT; // int에 대해 typedef를 사용했다.

int main(string[] argv)
{
	writefln("foo(int) = %d, foo(INT) = %d", foo(2), foo(cast(INT)2));
	readln(); //키보드 입력을 기다린다.
	return 0;
}

int foo(int i) //D는 전방선언이 필요하지 않다.
{
	return i ^^ 2; //pow연산자이다. i의 2제곱.
}

int foo(INT i) //오버로딩된다.
{
	return 0;
}


D에서는 위와 같이 int를 typedef한 INT라는 타입을 int와는 별개의 타입으로 취급한다. 따라서 오버로딩이 가능해진다. 위 샘플에서는 foo(INT)를 호출하기 위해 2를 INT타입으로 캐스팅했다. 위 코드에서 typedef를 alias로 바꿔 보자.

만약 해당 함수들에 대한 호출이 없었다면 일단 컴파일러는 에러를 내지 않는데, 링커에 가서 기존에 정의된 심볼이라는 내용의 에러가 뜨게 된다. DMD는 일단 시그니처가 같더라도 에러로 취급하지 않는 것으로 보인다. 이 부분은 조금 문제가 있어 보이기도 하지만, 호출이 존재하면 지정된 시그니처에 해당하는 함수가 여러 개 매칭된다는 내용의 에러가 컴파일시 뜨게 된다.


보다 나아가서, 결론적으로:
typedef는 D2에서 deprecated다.
Tag // Programming D