Warning: opendir(/var/www/html/wp-content/mu-plugins): Failed to open directory: Permission denied in /var/www/html/wp-includes/load.php on line 981
여튼 예전에 만든 앱을 업데이트 하면서 아이폰5 화면에서도 테스트를 해 보았습니다. 테스트 결과는 아래와 같이 위 아래가 짤려서 나오더군요.
헉~ 뭐지;;; 뭔가 설정을 해줘야 할 필요가 있을 것 같은데 아무리 Application-app.xml 을 봐도 뭘 수정해야 할 지 모르겠습니다. 역시 이럴때는 구글느님이죠~ 검색을 해보니 해결 방법은 이외로 간단하더군요. Default-568h@2x.png 를 패키지에 포함해서 빌드를 하면 됩니다.
이제 아이폰5 전체 화면으로 정상적으로 나오네요. 뭔가 꽁수같은 방법이긴 하나 제대로 돌아가니 패스~ 현재 플래시 빌더 4.7을 사용하고 있는데 모바일 앱 만들기 정말 좋네요. 시뮬레이터, 디바이스 모두 빠르게 테스트 할 수 가 있어서 이전보다 훨씬 빠르게 개발을 할 수 있게 되었습니다.
iOS용 AIR를 빌드할때는 Splash 이미지로 사옹되는 DefaultXXX.png 와 아이콘들을 함께 빌드에 포함 시켜야 합니다. 이 파일들을 빌드에 포함시키는 방법을 알아보도록 하겠습니다.
프로젝트 빌드는 Project 메뉴에서 Export Release Build… 로 수행 할 수 있습니다. Target Platform 을 선택하고 Next 버튼으로 진행하면 Package Setting 을 합니다. Package Contents 탭 에서 함께 페키징 할 컨텐츠를 선택합니다.
Package Contents 탭을 선택하면 기본 SWF 외에 아무것도 없는 것을 볼 수 있습니다. bin-release-temp 폴더에 아래와 같이 함께 패키징 할 파일들을 추가 합니다.
파일 복사를 한 후 Package Contents를 돌아옵니다. 새로 추가한 파일들을 불러오기 위해서 Back 버튼을 눌렀다 다시 돌아오면 아래와 같이 파일이 추가 된 것을 확인 할 수 있습니다.
여기서 패키징할 파일을 선택한 후 Finish 버튼을 누르면 빌드가 시작 됩니다.
]]>그런데 여기서 재미있는 점은 as3-arduino-connector 가 단지 아두이노에서만 작동하는 ANE 가 아니라는 점입니다. 네이티브코드를 확인해보면 이 ANE 의 실제 구동방식은 COM 포트를 열고 데이터를 주고 받도록 되어있습니다. 이 말인즉슨 꼭 아두이노가 아니여도 COM 포트를 이용하는 각종 장비를 바로 플래시에서 사용 할 수 있게 된다는 의미입니다.
위의 이미지는 제 PC의 장치관리자 입니다. COM4, COM5 에 각각 USB Serial Port 가 잡혀 있는 것을 확인 할 수 있습니다. COM4 는 현재 RFID 리더 장비가 연결되어 있고 COM5 에는 아두이노가 연결 되어 있습니다. 이렇게 연결된 장비에 아래의 코드와 같은 방식으로 데이터를 주고 받을 수 있습니다.
[as3]
package
{
import com.bit101.components.*;
import com.bit101.utils.MinimalConfigurator;
import com.quetwo.Arduino.ArduinoConnector;
import com.quetwo.Arduino.ArduinoConnectorEvent;
import flash.display.Sprite;
import flash.events.MouseEvent;
import net.eriksjodin.arduino.Arduino;
public class ArduinoConnectorTester extends Sprite
{
private var _configure:XML = <comps>
<Panel x="10" y="10" width="230" height="120">
<Label text="Arduino Connector" x="10" y="10"/>
<PushButton id="connectArduino" label="Connect" x="60" y="40"/>
<PushButton id="turnOn" label="Turn ON" x="60" y="80"/>
</Panel>
<Panel x="260" y="10" width="230" height="120">
<Label text="RFID Connector" x="10" y="10"/>
<PushButton id="connectRFID" label="Connect" x="60" y="40"/>
</Panel>
<TextArea id="output" x="10" y="140" width="480" height="210"/>
</comps>;
public var connectArduino:PushButton;
public var turnOn:PushButton;
public var connectRFID:PushButton;
public var output:TextArea;
private var _config:MinimalConfigurator;
private var _isConnectedArduino:Boolean = false;
private var _isTurnedOn:Boolean = false;
private var _isConnectedRFID:Boolean = false;
private var _arduino:Arduino;
private var _rfid:ArduinoConnector;
public function ArduinoConnectorTester()
{
Component.initStage(stage);
_config = new MinimalConfigurator(this);
_config.parseXML(_configure);
connectArduino.addEventListener(MouseEvent.CLICK, onClickArduino);
turnOn.addEventListener(MouseEvent.CLICK, onClickTurnOn);
connectRFID.addEventListener(MouseEvent.CLICK, onClickRFID);
}
protected function onClickArduino(event:MouseEvent):void
{
if(_isConnectedArduino){
_arduino.dispose();
_arduino = null;
connectArduino.label = "Connect";
log("===> disconnect arduino");
} else {
_arduino = new Arduino("COM5", 57600);
connectArduino.label = "Disconnect";
log("===> connect arduino");
}
_isConnectedArduino = !_isConnectedArduino;
}
protected function onClickTurnOn(event:MouseEvent):void
{
if(_isConnectedArduino){
if(_isTurnedOn){
_arduino.writeDigitalPin(11, Arduino.LOW);
turnOn.label = "Turn ON";
log("turn off pin11");
} else {
_arduino.writeDigitalPin(11, Arduino.HIGH);
turnOn.label = "Turn OFF";
log("turn on pin11");
}
_isTurnedOn = !_isTurnedOn;
}
}
protected function onClickRFID(event:MouseEvent):void
{
if(_isConnectedRFID){
_rfid.dispose();
_rfid = null;
connectRFID.label = "Connect";
log("===> disconnect rfid");
} else {
_rfid = new ArduinoConnector();
_rfid.connect("COM4", 9600);
connectRFID.label = "Disconnect";
_rfid.addEventListener("socketData", onDataRFID);
log("===> connect rfid");
}
_isConnectedRFID = !_isConnectedRFID;
}
protected function onDataRFID(event:ArduinoConnectorEvent):void
{
log(_rfid.readBytesAsString());
}
private function log($message:*, $prop:*="", $tap:String=""):void
{
if($message is String || $message is Number){
if($prop==""){
output.text += $message + "n";
} else {
output.text += $tap + $prop+" : "+$message + "n";
}
} else if ($message is Object){
for(var prop:* in $message){
log($message[prop], prop, $tap+" – ");
}
}
}
}
}
[/as3]
코드는 워낙 단순하니 따로 설명을 하진 않겠습니다. 주의해야 할 점은 연결해야 할 장비가 작동하는 baud 를 정확히 알아야 합니다. 위의 경우 RFID 리더는 9600, 아두이노는 57600 으로 작동합니다.
이렇게 as3-arduino-connector 를 이용하면 시리얼포트를 이용 한 다양한 장비를 플래시에서 쉽게 제어 및 사용 할 수 있습니다. 이를 통해 더욱 멋진 인터렉티브 컨텐츠가 만들어 지길 기대해 봅니다.
]]>여튼 이번에도 지옥같은 일정으로 진행되고 있는 작업 하나가 있는데 저희 회사 진짜 호주인 PM 이 해맑은 미소를 지으며 코멘트 작성시 불량단어 체크해 달랍니다. 뭐 단어 체크하는 거야 그냥 search() 한번 해주면 되니 ok 했는데 그게 끝이 아니였습니다. 유저가 쓴 코멘트에 불량단어 마다 하이라이트를 해달라는 겁니다. 오~마이~~~갓~~ 이런 알흠다운십장생을 봤나. 당장 가장 중요한 기능들도 기간내에 만들까 말까 하고만 어디서 본 건 있어 가지고~~ 여튼 해달랍니다. 물론 저는 기성개발자로서 이건 이래서 안되고 저건 저래서 안된다고 말 하고 싶었지만 아~~ 아직 그런 고차원 적인 말을 영어로 할 수가 없습니다. 그래서 마냥 울며 ok 를 했습니다.
이 필터링의 어려운 문제는 대소문자 입니다. 보통 이런 단어 필터링을 할때는 코멘트와 키워드의 대,소문자를 일치시키고 검색을 하게 되는데 이렇게 되면 유저가 쓴 원글이 수정되므로 이러한 수정없이 단어를 체크해야 했습니다. 남들 다 쉬는 토요일에 저와 구준호님이 함께 이 문제를 고민해봤습니다. 그래서 나온 결과를 공유 합니다. 제 경우는 정규표현식으로 해당단어를 치환했고 구준호님은 for 문으로 단어를 확인했습니다.
1. 정규표현식
private function checkByReg(input:String, keyword:Array):void
{
var temp:String = input;
for (var i:int = 0; i < keyword.length; i++)
{
var pattern:RegExp = new RegExp('(' + keyword[i] + ')', 'ig');
temp = temp.replace(pattern, '$&');
}
_comment.textField.htmlText = temp;
}
var pattern:RegExp = new RegExp(‘(‘ + keyword[i] + ‘)’, ‘ig’); 에서 i 옵션이 중요합니다. i 는 대소문자를 구분하지 않겠다 입니다. 정규표현식이 좋은 점은 정규표현식 자체의 다양한 식을 적용 할 수 있다는 겁니다. 다만 replace 해야 하므로 위와 같은 html 을 사용 해야 합니다.
2. for 문
public function checkByFor(input:String, keyword:Array):void
{
var format:TextFormat = new TextFormat(null, null, 0xFF0000);
for ( var i:int = 0 ; i < input.length ; i++ )
{
for ( var j:int = 0 ; j < keyword.length ; j++ )
{
var badWord:String = keyword[j];
if ( checkWord(i, input, badWord) )
{
_comment.textField.setTextFormat(format, i, i + badWord.length);
}
}
}
}
private function checkWord(index:int, input:String, badWord:String):Boolean
{
for ( var i:int = 0 ; i < badWord.length ; i++ )
{
var currentCharOfBadWord:String = badWord.charAt(i).toUpperCase();
var currentCharOfInput:String = input.charAt(index + i).toUpperCase();
if ( currentCharOfBadWord != currentCharOfInput )
return false;
}
return true;
}
구준호님이 만들어 주신 로직입니다. 각 단어의 길이로 잘라서 비교하고 해당 되는 단어 부분의 TextFormat 을 바꿔 줍니다. 덕분에 몰랐던 setTextFormat 함수를 알게 되었네요. 그리고 이 함수들은 원래 목적은 불량단어 검출이였지만 하이라이트 기능을 바꾸어 써도 좋을 것 같네요.
얼마전에 히카님과 String 형에 대해서 이야기 나눈 적이 있는데 얼마 지나지 않아 이렇게 복습을 하니 참 좋네요. 다만 조건의 차이가 있기는 하겠지만 아무래도 단순비교에서는 정규식이 더 빠른 것 같은데... 히카님게 묻습니다. 언제 for 문이 더 빨라지는 겁니까? ㅎㅎㅎ
Pixel Bender 는 강력하지만 자주 사용하는 툴은 아니라서 플래시에 연동하는 방법을 매번 까먹곤 합니다. 그래서 기억력 보조 차원에서 간단하게 Pixel Bender 파일을 플래시에서 사용하는 법을 알아보겠습니다.
parameter float d<
minValue: 0.001;
maxValue:10.0;
defaultValue:2.0;
>;
parameter float r<
minValue: 0.001;
maxValue:10.0;
defaultValue:0.425;
>;
pbk 파일에 사용할 수 있는 파라미터가 위와 같이 정의 되어 있을 경우 아래의 as3 처럼 사용 하면 됩니다.
[Embed("assets/BilateralBlur.pbj", mimeType="application/octet-stream")]
private var BilateralBlur:Class;
_shader = new Shader(new BilateralBlur() as ByteArray);
_shader.data.d.value = [d_slider.value];
_shader.data.r.value = [r_slider.value];
_filter = new ShaderFilter(_shader);
wm1.filters = [_filter];
필요한 부분만 간략하게 줄였습니다.
_shader.data.d.value = [d_slider.value];
이렇게 파라미터에 값을 넣어주면 됩니다. 주의 할 점은 값을 Array 형으로 전달 해야 한다는 겁니다. 이유는 정확히 모르겠지만 파리미터 타입이 여러개 이므로 그에 맞게 값을 넘겨줘야 하기 때문이 아닌가 추측해 봅니다.
단순히 새버전이 나온다고 무작정 따라가는 것도 답은 아니라고 생각합니다. 회사입장에서는 프로그램 구입비도 만만치 않을테니까요. 더군다나 현재 사용하고 있는 제품이 업무에 전혀 문제가 없다면 특히 그럴 것 입니다. 하지만 버전업으로 인해 얻는게 많다면 당연히 시도해 볼 가치는 있겠지요.
Flash CS4 로 버전 업 하면서 주목할 만한 변화는 개인적으로 PixelBender 의 사용과 Vector 가 아닐가 생각됩니다. 두기능 모두 Flash 의 성능과 가능성을 비약적으로 높혀 주지요. 다만 두 기능 모두 Flash Player 10 이상을 요구합니다. CS4 가 나온 초기 시점에는 클라이언트 환경이 대부분 Flash Player 9 였으므로 무리해서 두기능을 추가하기가 어려웠습니다. 하지만 그건 이미 2년이나 지난 이야기이지요. 현재 Flash Player 최상위 버전은 10.1 입니다. 또 대부분의 클라이언트 환경도 업그레이드 되었으므로 여지껏 아껴두었던 위의 기능들을 충분히 사용해도 좋을 것 같습니다.
오늘은 두 기능 중 PixelBender 를 테스트 해 보았습니다. PixelBender 는 다들 아시다시피 Adobe 의 이미지,비디오 프로세싱 기술입니다. 해서 당연히 Flash 외에 Photoshop, AfterEffect 등에서도 함께 사용 할 수 있습니다. 저는 이런 PixelBender 를 Flash 에서 어떻게 효과적으로 사용 할 까? 를 생각해보았습니다. 제 개인적으로 Flash는 인터렉티브 입니다. 그 어떤 환경보다도 유저와 상호작용하기 좋은 환경이죠. 그러므로 PixelBender 도 이러한 용도로 사용하고 싶었습니다. 물론 가장 기본적인 이미지프로세싱 (레벨, 컨트라스트 등)도 가능하겠지만 이건 별로 임팩트가 없어서 Mac OS X 의 기능중 Dashboard 의 있는 효과를 흉내내 봤습니다. 쉽게 말해 물결효과 입니다.
물결파는 아래 이미지의 공식으로 만들어 집니다.
이를 PixelBender 언어로 바꾸면 다음과 같습니다.
kernel HorizontalAverage < namespace: "com.impossiblearts"; vendor: "Hannes Moser"; version: 1; description: "damped sin-wave depending on time"; > { input image4 source; output pixel4 result; const float PI = 3.14159265358979323846264338327950288; parameter float2 pos < minValue:float2(0.0, 0.0); maxValue:float2(2880.0, 2880.0); defaultValue:float2(400.0, 400.0); >; parameter float amount < minValue:0.0; maxValue:10000.0; defaultValue:5000.0; >; parameter float wavelength < minValue:1.0; maxValue:2500.0; defaultValue:150.0; >; parameter float t < minValue:0.0; maxValue:1000.0; defaultValue:0.0; >; parameter float overlay < minValue:0.0; maxValue:100.0; defaultValue:10.0; >; void evaluatePixel() { float2 coord = outCoord(); float dist = distance(pos, coord); float curve = (1.0 / sqrt(1000.0 * PI)) * exp((-1.0/wavelength) * pow(-t + dist, 2.0)) * sin(dist / overlay) * amount; float2 cw; cw = coord + curve; result = sample(source, cw); } }
출처 : http://impossiblearts.com/blog/2008/10/pixelbender-damped-sin-waves-gauss-curve/
위의 Effect 를 Flash 로 가져와 인터렉션과 애니메이션을 추가해서 다음과 같이 완성 했습니다. PixelBender의 Effect 는 DisplayObject 의 filters 에 적용 되므로 아래에서 보는 것과 같이 다이나믹텍스트나 인풋텍스트에도 아주 잘 적용 됩니다.(플래시 영역을 클릭하세요.)
아래의 사이트에 방문해 보면 좀 더 흥미롭고 재밌는 PixelBender 예제들을 만나 볼 수 있습니다.
]]>
1. Light Gray
출처 : http://www.flashdevelop.org/community/viewtopic.php?f=10&t=2573
2. Dark Gray
출처 : http://ghostinteractive.net/blog/?p=434
3. Black

