으음. 참고로 저는 DirectXMath.h(Windows 8 or higher)를 사용합니다..'만'
옛날에 책 따라서 만든 Box 예제가 있길래 그 위에다가 대충 테스트 할려고 구현한거라 XNAMath를 사용하게 됐습니다 ㅇㅂㅇ;

XNAMath(혹은 DirectXMath.h를 include 한 후 using namespace DirectX를 타이핑 했다면)와 Bullet Physics를 동시에 사용하게 되면 C2084에러를 보게 되는데 이 때는 당황하지 마시고

#define BT_NO_SIMD_OPERATOR_OVERLOADS
#include <btBulletDynamicsCommon.h>

이렇게 처리해 주시면됩니다.

자 시작해볼까요?

먼저 엔진을 사용할 준비를 해야겠죠? Bullet Physics Hello World예제는 한번 보고 오셨을거라 생각합니다.
아직 안보신 분은 Hello World! <- 클릭.

btBroadphaseInterface* broadphase = new btDbvtBroadphase;
btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration;

btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);

// 제가 테스트 해볼 박스 위치가 (-1,-1,-1), (1,1,1)입니다. 그러니 아래처럼 해줘야죠.
btCollisionShape* boxCollisionShape = new btBoxShape(btVector3(1, 1, 1));
// 변환따위 하지 않습니다. 카메라는 돌아가지만 변환은 하지 않습니다.
btDefaultMotionState* motionStatenew btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, btVector3(0, 0, 0)));

btRigidBody::btRigidBodyConstructionInfo rigidBodyCI(0, bulletUtil.motionState, bulletUtil.boxCollisionShape, btVector3(0, 0, 0))
btRigidBody* rigidBody =  new btRigidBody(rigidBodyCI);
dynamicsWord->addRigidBody(rigidBody);

이제 여러분의 DirectX 프레임워크에 있는 마우스 이벤트 처리 함수에 다음과 같이 코딩해봅니다.


void PickingTest::OnMouseDown(WPARAM btnState, int x, int y)
{
	XMMATRIX proj = XMLoadFloat4x4(&m_Proj); // Projection 행렬입니다.
	XMMATRIX view = XMLoadFloat4x4(&m_View); // View 행렬입니다.

	XMVECTOR rayOrigin = XMLoadFloat4(&XMFLOAT4(0, 0, 0, 1.0f));
	// 혹시 Origin 점 위치가 왜 원점인지 이해가 안가시나요?
	//      /                 |                                             |
	//    /                   |                                             |
	//  /                     |                                             |
	//뷰----광선---NEAR--------->물체                  FAR
	//  \                    |                                             |
	//     \                 |                                             |
	//        \              |                                             |
	//
	// 즉, 광선의 시작점은 카메라 공간(View)에서 카메라 위치인 0,0,0 에서 시작됩니다.
	//
	XMVECTOR rayDirection = XMLoadFloat4(&XMFLOAT4(
		((float)(2.0f * x) / (float)mScreenViewport.Width - 1.0f) / m_Proj._11,
		((float)(-2.0f * y) / (float)mScreenViewport.Height + 1.0f) / m_Proj._22,
		1.0f,
		0.0f));
	// 광선의 시작지점이 원점이므로, 이 원점에서 어떤 "방향"으로 광선이 발사될지 정해줘야합니다.
	// 광선의 방향은 Point(x, y, z) - O(0, 0, 0) = Point(x, y, z)이므로 
	// 저희는 스크린 포인트의 마우스 위치를 뷰 행렬까지 옮겨주면 될 듯 합니다.
	// 원래 Loacl * World * View * Proj * ViewPort 순서대로 곱한 결과가 최종 스크린 좌표이므로(마우스는 이미 최종 스크린 좌표입니다.)
	// Local * World * View = ScreenPos * ViewPort^-1 * Proj^-1 이겠네요.

	XMVECTOR det2 = XMMatrixDeterminant(view); // 만약 DirectXMath 가 아니라 XNAMath 를 사용하신다면 반드시 필요합니다.
	XMMATRIX invView = XMMatrixInverse(&det2, view); // XNAMath XMMatrixInverse는 첫번째 인자가 nullptr 일 수 없습니다. DirectXMath는 nullptr 가능.

	rayOrigin = XMVector4Transform(rayOrigin, invView);
	rayDirection = XMVector4Transform(rayDirection, invView);

	rayDirection = XMVector4Normalize(rayDirection) * 1000.0f;
	// 방향을 구한다음에 길이를 재설정해줍니다. 으음. 길이를 설정해줘야 하더군요. 노말라이즈 안하고 그냥 값 넣어도 작동은 됩니다.

	XMFLOAT3 rayOriginF;
	XMFLOAT3 rayDirectionF;

	XMStoreFloat3(&rayOriginF, rayOrigin);
	XMStoreFloat3(&rayDirectionF, rayDirection);

	// 아래 코드는 이제 보시면 이해 할 것입니다.
	// 자세한 함수 사용 방법은 Bullet Physics Document를 참고해주세요.
	btCollisionWorld::ClosestRayResultCallback rayCallback(
		btVector3(rayOriginF.x, rayOriginF.y, rayOriginF.z),
		btVector3(rayDirectionF.x, rayDirectionF.y, rayDirectionF.z));
	bulletUtil.dynamicsWorld->rayTest(
		btVector3(rayOriginF.x, rayOriginF.y, rayOriginF.z),
		btVector3(rayDirectionF.x, rayDirectionF.y, rayDirectionF.z),
		rayCallback);

	if (rayCallback.hasHit())
	{
		SetWindowText(mhMainWnd, L"성공");
	}
	else
	{
		SetWindowText(mhMainWnd, L"실패");
	}
}

