1 /**
  2  * The Render Engine
  3  * BillboardComponent
  4  *
  5  * @fileoverview A render component which will render the contents of
  6  *               a generated image until the contents are updated.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  * @author: $Author: bfattori $
 10  * @version: $Revision: 1556 $
 11  *
 12  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 13  *
 14  * Permission is hereby granted, free of charge, to any person obtaining a copy
 15  * of this software and associated documentation files (the "Software"), to deal
 16  * in the Software without restriction, including without limitation the rights
 17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18  * copies of the Software, and to permit persons to whom the Software is
 19  * furnished to do so, subject to the following conditions:
 20  *
 21  * The above copyright notice and this permission notice shall be included in
 22  * all copies or substantial portions of the Software.
 23  *
 24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30  * THE SOFTWARE.
 31  *
 32  */
 33 
 34 // The class this file defines and its required classes
 35 R.Engine.define({
 36     "class":"R.components.render.Billboard2D",
 37     "requires":[
 38         "R.components.Render",
 39         "R.util.RenderUtil",
 40         "R.text.AbstractTextRenderer",
 41         "R.math.Point2D"
 42     ]
 43 });
 44 
 45 /**
 46  * @class The billboard component renders the contents of an image which
 47  *        was generated by a linked render component.  When the contents
 48  *        of the linked component are re-rendered, the contents of the
 49  *        image are updated.  The best usage of this component is for infrequently
 50  *        changing vector drawn objects.  For example:
 51  *        <pre>
 52  *     // Add component to draw the object
 53  *     this.add(R.components.Billboard2D.create("draw", R.components.Vector2D.create("vector")));
 54  *        </pre>
 55  *        Accessing the <tt>R.components.Vector2D</tt> within the <tt>R.components.Billboard2D</tt>
 56  *        is as simple as calling {@link #getComponent}.  If the contents of the linked
 57  *        component are updated, you will need to call {@link #regenerate} to recreate the
 58  *        billboard image.
 59  *
 60  *
 61  * @param name {String} The name of the component
 62  * @param renderComponent {R.components.Render} A render component to create the billboard from
 63  * @param priority {Number} The priority of the component between 0.0 and 1.0
 64  * @constructor
 65  * @extends R.components.Render
 66  * @description Creates a 2d billboard component.
 67  */
 68 R.components.render.Billboard2D = function () {
 69     "use strict";
 70     return R.components.Render.extend(/** @scope R.components.render.Billboard2D.prototype */{
 71 
 72         billboard:null,
 73         mode:null,
 74         renderComponent:null,
 75         hostRect:null,
 76 
 77         /**
 78          * @private
 79          */
 80         constructor:function (name, renderComponent, priority) {
 81             Assert(renderComponent instanceof R.components.Render ||
 82                 renderComponent instanceof R.text.AbstractTextRenderer, "Attempt to assign a non-render component to a billboard component");
 83             this.base(name, priority || 0.1);
 84             this.mode = R.components.render.Billboard2D.REDRAW;
 85             this.renderComponent = renderComponent;
 86 
 87         },
 88 
 89         /**
 90          * Destroy the object
 91          */
 92         destroy:function () {
 93             this.renderComponent.destroy();
 94             this.base();
 95         },
 96 
 97         /**
 98          * Releases the component back into the object pool. See {@link R.engine.PooledObject#release}
 99          * for more information.
100          */
101         release:function () {
102             this.base();
103             this.mode = null;
104             this.renderComponent = null;
105         },
106 
107         /**
108          * Deprecated in favor of {@link #setGameObject}.
109          * @deprecated
110          */
111         setHostObject:function (hostObject) {
112             this.setGameObject(hostObject);
113         },
114 
115         /**
116          * Establishes the link between this component and its game object.
117          * When you assign components to a game object, it will call this method
118          * so that each component can refer to its game object, the same way
119          * a game object can refer to a component with {@link R.engine.GameObject#getComponent}.
120          *
121          * @param hostObject {R.engine.GameObject} The object which hosts this component
122          */
123         setGameObject:function (gameObject) {
124             this.renderComponent.setGameObject(gameObject);
125             this.base(gameObject);
126         },
127 
128         /**
129          * Call this method when the linked render component has been updated
130          * to force the billboard to be redrawn.
131          */
132         regenerate:function () {
133             this.mode = R.components.render.Billboard2D.REDRAW;
134             this.hostRect = null;
135             this.getGameObject().markDirty();
136         },
137 
138         /**
139          * Get the linked render component.
140          * @return {R.components.Render}
141          */
142         getComponent:function () {
143             return this.renderComponent;
144         },
145 
146         /**
147          * Draws the contents of the billboard to the render context.  This
148          * component operates in one of two modes.  When the contents of the
149          * subclassed component are redrawing, a temporary render context is created
150          * to which the component renders.  The second mode is where the contents
151          * of the context from the first mode are rendered instead of performing
152          * all of the operations required to render the component.  This component
153          * is only good if the contents don't change often.
154          *
155          * @param renderContext {R.rendercontexts.AbstractRenderContext} The rendering context
156          * @param time {Number} The engine time in milliseconds
157          * @param dt {Number} The delta between the world time and the last time the world was updated
158          *          in milliseconds.
159          */
160         execute:function (renderContext, time, dt) {
161             if (!this.base(renderContext, time, dt)) {
162                 return;
163             }
164 
165             // Get the host object's bounding box
166             var hostBox = this.getGameObject().getBoundingBox();
167             var o = R.math.Point2D.create(this.getGameObject().getOrigin());
168 
169             if (this.mode == R.components.render.Billboard2D.REDRAW) {
170                 // We'll match the type of context the component is rendering to
171                 //var ctx = this.getGameObject().getRenderContext().constructor;
172 
173                 if (!this.billboard) {
174                     // Due to pooling, we don't need to recreate this each time
175                     this.billboard = $("<img/>");
176                 }
177 
178                 this.billboard.attr({
179                     "src":R.util.RenderUtil.renderComponentToImage(R.rendercontexts.CanvasContext, this.renderComponent,
180                         hostBox.w, hostBox.h, null, o),
181                     "width":hostBox.w,
182                     "height":hostBox.h
183                 });
184 
185                 this.mode = R.components.render.Billboard2D.NORMAL;
186             }
187 
188             // Render the billboard.  If the bounding box's origin is negative in
189             // either X or Y, we'll need to move the transformation there before rendering the object
190             this.transformOrigin(renderContext, true);
191             try {
192                 renderContext.drawImage(this.getGameObject().getBoundingBox(), this.billboard[0], this.getGameObject());
193             }
194             catch (ex) {
195                 // TODO: Find a better way to perform this operation since try/catch is SLOW
196                 // It appears that Firefox might not have a full image rendered, so calling
197                 // drawImage fails with a component exception.  To abate this possible issue,
198                 // we try the call and catch the failure...
199             }
200 
201             /* pragma:DEBUG_START */
202             // Debug the billboard image box
203             if (R.Engine.getDebugMode()) {
204                 renderContext.setLineStyle("green");
205                 renderContext.drawRectangle(this.getGameObject().getBoundingBox(), this.getGameObject());
206             }
207             /* pragma:DEBUG_END */
208 
209             this.transformOrigin(renderContext, false);
210             o.destroy();
211         }
212 
213     }, /** @scope R.components.render.Billboard2D.prototype */{
214 
215         /**
216          * Get the class name of this object
217          *
218          * @return {String} "R.components.render.Billboard2D"
219          */
220         getClassName:function () {
221             return "R.components.render.Billboard2D";
222         },
223 
224         /**
225          * The component will render to a temporary context from which the
226          * actual content will be rendered.
227          * @type {Number}
228          */
229         REDRAW:0,
230 
231         /**
232          * The component will render the contents of the billboard.
233          * @type {Number}
234          */
235         NORMAL:1,
236 
237         /**
238          * A temporary context to which all billboards will render their
239          * bitmaps.
240          * @private
241          */
242         tempContext:null
243 
244     });
245 };