4. White

[button url=’https://sewonist.com/wp-content/uploads/1/1330861901.zip’ target=’_blank’ size=’large’]Color syntax 다운로드[/button]
[button url=’https://sewonist.com/wp-content/uploads/2009/06/sewonist.zip’ target=’_blank’ size=’large’]sewonist 다운로드[/button]

TraceMonitor 란?
TraceMonitor는 플래시의 trace 기능을 확장하여 모니터 해 줍니다. 브라우저나 플래시 플레이어로 SWF를 볼 경우 trace 가 되지 않아 값을 볼 수 없어 매우 불편한데 이를 해결 하기 위한 솔루션입니다. FLEX 나 FlashDeveop 과 같은 개발툴에도 이와 유사한 Output 이 있으나 이를 설치하기 어려운 환경이나 간단히 trace 값만을 보고자 할 때 유용하게 사용 할 수 있습니다. 예를 들어 프로그래머와 협업시 프로그래머의 PC에 설치하여 값을 trace 해 볼 수 있습니다.
TraceMonitor 사용법
TraceMonitor 를 사용하기 위해서는 함께 들어있는 TraceMonitor.as 의 trace를 이용해야 합니다. TraceMonitor 를 실행 시킨 후 아래 코드를 삽입하여 trace 할 수 있습니다.
[sourcecode language=”as3″]
import TraceMonitor;
TraceMonitor.trace(“Hello~ World!”);
[/sourcecode]
주의사항
TraceMonitor 는 JRE 1.5 이상을 필요로 합니다. 만약 설치 되어 있지 않다면 TraceMonitor 설치 시 함께 설치 하시기 바랍니다.
Download
다른 솔루션
]]>Collada를 이용한 애니메이션이다. 이것을 테스트 하기 위해서 로우폴리곤 소스를 열심히 찾았는데 결국 국내에서는 쓸만한 것을 찾을 수가 없었다. 어찌어찌 하다가 재미난 프로젝트를 발견했는데 FFX Project 이다. FFX 에서 나오는 모델링 데이터를 뽑아내는 프로젝트 인거 같은데 스퀘어에서 만들어낸 양질의 모델링 데이터를 가져다 쓸 수 있어서 완전 좋았다. 이 프로젝트에 대해서는 추후에 한번 더 포스트 할 기회가 있을 것 같다.
위의 테스트 플래쉬에서 사용된 소스는 총 3030 triangles 와 2 png 로 이루어져있다. 테스트 해 본 결과 3000개 까지는 어느 정도 소화 하는 것 같지만 더 많아지면 스크립트를 파싱하는 시간 15초를 초과 하게 되서 플래시플레이어가 스크립을 강제종료시켜 버리는 문제가 있었다. 그렇지 않고 로드가 된다고 해도 위에서 처럼 퍼포먼스가 너무 떨어져서 1000개 이상의 폴리곤은 사용은 좀 어려울 것 같다.