아래는 결과. 마우스를 캡쳐 할 수 있는 캡쳐 프로그램이 없어서 그냥 그림판으로 대충 동그라미 그렸습니다. 동그라미 가운데 부분이 클릭지점 입니다.




고딩 수학을 말아먹은 관계로 뭔가 틀린 부분이 있다면 알려주시면 감사하겠습니다.

업데이트 내용


Version : 5.0-4493 Update 5

(2014/09/10)

Improvement

  1. Improve the stability of file copy to encrypted shared folders.

Fixed Issues

  1. Fixed a vulnerability that could allow servers to accept unauthorized access.



핵놀 유저들은 Nanoboot-5.0.3.1 DSM 5.0-4493 X64 기준으로


제어판 - 업데이트 및 복원 - 다운로드까지만 누른다.

SSH 접근해서 밑에껄 입력 해준다.

sed 's/flashupdateDeb/flashupdateDeb1/' /autoupd@te.info > /autoupd@te.info1 

mv /autoupd@te.info1 /autoupd@te.info


자세한 방법은 이전 글 참조

http://youtil.wo.tc/123




왼쪽 - MASS 적용, 오른쪽 - MSAA 미적용
#근대_왜_미쿠댄이_더_자연스럽고_퀄리티가_높은거냐

아직 스피어(PMX 포맷 설명에서는 그냥 일본어로 スフィア 라고 나와있습니다. 이펙트 셰이더 소스코드를 살펴보면 Sphia라고 나와있는데 Sphere인지 뭔지 모르겠네요)를 어떻게 처리해야 하는지 감이 안잡힙니다.
추측으로는 미쿠댄 방식대로 dist ratate_x_y_z의 값을 일일이 하나하나 저장해놓고 이걸 토대로 카메라를 구현한 후, 회전행렬을 만들어 VertexNormal * matRotate 형식으로 곱해주는게 아닌가 라는게 현재 추측 입니다.
하지만
카메라 구현하기 귀찮아 -ㅅ-; (일해라)

매직넘버 노가다와 MikuMikuDance를 잠시 뜯어(...) 이펙트 셰이더를 추출해서 대충 어떻게 렌더링 하는지는 보았습니다.
어음.. 뭐하는 코드인지 이해가 불가능합니다. 일단 따라 타이핑 해본 결과가 위 사진이긴 합니다.(그런데 베껴써도 결과가 똑같이 안나와!? 어떻게 된거야!?)
코드를 보면 아시겠지만 캐릭터 텍스쳐 샘플러를 s1 이라고 이름 지어놓고 주석으로 샘플러 라고 써놓은 센스를 보아 아마도 누군가 봐도 분석하기 어렵게를 노린게 아닌가 싶으니(그렇게 믿고싶습니다.)
코드를 암호화 안해준 것 만으로 감사히 여겨야겠습니다.

일단 위 사진은 대충 PCF 기법을 사용한 그림자 맵과 대충 VIew행렬 때려넣은 스피어와 게임 개발 포에버에 나온 글을 참고하여 외곽선을 구현한 결과입니다. 제 실력의 혼자 덩그러니 있는 무쓸모 소립자 수준이라 이거 만드는데만 한달 걸렸네요 -ㅅ-;;

시작은 엔하위키에 미쿠댄이 사실상 오픈소스랑 다름 없다 라는 내용을 보고 어라 그래? 하고 개발 시작한건데 오픈소스는 개뿔때기나 정보가 드럽게 없어서 개노가다짓을 하고 있습니다.
뭐 그나마 최신버전의 미쿠댄에서 셰이더 파일을 뜯어네 카툰렌더링을 어떤 방식으로 하는지 알아낸것 만으로도 감격의 눈물이...
사실 이 셰이더 뜯어내는 방법은 안쓰려고 했던 방법인데 2주 동안 아무리 값을 바꾸고 방식을 바꿔봐도 똑같이 안나오길래 결국 써버렸습니다..

