пятница, 15 апреля 2011 г.

Использование карты прозрачности (ActionScript)

Преамбула:
Заказали как то раз сварганить флешку назначение которой было простое: обеспечить возможность всестороннего разглядывания новой упаковки, с достаточно фотореалистичным отображением. Т.к. нужен всесторонний обзор, решено было крутить-вертеть упаковку в 3D пространстве.

В качестве движка заюзал, всеми горячо любимый (особенно после нового лицензионного соглашения, разрешающего свободное его использование), движек Alternativa3D. Особенно кстати пришлась вышедшая, к началу работы над проектом, версия 7.7. Где были добавлены новые материалы, свет и проч.

Модель во флешке собирается в рантайме, т.е. подгружаются/вкоприливаются необходимые ресурсы (3DS-модель + текстуры), средствами движка парсится модель, на основе текстур создаются соответствующие материалы, все это накладывается/состыковывается.

Встала проблема с прозрачностью некоторых материалов, а именно материала покрывающего большую часть модели (упаковка полупрозрачная). В библиотеке движка есть такой полезный класс как MaterialLoader, которому нужно скормить Vector из экземпляров класса TextureMaterial или его потомков с предварительно заполненными полями (урл дифузной текстуры, урл текстуры карты прозрачности, ...). Т.к. создание материалов идет "вручную", то MaterialLoader мне показался плохим помощником. В башке зародилась мысля, а что если в качестве дифузной текстуры скормить предварительно подготовленную текстуру с наложенной картой прозрачности. Осталось дело за малым - наложить эту самую карту прозрачности.

Проблема:
Применить к текстуре карту прозрачности.

Решение:
Родился единственный метод который эту карту накладывает.
private function overlayOpacityMap(texture:BitmapData, opacityMap:BitmapData):BitmapData
{
   if (!opacityMap)
      return texture;
   
   var retval:BitmapData = new BitmapData(texture.width, texture.height, true, 0xffffffff);
   
   retval.copyPixels(texture, new Rectangle(0, 0, texture.width, texture.height), new Point());
   
   var textureColor:uint;
   var opacityColor:uint;
   for (var px:int = 0; px < retval.width; px++) 
   {
      for (var py:int = 0; py < retval.height; py++) 
      {
         textureColor = retval.getPixel32(px, py);
         opacityColor = opacityMap.getPixel(px, py);
         
         opacityColor &= 0x0000ff;
         textureColor &= 0x00ffffff;
         textureColor |= (0xff - opacityColor) << 24; 
         
         retval.setPixel32(px, py, textureColor);
      }
   }
   
   return retval;
}
В результате волшебных пассов результат получился достаточно достойным и фотореалистичным, жаль заказчик не понял всю "тяжесть, степень, глубину" и в финальной стадии разработки принял решение заменить 3D на секвенцию из 30, на мой взгляд, не лучшим образом отфотканных ракурсов.
В дополнение к выше сказанному привожу скриншот демо-проектика(used FlashDevelop) демонстрирующего наложение карты прозрачности на изображение.

В ходе написания данного поста, вспомнил, что видел где-то данный подход для эмуляции (назову её так) изображений в формате PNG с прозрачностью, и это надо сказать очень эффективно в плане трафика и места для хранения изображений аля png, по другим параметрам может и уступает.

По использованным в демо-проекте изображениям картина выглядит так:
  1. исходное изображение (jpg) + серая карта прозрачности (jpg) = 29.8 + 15.7 = 45.4 Кб
  2. исходное изображение (png) = 155.8 Кб

Простая схема защиты от "кидалова" со стороны заказчика/клиента (ActionScript)

Преамбула:
Буквально на днях закончил один прожект который делался конечному клиенту через посредника веб-студию. Т.к. с моим непосредственным заказчиком (веб-студией) я имел первый опыт (мозготрах) работы, то зародились сомнения, а не кинут ли (тьфу х3, пока такого ни разу не случалось).
В общем решил хоть как то предохранится от возможного конфуза.

Проблема:
По быстрому/простому включить в проект блокировку, в случае если клиент не расплатится за заказ в оговоренный с ним срок или прихватизирует проект на финальном этапе разработки.

Решение:
Идея думаю далеко не нова, но как говорится, свой "велосипед" ближе к телу :) (давно хочу обзавестись железным великом, да все никак да не эдак).
Решил включить код, который бы обращался по определенному адресу и запрашивал определенный ресурс и исходя из полученного результата "блокировал" флешку.
Вначале хотел запрашивать URLLоader-ом в качестве ресурса текстовый файл или php-скрипт который выдавал например слово "trusted", но из-за танцев вокруг секьюрности решил отказаться от этой затеи и воспользоватся "добрым" Lоader-ом.

Итого:
1. ресурс - gif-файл 1х1 пиксель (35 байт);
2. хостинг - публичная папка Dropbox-а (если решите воспользоватся этим сервисом, могу прислать инвайт на регпочту, обоим к положенным 2 гигам по 250 метров накинут);
3. выбрана схема блокировки по наличию ресурса, а не по его отсутствию (т.к. флешку могут просматривать локально, внутри ethernet, проблемы с хостингом и т.п.).

Реализация:
public class TrustChecker
{
   private var _stage:Stage;
   
   public function TrustChecker(stage:Stage) 
   {
      _stage = stage;
      
      var loader:Loader = new Loader();
      loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
      loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onError);
      loader.load(new URLRequest("http://myurl.com/trust.gif?" + (new Date().getTime())));
   }
   
   private function onError(e:Event):void 
   {
      var target:LoaderInfo = e.target as LoaderInfo;
      target.removeEventListener(Event.COMPLETE, onComplete);
      target.removeEventListener(IOErrorEvent.IO_ERROR, onError);
   }
   
   private function onComplete(e:Event):void 
   {
      var target:LoaderInfo = e.target as LoaderInfo;
      target.removeEventListener(Event.COMPLETE, onComplete);
      target.removeEventListener(IOErrorEvent.IO_ERROR, onError);
      
      // вот тут то можно дать волю своей фантазии для донесения мысли заказчику
      var shape:Shape = new Shape();
      shape.graphics.beginFill(0x000000);
      shape.graphics.drawRect(0, 0, _stage.stageWidth, _stage.stageHeight);
      shape.graphics.endFill();
      _stage.addChild(shape);
   }
   
}

Защита достаточно тривиальна и прошаренный заказчик её в 5 сек обойдет, отредактировав байт код или декомпилировав флеху (от этого может в какой то степени спасти укус хорошего обфускатора secureSWF).