package alternativa.engine3d.materials { import __AS3__.vec.Vector; import alternativa.engine3d.*; import alternativa.engine3d.core.Camera3D; import alternativa.engine3d.core.Face; import alternativa.engine3d.core.PolyPrimitive; import alternativa.engine3d.display.Skin; import alternativa.types.*; import flash.display.BlendMode; import flash.geom.Matrix; import flash.display.BitmapData; import flash.display.Graphics; use namespace alternativa3d; use namespace alternativatypes; /** * Материал, заполняющий грань текстурой. Помимо наложения текстуры, материал может рисовать границу грани линией * заданной толщины и цвета. */ public class TextureMaterial extends SurfaceMaterial { private static var stubBitmapData:BitmapData; private static var stubMatrix:Matrix; private var gfx:Graphics; private var textureMatrix:Matrix = new Matrix(); private var focalLength:Number; private var distortion:Number; /** * @private * Текстура */ alternativa3d var _texture:Texture; /** * @private * Повтор текстуры */ alternativa3d var _repeat:Boolean; /** * @private * Сглаженность текстуры */ alternativa3d var _smooth:Boolean; /** * @private * Точность перспективной коррекции */ alternativa3d var _precision:Number; /** * @private * Толщина линий обводки */ alternativa3d var _wireThickness:Number; /** * @private * Цвет линий обводки */ alternativa3d var _wireColor:uint; /** * Создание экземпляра текстурного материала. * * @param texture текстура материала * @param alpha коэффициент непрозрачности материала. Значение 1 соответствует полной непрозрачности, значение 0 соответствует полной прозрачности. * @param repeat повтор текстуры при заполнении * @param smooth сглаживание текстуры при увеличении масштаба * @param blendMode режим наложения цвета * @param wireThickness толщина линии обводки * @param wireColor цвет линии обводки * @param precision точность перспективной коррекции. Может быть задана одной из констант класса * TextureMaterialPrecision или числом типа Number. Во втором случае, чем ближе заданное значение к единице, тем более * качественная перспективная коррекция будет выполнена, и тем больше времени будет затрачено на расчёт кадра. * * @see TextureMaterialPrecision */ public function TextureMaterial(texture:Texture, alpha:Number = 1, repeat:Boolean = true, smooth:Boolean = false, blendMode:String = BlendMode.NORMAL, wireThickness:Number = -1, wireColor:uint = 0, precision:Number = TextureMaterialPrecision.MEDIUM) { super(alpha, blendMode); _texture = texture; _repeat = repeat; _smooth = smooth; _wireThickness = wireThickness; _wireColor = wireColor; _precision = precision; useUV = true; } /** * @private * Метод определяет, может ли материал нарисовать указанный примитив. Метод используется в системе отрисовки сцены и должен использоваться * наследниками для указания видимости связанной с материалом поверхности или отдельного примитива. * * @param primitive примитив для проверки * * @return true, если материал может отрисовать указанный примитив, иначе false */ override alternativa3d function canDraw(primitive:PolyPrimitive):Boolean { return _texture != null; } /** * @private * @inheritDoc */ override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void { skin.alpha = _alpha; skin.blendMode = _blendMode; var i:uint; var point:DrawPoint; gfx = skin.gfx; // Проверка на нулевую UV-матрицу if (skin.primitive.face.uvMatrixBase == null) { if (stubBitmapData == null) { // Создание текстуры-заглушки stubBitmapData = new BitmapData(2, 2, false, 0); stubBitmapData.setPixel(0, 0, 0xFF00FF); stubBitmapData.setPixel(1, 1, 0xFF00FF); stubMatrix = new Matrix(10, 0, 0, 10, 0, 0); } gfx.beginBitmapFill(stubBitmapData, stubMatrix); if (camera._orthographic) { if (_wireThickness >= 0) { gfx.lineStyle(_wireThickness, _wireColor); } point = points[0]; gfx.moveTo(point.x, point.y); for (i = 1; i < length; i++) { point = points[i]; gfx.lineTo(point.x, point.y); } if (_wireThickness >= 0) { point = points[0]; gfx.lineTo(point.x, point.y); } } else { if (_wireThickness >= 0) { gfx.lineStyle(_wireThickness, _wireColor); } point = points[0]; var perspective:Number = camera.focalLength/point.z; gfx.moveTo(point.x*perspective, point.y*perspective); for (i = 1; i < length; i++) { point = points[i]; perspective = camera.focalLength/point.z; gfx.lineTo(point.x*perspective, point.y*perspective); } if (_wireThickness >= 0) { point = points[0]; perspective = camera.focalLength/point.z; gfx.lineTo(point.x*perspective, point.y*perspective); } } return; } if (camera._orthographic) { // Расчитываем матрицу наложения текстуры var face:Face = skin.primitive.face; // Если матрица не расчитана, считаем if (!camera.uvMatricesCalculated[face]) { camera.calculateUVMatrix(face, _texture._width, _texture._height); } gfx.beginBitmapFill(_texture._bitmapData, face.uvMatrix, _repeat, _smooth); if (_wireThickness >= 0) { gfx.lineStyle(_wireThickness, _wireColor); } point = points[0]; gfx.moveTo(point.x, point.y); for (i = 1; i < length; i++) { point = points[i]; gfx.lineTo(point.x, point.y); } if (_wireThickness >= 0) { point = points[0]; gfx.lineTo(point.x, point.y); } } else { // Отрисовка focalLength = camera.focalLength; //distortion = camera.focalDistortion*_precision; var front:int = 0; var back:int = length - 1; var newFront:int = 1; var newBack:int = (back > 0) ? (back - 1) : (length - 1); var direction:Boolean = true; var a:DrawPoint = points[back]; var b:DrawPoint; var c:DrawPoint = points[front]; var drawVertices:Vector. = new Vector.(); var drawUVTs:Vector. = new Vector.(); for (i = 0; i < length; i++) { var p:DrawPoint = points[i]; var t:Number = focalLength/p.z; drawVertices[i << 1] = p.x*t; drawVertices[(i << 1) + 1] = p.y*t; drawUVTs.push(p.u, 1 - p.v, t); } var drawIndices:Vector. = new Vector.(); while (front != newBack) { if (direction) { /* a = points[front]; b = points[newFront]; c = points[back]; */ drawIndices.push(front, newFront, back); front = newFront; newFront = (front < length - 1) ? (front + 1) : 0; } else { /* a = points[newBack]; b = points[back]; c = points[front]; */ drawIndices.push(newBack, back, front); back = newBack; newBack = (back > 0) ? (back - 1) : (length - 1); } direction = !direction; } gfx.beginBitmapFill(_texture.bitmapData, null, _repeat, _smooth); if (_wireThickness >= 0) { gfx.lineStyle(_wireThickness, _wireColor); } gfx.drawTriangles(drawVertices, drawIndices, drawUVTs); } } /** * Текстура материала. Материал не выполняет никаких действий по отрисовке, если не задана текстура. */ public function get texture():Texture { return _texture; } /** * @private */ public function set texture(value:Texture):void { if (_texture != value) { _texture = value; if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } } } /** * Повтор текстуры при заливке. Более подробную информацию можно найти в описании метода * flash.display.Graphics#beginBitmapFill(). */ public function get repeat():Boolean { return _repeat; } /** * @private */ public function set repeat(value:Boolean):void { if (_repeat != value) { _repeat = value; if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } } } /** * Сглаживание текстуры при увеличении масштаба. Более подробную информацию можно найти в описании метода * flash.display.Graphics#beginBitmapFill(). */ public function get smooth():Boolean { return _smooth; } /** * @private */ public function set smooth(value:Boolean):void { if (_smooth != value) { _smooth = value; if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } } } /** * Толщина линии обводки полигона. Если значение отрицательное, то обводка не рисуется. */ public function get wireThickness():Number { return _wireThickness; } /** * @private */ public function set wireThickness(value:Number):void { if (_wireThickness != value) { _wireThickness = value; if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } } } /** * Цвет линии обводки полигона. */ public function get wireColor():uint { return _wireColor; } /** * @private */ public function set wireColor(value:uint):void { if (_wireColor != value) { _wireColor = value; if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } } } /** * Точность перспективной коррекции. */ public function get precision():Number { return _precision; } /** * @private */ public function set precision(value:Number):void { if (_precision != value) { _precision = value; if (_surface != null) { _surface.addMaterialChangedOperationToScene(); } } } /** * @inheritDoc */ override public function clone():Material { var res:TextureMaterial = new TextureMaterial(_texture, _alpha, _repeat, _smooth, _blendMode, _wireThickness, _wireColor, _precision); return res; } } }