이거 만들면서 느낀게 카툰 렌더링 만드는 방법은 참 많구나.. 하나와 만들기 진짜 귀찮구나... 정도..

만들면서 멘붕했던건

1. 카툰 렌더링 방법. 셰이더 뜯고 어느정도는 해결 됨.
2. Toon 텍스쳐는 대체 뭐냐... 현재 추측으로는 float2 ToonUV = float2(그림자_맵_에서_얻어온_깊이_값, 텍스쳐UV.v) <-인데 테스트는 아직입니다.
3. 스피어.. 이것도 추측일 뿐...
4. 추가 UV가 뭔지 모르겠음.
5. 모프가 뭔지 모르겠음.
6. 캐릭터 애니메이션도 문제라면 문제인데 BDEF라던가 뭐 이런 본 방식들이 뭘 말하는건지 전혀 모르겠음ㅜ_ㅜ..
7. 애초에 미쿠댄스 사용 방법도 모릅니다(사용해 본 적이 있어야지...)
8. 한자를 읽을 줄 모름... 일본에서 만든거다 보니 Wiki라던가 대부분의 글이 일본어라 한문이 들어가 있는데 번역기의 한계 때문에 읽기가 힘듭니다..

그럼 저는 이제... 아마도 애니메이션을 구현하러 가야겠군요... 으음.. 다..다녀오겠습니다.
좀 더 자세한 구현 이야기는 다음 멘붕 시간에뵈여!

업데이트 내용

Version: 5.0-4493 Update 4

(2014/08/26)

Fixed Issues

  1. Upgraded OpenSSL to Version 1.0.1i to fix multiple security issues (CVE-2014-0224, CVE-2014-0221, CVE-2014-0195, CVE-2014-0198, CVE-2010-5298, and CVE-2014-3470).
  2. Implemented fixes for Kerberos 5 to address multiple security issues that allow remote attackers to cause a denial of service (CVE-2012-1013, CVE-2014-4341, CVE-2014-4342, CVE-2014-4344).
  3. Fixed an issue where files with ACL attributes could become inaccessible after being loaded in large batches when DiskStation acts as an NFS server.
  4. Fixed an issue where the restore might not be successful when containing multiple subfolders in Time Backup.
  5. Fixed an issue where DHCP server service does not map to the correct VLAN interface if the service started before VLAN is enabled.



핵놀 유저들은 Nanoboot-5.0.3.1 DSM 5.0-4493 X64 기준으로


제어판 - 업데이트 및 복원 - 다운로드까지만 누른다.

SSH 접근해서 밑에껄 입력 해준다.

sed 's/flashupdateDeb/flashupdateDeb1/' /autoupd@te.info > /autoupd@te.info1 

mv /autoupd@te.info1 /autoupd@te.info

 

지금 업데이트를 누른다.



그럼 알아서 진행 후 재시작이 된다.



 재붓하고도 접속이 안된다면 다시 재붓을 해주면 접속이된다!






x86osx에서 계속 올라오는 질문에 지쳐서 작성한 글입니다.

사실 구글에서 검색을 해보신 분들이면 볼 가치도 없을만한 글이죠 (웃음)


여러가지 앱이 있지만 전 개인적으로 Kext Utility 앱을 쓰는걸 좋아합니다.

간단해서 누구나 쉽게 따라 할 수 있다는게 장점이죠

http://cvad-mac.narod.ru/index/0-4


위 링크로 가신 후 다운로드를 하시면 됩니다.


사용방법도 엄청 간단합니다.

원하는 kext를 때려넣고 비번 입력하면 끝!

무슨 소리인지 모르신다고요? 보고 따라하세요!


참 쉽죠?


Windows 8.1 기준이긴 하지만 어지간한 구형 윈도우가 아닌 이상 다 될 거라 본다.


일단 URL 재작성 (Rewrite)부터 설치해야 하는데 이미 있는 분들은 생략한다.

http://www.iis.net/downloads/microsoft/url-rewrite

위 링크에서 'Install this extension' 클릭 후 대충 확인 눌려주고 설치를 해준다. 



설치가 대략 끝나면 URL 재작성이 생기는데 (없으면 끄고 켠다.) 원하는 사이트에서 더블 클릭으로 들어가 본다.


여기서 오른쪽 메뉴에서 '작업 - 규칙 추가' 빈규칙으로 확인을 눌러준다.


이제 아무것도 없는 빈 페이지가 나온다.

인바운드 규칙 편집에서 이름은 아무거나 원하는 걸로 입력한다.


URL 검색에서는 요청한 URL: 패턴과 일치, 사용: 정규식

패턴은 대/소문자 무시를 체크한 후 밑에 값을 넣어준다.

.*\.(jpg|jpeg|png|gif|bmp)$

조건에서는 논리 그룹화 모두 일치로 설정하고 밑에 조건에 따라 캡처 그룹 추적은 체크하지 않는다.

추가를 눌려준 후 밑에 값처럼 넣어준다.

