Sunday, October 6, 2013

Fate Standalone Project Update

It's been about a year since I first announced project Fate. Surprisingly, I'm still getting inquiries about the current status of the project from numerous people (which by the way thank you so much for your interest and support), so I will try to provide some clarification.

Long story short, the project is currently suspended for the following reasons:


  1. Both the project leaders, myself and Squally are fairly busy with our jobs.
  2. We don't have enough members. (Especially designers)
  3. We'd like to get the current Fate / Another III completely out of the way before proceeding with Fate project
The last bit is especially important for us. We're currently working on Fate / Another to utilize it as an example case for Fate project. In other words, by implementing features in Fate / Another, we can determine in advance how public reacts to the game. Without this anecdote, we'd have to perform a lot of experimentation as the project progresses, potentially consuming a lot of time and degrading members' morale.

We are not entirely sure when exactly we will continue with the project. One thing I do want to emphasize is that making a standalone platform at the scale of Fate / Another is no trivial task. When I initially mentioned 3 years, I realized I was being exceedingly optimistic about the estimation. Realistically speaking, we are looking at 5 years minimum. Even this estimation is under the assumption that we have a designer by the time we begin.

At any rate, we are still accepting members for our team (Again, especially designers) so if you are interested, please read this post first and send us your applications! Thank you very much.

Thursday, June 20, 2013

Restlet how to post XML data from client

Recently, I was issued a project to set up a restful web server on Android platform. Googling landed me Restlet Framework and I must say it's a pretty good framework that gives a nice abstraction layer to do any REST related work.

Two things I really didn't like about Restlet is that initial setup was a bit painful, but what really ticked me off is the lack of documentations, which forced me to delve into the source code whenever I was stuck with something. Seriously guys, if you are going to make a framework and you actually want other developers using it, save us the trouble and provide a good documentation. Not everyone has the patience to look through source codes.

Anyways, I had a really weird issue with posting XML data from client to server using DomRepresentation. Both ends are using Restlet, using the exact same class to handle XML data but the server wasn't receiving the DOM no matter what I tried:

public void sendPostRequest(final String uri, final DomRepresentation entity) throws ResourceException, IOException {
 try {
  ClientResource client = new ClientResource(uri);
  client.post(entity); 
 }
 catch (Exception ex) {
  ex.printStackTrace();
 }
}