위 이미지는 똑같은 Collada 파일을 3D MAX에서 렌더링 한 것으로 확실히 광원효과와 하드웨어 가속을 받는 3D 프로그램이 더 좋은 퀄리티를 보여준다. 당연한건가? ㅡ.ㅡa ㅋㅋㅋ
요청으로 위의 작업물 소스를 올립니다. 이 소스에는 Papervision3D가 없습니다. Papervision3D는 다음의 주소에서 받으 실 수 있습니다. http://pv3d.org/2007/12/12/1-downloading-papervision3d-20-alpha-great-white/ 버전은 반드시 2.0 GreattWhite로 사용하셔야 합니다.
]]>현재 널리 알려져 있는 라이브러리로는 Sandy3D, Papervision3D, Away3D 등이 있는데 각각 장단점이 있는 것 같으나 전부 속속들이 들여다 보지는 못해서 아직 정확한 성능의 차이나 사용상의 용이성등의 평가는 어렵다. 이번 글에서는 셋중 실제 홈페이지에 가장 많이 적용되고 있는 Papervision3D (PV3D)를 기준으로 작성하였다. PV3D는 실무에서도 많이 쓰이고 있을 뿐 아니라 개발 또 한 활발히 이뤄지고 있어 앞으로 더욱 기대되는 라이브러리 이다. 참고로 사용된 PV3D의 버전은 2.0a GreatWhite 이다.
II. Object Build Solution
이번에 테스트 해본 오브젝트 생성 방법은 총 4가지 이다. 첫번째는 PV3D에 기본으로 있는 Object Class를 이용한 방법이고, 두번째는 AS3 Geom Class Exporter를 이용한 방법, 세번째는 DAE 파일을 PV3D의 기본 collada 클래스로 생성한 방법이고 마지막은 같은 DAE 파일을 ASCollada의 DAE 클래스로 생성한 방법이다.
1. 기본 내장 클래스
PV3D는 기본적으로 org.papervision3d.objects 패키지에서 오브젝트를 선언하고 있다. 하위로 종류별로 parsers, primitives, spceial로 구분되며 이 구문에서는 primitives 안의 기본 오브젝트들로 생성하였다.
[sourcecode language=”as3″]var cube:Cube = new Cube(materialList, 100, 100, 100);
var cone:Cone = new Cone(new WireframeMaterial(0xFF0000));
var sphere:Sphere = new Sphere(new WireframeMaterial(0xFF0000));[/sourcecode]
가장 빠른 성능을 보이지만 기본으로 제공하는 이외의 형태를 만들지 못하기 때문에 UI 나 Navigation 이 외에는 활용성 면에서는 조금 부족하지 않나 싶다. 결국 사용자화된 오브젝트를 쓰기 위해서는 외부파일을 로드 해야만 한다.
2. AS3 Geom Class Exporter
이는 3D MAX 에서 AS3 Geom Exporter라는 Max Script를 설치하여서 각 라이브러리 별 .as파일로 바로 export하는 방법이다. 이 방법 또한 각 라이브러리에 맞게 .as파일을 만들어 주기 때문에 성능면에서는 좋지만 이름에서 처럼 Geom 에 대한 데이터만 만들어 주기 때문에 위치나 맵핑에 관한 정보없이 단순한 오브젝트만 만들어 주는 것같다. 결국 애니메이션과 맵핑을 위해서 손이 한번 더 가야 한다.
[sourcecode language=”as3″]test = new Test(new WireframeMaterial(0xFF0000));[/sourcecode]
3. Collada
collada 는 뒤에 좀 더 자세히 설명하겠지만 간단히 말해서 3D데이터를 표준 XML 로 만들어주는 프로젝트이다. 확장자는 .dae 를 사용한다. 이 방법은 PV3D에서 기본으로 지원하던 collada 클래스를 이용하는 방법이다.
[sourcecode language=”as3″]collada = new Collada(“test.DAE” , materialList);[/sourcecode]
4. DAE
이 방법은 위의 방법과 .dae 파일을 사용한다는 점에서 동일하나 .dae 파일을 파싱하는 클래스가 Collada 가 아닌 DAE 이다. 두 파일의 차이는 DAE는 ASCollada 프로젝트의 파일을 PV3D에 합친것으로 이는 PV3D 2.0a 이후 버전에 적용되었다. 또한 위의 방법과 결정적 차이는 .dae파일의 Animation 을 컨트롤 할 수 있다는 점이다.
[sourcecode language=”as3″]dae = new DAE();
dae.animate = true;
dae.load(“test.DAE”, materialList);[/sourcecode]
III. Collada
collada의 정의를 홈페이지에서 보면 다음과 같이 규정하고 있다. ‘COLLADA is an open digital-asset exchange schema for the interactive 3D industry’ collada 에는 대부분의 3D 데이터가 저장되는데 특히 맵핑과 애니메이션이 매우 유용하다. 3D MAX 와 같은 프로그램에서 모델링과 맵핑을 하고 이를 collada로 export 한 후 PV3D 를 통해 Flash 에 바로 적용 할 수 있기 때문이다. 참고로 3D MAX 에서 collada 파일을 export 하기 위해서는 ColladaMax 같은 플러그인을 별도로 설치 해야한다.
IV. Example
위에서 말한 4가지 방법으로 예제를 만들어 보았다. 아래 소스를 보면 알 수 있겠지만 똑같은 조건하에 오브젝트만 생성하고 있는데 생성되어지는 기본값이 조금씩 상이한 것을 알 수 있다. 특히 같은 .dae 파일을 사용하는 Collada 와 DAE 클래스의 다른 점을 눈여겨 볼 필요가 있을 것 같다.
[sourcecode language=”as3″]
package {
import fl.events.ScrollEvent;
import fl.events.SliderEvent;
import flash.display.*;
import flash.filters.*;
import flash.display.Stage;
import flash.events.*;
import flash.ui.Mouse;
import org.papervision3d.core.proto.DisplayObjectContainer3D;
import org.papervision3d.objects.parsers.Collada;
import org.papervision3d.objects.parsers.DAE;
// Import Papervision3D
import org.papervision3d.cameras.*;
import org.papervision3d.scenes.*;
import org.papervision3d.objects.*;
import org.papervision3d.objects.special.*;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.materials.*;
import org.papervision3d.materials.special.*;
import org.papervision3d.materials.shaders.*;
import org.papervision3d.materials.utils.*;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.lights.*;
import org.papervision3d.render.*;
import org.papervision3d.view.*;
import org.papervision3d.events.*;
import org.papervision3d.core.utils.*;
import org.papervision3d.core.utils.virtualmouse.VirtualMouse;
import Test;
public class TestObject extends MovieClip {
public var viewport :Viewport3D;
public var scene :Scene3D;
public var camera :Camera3D;
public var renderer :BasicRenderEngine;
private var materialList : MaterialsList;
private var cont:DisplayObject3D;
private var test:Test;
private var collada:Collada;
private var dae:DAE;
public function TestObject() {
init();
}
public function init():void {
stage.scaleMode = “noScale”
stage.showDefaultContextMenu = false;
init3D();
addEventListener( Event.ENTER_FRAME, loop );
for (var i:int = 1; i < 5; i++) {
this.getChildByName("btn"+i).addEventListener( MouseEvent.CLICK, clickHandler );
}
function clickHandler( e:MouseEvent ):void {
changeObject( e.currentTarget.name );
}
this.getChildByName("carZoom").addEventListener( SliderEvent.CHANGE, carZoomHandler );
function carZoomHandler( e:SliderEvent ):void {
camera.zoom = e.currentTarget.value - 100;
doTrace( String( "camera.zoom : "+camera.zoom ));
}
}
public function doTrace( val:String ):void {
var temp:String = TextArea(this.getChildByName("traceTA")).text;
temp = val + "n" + temp;
TextArea(this.getChildByName("traceTA")).text = temp;
}
private function changeObject( _name:String ):void {
removeObject();
switch (_name) {
case "btn1" :
cont = new DisplayObject3D();
var cube:Cube = new Cube(materialList, 100, 100, 100);
var cone:Cone = new Cone(new WireframeMaterial(0xFF0000));
var sphere:Sphere = new Sphere(new WireframeMaterial(0xFF0000));
cube.x = 400;
cone.z = 400;
sphere.z = -100
cont.addChild( cube );
cont.addChild( cone );
cont.addChild( sphere );
scene.addChild(cont);
doTrace("-------------------------");
doTrace("var cube:Cube = new Cube(materialList, 100, 100, 100);nvar cone:Cone = new Cone(new WireframeMaterial(0xFF0000));nvar sphere:Sphere = new Sphere(new WireframeMaterial(0xFF0000));");
doTrace("-------------------------");
break;
case "btn2" :
//Create Test
test = new Test(new WireframeMaterial(0xFF0000));
scene.addChild(test);
doTrace("-------------------------");
doTrace("test = new Test(new WireframeMaterial(0xFF0000));");
doTrace("-------------------------");
break;
case "btn3" :
//Create Collada
collada = new Collada("https://sewonist.com/examples/TestObject/test.DAE” , materialList);
scene.addChild(collada);
doTrace(“————————-“);
doTrace(“collada = new Collada(“test.DAE” , materialList);”);
doTrace(“————————-“);
break;
case “btn4” :
//Create DAE
dae = new DAE();
dae.animate = true;
dae.load(“https://sewonist.com/examples/TestObject/test.DAE“, materialList);
scene.addChild(dae);
doTrace(“————————-“);
doTrace(“dae = new DAE();ndae.animate = true;ndae.load(“test.DAE”, materialList);”);
doTrace(“————————-“);
break;
}
}
private function removeObject():void {
scene.removeChild( cont );
scene.removeChild( test );
scene.removeChild( collada );
scene.removeChild( dae );
}
// ___________________________________________________________________ Init3D
public function init3D():void {
// Create viewport
viewport = new Viewport3D();
addChild( viewport );
// Create Renderer
renderer = new BasicRenderEngine();
// Create scene
scene = new Scene3D(true);
// Create Material
materialList = new MaterialsList();
materialList.addMaterial( new WireframeMaterial(0xFF0000), “ColorMaterial” );
materialList.addMaterial( new WireframeMaterial(0xFF0000), “all” );
// Create camera
camera = new Camera3D();
renderer.renderScene(scene, camera, viewport);
}
// ___________________________________________________________________ Loop
public function loop(event:Event = null):void
{
if ( cont ) {
cont.yaw(1);
}
if ( test ) {
test.yaw(1);
}
if ( collada ) {
collada.yaw(1);
}
if ( dae ) {
dae.yaw(1);
}
renderer.renderScene(scene, camera, viewport);
}
}
}
[/sourcecode]
V. Results
위의 결과표는 매우 주관적인 견해라는 것을 꼭 기억해주기 바란다. 기본클래스를 이용한 경우에도 따로 추가해주면 맵핑과 애니메이션이 가능하다. 또 한 COLLADA를 이용하는 클래스도 사용성이 꼭 나쁘다고 할 수는 없다. 다만 위의 표는 각 방법에 대한 차이를 좀더 쉽게 이해하기 위해서 만들었으니 차이에 대해 참고 정도의 역활이었으면 한다.
확실히 플래쉬에서 구현되는 3D는 많은 제약을 가지고 있다. 특히 하드웨어 가속을 받지 않기 때문에 성능이나 기능 면에서 현란한 3D게임 같은 화면을 기대 하는 것은 무리가 있다. 하지만 날로 좋아지는 컴퓨터의 성능과 AS3의 성능 향상으로 플래쉬에서 3D를 이용한 다양한 시도가 가능 할 것으로 기대 된다. 조만간 웹에서 Coverflow 와 같은 화려한 UI 나 스페셜포스 같은 FPS 게임을 경험하게 될 지도 모른다.
VI. Links
Papervision3D : http://pv3d.org/
Collada : http://www.collada.org/mediawiki/index.php/Main_Page
ColladaMax : http://www.feelingsoftware.com/content/view/65/79/lang,en/
AS3 Geom Exporter : http://seraf.mediabox.fr/showcase/as3-geom-class-exporter-for-3ds-max-english/
ASCollada : http://ascollada.org/