조건 입력: {HTTP_REFERER}

유형: 패턴과 일치하지 않음

패턴:

^$ 

^http://www.mydomain.com/.*$

^http://mydomain.com/.*$

대/소문자 무시


마지막으로 작업 유형 재작성 나머지 체크는 모두 해제하고

URL 재작성에 원하는 링크나 보여줄 이미지로 연결해주면 된다.


이제 이미지 링크를 누군가가 퍼가도 위에 작성한 링크로 이미지가 출력된다.


보너스! 위 작업을 좀 더 간단하게 작업하기!

web.config 편집하기를 눌려서

<!--?xml version="1.0" encoding="UTF-8"?-->
<configuration>
    <system.webserver>
        <rewrite>
            <rules>
                <rule name="Prevent hotlinking">
                    <match url=".*\.(jpg|jpeg|png|gif|bmp)$">
         		 <conditions>
        		    <add input="{HTTP_REFERER}" pattern="^$" negate="true">
        		    <add input="{HTTP_REFERER}" pattern="^http://www.mydomain.com/.*$" negate="true">
        		    <add input="{HTTP_REFERER}" pattern="^http://mydomain.com/.*$" negate="true">
                    </add></add></add></conditions>
                    <action type="Rewrite" url="/hotlink-ok/noimage.jpg" appendquerystring="false">
                </action></match></rule>
            </rules>
        </rewrite>
    </system.webserver>
</configuration>


위에 코드를 붙여넣기 후 저장하면 좀 더 간단하게 작업이 가능하다.


참조한 글: http://www.gamedevforever.com/317

'Web > IIS' 카테고리의 다른 글

IIS8 에서 LetsEncrypt simple windows 문제  (0) 2017.04.16
IIS 8.5에 Redmine(3.2.0)을 올려보자.  (0) 2015.12.26

안녕하세요! 2시간 쯤 전에 개발을 마치고 소드 아트 온라인 프로그레시브 2권을 읽고오니 공돌이가 싫어하는 태양은 사라지고 기분 좋은 밤 공기가 맞이해주는군요.
아 정말 좋은 밤입니다. 맥주 마시고 싶네요 ㅇㅅ;;

이 글은 이걸로 마지막입니다.

아직 개발해야 할게 몇 개 남았지만 제 실력에는 좀 무리일거 같고 여러분 실력이면 충분히 개발 가능하지 않을까 싶습니다.
이미 개발하시고 'ㅋㅋㅋ 이거 글 언제 올라오냐 ㅋㅋ 포기한건가 ㅋㅋㅋ' 라는 생각을 하고 계신 분들도 계시겠죠 ㅜ_ㅜ 제가 머리가 나빠서 그렇습니다 ㅜㅜ

뭐 기분 좋게 스크린샷으로 시작해보죠. 다운그레이드 됐다면 다운그레이드 됐지만 설치된 글꼴만 사용 가능합니다.



오오옹. 잘 되는 거십니다.

이 글은 http://youtil.wo.tc/113 <- 에서 설명하는 글을 마친 상태에서 진행됩니다.

먼저 EditBox를 띄우는 방법을 알아야겠죠.
이미 알고 계시겠지만 3D에서 흔히 사용하는 투영 행렬은 마지막 계산 결과가 -1, -1, 0 ~ 1, 1, 1 사이에 있게됩니다. 다시 말해서 프로젝션 행렬을 굳이 연산하지 않아도 괜찮다는 말이죠.

그래서 버텍스가 (-1, -1, 0.1), (1, 1, 0.1)인 사각형을 렌더링 하려면 그냥 identity 행렬을 곱해주고 그대로 렌더링하면 되고 그 결과는 Client Window의 0,0 부터 Width, Height까지 완벽하게 꽉 찬 사각형이 렌더링 되게 됩니다. 오호, 마침 저희에게 딱 필요하지 않나요? 이렇게 된거 아예 저 버텍스에다가 Scaling하고 Translation을 먹여서 크기를 바꿔서 클라이언트 좌표에 맵핑시켜서 렌더링 하는것도 괜찮은 방법인거 같습니다. 오오. 속도는 모르겠지만요.....

먼저 클라이언트 좌표계를 NDC공간으로 바꿔보죠. 으음. 일단 Client Window의 Width와 Height가 필요할것 같군요.

struct TransformNDC
{
	float scaleX, scaleY, scaleZ;
	float movX, movY, movZ;
};

TransformNDC ClientToNDC(const RECT& target, int windowWidth, int windowHeight)
{
	float width = target.right - target.left;
	float height = target.bottom - target.top;

	float scaleX = width / windowWidth;
	float scaleY = height / windowHeight;
	float scaleZ = 1;

	float ratioX = 2.0f / windowWidth;
	float ratioY = 2.0f / windowHeight;

	float movX = -(1 - scaleX) + (ratioX * target.left);
	float movY = (1 - scaleY) - (ratioY * target.top);
	float movZ = 0;

	return TransformNDC{ scaleX, scaleY, scaleZ, movX, movY, movZ };
}