@Post
public void handleQuery(Representation rep) {
 try {
  if (rep == null) {
   this.setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
   return;
  }
  DomRepresentation domRep = new DomRepresentation(rep); 
  Document doc = domRep.getDocument(); //THROWS ERROR. 
                ....


Needless to say, this left me bewildered and I almost considered tossing the abstraction from the framework out the window and handling this at the HTTP level. Luckily, I eventually found out that DomRepresentation didn't work as advertised and instead, I had to work with StringRepresentation:

public void sendPostRequest(final String uri, final DomRepresentation entity) throws public static void sendPostRequest(final String uri, final Document doc) throws ResourceException, IOException {
 try {
  ClientResource client = new ClientResource(uri);
  StringRepresentation entity = new StringRepresentation(XMLUtils.getStringFromDocument(doc)); //Turn DOM into string and then instantiate StringRepresentation
  entity.setCharacterSet(null); 
  entity.setMediaType(MediaType.TEXT_XML);
  client.post(entity); 
 }
 catch (Exception ex) {
  ex.printStackTrace();
 }

I'm still not sure why DomRepresentation did not work in my case. It's either a bug or some obscure configuration that I must've missed (which clearly isn't mentioned in the documentation). Either way, this was pretty frustrating and I thought it merited a post in case other people had a similar problem.
By the way, if you are using Restlet, get yourself a copy of Restlet in Action. Given the lack of documentation, this will help you out a bunch. Seriously, just do it.

Monday, June 10, 2013

IE 9/10 Enter Key causing Form Submit / Button Click workaround

If you ever run into a problem where if you press enter inside a page and the page suddenly submits or a button click event launches, you need to manually stop the enter event from propagating outside a particular div.

//"Enter" Key is taken as a submit action by default on IE 9/10
//Prevent Enter key event from propagating outside the grid.
$('#SUBUL_01_DIV_GRID').keypress(function(e) {
    if(e.which == 13) { 
        e.preventDefault(); 
    }
});
IE, causing massive amount of frustration to web developers like always..

Tuesday, May 21, 2013

Shingeki no Cola - 진격의 콜라


I'm sure many of you have seen Shingeki no Kyoujin parody videos.

This one is my favorite.

Monday, May 13, 2013

PE 101 Walkthrough

This is absolutely beautiful that I had to post it here:

http://i.imgur.com/tnUca.jpg


Everything you wanted to know about how exe files run on a windows machine. Well, not quite everything, but covers a substantial ground.

Saturday, March 9, 2013

Windows 7/8 Grant Permission for Deleting Folders

Make a batch file named "delete.bat" and enter the following:

SET DIRECTORY_NAME="D:\NAME OF LOCKED FOLDER"
TAKEOWN /f %DIRECTORY_NAME% /r /d y
ICACLS %DIRECTORY_NAME% /grant administrators:F /t
PAUSE

This should give you the ownership to the folder you want to delete.

Sunday, March 3, 2013

Android NDK-Build 문제

최근에 NDK로 앱을 만들 일이 생겨서 cygwin 인스톨 하고 ndk-build 돌리려니 참 설정하는데에 머리 썩이고 골치아픈 문제들이 몇가지 발견되서 여기에 적어본다.

1. cygwin 설치시 주의사항

cygwin을 인스톨 할 시 패키지를 추가적으로 인스톨 시킬 수 있는 항목들이 나온다. 반드시 makevim을 설치해 주도록 하자.



윗 스샷을 보면 make가 Reinstall이라고 되어있는데 필자의 경우 이미 make 패키지가 설치되어 있어서 저렇게 표시되는 것이며, 독자들은 처음 인스톨시 저걸 클릭해서 Install로 바꿔주면 된다.


2. ndk-build 경로 설정법

웹상에 떠돌아 다니는 튜토리얼들을 보면 Android NDK 폴더에 있는 ndk-build의 경로를 환경 경로로 맵핑시켜주라는 말들이 많은데 그거 하지 마라. 내가 했을때는 아무리 해도 안됬고 되는 사람들도 있겠지만 그것보다 더 확실한 방법이 있다.

처음 cygwin 루트 들어가면 vim .bash_profile 키고 다음과 같이 path를 설정하면 된다:



앞에 주석 (#) 빼주는거 절대로 잊지 말고, 예를들어 NDK의 경로가 D:\android-ndk-r8d라고 있다면 위에 적은것과 같이 /cygdrive/d/android-ndk-r8d식으로 맵핑해 주면 된다.

3. Android.mk 작성시 절대적으로 주의해야 하는 사항

다음과 같은 android.mk가 있다고 치자
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE    := ndk1
LOCAL_SRC_FILES := native.c
include $(BUILD_SHARED_LIBRARY)
문제 없이 돌아가야 정상인데, 실제로 ndk-build를 때려보면 이런 X같은 에러가 뜨는 경우가 있을수도 있다:

make: * No rule to make target '/native.c', needed by'/Users/ivan/Documents/workspace/TestNDK/obj/local/armeabi/objs/myNDK/native.o'. Stop.


이런 문제가 발생하면 android.mk에 들어가서 스페이스(공백)이 없는지 샅샅이 살펴본다. 공백이 하나라도 있으면 저 말도 안되는 에러가 발생한다 (THANKS ANDROID!)

... 

4.기타 사항

소스코드 및 ndk 경로에 공백이 포함되어 있으면 제데로 컴파일이 되지 않는다. 그래서 모든 경로에 공백이 들어가 있으면 꼭 빼주도록..

Tuesday, February 5, 2013

Linkin Park - What I've done




Legend never dies.

Wednesday, January 9, 2013

DirectX11 렌더링 기법

현재 팀 Project Fate 소속원 중 Hyeon님이 나한테 나름 괜찮은 질문을 하나 주셔서 여기에도 올려본다. 사실 요즘 이런 저런일이 많이 생기다 보니깐 Project Fate과 그 팀원들에 대해 많이 소홀하게 여겼는데 가장 나이가 어린 멤버가 이렇게 열심히 공부하는 모습보고 감탄하고 동시에 미안한 마음이 많이 들기도 했다. 많이 반성해야 할지도..

아무튼 메일로 주신 질문 내용:


일반적으로 렌더링을 할때 가지고있는 object의 vertices가 갖는 위치벡터를 
object space에서 world space로 옮기기 위해 world transform 한번
world space에서 camera space로 옮기기 위해 view transform 한번
camera space에서 canonical view volume으로 perspective projection transform 시키잖아요?  
여기서 궁금한건... 일반적인 경우에 카메라는 하나고, 화면도 하나니까
한 프레임을 렌더링할때 사용할 view transform matrix와 projection transform matrix는 하나잖아요? 
렌더링을 시작하기전에 어디 constant buffer에 집어넣고있으면 만사 오케이인데...
world transform matrix는 물체 하나별로 하나씩 있어야 하잖아요???? 호옹이
vertex shader에서 vertices를 transform 할때 물체별로 다른 world matrix를 적용시켜야 하는건데 
실제로 작업할때엔 저걸 어떻게 구현하나요?? ??_??지금 콬님이 주신 튜토리얼에선 world matrix를 constant buffer에 저장하고있더라구요 
그런데 제가 CUDA 쓰면서 알게된 비디오카드의 메모리 구조에 대한 제 지식이 맞다면 constant buffer은 dynamic allocation이 불가능한걸로 알고잇어요 ㄷㄷ
실제 상황에서 렌더링할 object의 수가 dynamic하게 변할텐데 말이져

제가 찾으면서 스스로 답을 좀 내봤는데요.. 
1. 튜토리얼에서 알려준 방법이 이건데요, 모든 object들을 일시에 vertex shader로 넘기지 않고, object 가 A, B, C 있다면
A object의 world matrix를 상수버퍼에 입력 후, draw A object
B object의 world matrix를 상수버퍼에 입력 후, draw B object
C object의 world matrix를 상수버퍼에 입력 후, draw C object
이렇게 하더라구요
제대로 작동이야 하겠지만, 렌더링 한번하는데 저렇게 Synchronize를 자주해서야 그리는 시간이 물체의 갯수에 비례해서 O(n) 이 되어버리고
작업을 병렬로 처리하고싶은 셰이더의 철학에도 좀 맞지 않는것같아요. 사실 이방법이 조금 아닌거같다는 생각에 이 질문이 나왔어요 허허 
2. 이건 그냥 제스스로 생각해본건데
GPU에 메모리를 sizeof(XMMatrix)*( 물체의 수 ) 만큼 dynamically allocate한후에
vertex shader에서 vertex를 변환할때, 여러 world matrix중에 어느 matrix를 쓸지 선택하는거죠
문제는 제가 이걸 CUDA로는 구현할수있겠는데 HLSL로는 구현을 몬하겟어요 흑흑ㅎ긓ㄱㅎ
왜 구현을 못하겠냐면요
A. CPU 코드에서 dynamic allocate 시킨 메모리를 어떻게 HLSL에서 접근시키는지도 잘 모르겠고요 .............. HLSL문법을 아직 제대로 몰라서 .........
B. vertex shader에다가 이 vertex가 어느 world matrix의 vertex인지 어떻게 알려줄지를 모르겠어요. 이건 방법론적으로 어떻게 해야될지 몰라서 ㅋㅋㅋ 
3. 아니면 object의 vertices들을 애초에 world space에 위치시킨채로 Vertex Shader로 보낸다던가! 
4. 아니면 저따위는 감히 상상할수없는 어떤 사악한 방법이 있어서 잘 할수있다던가!!!
ㅋㅋㅋㅋㅋㅋㅋㅋ 저 혼자선 도저히 방법을 모르겠네요
혹시 아신다면 짧게나마 도움을 부탁드립니다 ㅠㅠ 


사실 본인도 처음에 Constant Buffer에 넣고 하는걸 보고 "그냥 그러려니" 싶었지, 자세하게 왜 그러는지까지는 생각해 본적 없다. 일단 인터넷 검색 조금 해보고 나름 아는것 토대로 답변:

안녕하세요~ 질문 잘 받았어요
항상 열심히 하는 모습 보고 감명 받네요~ 동시에 질문을 읽으면서 "신님아 밸런스 패치 제데로좀"이라는 생각도 들고요
어흑헉ㅇ흐ㅓㄱ흐ㅓ 난 고등학교때 대체 뭘한거지
ㅋㅋㅋ 암튼 질문으로 넘어가자면요.. 우선 저도 DirectX를 거의 책에서 배운거라 실제 AAA퀄리티 게임을 만들때 사용하는 정확한 기법은 저도 잘 몰라요 ㅠㅠㅠ
일단 제가 알고 있는 내용 바탕으로 최대한 정확하게 대답을 할건데요, 저도 아직 배우는 입장이라 모르는 부분이 많을거라 예상되서요; 너그럽게 용서해주세용 ㅎㅎ
또 아시겠지만 제 국어 실력이 좀 병맛이라.. 이해하기 쉽게 설명을 할 수 있을련지도 걱정이 되네요; 조금 이해하기 어렵다라는 부분이 있으면 알려주세요

1. 각각 다른 World Transforms를 가지고 있는 오브젝트 처리를 어떻게 하느냐에 대해서 물어봐 주셨는데요, 제가 알기로는 적으신 방법이 가장 적절하고 무난한 방법이며 실제로 만들어지는 게임에서 가장 자주 채택되는 방법이에요. 즉, 각 오브젝트 마다 Draw가 호출되기 전에 알맞는 World Matrix를 설정하는거죠. 예전에는 World Matrix자체를 Shader Uniform으로 지정해서 썼지만, DirectX 10부터 적으신 Constant Buffer라는게 나오고 나서 부턴 어떤 변수가 외부 소스 (External Source)로 통해 변해지지 않는 이상 쓰기 더 간편하기 때문에 Constant Buffer를 많이 채택해서 쓰는걸로 알고있어요. 
그리고 Synchronization이랑 O(n) Time Complexity에 대해서 얘기해주셨는데요, 사실 프로그램 상에서 강제적으로 GPU에 Synchronization 명령을 던지지 않는다면 실질적으로 일어나는 Synchronization의 횟수는 극히 적어요.. 왜냐하면 GPU 명령들은 다 파이프라인에 축적된 후 때가되면 한번에 모아서 처리해 주기 때문이죠!! (DirectX 책들 보면 아시겠지만 괜히 파이프라인 설명하는데에 두 챕터씩 소모하던게 아니더군요;) 
즉, 아무로 많은 Draw를 호출하셔도 어짜피 다 Overlapping 시키죠.
렌더링은 안타깝지만 병렬 처리가 이 부분을 감쳐주는것 처럼 보이지만 무슨 짓을 하더라도 O(n)보다 더 좋을수는 없다네요.. 정확한건 저도 잘 모르겠지만 Predicate Calculus를 통해 수학적으로 증명이 가능하다라고 들은적이 있어요... 아 물론 저는 논리 해석학 같은거는 잼병이라 증명같은거 기대하시면 안 됩니다 ㅎㅎㅎㅎㅎㅎㅎ 
그래서 Static Geometry같은거 그릴때는 사전에 Transform 시킨 World Coordinate들을 준비시킨 후 미리 존내 큰 Vertex Buffer에다가 갖다 집어넣은 후 한꺼번에 렌더링 시키는 기법도 많이 쓰곤 합니당

2. Instancing에 대해서 설명해 주셨네요. 설명 끝.
ㅋㅋㅋㅋ 농담이고 일단 이 페이지에서 아주 명확하게 설명해 주니깐 혹시라도 Instancing에 대해서 자세하게 알고 계시지 않는다면 여기서 읽으시면 되고요 http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter03.html (영문이에요; 죄송해요) 
간단히 요약하자면.. 여러개의 Vertex Buffer를 이용하는데요, 하나는 Baseline Geometry Data를 가지고, 다른 하나는 오브젝트 마다 Per-Instance data를 지정해줍니다. DrawInstancedIndexed라는 API가 DirectX9에 추가됬지만.. 사실 그 이후 버젼 부터는 거의 추가된 부분이 없어요. 그것도 그럴만한게, Instancing의 주 목적이 GPU에서 하는 일을 CPU로 덜어준다는건데 (XMMatrix가 CPU측 Matrix이에요).. 요새 GPU가 워낙 빨라서 그럴 필요가 점차 줄어들게 되다보니 지금은 거의 안 쓰는 기법이에요. 아무튼 Direct3D에서 하시는 방법을 보고 싶으면 http://www.rastertek.com/dx10tut37.html 여기 참조해주세요

3. 이건 제가 실제로 본 사례가 없어서 실질적으로 사용되는 기법인지는 잘 모르겠지만 아마 안 하는게 좋을거라고 판단되요. 이론적으로 따지자면, 유동적이지 않는 오브젝트거 정말로 많고  Shader내의 Single Matrix Multiplication으로 처리하기 정말로 느린 경우에는 괜찮을지 몰라도 이런 케이스가 실질적인 상황에서 나올지요;

4. 저도 그런 방법이 있으면 듣고싶네욬ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

아무튼 뭘 먹고 그렇게 열심히 공부하시는지는 잘 모르겠는데 그 먹는거 저도 좀 주세요. 저도 님 처럼 똑똑해지게욬ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
그럼 수고하세요~~

결론: 자네는 고등학교때 뭐했나.jpg

Thursday, January 3, 2013

Android Threading Made Easy

Googling shows me hundreds of different ways to achieve multithreading on Android. Sure, there's nothing inherently wrong with just using the generic Thread class, but it's a little bit lacking on the functionality department.

I'm going to cut all the bulls$*% and show you the easiest way to crack open an asynchronous task.


   1:      public void somefunction() {
   2:          new TaskDemo().execute();
   3:      }
   4:      
   5:      private class TaskDemo extends AsyncTask<Void, Void, Void> {
   6:          //Run your favorite task to be ran on a separate thread here!
   7:              return null;
   8:          }
   9:      }







Seriously, it's as easy as that. Now if you want to pass in parameters or get a meaningful value returned after the asynctask is completed, then you have to mess around with the generic types that goes inside AsyncTask. Otherwise, what you see above works perfectly.

But try to keep the principles of SCSR (Single-Class, Single-Responsibility) at heart. For example, the following code needs to be invoked on a separate thread:


   1:  public class PointControlActivity extends DDCMenuBaseActivity {
   2:      @Override
   3:      public void onCreate(Bundle savedInstanceState) {
   4:          super.onCreate(savedInstanceState);
   5:          setContentView(R.layout.point_control_activity);
   6:      }
   7:      
   8:      public void onClickPointRefresh(View v) {
   9:          Server soapService = new Server();
  10:          soapService.setUrl("http://192.168.0.19:7000");
  11:          VectorString pointID = new VectorString();
  12:          pointID.add("IO12345");
  13:          VectorpointReadDefaultValue pointResponse = soapService.ReadDefaultPoint(pointID);
  14:      }
  15:  }
  16:   
  17:  public class Server {
  18:      public String NAMESPACE = " http://www.lge.com/ddc";
  19:      public String url = "{0}";
  20:      public int timeOut = 8000;
  21:   
  22:      public void setTimeOut(int seconds) {
  23:          this.timeOut = seconds * 1000;
  24:      }
  25:   
  26:      public void setUrl(String url) {
  27:          this.url = url;
  28:      }
  29:   
  30:      public VectorpointReadDefaultValue ReadDefaultPoint(
  31:              VectorString pointIdentifier) {
  32:          return ReadDefaultPoint(pointIdentifier, null);
  33:      }
  34:   
  35:      public VectorpointReadDefaultValue ReadDefaultPoint(
  36:              VectorString pointIdentifier, List<HeaderProperty> headers) {
  37:   
  38:          SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(
  39:                  SoapEnvelope.VER11);
  40:          soapEnvelope.implicitTypes = true;
  41:          soapEnvelope.dotNet = true;
  42:          SoapObject soapReq = new SoapObject("http://www.lge.com/ddc",
  43:                  "ReadDefaultPoint");
  44:          soapEnvelope.addMapping("http://www.lge.com/ddc",
  45:                  "pointReadDefaultValue[]",
  46:                  new VectorpointReadDefaultValue().getClass());
  47:          soapEnvelope.addMapping("http://www.lge.com/ddc", "pointIdentifier",
  48:                  new VectorString().getClass());
  49:          soapReq.addProperty("pointIdentifier", pointIdentifier);
  50:   
  51:          soapEnvelope.setOutputSoapObject(soapReq);
  52:          HttpTransportSE httpTransport = new HttpTransportSE(url, timeOut);
  53:          try {
  54:   
  55:              if (headers != null) {
  56:                  httpTransport.call("http://www.lge.com/ddc/ReadDefaultPoint",
  57:                          soapEnvelope, headers);
  58:              } else {
  59:                  httpTransport.call("http://www.lge.com/ddc/ReadDefaultPoint",
  60:                          soapEnvelope);
  61:              }
  62:              SoapObject result = (SoapObject) soapEnvelope.bodyIn;
  63:              SoapObject soapObject = (SoapObject) result
  64:                      .getPropertySafely("ReadDefaultPointResult");
  65:              VectorpointReadDefaultValue ReadDefaultPointResult = new VectorpointReadDefaultValue(
  66:                      soapObject);
  67:              return ReadDefaultPointResult;
  68:          } catch (Exception e) {
  69:              e.printStackTrace();
  70:          }
  71:          return null;
  72:      }
  73:  }

I could always create the thread on the code that initiates the network connection, but it's a much better idea to separate the code by creating an extra class for the task:

   1:  public class PointControlActivity extends DDCMenuBaseActivity {
   2:      @Override
   3:      public void onCreate(Bundle savedInstanceState) {
   4:          super.onCreate(savedInstanceState);
   5:          setContentView(R.layout.point_control_activity);
   6:      }
   7:      
   8:      public void onClickPointRefresh(View v) {
   9:          new DDCSoapRequestTask().execute();
  10:      }
  11:      
  12:      private class DDCSoapRequestTask extends AsyncTask<Void, Void, Void> {
  13:          @Override
  14:          protected Void doInBackground(Void... params) {
  15:              Server soapService = new Server();
  16:              soapService.setUrl("http://192.168.0.19:7000");
  17:              VectorString pointID = new VectorString();
  18:              pointID.add("IO12345");
  19:              VectorpointReadDefaultValue pointResponse = soapService.ReadDefaultPoint(pointID);
  20:              return null;
  21:          }
  22:      }
  23:  }