diff --git a/changelog_en.txt b/changelog_en.txt new file mode 100644 index 0000000..355dbd6 --- /dev/null +++ b/changelog_en.txt @@ -0,0 +1,196 @@ +Changelog Alternativa3D + +NEXT +---- +Added: ++ Added: ++ Object3DUtils: setPosition, lookAt to Camera3D and converting between Radians and Degrees ++ Stage3D constrained profile support ++ BitmapTextureResource: auto resize for GPU option ++ MouseEvent3D: right and middle mouse buttons support (FP 11.2 and -swf-version=15 required) ++ Object3D: excludeLight() ++ OmniLightShadow: add omni radius in debug scale ++ Camera3D: light sorting + +Fixed: += Object3D.toString() += AnimationClip: animated and loop properties in AnimationClip.slice(), AnimationClip.clone() += Bubbling in MouseEvent3D += ExporterA3D: export meshes without geometry as Object3D += Box: correct tangents and binormals += WireFrame:fix createBinormals() += Decal: Fixed incorrect drawing with extremaly low nearClipping. += View: Fixed bug with mouse events and postprosessing += Several minor fixes + +Removed: +-Diagram: removed CPU time + +8.31.0 +--- += Rendering optimizations += Increased materials performance += AnimationController:fixed error when using notifiers cause animation goes in infinite loop += Camera3D: fixed a bug with duplicating diagram + +8.30.0 +--- +Fixed: += OmniLightShadow: fixed some errors and increased performance += ParserCollada: loading Skin without normals and tangents += ParserA3D: parsing scene with spot lights += DirectionalLigthShadow, OmniLightShadow:default value of biasMultiplyer property was changed to 0.97 += StandardMaterial:removed traces + +8.29.0 +--- +Added: ++ Possibility to use unlimited light sources and shadows count with StandardMaterial ++ A flag Object3D.useShadow which controls shadow visibility on object. ++ OmniLightShadow class + +Fixed: += Fixed issue with Skin lighting += StandardMaterial does not throw exception about limitation number of light sources and shadows anymore. + +8.27.0 +---- +Added: ++ Added DirectionalLightShadow class for calculation of shadow from directional light source. ++ ParserA3D: implemented import of animation, LOD objects and layers from binary format A3D. ++ ExporterA3D: implemented export of Skin class objects, animation and materials to binary format A3D. ++ Added rendering of materials with alpha-test and two-pass alpha-test. + API of materials has been changed: now, if you want to show objects with translucent textures, you must to set value of alphaThreshold. ++ Implemented EnvironmentMaterial with simulation of reflection on cube texture. ++ StandardMaterial: added ability to use pre-computed texture of light together with dynamic light. ++ Added check for maximum number of light sources influencing on object. ++ Added LOD class for switching of detail levels depending on the distance between camera and object. ++ Implemented Occluder class that removes objects, covered by obstacles from rendering. ++ Object3D: added field userData with type Object. ++ Skin, Joint: added methods of setting and calculation of bind position matrix. ++ Skin: implemented method of bound-box calculation, taking into account bone transformation. ++ Geometry: added calculateNormals() and calculateTangents() methods for calculation of normals and vertex tangents, respectively. ++ Added cloning methods at all materials and Camera3D class. ++ Added base Parser class, that is parent class for ParserA3D, ParserCollada and Parser3DS. ++ Camera3D: added display of average frame time and CPU calculation time in statistics. ++ ParserCollada: now you can import intensivity for light sources. + +Fixed: += Fixed incorrect engine work after changing of current Context3D. += ExporterA3D: fixed the export of materials to binary format A3D. += Fixed some problems with mouse events: + - mouse events at flash player version 11.1; + - mouse events at software rendering; + - mouse events with non-convex objects. += VertexLightTextureMaterial: fixed incorrect light from SpotLight. += Object3D: removed incorrect dispatch of events Event3D.ADDED, Event3D.REMOVED when you re-add object to its parent. += AmbientLight, DirectionalLight: fixed calculation of bound-box. += Wireframe: fixed bug with incorrect thickness of lines on some sizes of view. += Optimized playing of animation. += Accelerated the import and creation of animation using format parser. += View: fixed joint work with Flash-profiler. + +Removed: +- Removed support of Flash Player Incubator. +- TextureMaterial: removed parameter useDiffuseAlphaChannel. +- Skin: parameter numJoints removed from constructor. + +8.17.0 +---- ++ ParserA3D: added the import of objects of Skin type and the import of light sources. ++ Added Parser3DS class for import models from 3DS format. ++ Added intersectRay() - The method of finding a ray intersects with an object of Mesh type. ++ Added AxisAlignedSprite class to display plane oriented along one axis to the camera. ++ Export to the binary format A3D is supported. ++ Added debug mode displaying of objects bounds. ++ Added primitive Plane. ++ GeoSphere primitive has normals and tangents of vertices after creation. ++ Normalmaps supported with left-sided system of coordinates in StandardMaterial. + += Decal: removed the offset property and changed the logic of work. += StandardMaterial: fixed a bug with lighting of some light sources. += FillMaterial: color in construstor is grey by default now. += Box: constructor is optimized. += Box: fixed a bug in the cloning of the primitive. Surfaces is not duplicated now. += WireFrame.getResources() allows parameter resourceType now. + +8.12.0 +---- ++ The Public version Flash Player 11 Beta 2 is now supported. ++ The orthographic mode has been added to the Camera3D. ++ The MouseEvent system has been optimized and accelerated. ++ Logo "AlternativaPltaform" has been updated. ++ Now when objects are added and removed from the parent, the event will be sent (Event3D.ADDED, Event3D.REMOVED). ++ The ability to change the property renderToBitmap into View after creating has been added. + += The height and width of the View has been limited to the minimum size of 50x50. += Bug in mouse events system when used skins divided on surfaces by divide() was fixed. += A bug has been fixed in Decal. += Skin lighting algorithm changed to more precise one. += ParserCollada: Fixed a bug when binormal vector of the vertices of the object was incorrectly parsed. += The value of backgroundAlpha in the View constructor changed to 1.0 by default. += VertexLightTextureMaterial now draws correctly without lights in scene. += MouseEvent3D was moved from alternativa.engine3d.core to alternativa.engine3d.core.events. += A bug has been fixed in Object3D.dispatchEvent(). += The offset property has been added to the constructor Decal and it is compulsory. += Now the offset property can be copied using the clone () method of class Decal. + +- The ratio property has been removed from View class. +- VertexLightTextureMaterial now can be used with skin with the maximum number of bones in surface no more than 33. + +8.8.0 +---- +- TextureMaterial, VertexLightMaterial, LightmapMaterial now supports the map-transparency and alpha property. +- Added EllipsoidCollider class for continuous collision detection. +- Added Decal class for detalization of texture on models. +- WireFrame class was added. +- New class SkyBox was added. +- StandardMaterial supports Object-space normal maps now. +- StandardMaterial supports glossiness maps now +- Property alwaysOnTop was added in the Sprite. +- clone() method was added to Skin. +- concatenatedMatrix property was added in Object3D. +- Primitive Box contains vertex tangents now. +- ParserA3D: glossiness and opacity maps are supported now. +- Parsing of Skin with animation from collada was fixed. +- ParserCollada: a bug, when model without vertex normals parsed, was fixed. +- Lighting in StandartMaterial, VertexLightMaterial, when models have non-identity scale, was fixed. +- View can be any size now without throwing exceptions. +- Mouse events work properly now after creating the View with zero width or height. +- Bug with culling of scaled objects in camera frustum was fixed. +- A bug in dispose() method of Geometry class was fixed. +- DirectionalLight: bug with wrong light direction after parsing from Collada was fixed. +- ParserA3D: bug with wrong textures assignment was fixed. +- ParserA3D: vertex tangents are supported now. +- ParserA3D: bug, when Geometry methods worked incorrectly on a parsed model, was fixed. +- FileTextureResource: after a repeated call to upload() the resource is not destroyed now. +- FileTextureResource: you can get and set the texture data now. +- FileTextureResource renamed to ExternalTextureResource. +- ColladaMaterial renamed to ParserMaterial. +- Surface: owner property renamed to object. +- Geometry: findVertexBufferByAttribute renamed to findVertexStreamByAttribute. +- Sprite3D: StandartMaterial and VertexLightMaterial are not supported with it now. +- Fillmaterial: the color property has uint type now. + +8.5.0 +----- +- GPU support +- Directional, omni, spot lights +- Hierarchical exclusion of light sources +- Material with normal, specular, opacity mapping +- Lightmap Material +- Vertex light Material +- Fill Material +- Skin +- Skin subdividing +- Semi-transparent Material +- Mesh with several materials +- Sprite +- Animated Sprite +- GPU-based MouseEvents +- ATF textures loading +- Collada loading +- Binary A3D loading +- Drawing to DisplayObject mode +- Animation engine +- Hierarchical Animated blending tree diff --git a/src/alternativa/engine3d/animation/AnimationClip.as b/src/alternativa/engine3d/animation/AnimationClip.as index 584a6a2..12b1a66 100644 --- a/src/alternativa/engine3d/animation/AnimationClip.as +++ b/src/alternativa/engine3d/animation/AnimationClip.as @@ -465,6 +465,8 @@ package alternativa.engine3d.animation { */ public function slice(start:Number, end:Number = Number.MAX_VALUE):AnimationClip { var sliced:AnimationClip = new AnimationClip(name); + sliced.animated = animated; + sliced.loop = loop; sliced._objects = (_objects == null) ? null : [].concat(_objects); for (var i:int = 0; i < _numTracks; i++) { sliced.addTrack(_tracks[i].slice(start, end)); @@ -477,6 +479,8 @@ package alternativa.engine3d.animation { */ public function clone():AnimationClip { var cloned:AnimationClip = new AnimationClip(name); + cloned.animated = animated; + cloned.loop = loop; cloned._objects = (_objects == null) ? null : [].concat(_objects); for (var i:int = 0; i < _numTracks; i++) { cloned.addTrack(_tracks[i]); diff --git a/src/alternativa/engine3d/core/Camera3D.as b/src/alternativa/engine3d/core/Camera3D.as index e7a2d82..e69d9ee 100644 --- a/src/alternativa/engine3d/core/Camera3D.as +++ b/src/alternativa/engine3d/core/Camera3D.as @@ -46,6 +46,12 @@ package alternativa.engine3d.core { */ public class Camera3D extends Object3D { + /** + * @private + * Key - context, value - properties. + */ + alternativa3d static var context3DPropertiesPool:Dictionary = new Dictionary(true); + /** * The viewport defines part of screen to which renders image seen by the camera. * If viewport is not defined, the camera would not draws anything. @@ -156,6 +162,11 @@ public class Camera3D extends Object3D { */ alternativa3d var context3D:Context3D; + /** + * @private + */ + alternativa3d var context3DProperties:RendererContext3DProperties; + /** * @private * Camera's renderer. If is not defined, the camera will no draw anything. @@ -195,7 +206,6 @@ public class Camera3D extends Object3D { * @param stage3D Stage3D to which image will be rendered. */ public function render(stage3D:Stage3D):void { - // TODO: don't check mouse events if no listeners var i:int; var j:int; var light:Light3D; @@ -214,13 +224,27 @@ public class Camera3D extends Object3D { ambient[2] = 0; ambient[3] = 1; // Receiving the context - context3D = stage3D.context3D; + var currentContext3D:Context3D = stage3D.context3D; + if (currentContext3D != context3D) { + if (currentContext3D != null) { + context3DProperties = context3DPropertiesPool[currentContext3D]; + if (context3DProperties == null) { + context3DProperties = new RendererContext3DProperties(); + context3DProperties.isConstrained = currentContext3D.driverInfo.lastIndexOf("(Baseline Constrained)") >= 0; + context3DPropertiesPool[currentContext3D] = context3DProperties; + } + context3D = currentContext3D; + } else { + context3D = null; + context3DProperties = null; + } + } if (context3D != null && view != null && renderer != null && (view.stage != null || view._canvas != null)) { renderer.camera = this; // Projection argument calculating calculateProjection(view._width, view._height); // Preparing to rendering - view.prepareToRender(stage3D, context3D); + view.configureContext3D(stage3D, context3D, this); // Transformations calculating if (transformChanged) composeTransforms(); localToGlobalTransform.copy(transform); @@ -236,14 +260,12 @@ public class Camera3D extends Object3D { // Check if object of hierarchy is visible if (root.visible) { - globalMouseHandlingType = 0; - // Calculating the matrix to transform from the camera space to local space root.cameraToLocalTransform.combine(root.inverseTransform, localToGlobalTransform); // Calculating the matrix to transform from local space to the camera space root.localToCameraTransform.combine(globalToLocalTransform, root.transform); - if (root.mouseEnabled) globalMouseHandlingType |= root.mouseHandlingType; + globalMouseHandlingType = root.mouseHandlingType; // Checking the culling if (root.boundBox != null) { calculateFrustum(root.cameraToLocalTransform); @@ -251,7 +273,7 @@ public class Camera3D extends Object3D { } else { root.culling = 63; } - // Calculations of conent visibility + // Calculations of content visibility if (root.culling >= 0) root.calculateVisibility(this); // Calculations visibility of children root.calculateChildrenVisibility(this); @@ -334,6 +356,16 @@ public class Camera3D extends Object3D { } raysLength = view.raysLength; + var r:Number = ((view.backgroundColor >> 16) & 0xff)/0xff; + var g:Number = ((view.backgroundColor >> 8) & 0xff)/0xff; + var b:Number = (view.backgroundColor & 0xff)/0xff; + if (view._canvas != null) { + r *= view.backgroundAlpha; + g *= view.backgroundAlpha; + b *= view.backgroundAlpha; + } + context3D.clear(r, g, b, view.backgroundAlpha); + // Check getting in frustum and occluding if (root.culling >= 0 && (root.boundBox == null || occludersLength == 0 || !root.boundBox.checkOcclusion(occluders, occludersLength, root.localToCameraTransform))) { // Check if the ray crossing the bounding box @@ -387,7 +419,6 @@ public class Camera3D extends Object3D { } // Gather the draws for children root.collectChildrenDraws(this, lights, lightsLength, root.useShadow); - // Mouse events prosessing view.processMouseEvents(context3D, this); // Render @@ -405,7 +436,6 @@ public class Camera3D extends Object3D { lights.length = 0; childLights.length = 0; occluders.length = 0; - context3D = null; } /** @@ -424,7 +454,7 @@ public class Camera3D extends Object3D { var deltaX:Number = x - this.x; var deltaY:Number = y - this.y; var deltaZ:Number = z - this.z; - var rotX = Math.atan2(deltaZ, Math.sqrt(deltaX * deltaX + deltaY * deltaY)); + var rotX:Number = Math.atan2(deltaZ, Math.sqrt(deltaX * deltaX + deltaY * deltaY)); rotationX = rotX - 0.5 * Math.PI; rotationY = 0; rotationZ = - Math.atan2(deltaX,deltaY); diff --git a/src/alternativa/engine3d/core/Object3D.as b/src/alternativa/engine3d/core/Object3D.as index ec3d9ef..c0ae40c 100644 --- a/src/alternativa/engine3d/core/Object3D.as +++ b/src/alternativa/engine3d/core/Object3D.as @@ -172,26 +172,53 @@ package alternativa.engine3d.core { */ public class Object3D implements IEventDispatcher { + // Mouse moving + private static const MOUSE_MOVE_BIT:uint = 1; + private static const MOUSE_OVER_BIT:uint = 2; + private static const MOUSE_OUT_BIT:uint = 4; + private static const ROLL_OVER_BIT:uint = 0x8; + private static const ROLL_OUT_BIT:uint = 0x10; + private static const USE_HAND_CURSOR_BIT:uint = 0x20; + + // Mouse pressing + private static const MOUSE_DOWN_BIT:uint = 0x40; + private static const MOUSE_UP_BIT:uint = 0x80; + private static const CLICK_BIT:uint = 0x100; + private static const DOUBLE_CLICK_BIT:uint = 0x200; + + // Mouse wheel + private static const MOUSE_WHEEL_BIT:uint = 0x400; + + // Mouse middle button + private static const MIDDLE_CLICK_BIT:uint = 0x800; + private static const MIDDLE_MOUSE_DOWN_BIT:uint = 0x1000; + private static const MIDDLE_MOUSE_UP_BIT:uint = 0x2000; + + // Mouse right button + private static const RIGHT_CLICK_BIT:uint = 0x4000; + private static const RIGHT_MOUSE_DOWN_BIT:uint = 0x8000; + private static const RIGHT_MOUSE_UP_BIT:uint = 0x10000; + /** * @private */ - alternativa3d static const MOUSE_HANDLING_MOVING:uint = 1; + alternativa3d static const MOUSE_HANDLING_MOVING:uint = MOUSE_MOVE_BIT | MOUSE_OVER_BIT | MOUSE_OUT_BIT | ROLL_OVER_BIT | ROLL_OUT_BIT | USE_HAND_CURSOR_BIT; /** * @private */ - alternativa3d static const MOUSE_HANDLING_PRESSING:uint = 2; + alternativa3d static const MOUSE_HANDLING_PRESSING:uint = MOUSE_DOWN_BIT | MOUSE_UP_BIT | CLICK_BIT | DOUBLE_CLICK_BIT; /** * @private */ - alternativa3d static const MOUSE_HANDLING_WHEEL:uint = 4; + alternativa3d static const MOUSE_HANDLING_WHEEL:uint = MOUSE_WHEEL_BIT; /** * @private */ - alternativa3d static const MOUSE_HANDLING_MIDDLE_BUTTON:uint = 8; + alternativa3d static const MOUSE_HANDLING_MIDDLE_BUTTON:uint = MIDDLE_CLICK_BIT | MIDDLE_MOUSE_DOWN_BIT | MIDDLE_MOUSE_UP_BIT; /** * @private */ - alternativa3d static const MOUSE_HANDLING_RIGHT_BUTTON:uint = 16; + alternativa3d static const MOUSE_HANDLING_RIGHT_BUTTON:uint = RIGHT_CLICK_BIT | RIGHT_MOUSE_DOWN_BIT | RIGHT_MOUSE_UP_BIT; /** * Custom data available to store within Object3D by user. @@ -249,12 +276,6 @@ package alternativa.engine3d.core { */ public var doubleClickEnabled:Boolean = false; - /** - * A Boolean value that indicates whether the pointing hand (hand cursor) - * appears when the pointer rolls over a Object3D. - */ - public var useHandCursor:Boolean = false; - /** * Bounds of the object described as rectangular parallelepiped. */ @@ -587,6 +608,25 @@ package alternativa.engine3d.core { transformChanged = true; } + /** + * A Boolean value that indicates whether the pointing hand (hand cursor) + * appears when the pointer rolls over a Object3D. + */ + public function get useHandCursor():Boolean { + return (mouseHandlingType & USE_HAND_CURSOR_BIT) != 0; + } + + /** + * @private + */ + public function set useHandCursor(value:Boolean):void { + if (value) { + mouseHandlingType |= USE_HAND_CURSOR_BIT; + } else { + mouseHandlingType &= ~USE_HAND_CURSOR_BIT; + } + } + /** * Searches for the intersection of an Object3D and given ray, defined by origin and direction. * @@ -739,20 +779,56 @@ package alternativa.engine3d.core { vector = new Vector.(); listeners[type] = vector; - if (type == MouseEvent3D.MOUSE_MOVE || type == MouseEvent3D.MOUSE_OVER || type == MouseEvent3D.MOUSE_OUT || type == MouseEvent3D.ROLL_OVER || type == MouseEvent3D.ROLL_OUT) { - mouseHandlingType |= MOUSE_HANDLING_MOVING; - } - if (type == MouseEvent3D.MOUSE_DOWN || type == MouseEvent3D.MOUSE_UP || type == MouseEvent3D.CLICK || type == MouseEvent3D.DOUBLE_CLICK) { - mouseHandlingType |= MOUSE_HANDLING_PRESSING; - } - if (type == MouseEvent3D.MOUSE_WHEEL) { - mouseHandlingType |= MOUSE_HANDLING_WHEEL; - } - if (type == MouseEvent3D.MIDDLE_CLICK || type == MouseEvent3D.MIDDLE_MOUSE_DOWN || type == MouseEvent3D.MIDDLE_MOUSE_UP) { - mouseHandlingType |= MOUSE_HANDLING_MIDDLE_BUTTON; - } - if (type == MouseEvent3D.RIGHT_CLICK || type == MouseEvent3D.RIGHT_MOUSE_DOWN || type == MouseEvent3D.RIGHT_MOUSE_UP) { - mouseHandlingType |= MOUSE_HANDLING_RIGHT_BUTTON; + // update mouseHandlingType bits + switch (type) { + case MouseEvent3D.MOUSE_MOVE: + mouseHandlingType |= MOUSE_MOVE_BIT; + break; + case MouseEvent3D.MOUSE_OVER: + mouseHandlingType |= MOUSE_OVER_BIT; + break; + case MouseEvent3D.MOUSE_OUT: + mouseHandlingType |= MOUSE_OUT_BIT; + break; + case MouseEvent3D.ROLL_OVER: + mouseHandlingType |= ROLL_OVER_BIT; + break; + case MouseEvent3D.ROLL_OUT: + mouseHandlingType |= ROLL_OUT_BIT; + break; + case MouseEvent3D.MOUSE_DOWN: + mouseHandlingType |= MOUSE_DOWN_BIT; + break; + case MouseEvent3D.MOUSE_UP: + mouseHandlingType |= MOUSE_UP_BIT; + break; + case MouseEvent3D.CLICK: + mouseHandlingType |= CLICK_BIT; + break; + case MouseEvent3D.DOUBLE_CLICK: + mouseHandlingType |= DOUBLE_CLICK_BIT; + break; + case MouseEvent3D.MOUSE_WHEEL: + mouseHandlingType |= MOUSE_WHEEL_BIT; + break; + case MouseEvent3D.MIDDLE_CLICK: + mouseHandlingType |= MIDDLE_CLICK_BIT; + break; + case MouseEvent3D.MIDDLE_MOUSE_DOWN: + mouseHandlingType |= MIDDLE_MOUSE_DOWN_BIT; + break; + case MouseEvent3D.MIDDLE_MOUSE_UP: + mouseHandlingType |= MIDDLE_MOUSE_UP_BIT; + break; + case MouseEvent3D.RIGHT_CLICK: + mouseHandlingType |= RIGHT_CLICK_BIT; + break; + case MouseEvent3D.RIGHT_MOUSE_DOWN: + mouseHandlingType |= RIGHT_MOUSE_DOWN_BIT; + break; + case MouseEvent3D.RIGHT_MOUSE_UP: + mouseHandlingType |= RIGHT_MOUSE_UP_BIT; + break; } } if (vector.indexOf(listener) < 0) { @@ -781,7 +857,68 @@ package alternativa.engine3d.core { if (length > 1) { vector.length = length - 1; } else { + // update mouseHandlingType bits + var noListeners:Boolean; + if (listeners == captureListeners) { + noListeners = (bubbleListeners == null || bubbleListeners[type] == null); + } else { + noListeners = (captureListeners == null || captureListeners[type] == null); + } + if (noListeners) { + switch (type) { + case MouseEvent3D.MOUSE_MOVE: + mouseHandlingType &= ~MOUSE_MOVE_BIT; + break; + case MouseEvent3D.MOUSE_OVER: + mouseHandlingType &= ~MOUSE_OVER_BIT; + break; + case MouseEvent3D.MOUSE_OUT: + mouseHandlingType &= ~MOUSE_OUT_BIT; + break; + case MouseEvent3D.ROLL_OVER: + mouseHandlingType &= ~ROLL_OVER_BIT; + break; + case MouseEvent3D.ROLL_OUT: + mouseHandlingType &= ~ROLL_OUT_BIT; + break; + case MouseEvent3D.MOUSE_DOWN: + mouseHandlingType &= ~MOUSE_DOWN_BIT; + break; + case MouseEvent3D.MOUSE_UP: + mouseHandlingType &= ~MOUSE_UP_BIT; + break; + case MouseEvent3D.CLICK: + mouseHandlingType &= ~CLICK_BIT; + break; + case MouseEvent3D.DOUBLE_CLICK: + mouseHandlingType &= ~DOUBLE_CLICK_BIT; + break; + case MouseEvent3D.MOUSE_WHEEL: + mouseHandlingType &= ~MOUSE_WHEEL_BIT; + break; + case MouseEvent3D.MIDDLE_CLICK: + mouseHandlingType &= ~MIDDLE_CLICK_BIT; + break; + case MouseEvent3D.MIDDLE_MOUSE_DOWN: + mouseHandlingType &= ~MIDDLE_MOUSE_DOWN_BIT; + break; + case MouseEvent3D.MIDDLE_MOUSE_UP: + mouseHandlingType &= ~MIDDLE_MOUSE_UP_BIT; + break; + case MouseEvent3D.RIGHT_CLICK: + mouseHandlingType &= ~RIGHT_CLICK_BIT; + break; + case MouseEvent3D.RIGHT_MOUSE_DOWN: + mouseHandlingType &= ~RIGHT_MOUSE_DOWN_BIT; + break; + case MouseEvent3D.RIGHT_MOUSE_UP: + mouseHandlingType &= ~RIGHT_MOUSE_UP_BIT; + break; + } + } + delete listeners[type]; + var key:*; for (key in listeners) break; if (!key) { @@ -791,21 +928,6 @@ package alternativa.engine3d.core { bubbleListeners = null; } } - if (type == MouseEvent3D.MOUSE_MOVE || type == MouseEvent3D.MOUSE_OVER || type == MouseEvent3D.MOUSE_OUT || type == MouseEvent3D.ROLL_OVER || type == MouseEvent3D.ROLL_OUT) { - mouseHandlingType &= ~MOUSE_HANDLING_MOVING; - } - if (type == MouseEvent3D.MOUSE_DOWN || type == MouseEvent3D.MOUSE_UP || type == MouseEvent3D.CLICK || type == MouseEvent3D.DOUBLE_CLICK) { - mouseHandlingType &= ~MOUSE_HANDLING_PRESSING; - } - if (type == MouseEvent3D.MOUSE_WHEEL) { - mouseHandlingType &= ~MOUSE_HANDLING_WHEEL; - } - if (type == MouseEvent3D.MIDDLE_CLICK || type == MouseEvent3D.MIDDLE_MOUSE_DOWN || type == MouseEvent3D.MIDDLE_MOUSE_UP) { - mouseHandlingType &= ~MOUSE_HANDLING_MIDDLE_BUTTON; - } - if (type == MouseEvent3D.RIGHT_CLICK || type == MouseEvent3D.RIGHT_MOUSE_DOWN || type == MouseEvent3D.RIGHT_MOUSE_UP) { - mouseHandlingType &= ~MOUSE_HANDLING_RIGHT_BUTTON; - } } } } @@ -1401,7 +1523,7 @@ package alternativa.engine3d.core { // Calculating matrix for converting from local coordinates to camera coordinates child.localToCameraTransform.combine(localToCameraTransform, child.transform); - if (child.mouseEnabled) camera.globalMouseHandlingType |= child.mouseHandlingType; + camera.globalMouseHandlingType |= child.mouseHandlingType; // Culling checking if (child.boundBox != null) { camera.calculateFrustum(child.cameraToLocalTransform); @@ -1531,7 +1653,12 @@ package alternativa.engine3d.core { /** - * Toggle off light source from litting this object + * Disables lighting of the object by given light. + * + * @param light Light which should not affect to the object + * @param updateChildren If true all children of this object will be also shielded from the given light. + * @see #excludedLights() + * @see #clearExcludedLights() */ public function excludeLight(light:Light3D, updateChildren:Boolean = false):void{ if (_excludedLights.indexOf(light) < 0) { @@ -1552,7 +1679,7 @@ package alternativa.engine3d.core { } /** - * Resets list of lights excluded from litting this object + * Resets list of lights excluded from lighting this object. */ public function clearExcludedLights(updateChildren:Boolean = false):void { _excludedLights.length = 0; @@ -1564,7 +1691,7 @@ package alternativa.engine3d.core { } /** - * Returns a copy of object + * Returns a copy of object. * @return A copy of this Object3D. */ public function clone():Object3D { @@ -1614,7 +1741,8 @@ package alternativa.engine3d.core { */ public function toString():String { var className:String = getQualifiedClassName(this); - return "[" + className.substr(className.indexOf("::") + 2) + " " + name + "]"; + var start:int = className.indexOf("::"); + return "[" + (start < 0 ? className : className.substr(start + 2)) + " " + name + "]"; } } diff --git a/src/alternativa/engine3d/core/Renderer.as b/src/alternativa/engine3d/core/Renderer.as index ad51b11..77fae35 100644 --- a/src/alternativa/engine3d/core/Renderer.as +++ b/src/alternativa/engine3d/core/Renderer.as @@ -4,8 +4,8 @@ * You may add additional accurate notices of copyright ownership. * * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ - * */ - + * + */ package alternativa.engine3d.core { import alternativa.engine3d.alternativa3d; @@ -16,7 +16,6 @@ package alternativa.engine3d.core { import flash.display3D.Context3DProgramType; import flash.display3D.IndexBuffer3D; import flash.display3D.Program3D; - import flash.utils.Dictionary; use namespace alternativa3d; @@ -37,17 +36,13 @@ package alternativa.engine3d.core { public static const NEXT_LAYER:int = 50; - // Key - context, value - properties. - protected static var properties:Dictionary = new Dictionary(true); - // Collector protected var collector:DrawUnit; alternativa3d var camera:Camera3D; alternativa3d var drawUnits:Vector. = new Vector.(); - - protected var _context3D:Context3D; + protected var _contextProperties:RendererContext3DProperties; alternativa3d function render(context3D:Context3D):void { @@ -59,29 +54,29 @@ package alternativa.engine3d.core { if (list != null) { switch (i) { case SKY: - _context3D.setDepthTest(false, Context3DCompareMode.ALWAYS); + context3D.setDepthTest(false, Context3DCompareMode.ALWAYS); break; case OPAQUE: - _context3D.setDepthTest(true, Context3DCompareMode.LESS); + context3D.setDepthTest(true, Context3DCompareMode.LESS); break; case OPAQUE_OVERHEAD: - _context3D.setDepthTest(false, Context3DCompareMode.EQUAL); + context3D.setDepthTest(false, Context3DCompareMode.EQUAL); break; case DECALS: - _context3D.setDepthTest(false, Context3DCompareMode.LESS_EQUAL); + context3D.setDepthTest(false, Context3DCompareMode.LESS_EQUAL); break; case TRANSPARENT_SORT: if (list.next != null) list = sortByAverageZ(list); - _context3D.setDepthTest(false, Context3DCompareMode.LESS); + context3D.setDepthTest(false, Context3DCompareMode.LESS); break; case NEXT_LAYER: - _context3D.setDepthTest(false, Context3DCompareMode.ALWAYS); + context3D.setDepthTest(false, Context3DCompareMode.ALWAYS); break; } // Rendering while (list != null) { var next:DrawUnit = list.next; - renderDrawUnit(list, _context3D, camera); + renderDrawUnit(list, context3D, camera); // Send to collector list.clear(); list.next = collector; @@ -90,6 +85,7 @@ package alternativa.engine3d.core { } } } + // TODO: not free buffers and textures in each renderer, only when full camera cycle finishes. freeContext3DProperties(context3D); // Clear drawUnits.length = 0; @@ -180,14 +176,7 @@ package alternativa.engine3d.core { } protected function updateContext3D(value:Context3D):void { - if (_context3D != value) { - _contextProperties = properties[value]; - if (_contextProperties == null) { - _contextProperties = new RendererContext3DProperties(); - properties[value] = _contextProperties; - } - _context3D = value; - } + _contextProperties = camera.context3DProperties; } /** diff --git a/src/alternativa/engine3d/core/RendererContext3DProperties.as b/src/alternativa/engine3d/core/RendererContext3DProperties.as index a92aad0..59a6502 100644 --- a/src/alternativa/engine3d/core/RendererContext3DProperties.as +++ b/src/alternativa/engine3d/core/RendererContext3DProperties.as @@ -8,7 +8,11 @@ package alternativa.engine3d.core { + import alternativa.engine3d.materials.ShaderProgram; + import alternativa.engine3d.resources.Geometry; + import flash.display3D.Program3D; + import flash.utils.Dictionary; /** * @private @@ -16,6 +20,12 @@ package alternativa.engine3d.core { */ public class RendererContext3DProperties { + public var isConstrained:Boolean = false; + + public var backBufferWidth:int = -1; + public var backBufferHeight:int = -1; + public var backBufferAntiAlias:int = -1; + public var usedBuffers:uint = 0; public var usedTextures:uint = 0; @@ -24,5 +34,11 @@ package alternativa.engine3d.core { public var blendSource:String; public var blendDestination:String; + // View: mouse events + // Key - vertex program of object, value - program. + public var drawDistancePrograms:Dictionary = new Dictionary(); + public var drawColoredRectProgram:ShaderProgram; + public var drawRectGeometry:Geometry; + } } diff --git a/src/alternativa/engine3d/core/View.as b/src/alternativa/engine3d/core/View.as index ec25310..4d3a503 100644 --- a/src/alternativa/engine3d/core/View.as +++ b/src/alternativa/engine3d/core/View.as @@ -61,10 +61,6 @@ package alternativa.engine3d.core { private static const renderEvent:MouseEvent = new MouseEvent("render"); - private static var properties:Dictionary = new Dictionary(true); - private var cachedContext3D:Context3D; - private var context3DProperties:Context3DViewProperties; - static private var drawDistanceFragment:Linker; static private var drawDistanceVertexProcedure:Procedure; @@ -309,6 +305,11 @@ package alternativa.engine3d.core { addEventListener(Event.REMOVED_FROM_STAGE, onRemoveFromStage); } + /** + * If true, you will able to handle following events MouseEvent3D.RIGHT_CLICK, + * MouseEvent3D.RIGHT_MOUSE_DOWN, MouseEvent3D.RIGHT_MOUSE_UP. + * The context menu will no longer open on clicking right mouse button. + */ public function get rightClick3DEnabled():Boolean { return _rightClick3DEnabled; } @@ -533,7 +534,7 @@ package alternativa.engine3d.core { /** * @private */ - alternativa3d function prepareToRender(stage3D:Stage3D, context:Context3D):void { + alternativa3d function configureContext3D(stage3D:Stage3D, context3D:Context3D, camera:Camera3D):void { if (_canvas == null) { var vis:Boolean = this.visible; for (var parent:DisplayObject = this.parent; parent != null; parent = parent.parent) { @@ -553,70 +554,55 @@ package alternativa.engine3d.core { createRenderBitmap(); } } - if (context != cachedContext3D) { - // Get properties. - cachedContext3D = context; - context3DProperties = properties[cachedContext3D]; - if (context3DProperties == null) { - context3DProperties = new Context3DViewProperties(); - // Inititalize data for mouse events - var rectGeometry:Geometry = new Geometry(4); - rectGeometry.addVertexStream([VertexAttributes.POSITION, VertexAttributes.POSITION, VertexAttributes.POSITION, VertexAttributes.TEXCOORDS[0], VertexAttributes.TEXCOORDS[0]]); - rectGeometry.setAttributeValues(VertexAttributes.POSITION, Vector.([0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1])); - rectGeometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], Vector.([0, 0, 0, 1, 1, 1, 1, 0])); - rectGeometry.indices = Vector.([0, 1, 3, 2, 3, 1]); - rectGeometry.upload(context); - var vLinker:Linker = new Linker(Context3DProgramType.VERTEX); - vLinker.addProcedure(Procedure.compileFromArray([ - "#a0=a0", - "#c0=c0", - "mul t0.x, a0.x, c0.x", - "mul t0.y, a0.y, c0.y", - "add o0.x, t0.x, c0.z", - "add o0.y, t0.y, c0.w", - "mov o0.z, a0.z", - "mov o0.w, a0.z", - ])); - var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); - fLinker.addProcedure(Procedure.compileFromArray([ - "#c0=c0", - "mov o0, c0", - ])); - var coloredRectProgram:ShaderProgram = new ShaderProgram(vLinker, fLinker); - coloredRectProgram.upload(context); + var context3DProperties:RendererContext3DProperties = camera.context3DProperties; + if (context3DProperties.drawRectGeometry == null) { + // Inititalize data for mouse events + var rectGeometry:Geometry = new Geometry(4); + rectGeometry.addVertexStream([VertexAttributes.POSITION, VertexAttributes.POSITION, VertexAttributes.POSITION, VertexAttributes.TEXCOORDS[0], VertexAttributes.TEXCOORDS[0]]); + rectGeometry.setAttributeValues(VertexAttributes.POSITION, Vector.([0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1])); + rectGeometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], Vector.([0, 0, 0, 1, 1, 1, 1, 0])); + rectGeometry.indices = Vector.([0, 1, 3, 2, 3, 1]); + rectGeometry.upload(context3D); + var vLinker:Linker = new Linker(Context3DProgramType.VERTEX); + vLinker.addProcedure(Procedure.compileFromArray([ + "#a0=a0", + "#c0=c0", + "mul t0.x, a0.x, c0.x", + "mul t0.y, a0.y, c0.y", + "add o0.x, t0.x, c0.z", + "add o0.y, t0.y, c0.w", + "mov o0.z, a0.z", + "mov o0.w, a0.z", + ])); + var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); + fLinker.addProcedure(Procedure.compileFromArray([ + "#c0=c0", + "mov o0, c0", + ])); + var coloredRectProgram:ShaderProgram = new ShaderProgram(vLinker, fLinker); + coloredRectProgram.upload(context3D); - context3DProperties.drawRectGeometry = rectGeometry; - context3DProperties.drawColoredRectProgram = coloredRectProgram; - properties[cachedContext3D] = context3DProperties; - } + context3DProperties.drawRectGeometry = rectGeometry; + context3DProperties.drawColoredRectProgram = coloredRectProgram; } if (_width != context3DProperties.backBufferWidth || _height != context3DProperties.backBufferHeight || antiAlias != context3DProperties.backBufferAntiAlias) { context3DProperties.backBufferWidth = _width; context3DProperties.backBufferHeight = _height; context3DProperties.backBufferAntiAlias = antiAlias; - context.configureBackBuffer(_width, _height, antiAlias); + context3D.configureBackBuffer(_width, _height, antiAlias); } - var r:Number = ((backgroundColor >> 16) & 0xff)/0xff; - var g:Number = ((backgroundColor >> 8) & 0xff)/0xff; - var b:Number = (backgroundColor & 0xff)/0xff; - if (canvas != null) { - r *= backgroundAlpha; - g *= backgroundAlpha; - b *= backgroundAlpha; - } - context.clear(r, g, b, backgroundAlpha); } /** * @private */ - alternativa3d function processMouseEvents(context:Context3D, camera:Camera3D):void { + alternativa3d function processMouseEvents(context3D:Context3D, camera:Camera3D):void { var i:int; // Mouse events if (eventsLength > 0) { if (surfacesLength > 0) { // Calculating the depth - calculateSurfacesDepths(context, camera, _width, _height); + calculateSurfacesDepths(context3D, camera, _width, _height); // Sorting by decreasing the depth for (i = 0; i < raysLength; i++) { var raySurfaces:Vector. = raysSurfaces[i]; @@ -776,8 +762,8 @@ package alternativa.engine3d.core { context.setVertexBufferAt(6, null); context.setVertexBufferAt(7, null); - var drawRectGeometry:Geometry = context3DProperties.drawRectGeometry; - var drawColoredRectProgram:ShaderProgram = context3DProperties.drawColoredRectProgram; + var drawRectGeometry:Geometry = camera.context3DProperties.drawRectGeometry; + var drawColoredRectProgram:ShaderProgram = camera.context3DProperties.drawColoredRectProgram; // Rectangle var vLinker:Linker, fLinker:Linker; @@ -883,7 +869,7 @@ package alternativa.engine3d.core { var procedure:Procedure = procedures[index]; var object:Object3D = surface.object; // Program - var drawDistanceProgram:ShaderProgram = context3DProperties.drawDistancePrograms[procedure]; + var drawDistanceProgram:ShaderProgram = camera.context3DProperties.drawDistancePrograms[procedure]; if (drawDistanceProgram == null) { // Assembling the vertex shader var vertex:Linker = new Linker(Context3DProgramType.VERTEX); @@ -902,7 +888,7 @@ package alternativa.engine3d.core { drawDistanceProgram = new ShaderProgram(vertex, drawDistanceFragment); drawDistanceProgram.fragmentShader.varyings = drawDistanceProgram.vertexShader.varyings; drawDistanceProgram.upload(context); - context3DProperties.drawDistancePrograms[procedure] = drawDistanceProgram; + camera.context3DProperties.drawDistancePrograms[procedure] = drawDistanceProgram; } var buffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION); if (buffer == null) return; @@ -1405,9 +1391,6 @@ package alternativa.engine3d.core { } } -import alternativa.engine3d.materials.ShaderProgram; -import alternativa.engine3d.resources.Geometry; - import flash.display.BitmapData; import flash.display.Sprite; import flash.events.MouseEvent; @@ -1415,7 +1398,6 @@ import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.net.URLRequest; import flash.net.navigateToURL; -import flash.utils.Dictionary; class Logo extends Sprite { @@ -1513,17 +1495,3 @@ class Logo extends Sprite { } } - -class Context3DViewProperties { - public var backBufferWidth:int = -1; - public var backBufferHeight:int = -1; - public var backBufferAntiAlias:int = -1; - - // Mouse events - - // Key - vertex program of object, value - program. - public var drawDistancePrograms:Dictionary = new Dictionary(); - public var drawColoredRectProgram:ShaderProgram; - public var drawRectGeometry:Geometry; - -} diff --git a/src/alternativa/engine3d/materials/EnvironmentMaterial.as b/src/alternativa/engine3d/materials/EnvironmentMaterial.as index d1e8478..24dcb83 100644 --- a/src/alternativa/engine3d/materials/EnvironmentMaterial.as +++ b/src/alternativa/engine3d/materials/EnvironmentMaterial.as @@ -310,6 +310,15 @@ package alternativa.engine3d.materials { "mov v0, a0" ], "passLightMapUVProcedure"); + /** + * @private + */ + alternativa3d static var fallbackTextureMaterial:TextureMaterial = new TextureMaterial(); + /** + * @private + */ + alternativa3d static var fallbackLightMapMaterial:LightMapMaterial = new LightMapMaterial(); + private var _normalMapSpace:int = NormalMapSpace.TANGENT_RIGHT_HANDED; /** @@ -815,6 +824,31 @@ package alternativa.engine3d.materials { if (_normalMap != null && _normalMap._texture == null) return; if (_reflectionMap != null && _reflectionMap._texture == null) return; if (_lightMap != null && _lightMap._texture == null) return; + + if (camera.context3DProperties.isConstrained) { + // fallback to simpler material + if (lightMap == null) { + fallbackTextureMaterial.diffuseMap = diffuseMap; + fallbackTextureMaterial.opacityMap = opacityMap; + fallbackTextureMaterial.alphaThreshold = alphaThreshold; + fallbackTextureMaterial.alpha = alpha; + fallbackTextureMaterial.opaquePass = opaquePass; + fallbackTextureMaterial.transparentPass = transparentPass; + fallbackTextureMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, objectRenderPriority); + } else { + fallbackLightMapMaterial.diffuseMap = diffuseMap; + fallbackLightMapMaterial.lightMap = lightMap; + fallbackLightMapMaterial.lightMapChannel = lightMapChannel; + fallbackLightMapMaterial.opacityMap = opacityMap; + fallbackLightMapMaterial.alphaThreshold = alphaThreshold; + fallbackLightMapMaterial.alpha = alpha; + fallbackLightMapMaterial.opaquePass = opaquePass; + fallbackLightMapMaterial.transparentPass = transparentPass; + fallbackLightMapMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, objectRenderPriority); + } + return; + } + var object:Object3D = surface.object; // Program diff --git a/src/alternativa/engine3d/materials/StandardMaterial.as b/src/alternativa/engine3d/materials/StandardMaterial.as index 7ce0217..f451c23 100644 --- a/src/alternativa/engine3d/materials/StandardMaterial.as +++ b/src/alternativa/engine3d/materials/StandardMaterial.as @@ -307,6 +307,15 @@ package alternativa.engine3d.materials { "mov v0, a0" ], "passLightMapUVProcedure"); + /** + * @private + */ + alternativa3d static var fallbackTextureMaterial:TextureMaterial = new TextureMaterial(); + /** + * @private + */ + alternativa3d static var fallbackLightMapMaterial:LightMapMaterial = new LightMapMaterial(); + /** * Normal map. */ @@ -986,6 +995,30 @@ package alternativa.engine3d.materials { // Check if textures uploaded in to the context. if (opacityMap != null && opacityMap._texture == null || glossinessMap != null && glossinessMap._texture == null || specularMap != null && specularMap._texture == null || lightMap != null && lightMap._texture == null) return; + if (camera.context3DProperties.isConstrained) { + // fallback to simpler material + if (lightMap == null) { + fallbackTextureMaterial.diffuseMap = diffuseMap; + fallbackTextureMaterial.opacityMap = opacityMap; + fallbackTextureMaterial.alphaThreshold = alphaThreshold; + fallbackTextureMaterial.alpha = alpha; + fallbackTextureMaterial.opaquePass = opaquePass; + fallbackTextureMaterial.transparentPass = transparentPass; + fallbackTextureMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, objectRenderPriority); + } else { + fallbackLightMapMaterial.diffuseMap = diffuseMap; + fallbackLightMapMaterial.lightMap = lightMap; + fallbackLightMapMaterial.lightMapChannel = lightMapChannel; + fallbackLightMapMaterial.opacityMap = opacityMap; + fallbackLightMapMaterial.alphaThreshold = alphaThreshold; + fallbackLightMapMaterial.alpha = alpha; + fallbackLightMapMaterial.opaquePass = opaquePass; + fallbackLightMapMaterial.transparentPass = transparentPass; + fallbackLightMapMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, objectRenderPriority); + } + return; + } + var object:Object3D = surface.object; // Buffers diff --git a/src/alternativa/engine3d/materials/TextureMaterial.as b/src/alternativa/engine3d/materials/TextureMaterial.as index 3504555..80ea821 100644 --- a/src/alternativa/engine3d/materials/TextureMaterial.as +++ b/src/alternativa/engine3d/materials/TextureMaterial.as @@ -34,7 +34,7 @@ package alternativa.engine3d.materials { use namespace alternativa3d; /** - * The materiall fills surface with bitmap image in light-independent manner. Can draw a Skin with no more than 41 Joints per surface. See Skin.divide() for more details. + * The material fills surface with bitmap image in light-independent manner. Can draw a Skin with no more than 41 Joints per surface. See Skin.divide() for more details. * * To be drawn with this material, geometry shoud have UV coordinates. * @see alternativa.engine3d.objects.Skin#divide() @@ -113,19 +113,19 @@ package alternativa.engine3d.materials { public var opacityMap:TextureResource; /** - * If true, perform transparent pass. Parts of surface, cumulative alpha value of which is below than alphaThreshold draw within transparent pass. + * If true, perform transparent pass. Parts of surface, cumulative alpha value of which is below than alphaThreshold will be drawn within transparent pass. * @see #alphaThreshold */ public var transparentPass:Boolean = true; /** - * If true, perform opaque pass. Parts of surface, cumulative alpha value of which is greater or equal than alphaThreshold draw within opaque pass. + * If true, perform opaque pass. Parts of surface, cumulative alpha value of which is greater or equal than alphaThreshold will be drawn within opaque pass. * @see #alphaThreshold */ public var opaquePass:Boolean = true; /** - * alphaThreshold defines starts from which value of alpha a fragment of surface will get into transparent pass. + * alphaThreshold defines starts from which value of alpha a fragment of the surface will get into transparent pass. * @see #transparentPass * @see #opaquePass */ diff --git a/src/alternativa/engine3d/materials/VertexLightTextureMaterial.as b/src/alternativa/engine3d/materials/VertexLightTextureMaterial.as index 009f231..ceb0664 100644 --- a/src/alternativa/engine3d/materials/VertexLightTextureMaterial.as +++ b/src/alternativa/engine3d/materials/VertexLightTextureMaterial.as @@ -98,6 +98,11 @@ package alternativa.engine3d.materials { private static const _lightsProcedures:Dictionary = new Dictionary(true); + /** + * @private + */ + alternativa3d static var fallbackMaterial:TextureMaterial = new TextureMaterial(); + /** * Creates a new VertexLightTextureMaterial instance. * @@ -282,6 +287,18 @@ package alternativa.engine3d.materials { override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { if (diffuseMap == null || diffuseMap._texture == null || opacityMap != null && opacityMap._texture == null) return; + if (camera.context3DProperties.isConstrained) { + // fallback to texture material + fallbackMaterial.diffuseMap = diffuseMap; + fallbackMaterial.opacityMap = opacityMap; + fallbackMaterial.alphaThreshold = alphaThreshold; + fallbackMaterial.alpha = alpha; + fallbackMaterial.opaquePass = opaquePass; + fallbackMaterial.transparentPass = transparentPass; + fallbackMaterial.collectDraws(camera, surface, geometry, lights, lightsLength, useShadow, objectRenderPriority); + return; + } + var object:Object3D = surface.object; // Buffers diff --git a/src/alternativa/engine3d/objects/LOD.as b/src/alternativa/engine3d/objects/LOD.as index 27af29f..04d38a0 100644 --- a/src/alternativa/engine3d/objects/LOD.as +++ b/src/alternativa/engine3d/objects/LOD.as @@ -176,6 +176,7 @@ package alternativa.engine3d.objects { * @private */ override alternativa3d function calculateVisibility(camera:Camera3D):void { + // TODO: optimize - use square of distance var distance:Number = Math.sqrt(localToCameraTransform.d*localToCameraTransform.d + localToCameraTransform.h*localToCameraTransform.h + localToCameraTransform.l*localToCameraTransform.l); for (level = levelList; level != null; level = level.next) { if (distance <= level.distance) { @@ -196,7 +197,7 @@ package alternativa.engine3d.objects { // Calculation of transfer matrix from local space to camera. child.localToCameraTransform.combine(parent.localToCameraTransform, child.transform); - if (child.mouseEnabled) camera.globalMouseHandlingType |= child.mouseHandlingType; + camera.globalMouseHandlingType |= child.mouseHandlingType; // Pass child.culling = parent.culling; // Calculating visibility of the self content diff --git a/src/alternativa/engine3d/objects/SkyBox.as b/src/alternativa/engine3d/objects/SkyBox.as index e047a1e..9f2299f 100644 --- a/src/alternativa/engine3d/objects/SkyBox.as +++ b/src/alternativa/engine3d/objects/SkyBox.as @@ -80,7 +80,7 @@ package alternativa.engine3d.objects { private var bottomSurface:Surface; private var topSurface:Surface; - private var size:Number; + private var halfSize:Number; /** * Creates a new SkyBox instance. @@ -95,10 +95,7 @@ package alternativa.engine3d.objects { * @see alternativa.engine3d.materials.Material */ public function SkyBox(size:Number, left:Material = null, right:Material = null, back:Material = null, front:Material = null, bottom:Material = null, top:Material = null, uvPadding:Number = 0) { - - size *= 0.5; - - this.size = size; + this.halfSize = size*0.5; geometry = new Geometry(24); @@ -111,35 +108,35 @@ package alternativa.engine3d.objects { geometry.addVertexStream(attributes); geometry.setAttributeValues(VertexAttributes.POSITION, Vector.([ - -size, -size, size, - -size, -size, -size, - -size, size, -size, - -size, size, size, + -halfSize, -halfSize, halfSize, + -halfSize, -halfSize, -halfSize, + -halfSize, halfSize, -halfSize, + -halfSize, halfSize, halfSize, + + halfSize, halfSize, halfSize, + halfSize, halfSize, -halfSize, + halfSize, -halfSize, -halfSize, + halfSize, -halfSize, halfSize, + + halfSize, -halfSize, halfSize, + halfSize, -halfSize, -halfSize, + -halfSize, -halfSize, -halfSize, + -halfSize, -halfSize, halfSize, - size, size, size, - size, size, -size, - size, -size, -size, - size, -size, size, + -halfSize, halfSize, halfSize, + -halfSize, halfSize, -halfSize, + halfSize, halfSize, -halfSize, + halfSize, halfSize, halfSize, - size, -size, size, - size, -size, -size, - -size, -size, -size, - -size, -size, size, + -halfSize, halfSize, -halfSize, + -halfSize, -halfSize, -halfSize, + halfSize, -halfSize, -halfSize, + halfSize, halfSize, -halfSize, - -size, size, size, - -size, size, -size, - size, size, -size, - size, size, size, - - -size, size, -size, - -size, -size, -size, - size, -size, -size, - size, size, -size, - - -size, -size, size, - -size, size, size, - size, size, size, - size, -size, size + -halfSize, -halfSize, halfSize, + -halfSize, halfSize, halfSize, + halfSize, halfSize, halfSize, + halfSize, -halfSize, halfSize ])); geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], Vector.([ @@ -214,44 +211,44 @@ package alternativa.engine3d.objects { var dy:Number; var dz:Number; var len:Number; - dx = -size - cameraToLocalTransform.d; - dy = -size - cameraToLocalTransform.h; - dz = -size - cameraToLocalTransform.l; + dx = -halfSize - cameraToLocalTransform.d; + dy = -halfSize - cameraToLocalTransform.h; + dz = -halfSize - cameraToLocalTransform.l; len = dx*dx + dy*dy + dz*dz; if (len > max) max = len; - dx = size - cameraToLocalTransform.d; - dy = -size - cameraToLocalTransform.h; - dz = -size - cameraToLocalTransform.l; + dx = halfSize - cameraToLocalTransform.d; + dy = -halfSize - cameraToLocalTransform.h; + dz = -halfSize - cameraToLocalTransform.l; len = dx*dx + dy*dy + dz*dz; if (len > max) max = len; - dx = size - cameraToLocalTransform.d; - dy = size - cameraToLocalTransform.h; - dz = -size - cameraToLocalTransform.l; + dx = halfSize - cameraToLocalTransform.d; + dy = halfSize - cameraToLocalTransform.h; + dz = -halfSize - cameraToLocalTransform.l; len = dx*dx + dy*dy + dz*dz; if (len > max) max = len; - dx = -size - cameraToLocalTransform.d; - dy = size - cameraToLocalTransform.h; - dz = -size - cameraToLocalTransform.l; + dx = -halfSize - cameraToLocalTransform.d; + dy = halfSize - cameraToLocalTransform.h; + dz = -halfSize - cameraToLocalTransform.l; len = dx*dx + dy*dy + dz*dz; if (len > max) max = len; - dx = -size - cameraToLocalTransform.d; - dy = -size - cameraToLocalTransform.h; - dz = size - cameraToLocalTransform.l; + dx = -halfSize - cameraToLocalTransform.d; + dy = -halfSize - cameraToLocalTransform.h; + dz = halfSize - cameraToLocalTransform.l; len = dx*dx + dy*dy + dz*dz; if (len > max) max = len; - dx = size - cameraToLocalTransform.d; - dy = -size - cameraToLocalTransform.h; - dz = size - cameraToLocalTransform.l; + dx = halfSize - cameraToLocalTransform.d; + dy = -halfSize - cameraToLocalTransform.h; + dz = halfSize - cameraToLocalTransform.l; len = dx*dx + dy*dy + dz*dz; if (len > max) max = len; - dx = size - cameraToLocalTransform.d; - dy = size - cameraToLocalTransform.h; - dz = size - cameraToLocalTransform.l; + dx = halfSize - cameraToLocalTransform.d; + dy = halfSize - cameraToLocalTransform.h; + dz = halfSize - cameraToLocalTransform.l; len = dx*dx + dy*dy + dz*dz; if (len > max) max = len; - dx = -size - cameraToLocalTransform.d; - dy = size - cameraToLocalTransform.h; - dz = size - cameraToLocalTransform.l; + dx = -halfSize - cameraToLocalTransform.d; + dy = halfSize - cameraToLocalTransform.h; + dz = halfSize - cameraToLocalTransform.l; len = dx*dx + dy*dy + dz*dz; if (len > max) max = len; drawUnit.setVertexConstantsFromNumbers(0, cameraToLocalTransform.d, cameraToLocalTransform.h, cameraToLocalTransform.l, camera.farClipping/Math.sqrt(max)); diff --git a/src/alternativa/engine3d/resources/BitmapTextureResource.as b/src/alternativa/engine3d/resources/BitmapTextureResource.as index 76a9722..d565db3 100644 --- a/src/alternativa/engine3d/resources/BitmapTextureResource.as +++ b/src/alternativa/engine3d/resources/BitmapTextureResource.as @@ -45,9 +45,9 @@ package alternativa.engine3d.resources { /** * Uploads textures from BitmapData to GPU. */ - public function BitmapTextureResource(data:BitmapData, resizeToPowerOfTwo:Boolean = false) { + public function BitmapTextureResource(data:BitmapData, resizeForGPU:Boolean = false) { this.data = data; - this.resizeForGPU = resizeToPowerOfTwo; + this.resizeForGPU = resizeForGPU; } /** @@ -58,7 +58,6 @@ package alternativa.engine3d.resources { if (data != null) { var source:BitmapData = data; if (resizeForGPU) { - // TODO: test this var wLog2Num:Number = Math.log(data.width)/Math.LN2; var hLog2Num:Number = Math.log(data.height)/Math.LN2; var wLog2:int = Math.ceil(wLog2Num); diff --git a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as index 4c11eb0..5c47afe 100644 --- a/src/alternativa/engine3d/shadows/DirectionalLightShadow.as +++ b/src/alternativa/engine3d/shadows/DirectionalLightShadow.as @@ -43,9 +43,9 @@ package alternativa.engine3d.shadows { use namespace alternativa3d; /** - * Class of shadow, that is created by one source of light(DirectionalLight). Shadow is rendered in fixed volume. + * Class of the shadow, that is created by one source of light(DirectionalLight). Shadow is rendered in fixed volume. * For binding of shadow to light source you need: - * 1) to set DirectionalLightShadow as a value of property shadow of light source; + * 1) to set instance of the DirectionalLightShadow as a value of property shadow of light source; * 2) to add Object3D to corresponding list, using the method addCaster(). * * @see #addCaster() @@ -820,7 +820,7 @@ package alternativa.engine3d.shadows { } /** - * Clears the list of objects, that cast shadow. + * Clears the list of objects, which cast shadow. */ public function clearCasters():void { _casters.length = 0; diff --git a/src/alternativa/engine3d/shadows/OmniLightShadow.as b/src/alternativa/engine3d/shadows/OmniLightShadow.as index 8e0a166..511d5f0 100644 --- a/src/alternativa/engine3d/shadows/OmniLightShadow.as +++ b/src/alternativa/engine3d/shadows/OmniLightShadow.as @@ -1,1085 +1,1099 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - * If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. - * You may add additional accurate notices of copyright ownership. - * - * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ - */ -package alternativa.engine3d.shadows { - - import alternativa.engine3d.alternativa3d; - import alternativa.engine3d.core.BoundBox; - import alternativa.engine3d.core.Camera3D; - import alternativa.engine3d.core.DrawUnit; - import alternativa.engine3d.core.Object3D; - import alternativa.engine3d.core.Renderer; - import alternativa.engine3d.core.Transform3D; - import alternativa.engine3d.core.VertexAttributes; - import alternativa.engine3d.lights.OmniLight; - import alternativa.engine3d.materials.Material; - import alternativa.engine3d.materials.ShaderProgram; - import alternativa.engine3d.materials.TextureMaterial; - import alternativa.engine3d.materials.compiler.Linker; - import alternativa.engine3d.materials.compiler.Procedure; - import alternativa.engine3d.materials.compiler.VariableType; - import alternativa.engine3d.objects.Joint; - import alternativa.engine3d.objects.Mesh; - import alternativa.engine3d.objects.Skin; - import alternativa.engine3d.objects.Surface; - import alternativa.engine3d.primitives.GeoSphere; - import alternativa.engine3d.resources.Geometry; - import alternativa.engine3d.resources.TextureResource; - - import flash.display3D.Context3D; - import flash.display3D.Context3DProgramType; - import flash.display3D.Context3DTextureFormat; - import flash.display3D.Context3DTriangleFace; - import flash.display3D.VertexBuffer3D; - import flash.display3D.textures.CubeTexture; - import flash.utils.Dictionary; - - use namespace alternativa3d; - - public class OmniLightShadow extends Shadow{ - - // TODO: calculate bias automaticaly - /** - * Degree of correcting offset of shadow map space. It need for getting rid of self-shadowing artifacts. - */ - public var biasMultiplier:Number = 0.97; - private static const DIFFERENCE_MULTIPLIER:Number = 32768; - private static const DEBUG_TYPE:String = "Sphere"; // Box - /** - * @private - */ - alternativa3d static var debugRadiusScale:Number = 0.2; - - private var renderer:Renderer = new Renderer(); - - // radius of the light source - private var radius:Number = 100; - - // cube map size - private var _mapSize:Number; - - private var _pcfOffset:Number; - - private var cubeShadowMap:CubeTexture; - - // Sides cameras - private var cameras:Vector. = new Vector.(); - - private var debugObject:Mesh; - private var debugMaterial:ShadowDebugMaterial; - - private var _casters:Vector. = new Vector.(); - - private var actualCasters:Vector. = new Vector.(); - private var actualCastersCount:int; - - // caster -> cube face - private var casterToEdgedCameraTransform:Transform3D = new Transform3D(); - // object -> light - private var objectToLightTransform:Transform3D = new Transform3D(); - // casters count in edge - private var prevActualCastersMask:int; - - private var cachedContext:Context3D; - private var programs:Dictionary = new Dictionary(); - - /** - * Создает экземпляр OmniLightShadow. - * @param mapSize Размер карты теней. Должен быть степенью 2. - * @param pcfOffset Смягчение границ тени. - */ - public function OmniLightShadow(mapSize:int = 128, pcfOffset:Number = 0) { - sections = new SectionPlane(0x11, 0x22, 0xC); // RU - sections.next = new SectionPlane(0x12, 0x21, 0xC); // LU - sections.next.next = new SectionPlane(0x14, 0x28, 0x3); // FU - sections.next.next.next = new SectionPlane(0x18, 0x24, 0x3); // BU - sections.next.next.next.next = new SectionPlane(0x5, 0xA, 0x30); // RF - sections.next.next.next.next.next = new SectionPlane(0x9, 0x6, 0x30); // RB - - this.mapSize = mapSize; - this.pcfOffset = pcfOffset; - - vertexShadowProcedure = getVShader(); - type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; - fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); - - debugMaterial = new ShadowDebugMaterial(); - debugMaterial.alpha = 0.3; - - for (var i:int = 0; i < 6; i++) { - var cam:Camera3D = new Camera3D(radius/1000, radius); - cam.fov = 1.910633237; - cameras[i] = cam; - } - - // Left - cameras[1].rotationY = -Math.PI/2; - cameras[1].scaleY = -1; - cameras[1].composeTransforms(); - // Right - cameras[0].rotationY = Math.PI/2; - cameras[0].scaleY = -1; - cameras[0].composeTransforms(); - // Back - cameras[3].rotationX = -Math.PI/2; - cameras[3].rotationZ = Math.PI; - cameras[3].scaleX = -1; - cameras[3].composeTransforms(); - // Front - cameras[2].rotationX = -Math.PI/2; - cameras[2].scaleY = -1; - cameras[2].composeTransforms(); - // Bottom - cameras[5].rotationX = Math.PI; - cameras[5].scaleX = -1; - cameras[5].composeTransforms(); - // Top - cameras[4].rotationX = 0; - cameras[4].scaleY = -1; - cameras[4].composeTransforms(); - } - - private function createDebugObject(material:Material, context:Context3D):Mesh{ - var geometry:Geometry; - var mesh:Mesh; - if (DEBUG_TYPE == "Box") { - mesh = new Mesh(); - geometry = new Geometry(8); - mesh.geometry = geometry; - - var attributes:Array = new Array(); - attributes[0] = VertexAttributes.POSITION; - attributes[1] = VertexAttributes.POSITION; - attributes[2] = VertexAttributes.POSITION; - geometry.addVertexStream(attributes); - - geometry.setAttributeValues(VertexAttributes.POSITION, Vector.([ - -1, -1, -1, - 1, -1, -1, - 1, 1, -1, - -1, 1, -1, - -1, -1, 1, - 1, -1, 1, - 1, 1, 1, - -1, 1, 1])); - geometry.indices = Vector.([ - 0, 1, 2, 3, 0, 2, 2, 1, 0, 3, 2, 0, - 2, 6, 1, 1, 6, 2, 1, 6, 5, 5, 6, 1, - 6, 4, 5, 5, 4, 6, 6, 4, 7, 7, 4, 6, - 0, 7, 4, 4, 7, 0, 0, 7, 3, 3, 7, 0, - 3, 6, 2, 2, 6, 3, 3, 7, 6, 6, 7, 3, - 0, 5, 1, 1, 5, 0, 0, 4, 5, 5, 4, 0]); - mesh.addSurface(material, 0, 24); - } else { - mesh = new GeoSphere(1, 4, true); - // Create two side - var triangles:Vector. = mesh.geometry.indices; - var numTriangles:int = triangles.length; - for (var i:int = 0; i < numTriangles; i += 3) { - var a:uint = triangles[i]; - var b:uint = triangles[int(i + 1)]; - var c:uint = triangles[int(i + 2)]; - triangles.push(c, b, a); - } - mesh.geometry.indices = triangles; - mesh.getSurface(0).numTriangles = triangles.length/3; - mesh.setMaterialToAllSurfaces(material); - } - mesh.geometry.upload(context); - return mesh; - } - - // Draw in shadow map - override alternativa3d function process(camera:Camera3D):void { - var i:int; - var j:int; - var caster:Object3D; - var context:Context3D = camera.context3D; - - // Checking changed context - if (context != cachedContext) { - programs = new Dictionary(); - cubeShadowMap = null; - cachedContext = context; - } - - // Culling invisible casters - if (cubeShadowMap == null) { - cubeShadowMap = context.createCubeTexture(_mapSize, Context3DTextureFormat.BGRA, true); - debugMaterial.cubeMap = cubeShadowMap; - prevActualCastersMask = 63; - } - - // Calculate parameters - radius = OmniLight(_light).attenuationEnd; - for (i = 0; i < 6; i++) { - var cam:Camera3D = cameras[i]; - cam.nearClipping = radius/1000; - cam.farClipping = radius; - cam.calculateProjection(1, 1); - } - - var castersCount:int = _casters.length; - actualCastersCount = 0; - - for (i = 0; i < castersCount; i++) { - caster = _casters[i]; - - var visible:Boolean = caster.visible; - var parent:Object3D = caster._parent; - while (visible && parent != null) { - visible = parent.visible; - parent = parent._parent; - } - - if (visible) { - // calculate transform matrices - _light.lightToObjectTransform.combine(caster.cameraToLocalTransform, _light.localToCameraTransform); - caster.localToLightTransform.combine(_light.cameraToLocalTransform, caster.localToCameraTransform); - - // collect actualCasters for light - if (caster.boundBox == null || OmniLight(_light).checkBound(caster)){ - actualCasters[actualCastersCount] = caster; - actualCastersCount++; - - // Pack camera culling - caster.culling <<= 16; - if (caster.boundBox != null) { - // 1 - calculate planes in object space - calculatePlanes(caster.localToLightTransform); - // 2 - check object location cameras (sections) - caster.culling |= recognizeObjectCameras(caster.boundBox); - } - } - - // update Skin Joints matrices - var skin:Skin = caster as Skin; - if (skin != null) { - // Calculate joints matrices - for (var child:Object3D = skin.childrenList; child != null; child = child.next) { - if (child.transformChanged) child.composeTransforms(); - // Write transformToSkin matrix to localToGlobalTransform property - child.localToGlobalTransform.copy(child.transform); - if (child is Joint) { - Joint(child).calculateTransform(); - } - skin.calculateJointsTransforms(child); - } - } - - if (caster.childrenList != null) collectActualChildren(caster); - } - } - - // Iterate through six cameras - for (i = 0; i < 6; i++) { - // Cube side camera - var edgeCamera:Camera3D = cameras[i]; - - var edgeBit:int = (1< 0) { - // Настройка параметров рендеринга: - renderer.camera = camera; - context.setRenderToTexture(cubeShadowMap, true, 0, i); - context.clear(1, 0, 0, 0.0); - - // Пробегаемся по кастерам - for (j = 0; j < actualCastersCount; j++) { - caster = actualCasters[j]; - - // Проверить находится ли кастер в зоне 4-х плоскостей - if ((caster.culling & edgeBit)) { - // собираем матрицу перевода из кастера в пространство edgeCamera - casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform); - // Собираем драуколлы для кастера и его дочерних объектов - collectDraws(context, caster, edgeCamera); - } - } - -// if (renderer.drawUnits.length == 0) context.clear(0, 0, 0, 0.0); - - // Drawing - renderer.render(context); - prevActualCastersMask |= edgeBit; - } - else{ - // Если относительно одной из камер ничего не менялось, не вызываем отрисовочный вызов - - if ((prevActualCastersMask & edgeBit)){ - context.setRenderToTexture(cubeShadowMap, false, 0, i); - context.clear(1, 0, 0, 0); - - prevActualCastersMask &= ~edgeBit; - } - } - } - context.setRenderToBackBuffer(); - - // Unpack camera culling value - for (j = 0; j < actualCastersCount; j++) { - caster = actualCasters[j]; - // If there was -1, after shift it will be -1 too - caster.culling >>= 16; - } - - if (debug) { - // Create debug object if needed - if (debugObject == null) { - debugObject = createDebugObject(debugMaterial, camera.context3D); - } - debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius*debugRadiusScale; - debugObject.composeTransforms(); - - // Формируем матрицу трансформации для debugObject - debugObject.localToCameraTransform.combine(_light.localToCameraTransform, debugObject.transform); - - // Отрисовываем - var debugSurface:Surface = debugObject._surfaces[0]; - debugMaterial.collectDraws(camera, debugSurface, debugObject.geometry, null, 0, false, -1); - } - actualCasters.length = 0; - } - - private function collectActualChildren(root:Object3D):void{ - for (var child:Object3D = root.childrenList; child != null; child = child.next) { - if (child.visible){ - // calculate transform matrices - _light.lightToObjectTransform.combine(child.cameraToLocalTransform, _light.localToCameraTransform); - child.localToLightTransform.combine(_light.cameraToLocalTransform, child.localToCameraTransform); - - // collect actualCasters for light - if (child.boundBox == null || OmniLight(_light).checkBound(child)){ - actualCasters[actualCastersCount] = child; - actualCastersCount++; - - // Pack camera culling - child.culling <<= 16; - if (child.boundBox != null) { - // 1 - calculate planes in object space - calculatePlanes(child.localToLightTransform); - // 2 - check object location cameras (sections) - child.culling |= recognizeObjectCameras(child.boundBox); - } - } - - // update Skin Joints matrices - var skin:Skin = child as Skin; - if (skin != null) { - // Calculate joints matrices - for (var skinChild:Object3D = skin.childrenList; skinChild != null; skinChild = skinChild.next) { - if (skinChild.transformChanged) skinChild.composeTransforms(); - // Write transformToSkin matrix to localToGlobalTransform property - skinChild.localToGlobalTransform.copy(skinChild.transform); - if (skinChild is Joint) { - Joint(skinChild).calculateTransform(); - } - skin.calculateJointsTransforms(skinChild); - } - } - - if (child.childrenList != null) collectActualChildren(child); - } - } - } - - private var sections:SectionPlane; - - private function calculatePlanes(transform:Transform3D):void { - // DUBFLR - var planeRU:SectionPlane = sections; - var planeLU:SectionPlane = sections.next; - var planeFU:SectionPlane = sections.next.next; - var planeBU:SectionPlane = sections.next.next.next; - var planeRF:SectionPlane = sections.next.next.next.next; - var planeRB:SectionPlane = sections.next.next.next.next.next; - - // 1, 0, 1 - planeRU.x = transform.a + transform.i; - planeRU.y = transform.b + transform.j; - planeRU.z = transform.c + transform.k; - planeRU.offset = -(transform.d + transform.l); - - // -1, 0, 1 - planeLU.x = transform.i - transform.a; - planeLU.y = transform.j - transform.b; - planeLU.z = transform.k - transform.c; - planeLU.offset = transform.d - transform.l; - - // 0, 1, 1 - planeFU.x = transform.e + transform.i; - planeFU.y = transform.f + transform.j; - planeFU.z = transform.g + transform.k; - planeFU.offset = -(transform.h + transform.l); - - // 0, -1, 1 - planeBU.x = transform.i - transform.e; - planeBU.y = transform.j - transform.f; - planeBU.z = transform.k - transform.g; - planeBU.offset = transform.h - transform.l; - - // 1, 1, 0 - planeRF.x = transform.a + transform.e; - planeRF.y = transform.b + transform.f; - planeRF.z = transform.c + transform.g; - planeRF.offset = -(transform.d + transform.h); - - // 1, -1, 0 - planeRB.x = transform.a - transform.e; - planeRB.y = transform.b - transform.f; - planeRB.z = transform.c - transform.g; - planeRB.offset = transform.h - transform.d; - - // var ax:Number = transform.c - transform.a + transform.b; // E -// var ay:Number = transform.g - transform.e + transform.f; -// var az:Number = transform.k - transform.i + transform.j; -// var bx:Number = transform.c - transform.a - transform.b; // H -// var by:Number = transform.g - transform.e - transform.f; -// var bz:Number = transform.k - transform.i - transform.j; -// planeRU.x = bz * ay - by * az; -// planeRU.y = bx * az - bz * ax; -// planeRU.z = by * ax - bx * ay; -// planeRU.offset = transform.d*planeRU.x + transform.h*planeRU.y + transform.l*planeRU.z; -// -// ax = transform.c + transform.a - transform.b; // D -// ay = transform.g + transform.e - transform.f; -// az = transform.k + transform.i - transform.j; -// bx = transform.c + transform.a + transform.b; // A -// by = transform.g + transform.e + transform.f; -// bz = transform.k + transform.i + transform.j; -// planeLU.x = bz * ay - by * az; -// planeLU.y = bx * az - bz * ax; -// planeLU.z = by * ax - bx * ay; -// planeLU.offset = transform.d*planeLU.x + transform.h*planeLU.y + transform.l*planeLU.z; -// -// ax = transform.c - transform.a - transform.b; // H -// ay = transform.g - transform.e - transform.f; -// az = transform.k - transform.i - transform.j; -// bx = transform.c + transform.a - transform.b; // D -// by = transform.g + transform.e - transform.f; -// bz = transform.k + transform.i - transform.j; -// planeFU.x = bz * ay - by * az; -// planeFU.y = bx * az - bz * ax; -// planeFU.z = by * ax - bx * ay; -// planeFU.offset = transform.d*planeFU.x + transform.h*planeFU.y + transform.l*planeFU.z; -// -// ax = transform.c + transform.a + transform.b; // A -// ay = transform.g + transform.e + transform.f; -// az = transform.k + transform.i + transform.j; -// bx = transform.c - transform.a + transform.b; // E -// by = transform.g - transform.e + transform.f; -// bz = transform.k - transform.i + transform.j; -// planeBU.x = bz * ay - by * az; -// planeBU.y = bx * az - bz * ax; -// planeBU.z = by * ax - bx * ay; -// planeBU.offset = transform.d*planeBU.x + transform.h*planeBU.y + transform.l*planeBU.z; -// -// ax = transform.a - transform.b + transform.c; // D -// ay = transform.e - transform.f + transform.g; -// az = transform.i - transform.j + transform.k; -// bx = transform.a - transform.b - transform.c; // C -// by = transform.e - transform.f - transform.g; -// bz = transform.i - transform.j - transform.k; -// planeRF.x = bz * ay - by * az; -// planeRF.y = bx * az - bz * ax; -// planeRF.z = by * ax - bx * ay; -// planeRF.offset = transform.d*planeRF.x + transform.h*planeRF.y + transform.l*planeRF.z; -// -// ax = transform.a + transform.b - transform.c; // B -// ay = transform.e + transform.f - transform.g; -// az = transform.i + transform.j - transform.k; -// bx = transform.a + transform.b + transform.c; // A -// by = transform.e + transform.f + transform.g; -// bz = transform.i + transform.j + transform.k; -// planeRB.x = bz * ay - by * az; -// planeRB.y = bx * az - bz * ax; -// planeRB.z = by * ax - bx * ay; -// planeRB.offset = transform.d*planeRB.x + transform.h*planeRB.y + transform.l*planeRB.z; - } - - private function recognizeObjectCameras(bb:BoundBox):int { - var culling:int = 63; - for (var plane:SectionPlane = sections; plane != null; plane = plane.next) { - var result:int = 0; - - if (plane.x >= 0) - if (plane.y >= 0) - if (plane.z >= 0) { - if (bb.maxX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras; - if (bb.minX*plane.x + bb.minY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras; - } else { - if (bb.maxX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras; - if (bb.minX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras; - } - else - if (plane.z >= 0) { - if (bb.maxX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras; - if (bb.minX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras; - } else { - if (bb.maxX*plane.x + bb.minY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras; - if (bb.minX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras; - } - else if (plane.y >= 0) - if (plane.z >= 0) { - if (bb.minX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras; - if (bb.maxX*plane.x + bb.minY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras; - } else { - if (bb.minX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras; - if (bb.maxX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras; - } - else if (plane.z >= 0) { - if (bb.minX*plane.x + bb.minY*plane.y + bb.maxZ*plane.z >= plane.offset) result = plane.frontCameras; - if (bb.maxX*plane.x + bb.maxY*plane.y + bb.minZ*plane.z < plane.offset) result |= plane.backCameras; - } else { - if (bb.minX*plane.x + bb.minY*plane.y + bb.minZ*plane.z >= plane.offset) result = plane.frontCameras; - if (bb.maxX*plane.x + bb.maxY*plane.y + bb.maxZ*plane.z < plane.offset) result |= plane.backCameras; - } - culling &= result | plane.unusedBits; - } - return culling; - } - - private function collectDraws(context:Context3D, caster:Object3D, edgeCamera:Camera3D):void{ - // если объект является мешем, собираем для него дроуколы - var mesh:Mesh = caster as Mesh; - if (mesh != null && mesh.geometry != null) { - var program:ShaderProgram; - var programListByTransformProcedure:Vector.; - var skin:Skin = mesh as Skin; - - // пробегаемся по сурфейсам - for (var i:int = 0; i < mesh._surfacesLength; i++) { - var surface:Surface = mesh._surfaces[i]; - if (surface.material == null) continue; - - var material:Material = surface.material; - var geometry:Geometry = mesh.geometry; - var alphaTest:Boolean; - var useDiffuseAlpha:Boolean; - var alphaThreshold:Number; - var materialAlpha:Number; - var diffuse:TextureResource; - var opacity:TextureResource; - var uvBuffer:VertexBuffer3D; - - // ловим параметры прозрачности - if (material is TextureMaterial) { - alphaThreshold = TextureMaterial(material).alphaThreshold; - materialAlpha = TextureMaterial(material).alpha; - diffuse = TextureMaterial(material).diffuseMap; - opacity = TextureMaterial(material).opacityMap; - alphaTest = alphaThreshold > 0; - useDiffuseAlpha = TextureMaterial(material).opacityMap == null; - uvBuffer = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]); - if (uvBuffer == null) continue; - } else { - alphaTest = false; - useDiffuseAlpha = false; - } - - - var positionBuffer:VertexBuffer3D = mesh.geometry.getVertexBuffer(VertexAttributes.POSITION); - if (positionBuffer == null) continue; - - // поднимаем и кэшируем programListByTransformProcedure - if (skin != null) { - caster.transformProcedure = skin.surfaceTransformProcedures[i]; - } - programListByTransformProcedure = programs[caster.transformProcedure]; - if (programListByTransformProcedure == null) { - programListByTransformProcedure = new Vector.(3, true); - programs[caster.transformProcedure] = programListByTransformProcedure; - } - - // собираем программу и Формируем дроуюнит - program = getProgram(caster.transformProcedure, programListByTransformProcedure, context, alphaTest, useDiffuseAlpha); - var drawUnit:DrawUnit = renderer.createDrawUnit(caster, program.program, mesh.geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program); - drawUnit.culling = Context3DTriangleFace.BACK; - - // Установка стрима - drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, mesh.geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]); - - if (alphaTest) { - drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV"), uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]); - drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, materialAlpha); - if (useDiffuseAlpha) { - drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sTexture"), diffuse._texture); - } else { - drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sTexture"), opacity._texture); - } - } - - // Установка констант - caster.setTransformConstants(drawUnit, surface, program.vertexShader, null); - drawUnit.setProjectionConstants(edgeCamera, program.vertexShader.getVariableIndex("cProjMatrix"), casterToEdgedCameraTransform); - drawUnit.setVertexConstantsFromTransform(program.vertexShader.getVariableIndex("cCasterToOmni"), caster.localToLightTransform); - - drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cConstants"), 1 / 255, 0, 255/radius, 1); - - renderer.addDrawUnit(drawUnit, Renderer.OPAQUE); - } - } - } - - /** - * @private - * Процедура для передачи UV координат во фрагментный шейдер - */ - static private const passUVProcedure:Procedure = new Procedure(["#v0=vUV", "#a0=aUV", "mov v0, a0"], "passUVProcedure"); - - // diffuse alpha test - private static const diffuseAlphaTestProcedure:Procedure = new Procedure([ - "#v0=vUV", - "#s0=sTexture", - "#c0=cThresholdAlpha", - "tex t0, v0, s0 <2d, linear,repeat, miplinear>", - "mul t0.w, t0.w, c0.w", - "sub t0.w, t0.w, c0.x", - "kil t0.w" - ], "diffuseAlphaTestProcedure"); - - // opacity alpha test - private static const opacityAlphaTestProcedure:Procedure = new Procedure([ - "#v0=vUV", - "#s0=sTexture", - "#c0=cThresholdAlpha", - "tex t0, v0, s0 <2d, linear,repeat, miplinear>", - "mul t0.w, t0.x, c0.w", - "sub t0.w, t0.w, c0.x", - "kil t0.w" - ], "opacityAlphaTestProcedure"); - - - private function getProgram(transformProcedure:Procedure, programListByTransformProcedure:Vector., context:Context3D, alphaTest:Boolean, useDiffuseAlpha:Boolean):ShaderProgram { - var key:int = (alphaTest ? (useDiffuseAlpha ? 1 : 2) : 0); - var program:ShaderProgram = programListByTransformProcedure[key]; - - if (program == null) { - var vLinker:Linker = new Linker(Context3DProgramType.VERTEX); - var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); - - var positionVar:String = "aPosition"; - vLinker.declareVariable(positionVar, VariableType.ATTRIBUTE); - - if (alphaTest) { - vLinker.addProcedure(passUVProcedure); - } - - if (transformProcedure != null) { - var newPosVar:String = "tTransformedPosition"; - vLinker.declareVariable(newPosVar); - vLinker.addProcedure(transformProcedure, positionVar); - vLinker.setOutputParams(transformProcedure, newPosVar); - positionVar = newPosVar; - } - - var proc:Procedure = Procedure.compileFromArray([ - "#v0=vDistance", - - "m34 t0.xyz, i0, c2", - "mov v0, t0.xyzx", - - "m44 o0, i0, c0" - ]); - proc.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4); - proc.assignVariableName(VariableType.CONSTANT, 2, "cCasterToOmni", 3); - - vLinker.addProcedure(proc, positionVar); - - if (alphaTest) { - if (useDiffuseAlpha) { - fLinker.addProcedure(diffuseAlphaTestProcedure); - } else { - fLinker.addProcedure(opacityAlphaTestProcedure); - } - } - fLinker.addProcedure(Procedure.compileFromArray([ - "#v0=vDistance", // xyz - "#c0=cConstants", // 1/255, 0, 255/radius, 1 - // calculate distance - "dp3 t0.z, v0.xyz, v0.xyz", - "sqt t0.z, t0.z", // x: [0, radius] - "mul t0.z, t0.z, c0.z", // x: [0, 255] - // codeing - "frc t0.y, t0.z", - "sub t0.x, t0.z, t0.y", - "mul t0.x, t0.x, c0.x", - - "mov t0.w, c0.w", - "mov o0, t0" - ])); - program = new ShaderProgram(vLinker, fLinker); - fLinker.varyings = vLinker.varyings; - programListByTransformProcedure[key] = program; - program.upload(context); - - } - return program; - } - - - - //------------- ShadowMap Shader in material---------- - - /** - * @private - */ - alternativa3d override function setup(drawUnit:DrawUnit, vertexLinker:Linker, fragmentLinker:Linker, surface:Surface):void { - // Устанавливаем матрицу перевода в шедоумапу - objectToLightTransform.combine(_light.cameraToLocalTransform, surface.object.localToCameraTransform); - drawUnit.setVertexConstantsFromTransform(vertexLinker.getVariableIndex("cObjectToLightTransform"), objectToLightTransform); - - // Устанавливаем шедоумапу - drawUnit.setTextureAt(fragmentLinker.getVariableIndex("sCubeMap"), cubeShadowMap); - - // Устанавливаем коеффициенты - if (_pcfOffset > 0) { - var offset:Number = Math.tan(_pcfOffset/180*Math.PI)/3; - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -3/2, 1/16, 0, 0); - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset); - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDecode"), -DIFFERENCE_MULTIPLIER, -DIFFERENCE_MULTIPLIER/255, biasMultiplier*DIFFERENCE_MULTIPLIER/radius, 10); - } else { - drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -DIFFERENCE_MULTIPLIER, -DIFFERENCE_MULTIPLIER/255, biasMultiplier*DIFFERENCE_MULTIPLIER/radius, 1.0); - } - } - - private static function getVShader():Procedure { - var shader:Procedure = Procedure.compileFromArray([ - "#v0=vSample", - - "m34 t0.xyz, i0, c0", - - "mov v0, t0.xyz" - ], "OmniShadowMapVertex"); - shader.assignVariableName(VariableType.CONSTANT, 0, "cObjectToLightTransform", 3); - return shader; - } - - private static function getFShader():Procedure { - var shaderArr:Array = [ - "#v0=vSample", - "#c0=cConstants", - "#s0=sCubeMap" - ]; - var line:int = 3; - // Расстояние - shaderArr[line++] = "dp3 t0.z, v0.xyz, v0.xyz"; - shaderArr[line++] = "sqt t0.z, t0.z"; // w: [0, radius] - shaderArr[line++] = "tex t0.xy, v0, s0 "; - shaderArr[line++] = "dp3 t0.x, t0.xyz, c0.xyz"; // декодируем, находим разницу между расстояниями и умножаем ее на большое число - - // рассчитываем значение тени - shaderArr[line++] = "sat t0.x, t0.x"; - shaderArr[line++] = "sub o0, c0.w, t0.x"; - -// shaderArr[line++] = "sat t0.x, t0.x"; -// shaderArr[line++] = "sub t0.x, c0.w, t0.x"; -// shaderArr[line++] = "sat t0.x, t0.x"; -// shaderArr[line++] = "mov o0, t0.x"; - - return Procedure.compileFromArray(shaderArr, "OmniShadowMapFragment"); - } - - private static function getFShaderPCF():Procedure { - var shaderArr:Array = [ - "#v0=vSample", - "#c0=cDecode", - "#c1=cConstants", - "#c2=cPCFOffsets", - "#s0=sCubeMap" - ]; - var line:int = 5; - var i:int; - var j:int; - - // допустимо использование временных переменных t0 t1 t2 t3 - // v0 - sample - - // calculate 2 ortogonal vectors - // (-y, x, 0) - shaderArr[line++] = "mov t1.xyzw, v0.yxzw"; - shaderArr[line++] = "mul t1.xyzw, t1.xyzw, c1.xyzz"; - - shaderArr[line++] = "crs t0.xyz, v0.xyz, t1.xyz"; - - // normalize vectors - shaderArr[line++] = "nrm t0.xyz, t0.xyz"; - shaderArr[line++] = "nrm t1.xyz, t1.xyz"; - - shaderArr[line++] = "dp3 t3.z, v0.xyz, v0.xyz"; - shaderArr[line++] = "sqt t3.z, t3.z"; // distance - - // apply pcf offset - shaderArr[line++] = "mul t0.w, c1.w, t3.z"; // с1.w = offset/radius - shaderArr[line++] = "mul t0.xyz, t0.xyz, t0.w"; - shaderArr[line++] = "mul t1.xyz, t1.xyz, t0.w"; - // --------- {13 opcode} - - // t0, t1 - ortogonals ↑→ - // t2 - current vector - - // t3.z distance to object - // t3.xy - result from shadow map - // t3.w - summ of sat - - // first point - shaderArr[line++] = "add t2.xyz, t0.xyz, t1.xyz"; - shaderArr[line++] = "mul t2.xyz, t2.xyz, c2.xxx"; - shaderArr[line++] = "add t2.xyz, t2.xyz, v0.xyz"; - - // получаем длинну из шадоумапы [0, 1] -// shaderArr[line++] = "mov t3.z, t0.w"; - - shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; - shaderArr[line++] = "dp3 o0." +componentByIndex[0] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число - - //----- - - for (j = 1; j < 4; j++) { - shaderArr[line++] = "add t2.xyz, t2.xyz, t1.xyz"; - - shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; - shaderArr[line++] = "dp3 o0." +componentByIndex[j] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число - } - - shaderArr[line++] = "sat o0, o0"; - shaderArr[line++] = "dp4 t3.w, o0, c2.y"; - - //----- - - for (i = 0; i < 3; i++) { - shaderArr[line++] = "add t2.xyz, t2.xyz, t0.xyz"; - - shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; - shaderArr[line++] = "dp3 o0." +componentByIndex[0] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число - - for (j = 1; j < 4; j++){ - shaderArr[line++] = (i%2 == 1)?("add t2.xyz, t2.xyz, t1.xyz"):("sub t2.xyz, t2.xyz, t1.xyz"); - - shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; - shaderArr[line++] = "dp3 o0." +componentByIndex[j] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число - } - shaderArr[line++] = "sat o0, o0"; - shaderArr[line++] = "dp4 o0.x, o0, c2.y"; - shaderArr[line++] = "add t3.w, t3.w, o0.x"; - } - - shaderArr[line++] = "sub o0, c1.y, t3.w"; - - //--------- {73 opcodes} - return Procedure.compileFromArray(shaderArr, "OmniShadowMapFragment"); - } - - private static const componentByIndex:Array = ["x", "y", "z", "w"]; - - /** - * Добавляет object в список объектов, отбрасывающих тень. - * @param object Добавляемый объект. - */ - public function addCaster(object:Object3D):void { - if (_casters.indexOf(object) < 0) { - _casters.push(object); - } - } - - public function removeCaster(object:Object3D):void { - var index:int = _casters.indexOf(object); - if (index < 0) throw new Error("Caster not found"); - _casters[index] = _casters.pop(); - } - - /** - * Очищает список объектов, отбрасывающих тень. - */ - public function clearCasters():void { - _casters.length = 0; - } - - /** - * Качество тени. Задает разрешение shadowmap. Может принимать значения от 2 до 11. - */ - public function get mapSize():int { - return _mapSize; - } - - /** - * @private - */ - public function set mapSize(value:int):void { - if (value != _mapSize) { - this._mapSize = value; - if (value < 2) { - throw new ArgumentError("Map size cannot be less than 2."); - } else if (value > 1024) { - throw new ArgumentError("Map size exceeds maximum value 1024."); - } - if ((Math.log(value)/Math.LN2 % 1) != 0) { - throw new ArgumentError("Map size must be power of two."); - } - if (cubeShadowMap != null) { - cubeShadowMap.dispose(); - } - cubeShadowMap = null; - } - } - - /** - * Смещение Percentage Closer Filtering. Этот способ фильтрации используется для смягчения границ тени. - * 1 pcfOffset equivalent 1 degree for all blur - */ - public function get pcfOffset():Number { - return _pcfOffset; - } - - /** - * @private - */ - public function set pcfOffset(value:Number):void { - _pcfOffset = value; - type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; - fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); - } - - } -} - -import alternativa.engine3d.alternativa3d; -import alternativa.engine3d.core.Camera3D; -import alternativa.engine3d.core.DrawUnit; -import alternativa.engine3d.core.Light3D; -import alternativa.engine3d.core.Object3D; -import alternativa.engine3d.core.Renderer; -import alternativa.engine3d.core.VertexAttributes; -import alternativa.engine3d.materials.Material; -import alternativa.engine3d.materials.ShaderProgram; -import alternativa.engine3d.materials.compiler.Linker; -import alternativa.engine3d.materials.compiler.Procedure; -import alternativa.engine3d.materials.compiler.VariableType; -import alternativa.engine3d.objects.Surface; -import alternativa.engine3d.resources.Geometry; - -import flash.display3D.Context3D; -import flash.display3D.Context3DBlendFactor; -import flash.display3D.Context3DProgramType; -import flash.display3D.VertexBuffer3D; -import flash.display3D.textures.CubeTexture; -import flash.utils.Dictionary; - -class ShadowDebugMaterial extends Material { - - use namespace alternativa3d; - /** - * Прозрачность. - * Является дополнительным множителем к прозрачности текстуры. - * Значение по умолчанию 1. - */ - alternativa3d var alpha:Number = 1; - - private var cachedContext3D:Context3D; - private static var caches:Dictionary = new Dictionary(true); - private var program:ShaderProgram; - - /** - * Текстура. - */ - alternativa3d var cubeMap:CubeTexture; - - /** - * @private - */ - override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { - var object:Object3D = surface.object; - // Стримы - var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION); - // Проверка на валидность - if (positionBuffer == null) return; - - // Обновляем кеш программы для данного контекста - if (camera.context3D != cachedContext3D) { - cachedContext3D = camera.context3D; - program = caches[cachedContext3D]; - } - - if (program == null) { - program = setupProgram(object); - program.upload(camera.context3D); - caches[cachedContext3D] = program; - } - - // Создание отрисовочного вызова - var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program); - // Установка стримов - drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]); - // Установка констант - drawUnit.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform); - drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cDecode"), 1, 1/255, 0, alpha); - drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sCubeMap"), cubeMap); - - // Отправка на отрисовку - if (alpha < 1) { - drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA; - drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA; - camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT); - } else { - camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE); - } - } - - private function copyDrawUnit(source:DrawUnit, dest:DrawUnit):void { - - } - - private function setupProgram(object:Object3D):ShaderProgram { - var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX); - var positionVar:String = "aPosition"; - vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE); - - var proc:Procedure = Procedure.compileFromArray([ - "#v0=vCubeMapCoord", - "mov v0, i0", - "m44 o0, i0, c0" - ]); - proc.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4); - vertexLinker.addProcedure(proc, positionVar); - - var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); - var colorProc:Procedure = Procedure.compileFromArray([ - "#v0=vCubeMapCoord", - "#s0=sCubeMap", - "#c0=cDecode", - - "tex t0.xy, v0, s0 ", - "dp3 t0.xyz, t0.xy, c0.xy", - "mov t0.w, c0.w", - "mov o0, t0" - ]); - fragmentLinker.addProcedure(colorProc, "vCubeMapCoord"); - fragmentLinker.varyings = vertexLinker.varyings; - return new ShaderProgram(vertexLinker, fragmentLinker); - } - -} - -class SectionPlane { - - public var x:Number = 0; - public var y:Number = 0; - public var z:Number = 0; - public var offset:Number = 0; - - public var next:SectionPlane; - - public var frontCameras:int; - public var backCameras:int; - public var unusedBits:int = 63; - - public function SectionPlane(frontCameras:int, backCameras:int, unused:int) { - this.frontCameras = frontCameras; - this.backCameras = backCameras; - this.unusedBits = unused; - } - -} +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + * If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. + * You may add additional accurate notices of copyright ownership. + * + * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ + */ +package alternativa.engine3d.shadows { + + import alternativa.engine3d.alternativa3d; + import alternativa.engine3d.core.BoundBox; + import alternativa.engine3d.core.Camera3D; + import alternativa.engine3d.core.DrawUnit; + import alternativa.engine3d.core.Object3D; + import alternativa.engine3d.core.Renderer; + import alternativa.engine3d.core.Transform3D; + import alternativa.engine3d.core.VertexAttributes; + import alternativa.engine3d.lights.OmniLight; + import alternativa.engine3d.materials.Material; + import alternativa.engine3d.materials.ShaderProgram; + import alternativa.engine3d.materials.TextureMaterial; + import alternativa.engine3d.materials.compiler.Linker; + import alternativa.engine3d.materials.compiler.Procedure; + import alternativa.engine3d.materials.compiler.VariableType; + import alternativa.engine3d.objects.Joint; + import alternativa.engine3d.objects.Mesh; + import alternativa.engine3d.objects.Skin; + import alternativa.engine3d.objects.Surface; + import alternativa.engine3d.primitives.GeoSphere; + import alternativa.engine3d.resources.Geometry; + import alternativa.engine3d.resources.TextureResource; + + import flash.display3D.Context3D; + import flash.display3D.Context3DProgramType; + import flash.display3D.Context3DTextureFormat; + import flash.display3D.Context3DTriangleFace; + import flash.display3D.VertexBuffer3D; + import flash.display3D.textures.CubeTexture; + import flash.utils.Dictionary; + + use namespace alternativa3d; + + /** + * Class of the shadow, that is created by one source of light(OmniLight). Shadow is rendered in fixed volume. + * For binding of shadow to light source you need: + * 1) to set instance of the OmniLight as a value of property shadow of light source; + * 2) to add Object3D to corresponding list, using the method addCaster(). + * + * @see #addCaster() + * @see alternativa.engine3d.lights.OmniLight#shadow + * @see #farBoundPosition + */ + + public class OmniLightShadow extends Shadow { + + // TODO: calculate bias automaticaly + /** + * Degree of correcting offset of shadow map space. It need for getting rid of self-shadowing artifacts. + */ + public var biasMultiplier:Number = 0.97; + private static const DIFFERENCE_MULTIPLIER:Number = 32768; + private static const DEBUG_TYPE:String = "Sphere"; // Box + /** + * @private + */ + alternativa3d static var debugRadiusScale:Number = 0.2; + + private var renderer:Renderer = new Renderer(); + + // radius of the light source + private var radius:Number = 100; + + // cube map size + private var _mapSize:Number; + + private var _pcfOffset:Number; + + private var cubeShadowMap:CubeTexture; + + // Sides cameras + private var cameras:Vector. = new Vector.(); + + private var debugObject:Mesh; + private var debugMaterial:ShadowDebugMaterial; + + private var _casters:Vector. = new Vector.(); + + private var actualCasters:Vector. = new Vector.(); + private var actualCastersCount:int; + + // caster -> cube face + private var casterToEdgedCameraTransform:Transform3D = new Transform3D(); + // object -> light + private var objectToLightTransform:Transform3D = new Transform3D(); + // casters count in edge + private var prevActualCastersMask:int; + + private var cachedContext:Context3D; + private var programs:Dictionary = new Dictionary(); + + /** + * Создает экземпляр OmniLightShadow. + * @param mapSize Размер карты теней. Должен быть степенью 2. + * @param pcfOffset Смягчение границ тени. + */ + public function OmniLightShadow(mapSize:int = 128, pcfOffset:Number = 0) { + sections = new SectionPlane(0x11, 0x22, 0xC); // RU + sections.next = new SectionPlane(0x12, 0x21, 0xC); // LU + sections.next.next = new SectionPlane(0x14, 0x28, 0x3); // FU + sections.next.next.next = new SectionPlane(0x18, 0x24, 0x3); // BU + sections.next.next.next.next = new SectionPlane(0x5, 0xA, 0x30); // RF + sections.next.next.next.next.next = new SectionPlane(0x9, 0x6, 0x30); // RB + + this.mapSize = mapSize; + this.pcfOffset = pcfOffset; + + vertexShadowProcedure = getVShader(); + type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; + fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); + + debugMaterial = new ShadowDebugMaterial(); + debugMaterial.alpha = 0.3; + + for (var i:int = 0; i < 6; i++) { + var cam:Camera3D = new Camera3D(radius / 1000, radius); + cam.fov = 1.910633237; + cameras[i] = cam; + } + + // Left + cameras[1].rotationY = -Math.PI / 2; + cameras[1].scaleY = -1; + cameras[1].composeTransforms(); + // Right + cameras[0].rotationY = Math.PI / 2; + cameras[0].scaleY = -1; + cameras[0].composeTransforms(); + // Back + cameras[3].rotationX = -Math.PI / 2; + cameras[3].rotationZ = Math.PI; + cameras[3].scaleX = -1; + cameras[3].composeTransforms(); + // Front + cameras[2].rotationX = -Math.PI / 2; + cameras[2].scaleY = -1; + cameras[2].composeTransforms(); + // Bottom + cameras[5].rotationX = Math.PI; + cameras[5].scaleX = -1; + cameras[5].composeTransforms(); + // Top + cameras[4].rotationX = 0; + cameras[4].scaleY = -1; + cameras[4].composeTransforms(); + } + + private function createDebugObject(material:Material, context:Context3D):Mesh { + var geometry:Geometry; + var mesh:Mesh; + if (DEBUG_TYPE == "Box") { + mesh = new Mesh(); + geometry = new Geometry(8); + mesh.geometry = geometry; + + var attributes:Array = new Array(); + attributes[0] = VertexAttributes.POSITION; + attributes[1] = VertexAttributes.POSITION; + attributes[2] = VertexAttributes.POSITION; + geometry.addVertexStream(attributes); + + geometry.setAttributeValues(VertexAttributes.POSITION, Vector.([ + -1, -1, -1, + 1, -1, -1, + 1, 1, -1, + -1, 1, -1, + -1, -1, 1, + 1, -1, 1, + 1, 1, 1, + -1, 1, 1])); + geometry.indices = Vector.([ + 0, 1, 2, 3, 0, 2, 2, 1, 0, 3, 2, 0, + 2, 6, 1, 1, 6, 2, 1, 6, 5, 5, 6, 1, + 6, 4, 5, 5, 4, 6, 6, 4, 7, 7, 4, 6, + 0, 7, 4, 4, 7, 0, 0, 7, 3, 3, 7, 0, + 3, 6, 2, 2, 6, 3, 3, 7, 6, 6, 7, 3, + 0, 5, 1, 1, 5, 0, 0, 4, 5, 5, 4, 0]); + mesh.addSurface(material, 0, 24); + } else { + mesh = new GeoSphere(1, 4, true); + // Create two side + var triangles:Vector. = mesh.geometry.indices; + var numTriangles:int = triangles.length; + for (var i:int = 0; i < numTriangles; i += 3) { + var a:uint = triangles[i]; + var b:uint = triangles[int(i + 1)]; + var c:uint = triangles[int(i + 2)]; + triangles.push(c, b, a); + } + mesh.geometry.indices = triangles; + mesh.getSurface(0).numTriangles = triangles.length / 3; + mesh.setMaterialToAllSurfaces(material); + } + mesh.geometry.upload(context); + return mesh; + } + + // Draw in shadow map + override alternativa3d function process(camera:Camera3D):void { + var i:int; + var j:int; + var caster:Object3D; + var context:Context3D = camera.context3D; + + // Checking changed context + if (context != cachedContext) { + programs = new Dictionary(); + cubeShadowMap = null; + cachedContext = context; + } + + // Culling invisible casters + if (cubeShadowMap == null) { + cubeShadowMap = context.createCubeTexture(_mapSize, Context3DTextureFormat.BGRA, true); + debugMaterial.cubeMap = cubeShadowMap; + prevActualCastersMask = 63; + } + + // Calculate parameters + radius = OmniLight(_light).attenuationEnd; + for (i = 0; i < 6; i++) { + var cam:Camera3D = cameras[i]; + cam.nearClipping = radius / 1000; + cam.farClipping = radius; + cam.calculateProjection(1, 1); + } + + var castersCount:int = _casters.length; + actualCastersCount = 0; + + for (i = 0; i < castersCount; i++) { + caster = _casters[i]; + + var visible:Boolean = caster.visible; + var parent:Object3D = caster._parent; + while (visible && parent != null) { + visible = parent.visible; + parent = parent._parent; + } + + if (visible) { + // calculate transform matrices + _light.lightToObjectTransform.combine(caster.cameraToLocalTransform, _light.localToCameraTransform); + caster.localToLightTransform.combine(_light.cameraToLocalTransform, caster.localToCameraTransform); + + // collect actualCasters for light + if (caster.boundBox == null || OmniLight(_light).checkBound(caster)) { + actualCasters[actualCastersCount] = caster; + actualCastersCount++; + + // Pack camera culling + caster.culling <<= 16; + if (caster.boundBox != null) { + // 1 - calculate planes in object space + calculatePlanes(caster.localToLightTransform); + // 2 - check object location cameras (sections) + caster.culling |= recognizeObjectCameras(caster.boundBox); + } + } + + // update Skin Joints matrices + var skin:Skin = caster as Skin; + if (skin != null) { + // Calculate joints matrices + for (var child:Object3D = skin.childrenList; child != null; child = child.next) { + if (child.transformChanged) child.composeTransforms(); + // Write transformToSkin matrix to localToGlobalTransform property + child.localToGlobalTransform.copy(child.transform); + if (child is Joint) { + Joint(child).calculateTransform(); + } + skin.calculateJointsTransforms(child); + } + } + + if (caster.childrenList != null) collectActualChildren(caster); + } + } + + // Iterate through six cameras + for (i = 0; i < 6; i++) { + // Cube side camera + var edgeCamera:Camera3D = cameras[i]; + + var edgeBit:int = (1 << i); + if (actualCastersCount > 0) { + // Настройка параметров рендеринга: + renderer.camera = camera; + context.setRenderToTexture(cubeShadowMap, true, 0, i); + context.clear(1, 0, 0, 0.0); + + // Пробегаемся по кастерам + for (j = 0; j < actualCastersCount; j++) { + caster = actualCasters[j]; + + // Проверить находится ли кастер в зоне 4-х плоскостей + if ((caster.culling & edgeBit)) { + // собираем матрицу перевода из кастера в пространство edgeCamera + casterToEdgedCameraTransform.combine(edgeCamera.inverseTransform, caster.localToLightTransform); + // Собираем драуколлы для кастера и его дочерних объектов + collectDraws(context, caster, edgeCamera); + } + } + +// if (renderer.drawUnits.length == 0) context.clear(0, 0, 0, 0.0); + + // Drawing + renderer.render(context); + prevActualCastersMask |= edgeBit; + } + else { + // Если относительно одной из камер ничего не менялось, не вызываем отрисовочный вызов + + if ((prevActualCastersMask & edgeBit)) { + context.setRenderToTexture(cubeShadowMap, false, 0, i); + context.clear(1, 0, 0, 0); + + prevActualCastersMask &= ~edgeBit; + } + } + } + context.setRenderToBackBuffer(); + + // Unpack camera culling value + for (j = 0; j < actualCastersCount; j++) { + caster = actualCasters[j]; + // If there was -1, after shift it will be -1 too + caster.culling >>= 16; + } + + if (debug) { + // Create debug object if needed + if (debugObject == null) { + debugObject = createDebugObject(debugMaterial, camera.context3D); + } + debugObject.scaleX = debugObject.scaleY = debugObject.scaleZ = radius * debugRadiusScale; + debugObject.composeTransforms(); + + // Формируем матрицу трансформации для debugObject + debugObject.localToCameraTransform.combine(_light.localToCameraTransform, debugObject.transform); + + // Отрисовываем + var debugSurface:Surface = debugObject._surfaces[0]; + debugMaterial.collectDraws(camera, debugSurface, debugObject.geometry, null, 0, false, -1); + } + actualCasters.length = 0; + } + + private function collectActualChildren(root:Object3D):void { + for (var child:Object3D = root.childrenList; child != null; child = child.next) { + if (child.visible) { + // calculate transform matrices + _light.lightToObjectTransform.combine(child.cameraToLocalTransform, _light.localToCameraTransform); + child.localToLightTransform.combine(_light.cameraToLocalTransform, child.localToCameraTransform); + + // collect actualCasters for light + if (child.boundBox == null || OmniLight(_light).checkBound(child)) { + actualCasters[actualCastersCount] = child; + actualCastersCount++; + + // Pack camera culling + child.culling <<= 16; + if (child.boundBox != null) { + // 1 - calculate planes in object space + calculatePlanes(child.localToLightTransform); + // 2 - check object location cameras (sections) + child.culling |= recognizeObjectCameras(child.boundBox); + } + } + + // update Skin Joints matrices + var skin:Skin = child as Skin; + if (skin != null) { + // Calculate joints matrices + for (var skinChild:Object3D = skin.childrenList; skinChild != null; skinChild = skinChild.next) { + if (skinChild.transformChanged) skinChild.composeTransforms(); + // Write transformToSkin matrix to localToGlobalTransform property + skinChild.localToGlobalTransform.copy(skinChild.transform); + if (skinChild is Joint) { + Joint(skinChild).calculateTransform(); + } + skin.calculateJointsTransforms(skinChild); + } + } + + if (child.childrenList != null) collectActualChildren(child); + } + } + } + + private var sections:SectionPlane; + + private function calculatePlanes(transform:Transform3D):void { + // DUBFLR + var planeRU:SectionPlane = sections; + var planeLU:SectionPlane = sections.next; + var planeFU:SectionPlane = sections.next.next; + var planeBU:SectionPlane = sections.next.next.next; + var planeRF:SectionPlane = sections.next.next.next.next; + var planeRB:SectionPlane = sections.next.next.next.next.next; + + // 1, 0, 1 + planeRU.x = transform.a + transform.i; + planeRU.y = transform.b + transform.j; + planeRU.z = transform.c + transform.k; + planeRU.offset = -(transform.d + transform.l); + + // -1, 0, 1 + planeLU.x = transform.i - transform.a; + planeLU.y = transform.j - transform.b; + planeLU.z = transform.k - transform.c; + planeLU.offset = transform.d - transform.l; + + // 0, 1, 1 + planeFU.x = transform.e + transform.i; + planeFU.y = transform.f + transform.j; + planeFU.z = transform.g + transform.k; + planeFU.offset = -(transform.h + transform.l); + + // 0, -1, 1 + planeBU.x = transform.i - transform.e; + planeBU.y = transform.j - transform.f; + planeBU.z = transform.k - transform.g; + planeBU.offset = transform.h - transform.l; + + // 1, 1, 0 + planeRF.x = transform.a + transform.e; + planeRF.y = transform.b + transform.f; + planeRF.z = transform.c + transform.g; + planeRF.offset = -(transform.d + transform.h); + + // 1, -1, 0 + planeRB.x = transform.a - transform.e; + planeRB.y = transform.b - transform.f; + planeRB.z = transform.c - transform.g; + planeRB.offset = transform.h - transform.d; + + // var ax:Number = transform.c - transform.a + transform.b; // E +// var ay:Number = transform.g - transform.e + transform.f; +// var az:Number = transform.k - transform.i + transform.j; +// var bx:Number = transform.c - transform.a - transform.b; // H +// var by:Number = transform.g - transform.e - transform.f; +// var bz:Number = transform.k - transform.i - transform.j; +// planeRU.x = bz * ay - by * az; +// planeRU.y = bx * az - bz * ax; +// planeRU.z = by * ax - bx * ay; +// planeRU.offset = transform.d*planeRU.x + transform.h*planeRU.y + transform.l*planeRU.z; +// +// ax = transform.c + transform.a - transform.b; // D +// ay = transform.g + transform.e - transform.f; +// az = transform.k + transform.i - transform.j; +// bx = transform.c + transform.a + transform.b; // A +// by = transform.g + transform.e + transform.f; +// bz = transform.k + transform.i + transform.j; +// planeLU.x = bz * ay - by * az; +// planeLU.y = bx * az - bz * ax; +// planeLU.z = by * ax - bx * ay; +// planeLU.offset = transform.d*planeLU.x + transform.h*planeLU.y + transform.l*planeLU.z; +// +// ax = transform.c - transform.a - transform.b; // H +// ay = transform.g - transform.e - transform.f; +// az = transform.k - transform.i - transform.j; +// bx = transform.c + transform.a - transform.b; // D +// by = transform.g + transform.e - transform.f; +// bz = transform.k + transform.i - transform.j; +// planeFU.x = bz * ay - by * az; +// planeFU.y = bx * az - bz * ax; +// planeFU.z = by * ax - bx * ay; +// planeFU.offset = transform.d*planeFU.x + transform.h*planeFU.y + transform.l*planeFU.z; +// +// ax = transform.c + transform.a + transform.b; // A +// ay = transform.g + transform.e + transform.f; +// az = transform.k + transform.i + transform.j; +// bx = transform.c - transform.a + transform.b; // E +// by = transform.g - transform.e + transform.f; +// bz = transform.k - transform.i + transform.j; +// planeBU.x = bz * ay - by * az; +// planeBU.y = bx * az - bz * ax; +// planeBU.z = by * ax - bx * ay; +// planeBU.offset = transform.d*planeBU.x + transform.h*planeBU.y + transform.l*planeBU.z; +// +// ax = transform.a - transform.b + transform.c; // D +// ay = transform.e - transform.f + transform.g; +// az = transform.i - transform.j + transform.k; +// bx = transform.a - transform.b - transform.c; // C +// by = transform.e - transform.f - transform.g; +// bz = transform.i - transform.j - transform.k; +// planeRF.x = bz * ay - by * az; +// planeRF.y = bx * az - bz * ax; +// planeRF.z = by * ax - bx * ay; +// planeRF.offset = transform.d*planeRF.x + transform.h*planeRF.y + transform.l*planeRF.z; +// +// ax = transform.a + transform.b - transform.c; // B +// ay = transform.e + transform.f - transform.g; +// az = transform.i + transform.j - transform.k; +// bx = transform.a + transform.b + transform.c; // A +// by = transform.e + transform.f + transform.g; +// bz = transform.i + transform.j + transform.k; +// planeRB.x = bz * ay - by * az; +// planeRB.y = bx * az - bz * ax; +// planeRB.z = by * ax - bx * ay; +// planeRB.offset = transform.d*planeRB.x + transform.h*planeRB.y + transform.l*planeRB.z; + } + + private function recognizeObjectCameras(bb:BoundBox):int { + var culling:int = 63; + for (var plane:SectionPlane = sections; plane != null; plane = plane.next) { + var result:int = 0; + + if (plane.x >= 0) + if (plane.y >= 0) + if (plane.z >= 0) { + if (bb.maxX * plane.x + bb.maxY * plane.y + bb.maxZ * plane.z >= plane.offset) result = plane.frontCameras; + if (bb.minX * plane.x + bb.minY * plane.y + bb.minZ * plane.z < plane.offset) result |= plane.backCameras; + } else { + if (bb.maxX * plane.x + bb.maxY * plane.y + bb.minZ * plane.z >= plane.offset) result = plane.frontCameras; + if (bb.minX * plane.x + bb.minY * plane.y + bb.maxZ * plane.z < plane.offset) result |= plane.backCameras; + } + else if (plane.z >= 0) { + if (bb.maxX * plane.x + bb.minY * plane.y + bb.maxZ * plane.z >= plane.offset) result = plane.frontCameras; + if (bb.minX * plane.x + bb.maxY * plane.y + bb.minZ * plane.z < plane.offset) result |= plane.backCameras; + } else { + if (bb.maxX * plane.x + bb.minY * plane.y + bb.minZ * plane.z >= plane.offset) result = plane.frontCameras; + if (bb.minX * plane.x + bb.maxY * plane.y + bb.maxZ * plane.z < plane.offset) result |= plane.backCameras; + } + else if (plane.y >= 0) + if (plane.z >= 0) { + if (bb.minX * plane.x + bb.maxY * plane.y + bb.maxZ * plane.z >= plane.offset) result = plane.frontCameras; + if (bb.maxX * plane.x + bb.minY * plane.y + bb.minZ * plane.z < plane.offset) result |= plane.backCameras; + } else { + if (bb.minX * plane.x + bb.maxY * plane.y + bb.minZ * plane.z >= plane.offset) result = plane.frontCameras; + if (bb.maxX * plane.x + bb.minY * plane.y + bb.maxZ * plane.z < plane.offset) result |= plane.backCameras; + } + else if (plane.z >= 0) { + if (bb.minX * plane.x + bb.minY * plane.y + bb.maxZ * plane.z >= plane.offset) result = plane.frontCameras; + if (bb.maxX * plane.x + bb.maxY * plane.y + bb.minZ * plane.z < plane.offset) result |= plane.backCameras; + } else { + if (bb.minX * plane.x + bb.minY * plane.y + bb.minZ * plane.z >= plane.offset) result = plane.frontCameras; + if (bb.maxX * plane.x + bb.maxY * plane.y + bb.maxZ * plane.z < plane.offset) result |= plane.backCameras; + } + culling &= result | plane.unusedBits; + } + return culling; + } + + private function collectDraws(context:Context3D, caster:Object3D, edgeCamera:Camera3D):void { + // если объект является мешем, собираем для него дроуколы + var mesh:Mesh = caster as Mesh; + if (mesh != null && mesh.geometry != null) { + var program:ShaderProgram; + var programListByTransformProcedure:Vector.; + var skin:Skin = mesh as Skin; + + // пробегаемся по сурфейсам + for (var i:int = 0; i < mesh._surfacesLength; i++) { + var surface:Surface = mesh._surfaces[i]; + if (surface.material == null) continue; + + var material:Material = surface.material; + var geometry:Geometry = mesh.geometry; + var alphaTest:Boolean; + var useDiffuseAlpha:Boolean; + var alphaThreshold:Number; + var materialAlpha:Number; + var diffuse:TextureResource; + var opacity:TextureResource; + var uvBuffer:VertexBuffer3D; + + // ловим параметры прозрачности + if (material is TextureMaterial) { + alphaThreshold = TextureMaterial(material).alphaThreshold; + materialAlpha = TextureMaterial(material).alpha; + diffuse = TextureMaterial(material).diffuseMap; + opacity = TextureMaterial(material).opacityMap; + alphaTest = alphaThreshold > 0; + useDiffuseAlpha = TextureMaterial(material).opacityMap == null; + uvBuffer = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]); + if (uvBuffer == null) continue; + } else { + alphaTest = false; + useDiffuseAlpha = false; + } + + + var positionBuffer:VertexBuffer3D = mesh.geometry.getVertexBuffer(VertexAttributes.POSITION); + if (positionBuffer == null) continue; + + // поднимаем и кэшируем programListByTransformProcedure + if (skin != null) { + caster.transformProcedure = skin.surfaceTransformProcedures[i]; + } + programListByTransformProcedure = programs[caster.transformProcedure]; + if (programListByTransformProcedure == null) { + programListByTransformProcedure = new Vector.(3, true); + programs[caster.transformProcedure] = programListByTransformProcedure; + } + + // собираем программу и Формируем дроуюнит + program = getProgram(caster.transformProcedure, programListByTransformProcedure, context, alphaTest, useDiffuseAlpha); + var drawUnit:DrawUnit = renderer.createDrawUnit(caster, program.program, mesh.geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program); + drawUnit.culling = Context3DTriangleFace.BACK; + + // Установка стрима + drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, mesh.geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]); + + if (alphaTest) { + drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aUV"), uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cThresholdAlpha"), alphaThreshold, 0, 0, materialAlpha); + if (useDiffuseAlpha) { + drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sTexture"), diffuse._texture); + } else { + drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sTexture"), opacity._texture); + } + } + + // Установка констант + caster.setTransformConstants(drawUnit, surface, program.vertexShader, null); + drawUnit.setProjectionConstants(edgeCamera, program.vertexShader.getVariableIndex("cProjMatrix"), casterToEdgedCameraTransform); + drawUnit.setVertexConstantsFromTransform(program.vertexShader.getVariableIndex("cCasterToOmni"), caster.localToLightTransform); + + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cConstants"), 1 / 255, 0, 255 / radius, 1); + + renderer.addDrawUnit(drawUnit, Renderer.OPAQUE); + } + } + } + + /** + * @private + * Процедура для передачи UV координат во фрагментный шейдер + */ + static private const passUVProcedure:Procedure = new Procedure(["#v0=vUV", "#a0=aUV", "mov v0, a0"], "passUVProcedure"); + + // diffuse alpha test + private static const diffuseAlphaTestProcedure:Procedure = new Procedure([ + "#v0=vUV", + "#s0=sTexture", + "#c0=cThresholdAlpha", + "tex t0, v0, s0 <2d, linear,repeat, miplinear>", + "mul t0.w, t0.w, c0.w", + "sub t0.w, t0.w, c0.x", + "kil t0.w" + ], "diffuseAlphaTestProcedure"); + + // opacity alpha test + private static const opacityAlphaTestProcedure:Procedure = new Procedure([ + "#v0=vUV", + "#s0=sTexture", + "#c0=cThresholdAlpha", + "tex t0, v0, s0 <2d, linear,repeat, miplinear>", + "mul t0.w, t0.x, c0.w", + "sub t0.w, t0.w, c0.x", + "kil t0.w" + ], "opacityAlphaTestProcedure"); + + + private function getProgram(transformProcedure:Procedure, programListByTransformProcedure:Vector., context:Context3D, alphaTest:Boolean, useDiffuseAlpha:Boolean):ShaderProgram { + var key:int = (alphaTest ? (useDiffuseAlpha ? 1 : 2) : 0); + var program:ShaderProgram = programListByTransformProcedure[key]; + + if (program == null) { + var vLinker:Linker = new Linker(Context3DProgramType.VERTEX); + var fLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); + + var positionVar:String = "aPosition"; + vLinker.declareVariable(positionVar, VariableType.ATTRIBUTE); + + if (alphaTest) { + vLinker.addProcedure(passUVProcedure); + } + + if (transformProcedure != null) { + var newPosVar:String = "tTransformedPosition"; + vLinker.declareVariable(newPosVar); + vLinker.addProcedure(transformProcedure, positionVar); + vLinker.setOutputParams(transformProcedure, newPosVar); + positionVar = newPosVar; + } + + var proc:Procedure = Procedure.compileFromArray([ + "#v0=vDistance", + + "m34 t0.xyz, i0, c2", + "mov v0, t0.xyzx", + + "m44 o0, i0, c0" + ]); + proc.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4); + proc.assignVariableName(VariableType.CONSTANT, 2, "cCasterToOmni", 3); + + vLinker.addProcedure(proc, positionVar); + + if (alphaTest) { + if (useDiffuseAlpha) { + fLinker.addProcedure(diffuseAlphaTestProcedure); + } else { + fLinker.addProcedure(opacityAlphaTestProcedure); + } + } + fLinker.addProcedure(Procedure.compileFromArray([ + "#v0=vDistance", // xyz + "#c0=cConstants", // 1/255, 0, 255/radius, 1 + // calculate distance + "dp3 t0.z, v0.xyz, v0.xyz", + "sqt t0.z, t0.z", // x: [0, radius] + "mul t0.z, t0.z, c0.z", // x: [0, 255] + // codeing + "frc t0.y, t0.z", + "sub t0.x, t0.z, t0.y", + "mul t0.x, t0.x, c0.x", + + "mov t0.w, c0.w", + "mov o0, t0" + ])); + program = new ShaderProgram(vLinker, fLinker); + fLinker.varyings = vLinker.varyings; + programListByTransformProcedure[key] = program; + program.upload(context); + + } + return program; + } + + + //------------- ShadowMap Shader in material---------- + + /** + * @private + */ + alternativa3d override function setup(drawUnit:DrawUnit, vertexLinker:Linker, fragmentLinker:Linker, surface:Surface):void { + // Устанавливаем матрицу перевода в шедоумапу + objectToLightTransform.combine(_light.cameraToLocalTransform, surface.object.localToCameraTransform); + drawUnit.setVertexConstantsFromTransform(vertexLinker.getVariableIndex("cObjectToLightTransform"), objectToLightTransform); + + // Устанавливаем шедоумапу + drawUnit.setTextureAt(fragmentLinker.getVariableIndex("sCubeMap"), cubeShadowMap); + + // Устанавливаем коеффициенты + if (_pcfOffset > 0) { + var offset:Number = Math.tan(_pcfOffset / 180 * Math.PI) / 3; + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cPCFOffsets"), -3 / 2, 1 / 16, 0, 0); + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -1, 1, 0, offset); + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cDecode"), -DIFFERENCE_MULTIPLIER, -DIFFERENCE_MULTIPLIER / 255, biasMultiplier * DIFFERENCE_MULTIPLIER / radius, 10); + } else { + drawUnit.setFragmentConstantsFromNumbers(fragmentLinker.getVariableIndex("cConstants"), -DIFFERENCE_MULTIPLIER, -DIFFERENCE_MULTIPLIER / 255, biasMultiplier * DIFFERENCE_MULTIPLIER / radius, 1.0); + } + } + + private static function getVShader():Procedure { + var shader:Procedure = Procedure.compileFromArray([ + "#v0=vSample", + + "m34 t0.xyz, i0, c0", + + "mov v0, t0.xyz" + ], "OmniShadowMapVertex"); + shader.assignVariableName(VariableType.CONSTANT, 0, "cObjectToLightTransform", 3); + return shader; + } + + private static function getFShader():Procedure { + var shaderArr:Array = [ + "#v0=vSample", + "#c0=cConstants", + "#s0=sCubeMap" + ]; + var line:int = 3; + // Расстояние + shaderArr[line++] = "dp3 t0.z, v0.xyz, v0.xyz"; + shaderArr[line++] = "sqt t0.z, t0.z"; // w: [0, radius] + shaderArr[line++] = "tex t0.xy, v0, s0 "; + shaderArr[line++] = "dp3 t0.x, t0.xyz, c0.xyz"; // декодируем, находим разницу между расстояниями и умножаем ее на большое число + + // рассчитываем значение тени + shaderArr[line++] = "sat t0.x, t0.x"; + shaderArr[line++] = "sub o0, c0.w, t0.x"; + +// shaderArr[line++] = "sat t0.x, t0.x"; +// shaderArr[line++] = "sub t0.x, c0.w, t0.x"; +// shaderArr[line++] = "sat t0.x, t0.x"; +// shaderArr[line++] = "mov o0, t0.x"; + + return Procedure.compileFromArray(shaderArr, "OmniShadowMapFragment"); + } + + private static function getFShaderPCF():Procedure { + var shaderArr:Array = [ + "#v0=vSample", + "#c0=cDecode", + "#c1=cConstants", + "#c2=cPCFOffsets", + "#s0=sCubeMap" + ]; + var line:int = 5; + var i:int; + var j:int; + + // допустимо использование временных переменных t0 t1 t2 t3 + // v0 - sample + + // calculate 2 ortogonal vectors + // (-y, x, 0) + shaderArr[line++] = "mov t1.xyzw, v0.yxzw"; + shaderArr[line++] = "mul t1.xyzw, t1.xyzw, c1.xyzz"; + + shaderArr[line++] = "crs t0.xyz, v0.xyz, t1.xyz"; + + // normalize vectors + shaderArr[line++] = "nrm t0.xyz, t0.xyz"; + shaderArr[line++] = "nrm t1.xyz, t1.xyz"; + + shaderArr[line++] = "dp3 t3.z, v0.xyz, v0.xyz"; + shaderArr[line++] = "sqt t3.z, t3.z"; // distance + + // apply pcf offset + shaderArr[line++] = "mul t0.w, c1.w, t3.z"; // с1.w = offset/radius + shaderArr[line++] = "mul t0.xyz, t0.xyz, t0.w"; + shaderArr[line++] = "mul t1.xyz, t1.xyz, t0.w"; + // --------- {13 opcode} + + // t0, t1 - ortogonals ↑→ + // t2 - current vector + + // t3.z distance to object + // t3.xy - result from shadow map + // t3.w - summ of sat + + // first point + shaderArr[line++] = "add t2.xyz, t0.xyz, t1.xyz"; + shaderArr[line++] = "mul t2.xyz, t2.xyz, c2.xxx"; + shaderArr[line++] = "add t2.xyz, t2.xyz, v0.xyz"; + + // получаем длинну из шадоумапы [0, 1] +// shaderArr[line++] = "mov t3.z, t0.w"; + + shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; + shaderArr[line++] = "dp3 o0." + componentByIndex[0] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число + + //----- + + for (j = 1; j < 4; j++) { + shaderArr[line++] = "add t2.xyz, t2.xyz, t1.xyz"; + + shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; + shaderArr[line++] = "dp3 o0." + componentByIndex[j] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число + } + + shaderArr[line++] = "sat o0, o0"; + shaderArr[line++] = "dp4 t3.w, o0, c2.y"; + + //----- + + for (i = 0; i < 3; i++) { + shaderArr[line++] = "add t2.xyz, t2.xyz, t0.xyz"; + + shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; + shaderArr[line++] = "dp3 o0." + componentByIndex[0] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число + + for (j = 1; j < 4; j++) { + shaderArr[line++] = (i % 2 == 1) ? ("add t2.xyz, t2.xyz, t1.xyz") : ("sub t2.xyz, t2.xyz, t1.xyz"); + + shaderArr[line++] = "tex t3.xy, t2.xyz, s0 "; + shaderArr[line++] = "dp3 o0." + componentByIndex[j] + ", t3.xyz, c0.xyz"; // декодируем, вычитаем, умножаем на большое число + } + shaderArr[line++] = "sat o0, o0"; + shaderArr[line++] = "dp4 o0.x, o0, c2.y"; + shaderArr[line++] = "add t3.w, t3.w, o0.x"; + } + + shaderArr[line++] = "sub o0, c1.y, t3.w"; + + //--------- {73 opcodes} + return Procedure.compileFromArray(shaderArr, "OmniShadowMapFragment"); + } + + private static const componentByIndex:Array = ["x", "y", "z", "w"]; + + /** + * Adds given object to list of objects, that cast shadow. + * @param object Added object. + */ + public function addCaster(object:Object3D):void { + if (_casters.indexOf(object) < 0) { + _casters.push(object); + } + } + + /** + * Removes given object from shadow casters list. + * @param object Object which should be removed from shadow casters list. + */ + public function removeCaster(object:Object3D):void { + var index:int = _casters.indexOf(object); + if (index < 0) throw new Error("Caster not found"); + _casters[index] = _casters.pop(); + } + + /** + * Clears the list of objects, that cast shadow. + */ + public function clearCasters():void { + _casters.length = 0; + } + + /** + * Set resolution of shadow map. This property can get value of power of 2 (up to 2048). + * OmniLightShadow uses 6 shadow maps. + */ + public function get mapSize():int { + return _mapSize; + } + + /** + * @private + */ + public function set mapSize(value:int):void { + if (value != _mapSize) { + this._mapSize = value; + if (value < 2) { + throw new ArgumentError("Map size cannot be less than 2."); + } else if (value > 1024) { + throw new ArgumentError("Map size exceeds maximum value 1024."); + } + if ((Math.log(value) / Math.LN2 % 1) != 0) { + throw new ArgumentError("Map size must be power of two."); + } + if (cubeShadowMap != null) { + cubeShadowMap.dispose(); + } + cubeShadowMap = null; + } + } + + /** + * Offset of Percentage Closer Filtering. This way of filtering is used for mitigation of shadow bounds. + */ + public function get pcfOffset():Number { + return _pcfOffset; + } + + /** + * @private + */ + public function set pcfOffset(value:Number):void { + _pcfOffset = value; + type = _pcfOffset > 0 ? Shadow.PCF_MODE : Shadow.SIMPLE_MODE; + fragmentShadowProcedure = _pcfOffset > 0 ? getFShaderPCF() : getFShader(); + } + + } +} + +import alternativa.engine3d.alternativa3d; +import alternativa.engine3d.core.Camera3D; +import alternativa.engine3d.core.DrawUnit; +import alternativa.engine3d.core.Light3D; +import alternativa.engine3d.core.Object3D; +import alternativa.engine3d.core.Renderer; +import alternativa.engine3d.core.VertexAttributes; +import alternativa.engine3d.materials.Material; +import alternativa.engine3d.materials.ShaderProgram; +import alternativa.engine3d.materials.compiler.Linker; +import alternativa.engine3d.materials.compiler.Procedure; +import alternativa.engine3d.materials.compiler.VariableType; +import alternativa.engine3d.objects.Surface; +import alternativa.engine3d.resources.Geometry; + +import flash.display3D.Context3D; +import flash.display3D.Context3DBlendFactor; +import flash.display3D.Context3DProgramType; +import flash.display3D.VertexBuffer3D; +import flash.display3D.textures.CubeTexture; +import flash.utils.Dictionary; + +class ShadowDebugMaterial extends Material { + + use namespace alternativa3d; + + /** + * Прозрачность. + * Является дополнительным множителем к прозрачности текстуры. + * Значение по умолчанию 1. + */ + alternativa3d var alpha:Number = 1; + + private var cachedContext3D:Context3D; + private static var caches:Dictionary = new Dictionary(true); + private var program:ShaderProgram; + + /** + * Текстура. + */ + alternativa3d var cubeMap:CubeTexture; + + /** + * @private + */ + override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector., lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void { + var object:Object3D = surface.object; + // Стримы + var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION); + // Проверка на валидность + if (positionBuffer == null) return; + + // Обновляем кеш программы для данного контекста + if (camera.context3D != cachedContext3D) { + cachedContext3D = camera.context3D; + program = caches[cachedContext3D]; + } + + if (program == null) { + program = setupProgram(object); + program.upload(camera.context3D); + caches[cachedContext3D] = program; + } + + // Создание отрисовочного вызова + var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program); + // Установка стримов + drawUnit.setVertexBufferAt(program.vertexShader.getVariableIndex("aPosition"), positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]); + // Установка констант + drawUnit.setProjectionConstants(camera, program.vertexShader.getVariableIndex("cProjMatrix"), object.localToCameraTransform); + drawUnit.setFragmentConstantsFromNumbers(program.fragmentShader.getVariableIndex("cDecode"), 1, 1 / 255, 0, alpha); + drawUnit.setTextureAt(program.fragmentShader.getVariableIndex("sCubeMap"), cubeMap); + + // Отправка на отрисовку + if (alpha < 1) { + drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA; + drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA; + camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT); + } else { + camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE); + } + } + + private function copyDrawUnit(source:DrawUnit, dest:DrawUnit):void { + + } + + private function setupProgram(object:Object3D):ShaderProgram { + var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX); + var positionVar:String = "aPosition"; + vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE); + + var proc:Procedure = Procedure.compileFromArray([ + "#v0=vCubeMapCoord", + "mov v0, i0", + "m44 o0, i0, c0" + ]); + proc.assignVariableName(VariableType.CONSTANT, 0, "cProjMatrix", 4); + vertexLinker.addProcedure(proc, positionVar); + + var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT); + var colorProc:Procedure = Procedure.compileFromArray([ + "#v0=vCubeMapCoord", + "#s0=sCubeMap", + "#c0=cDecode", + + "tex t0.xy, v0, s0 ", + "dp3 t0.xyz, t0.xy, c0.xy", + "mov t0.w, c0.w", + "mov o0, t0" + ]); + fragmentLinker.addProcedure(colorProc, "vCubeMapCoord"); + fragmentLinker.varyings = vertexLinker.varyings; + return new ShaderProgram(vertexLinker, fragmentLinker); + } + +} + +class SectionPlane { + + public var x:Number = 0; + public var y:Number = 0; + public var z:Number = 0; + public var offset:Number = 0; + + public var next:SectionPlane; + + public var frontCameras:int; + public var backCameras:int; + public var unusedBits:int = 63; + + public function SectionPlane(frontCameras:int, backCameras:int, unused:int) { + this.frontCameras = frontCameras; + this.backCameras = backCameras; + this.unusedBits = unused; + } + +} diff --git a/src/alternativa/utils/Utils.as b/src/alternativa/utils/Utils.as index 8711c5b..8713b85 100644 --- a/src/alternativa/utils/Utils.as +++ b/src/alternativa/utils/Utils.as @@ -42,8 +42,13 @@ package alternativa.utils { } /** - * @private - * Performs calculation of bound box of objects hierarchy branch. + * Calculates a BoundBox of hierarchy of objects. + * + * @param object Container which contains the hierarchy. + * @param boundBoxSpace Object3D in coordinates of which the BoundBox will be calculated. + * @param result Instance of BoundBox to which calculated properties will be set. + * + * @return Instance given as result property with properties updated according to calculations. If result property was not set, new instance of BoundBox will be created. */ public static function calculateHierarchyBoundBox(object:Object3D, boundBoxSpace:Object3D = null, result:BoundBox = null):BoundBox { if (result == null) result = new BoundBox();