굳이 클래스에 넣어야 하나 싶어서 그냥 만들어봤습니다.
대충 이렇게 씁니다.

auto translate = ClientToNDC(RECT{ 0, 0, 800, 600 }, mClientWidth, mClientHeight);

XMMATRIX scale = XMMatrixScaling(translate.scaleX, translate.scaleY, translate.scaleZ);
XMMATRIX world = XMMatrixTranslation(translate.movX, translate.movY, 0);

XMMATRIX를 블로그에서 쓰려니 갑자기 생각난건데 개발 할 때 DX초기화 하는걸 직접 하기가 좀 뭐시기 해서 제가 보는 책에 있는 프레임워크를 가져다 썼더니 쓰게됐네요. 전에 넥슨 다니시는(부럽) 학교 선배분께 'XNA라이브러리 쓰나요?' 라고 물었다가 '그게 망한지가 언젠데 아직도 쓰고있어?'라는 소리를 들어서(...) 저도 슬슬 버릴까 합니다만 책을 다 볼 때 까지는 쓰려고 합니다 ㅇㅅ.

뭐 이제
http://youtil.wo.tc/114 <- 이 글을 참고하셔서 IMM 컨텍스트를 적당한 메시지에 생성해 주시고...
http://youtil.wo.tc/115 <- 를 참고해서 적당히 insert와 erase '만' 쓰도록 하죠.
사실 115번 글에서 사용한걸 사용 가능할거라고 믿었는데 산산히 부숴졌습니다. 뭐 그래도 DirectWrite의 DrawText도 잘 만들어져 있어서요. 위 글에서 사용한 방법보단 좋을것같네요. 다소 귀찮지만.

적당히 첫번째 글을 마쳤다면 DWriteFactory(IDWriteFactory)객체가 하나 있을거라고 생각됩니다.(사실 저는 여기저기 짜집기한거라 DWriteFactroy가 첫번째 글에 있는 그 링크에 있는지 없는지 기억이 안나네요; 없으면 만들어주세요 ㅜ_ㅜ)

그래서 DWriteFactory를 잘 살펴보면 CreateTextLayout라는 맴버 함수가 있습니다. 이 녀석을 이용해 IDWriteTextLayout이라는 녀석을 만들어보죠.

IDWriteTextLayout* layout;
// DWriteFactory가 없다면 만들어주세요.
DWriteFactory->CreateTextLayout(printText.c_str(), cursorPos, TextFormat, mClientWidth, mClientHeight, &layout);

그러고 나서 layout 인터페이스에 짝대기를 하나 그어주니... GetMetrics 라는 맴버 함수가 하나 나오는군요!!! 우오오! 그리고 그 밑에 좀 더 찾아보니 무려 GetLineMetrics라는 녀석도 있습니다 우와아아! 이걸로 멀티 라인을 만들 수 있어요! 안에 무슨 변수가 들어가는지는 MSDN을 참고하시고! 여기 서중요한건 GetMetrics로 현재 몇 라인을 가지고 있냐와 폰트의 Height를 구할 수 있습니다. 그리고 GetLineMetrics라는 녀석을 사용하면 각 라인에 몇개의 글자가 들어가있는지 알 수 있죠.
자 그럼 아래 발로 짠 코드를 잠시 발로 볼까요?

IDWriteTextLayout* layout;
// 출력할 때는 모든 문자열이 필요하지만 길이를 계산할 때에는 현재 커서 위치만 알면 됩니다.
// 아래 두번째 인자를 봐주세요. 현재 커서 위치를 담고있는 변수입니다.
// mClient* 가 들어가는 곳은 에디트 박스의 크기를 넣으시면 됩니다. (만들어질 에디트 박스의 크기가 아닙니다. 보여지는 에디트 박스의 크기입니다!)
// 아래에서 사용되는 mClient*도 같은 의미입니다.
DWriteFactory->CreateTextLayout(printText.c_str(), cursorPos, TextFormat, mClientWidth, mClientHeight, &layout);

DWRITE_TEXT_METRICS metrics;
layout->GetMetrics(&metrics);

std::vector<DWRITE_LINE_METRICS> lineMetrics(metrics.lineCount);
UINT32 lineCount;

layout->GetLineMetrics(&lineMetrics[0], lineMetrics.capacity(), &lineCount);
ReleaseCOM(layout);

int length = lineMetrics[lineCount - 1].length;
// 전체 string에서 현재 라인의 string만 구하는 코드입니다.
// 현재 라인에서 커서를 움직이기 위해서는 현재 라인의 글자가 렌더링될 width를 알아야 하거든요.
const wchar_t* currentLineText = printText.c_str();
// 아래 배열이 음수가 될 일은 없냐구요? 없습니다. length의 최대 길이는 cursorPos와 같거든요.
currentLineText = ¤tLineText[cursorPos - length];
DWriteFactory->CreateTextLayout(currentLineText, length, TextFormat, mClientWidth, mClientHeight, &layout);

layout->GetMetrics(&metrics);
ReleaseCOM(layout);

자 이제RECT를 계산해서 위에서 만든 ClientToNDC 함수를 사용해서 사각형을 잘 변환 한 뒤 렌더링 하면 커서가 되겠죠?

// 블로그로 코드를 옮기다 보니 함수의 일부분만 올리게 되서
// 구조가 바뀌다보니 RECT변수를 급하게 하나 추가했습니다. 그래서 초기화 안했습니다.
// 실제 제가 사용하는 코드에서는 사용자 정의 구조체나 클래스는 제대로 참조자로 받아서 수정하지 return 따위도 하지 않습니다.
RECT rect;

rect.top = (lineCount - 1) * ceil(lineMetrics[0].height);
rect.bottom = rect->top + ceil(lineMetrics[0].height);
rect.left = ceil(metrics.widthIncludingTrailingWhitespace);
rect.right = rect->left + 1;

return rect;

드디어 제가 블로그를 시작한 이래로 가장 가장 길게 쓴 글이 막을 내렸군요.
너무 대충대충 설명하는 느낌이라 정말 죄송합니다. 이번 글은 1편 빼면나름 성실히 작성한다고 작성한건데 생각처럼 잘 되진 않았네요.

부디 제 글이 조금이나마 도움이 되길 바랍니다.
저는 이제 월광 1권을 읽어보러~~~ (저를 덕후로 만든게 제이노블인지라 제이노블을 좋아합니다.)

... 멋지게 실패했습니다.

아니 사실 실패라기보단..거의 포기에 가깝네요. 여러가지 한계가 너무 많이보여서요.
해결 방법도 찾았고 나름 간단한 방법으로 해결이 가능하다는 사실도 알고있지만.. 의욕이 너무 떨어지네요.

뭐 일단 만들어 볼 생각이긴 합니다.
그러나 폰트를 설치 안하고 직접 읽어들여서 사용하는건 좀 귀찮은 일이 되버려서(...) 아마 폰트를 직접 읽어들이는 방식은 아닐것 같습니다.

일단 폰트를 직접 로드하는 방식에 약간의 문제가 발생했습니다.
마지막 글자가 ' '<- 공백 문자라면 이것은 길이로 치지 않는다는 것. 그래서 커서를 옮길 때 마지막 문자가 공백일 경우 커서가 안옮겨집니다...
제가 원하는건 그런게 아니므로... 일단 이 방법은 잠시 포기해야 할 것 같습니다.

그렇다고 폰트를 읽어들여서 하는 방법이 없냐 라고 묻는다면 있습니다.
Microsoft Windows SDK 7 Sample 소스코드에 이 해결책이 있습니다. 바로 커스텀 폰트 컬렉션 인터페이스를 상속받아 이 인터페이스를 직접 구현해야하는데
구현해야할 인터페이스가 3개가 있습니다.

안타깝게도 제가 IUnknown에 대해 몰라서(쩝...) 소스코들를 봐도 이게 대체 뭐하는건지 알 수가 없네요.

두 번째 문제는 커서의 위치가 일정하지 않다는 것 입니다.
영문의 폰트에 보면 Metrics에 descent 와 ascent라는게 있는데 이 문제 때문에 Drawing 영역의 Top Bottom 위치가 일정하게 잡히지 않습니다.
그래서 글자를 입력할 때 마다 커서의 위치가 바뀌는 문제가 발생합니다 ㅜ_ㅜ...(f와 j... 이 두개의 글자가 제일 말썽이더군요..)


사진을 모시면 아시겠지만 마지막 j를 타이핑 한 후 커서가 진짜 조금 내려간 모습이 보입니다.

일단 이 방법도 사용은 가능하지만, 마지막 띄어쓰기가 보여지지 않는다는게 너무 찝찝해서(...)
일단 이 방법 말고 이미 설치된 폰트를 이용해서 출력하는 방법을 이용해 보려구요.
MS Sample 프로젝트 라이선스도 한번 확인해 본 다음에 그대로 써도 된다면 그 방식도 한번 써 보려고 합니다.

조금 시간이 더 걸리겠군요 ㅇㅅㅇ.; 일단 애니나 라노베좀 보면서 멘탈좀 정화하다가 다시 시도하겠습니다.

혹시 참고할 만한 자료가 있다면 알려주시면 감사하겠습니다.

참고 사이트

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=687984&ref=687984
http://support.microsoft.com/kb/74299
http://msdn.microsoft.com/en-us/library/windows/desktop/ms533820%28v=vs.85%29.aspx
http://msdn.microsoft.com/ko-kr/library/y505zzfw%28v=vs.110%29.aspx
http://msdn.microsoft.com/ko-kr/library/xwf9s90b%28v=vs.110%29.aspx
http://cx5software.sakura.ne.jp/blog/2011/01/18/directwrite-metrics/
http://msdn.microsoft.com/en-us/library/windows/desktop/dd941785%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/dd941710%28v=vs.85%29.aspx
http://msdn.microsoft.com/ko-kr/library/windows/desktop/dd756583%28v=vs.85%29.aspx
http://www.gamedev.net/topic/309327-direct3dfont-using-local-folder-ttf/

기타 등등.

업데이트 내용

Version: 5.0-4493 Update 3

(2014/07/24)

Improvements

  1. Improved the stability of volume expansion.

Fixed Issues

  1. Fixed two SAMBA vulnerabilities which allowed remote attackers to use the weaknesses to perform DoS attacks (CVE-2014-0244, CVE-2014-3493).
  2. Fixed an issue which prevented users from being able to log into DSM after completing QuickConnect Wizard after a fresh DSM installation.
  3. Fixed an issue causing slow response time in DSM on the early version of RS10613xs+ when too many concurrent connections were active.



핵놀 유저들은 Nanoboot-5.0.3.1 DSM 5.0-4493 X64 기준으로


제어판 - 업데이트 및 복원 - 다운로드까지만 누룬다.

SSH 접근해서

sed 's/flashupdateDeb/flashupdateDeb1/' /autoupd@te.info > /autoupd@te.info1 

mv /autoupd@te.info1 /autoupd@te.info

라고 입력 후 업데이트를 누른다.


그럼 끝!



우와. 포스팅량이 늘었군요. 자축 자축.
에... 2부에서 예고했던데로 커서를 그려보고 왔습니다.
사실 만드는건 한 시간도 안걸렸는데 놀다보니(...) 포스팅이 좀 늦어졌네요.
별것도 아닌데 3부를 괜히 날려먹는거 아닌가? 몇 부작으로 갈 생각이지? 라는 생각은 저 멀리 다른 차원으로 집어던져버리고 빨리 코드를 살펴보겠습니다.

일단 간단하게 한개만 인클루드 해보죠.

#include <usp10.h>

좋습니다. 자 이제 함수 하나를 만들어 보겠습니다.

// 귀찮으니 전역 변수로 때워봅시다.
SCRIPT_CONTROL ScriptControl;
SCRIPT_STATE ScriptState;
SCRIPT_STRING_ANALYSIS analysis = nullptr;

void InitScriptStringAnalysis(HDC hdc, std::wstring str)
{
	if ( analysis == nullptr )
	{
		ZeroMemory(&ScriptControl, sizeof(ScriptControl));
		ZeroMemory(&ScriptState, sizeof(ScriptState));
		ScriptApplyDigitSubstitution(nullptr, &ScriptControl, &ScriptState);
	}
	else
		ScriptStringFree(&analysis);

	ScriptStringAnalyse(hdc, str.c_str(), str.length() + 1,
						str.length() * 1.5 + 16,
						-1,
						SSA_BREAK | SSA_GLYPHS | SSA_FALLBACK | SSA_LINK,
						0,
						&ScriptControl,
						&ScriptState,
						nullptr,
						nullptr,
						nullptr,
						&analysis);
}


으음. 뭐. 그럭저럭 테스트 해볼만 한 함수가 나왔습니다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	static HIMC imeID = nullptr;
	static std::wstring str;

	wchar_t debug[256] = { 0 };
	static int x = 0;

	RECT rect = { 0, 0, 0, 0 };

	switch ( message )
	{
		case WM_ACTIVATE:
			if ( imeID == nullptr )
			{
				imeID = ImmCreateContext();
				ImmAssociateContext(hWnd, imeID);
			}
			break;
		case WM_IME_STARTCOMPOSITION:
			break;
		case WM_IME_CHAR:
			str.push_back((wchar_t)wParam);
			InvalidateRect(hWnd, NULL, TRUE);
			break;
		case WM_CHAR:
			if ( (wchar_t)wParam == '\b' )
			{
				str.pop_back();
			}
			else
			{
				str.push_back((wchar_t)wParam);
			}
			InvalidateRect(hWnd, NULL, true);
			break;
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			// DT_CALCRECT옵션은 출력할 글자의 범위를 구하는 옵션입니다.
			// 따라서, 아래 함수를 호출하게 되면 rect변수에 출력할 글자의 범위를 구해서 던져줍니다.
			// 여기에서 우리는 top와 bottom만 필요합니다. 전체 width 는 필요 없으니까요.
			DrawText(hdc, str.empty() ? L"A":str.c_str(), -1, &rect, DT_CALCRECT | DT_CENTER);
			DrawText(hdc, str.c_str(), str.length(), &rect, DT_CENTER);

			// 이 함수를 호출하게 되면 ScriptStringCPtoX를 사용하기 위한
			// SCRIPT_STRING_ANALYSIS 를 얻게됩니다. 자세한 내용은 MSDN을 참고해주세요.
			InitScriptStringAnalysis(hdc, str.c_str());
			ScriptStringCPtoX(analysis, str.length(), false, &nTrail);

			// 커서를 그립니다. DrawText에서 얻어온 글자의 출력 범위를 top와 bottom 으로 하고,
			// ScriptStringCPtoX에서 얻어온 마지막 글자 인덱스의 위치를 left로 하고 여기서 1을 더해 width가 1인
			// 사각형을 그립니다.
			Rectangle(hdc, x, rect.top, x + 1, rect.bottom);
			wsprintf(debug, L"커서 위치 : %d %d %d %d", x, rect.top, x + 1, rect.bottom);
			SetWindowText(hWnd, debug);

			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY:
			ImmDestroyContext(imeID);
			PostQuitMessage(0);
			break;
		// 코드가 길어져서 기본 프로시저 함수에서 수정되지 않은 이벤트는 쓰지 않았습니다.
	}
	return 0;
}

자 그럼 사진을 하나 투척해보죠.


짞짞짞.

으음. 하지만 커서가 안움직이니 뭔가 허전하군요. 명색의 커서인데 움직여야 하지 않겠어요? 움직였으면 움직인 자리에 무언가 입력하거나 삭제도 되야죠.
으음.. 한번 해보죠. 재밋을거같으니.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	// 기존 변수 생략
	static int nowPosition = 0;

	switch ( message )
	{
		case WM_ACTIVATE:
			if ( imeID == nullptr )
			{
				imeID = ImmCreateContext();
				ImmAssociateContext(hWnd, imeID);
			}
			break;
		case WM_IME_STARTCOMPOSITION:
			break;
		case WM_IME_CHAR:
		case WM_CHAR:
			if ( (wchar_t)wParam == '\b' )
			{
				if ( nowPosition == str.length() )
				{
					str.erase(nowPosition - 1);
				}
				else
				{
					// 중간 문자를 삭제하기 위한 코드입니다.
					const wchar_t* strBack = str.c_str();
					strBack = &strBack[nowPosition];

					str.erase(nowPosition - 1);
					str.append(strBack);
				}
				nowPosition = nowPosition - 1 < 0 ? 0:nowPosition - 1;
			}
			else
			{
				// 중간에 글자를 삽입하기 위해 insert로 바꾸었습니다.
				str.insert(nowPosition, (wchar_t*)&wParam);
				nowPosition = nowPosition + 1 > str.length() ? str.length():nowPosition + 1;
			}
			InvalidateRect(hWnd, NULL, true);
			break;
		case WM_KEYDOWN:
			if ( (int)wParam == VK_LEFT )
			{
				nowPosition = nowPosition - 1 < 0 ? 0:nowPosition - 1;
				InvalidateRect(hWnd, NULL, true);
			}
			else if ( (int)wParam == VK_RIGHT )
			{
				nowPosition = nowPosition + 1 > str.length() ? str.length():nowPosition + 1;
				InvalidateRect(hWnd, NULL, true);
			}
			break;
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			DrawText(hdc, str.empty() ? L"A":str.c_str(), -1, &rect, DT_CALCRECT | DT_CENTER);
			DrawText(hdc, str.c_str(), str.length(), &rect, DT_CENTER);


			InitScriptStringAnalysis(hdc, str.c_str());
			ScriptStringCPtoX(analysis, nowPosition, false, &x);

			Rectangle(hdc, x, rect.top, x + 1, rect.bottom);
			wsprintf(debug, L"커서 위치 : %d %d %d %d", x, rect.top, x + 1, rect.bottom);
			SetWindowText(hWnd, debug);
			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY:
			ImmDestroyContext(imeID);
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

이 기세라면! 마우스로 클릭해서 커서를 옮기는 것도 가능할거같군요. 확인해 보는것도 좋겠지만 이번 편은 ScriptString를 테스트해보기 위한 편이었으니 그냥 안할려고 합니다 ㅇㅅ.
작동되는거 확인했으니 됐죠 뭐 ㅋㅅㅋ.

이번에는 GDI로 직접 EditBox를 만들어 보았습니다. 코드는 좀 지저분하지만 어떠신지요?
중복된 코드를 지우기 위해서 WM_IME_CHAR에 keybd_event로 VK_RIGHT를 누르게 해 보았는데 왜인지 모르겠지만 한글 타이핑에 문제가 생기더군요.
원인을 모르겠네요. 아시는분 알려주시면 감사하겟습니다(_ _ )

그런고로! 다음에는 DirectX11에 직접 적용시켜 볼 차례군요.
자 그럼 다음시간에! (언제가 될지 모르겠지만 노력해 보겠습니다..)

커서 위치에 주목!

+ Recent posts