Torque3D Documentation / _generateds / guiMissionArea.cpp

guiMissionArea.cpp

Engine/source/gui/worldEditor/guiMissionArea.cpp

More...

Public Functions

ConsoleDocClass(GuiMissionAreaCtrl , "@brief Visual representation of Mission Area <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor.\n\n</a>" "@internal" )
DefineEngineMethod(GuiMissionAreaCtrl , setMissionArea , void , (MissionArea *area) , "@brief Set the <a href="/coding/class/classmissionarea/">MissionArea</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">edit.\n\n</a>" )
DefineEngineMethod(GuiMissionAreaCtrl , updateTerrain , void , () , "@brief Update the terrain <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bitmap.\n\n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(GuiMissionAreaCtrl , "@brief Visual representation of Mission Area <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor.\n\n</a>" "@internal" )

DefineEngineMethod(GuiMissionAreaCtrl , setMissionArea , void , (MissionArea *area) , "@brief Set the <a href="/coding/class/classmissionarea/">MissionArea</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">edit.\n\n</a>" )

DefineEngineMethod(GuiMissionAreaCtrl , updateTerrain , void , () , "@brief Update the terrain <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bitmap.\n\n</a>" )

IMPLEMENT_CONOBJECT(GuiMissionAreaCtrl )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "gui/worldEditor/guiMissionArea.h"
 25#include "console/consoleTypes.h"
 26#include "console/engineAPI.h"
 27#include "gfx/gfxDrawUtil.h"
 28#include "gfx/primBuilder.h"
 29#include "gfx/bitmap/gBitmap.h"
 30#include "gui/3d/guiTSControl.h"
 31#include "T3D/gameFunctions.h"
 32#include "terrain/terrData.h"
 33
 34namespace {
 35   F32 round_local(F32 val)
 36   {
 37      if(val >= 0.f)
 38      {
 39         F32 floor = mFloor(val);
 40         if((val - floor) >= 0.5f)
 41            return(floor + 1.f);
 42         return(floor);
 43      }
 44      else
 45      {
 46         F32 ceil = mCeil(val);
 47         if((val - ceil) <= -0.5f)
 48            return(ceil - 1.f);
 49         return(ceil);
 50      }
 51   }
 52}
 53
 54IMPLEMENT_CONOBJECT(GuiMissionAreaCtrl);
 55
 56ConsoleDocClass( GuiMissionAreaCtrl,
 57   "@brief Visual representation of Mission Area Editor.\n\n"
 58   "@internal"
 59);
 60
 61GuiMissionAreaCtrl::GuiMissionAreaCtrl()
 62{
 63   mHandleBitmap = StringTable->EmptyString();
 64   mHandleTexture = NULL;
 65   mHandleTextureSize = Point2I::Zero;
 66   mHandleTextureHalfSize = Point2F::Zero;
 67
 68   mSquareBitmap = true;
 69
 70   mMissionArea = 0;
 71   mTerrainBlock = 0;
 72
 73   mMissionBoundsColor.set(255,0,0);
 74   mCameraColor.set(255,0,0);
 75
 76   mBlendStateBlock = NULL;
 77   mSolidStateBlock = NULL;
 78
 79   mLastHitMode = Handle_None;
 80   mSavedDrag = false;
 81}
 82
 83GuiMissionAreaCtrl::~GuiMissionAreaCtrl()
 84{
 85}
 86
 87//------------------------------------------------------------------------------
 88
 89void GuiMissionAreaCtrl::initPersistFields()
 90{
 91   addField( "squareBitmap",        TypeBool,      Offset(mSquareBitmap, GuiMissionAreaCtrl));
 92
 93   addField( "handleBitmap",        TypeFilename,  Offset( mHandleBitmap, GuiMissionAreaCtrl ),
 94      "Bitmap file for the mission area handles.\n");
 95
 96   addField( "missionBoundsColor",  TypeColorI,    Offset(mMissionBoundsColor, GuiMissionAreaCtrl));
 97   addField( "cameraColor",         TypeColorI,    Offset(mCameraColor, GuiMissionAreaCtrl));
 98
 99   Parent::initPersistFields();
100}
101
102//------------------------------------------------------------------------------
103
104bool GuiMissionAreaCtrl::onAdd()
105{
106   if(!Parent::onAdd())
107      return(false);
108
109   GFXStateBlockDesc desc;
110   desc.setCullMode(GFXCullNone);
111   desc.setZReadWrite(false);
112   desc.setBlend(false, GFXBlendOne, GFXBlendZero);
113   mSolidStateBlock = GFX->createStateBlock( desc );
114
115   desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
116   mBlendStateBlock = GFX->createStateBlock( desc );
117
118   if (*mHandleBitmap)
119   {
120      mHandleTexture = GFXTexHandle( mHandleBitmap, &GFXTexturePersistentSRGBProfile, avar("%s() - mHandleTexture (line %d)", __FUNCTION__, __LINE__) );
121      mHandleTextureSize = Point2I( mHandleTexture->getWidth(), mHandleTexture->getHeight() );
122      mHandleTextureHalfSize = Point2F(mHandleTextureSize.x, mHandleTextureSize.y) * 0.5f;
123   }
124   else
125   {
126      mHandleTexture = NULL;
127      mHandleTextureSize = Point2I::Zero;
128      mHandleTextureHalfSize = Point2F::Zero;
129   }
130
131   return(true);
132}
133
134bool GuiMissionAreaCtrl::onWake()
135{
136   if(!Parent::onWake())
137      return(false);
138
139   //mMissionArea = const_cast<MissionArea*>(MissionArea::getServerObject());
140   //if(!bool(mMissionArea))
141   //   Con::warnf(ConsoleLogEntry::General, "GuiMissionAreaCtrl::onWake: no MissionArea object.");
142
143   //mTerrainBlock = getTerrainObj();
144   //if(!bool(mTerrainBlock))
145   //   Con::warnf(ConsoleLogEntry::General, "GuiMissionAreaCtrl::onWake: no TerrainBlock object.");
146
147   //if ( !bool(mMissionArea) || !bool(mTerrainBlock) )
148   //   return true;
149
150   updateTerrainBitmap();
151
152   // make sure mission area is clamped
153   setArea(getArea());
154
155   //onUpdate();
156   setActive(true);
157
158   return(true);
159}
160
161void GuiMissionAreaCtrl::onSleep()
162{
163   mTextureObject = NULL;
164   mMissionArea = 0;
165   mTerrainBlock = 0;
166
167   Parent::onSleep();
168}
169
170//------------------------------------------------------------------------------
171
172void GuiMissionAreaCtrl::onMouseUp(const GuiEvent & event)
173{
174   if(!bool(mMissionArea))
175      return;
176
177   RectI box;
178   getScreenMissionArea(box);
179   S32 hit = getHitHandles(event.mousePoint, box);
180
181   // set the current cursor
182   //updateCursor(hit);
183   mLastHitMode = hit;
184
185   if(mSavedDrag)
186   {
187      // Let the script get a chance at it.
188      Con::executef( this, "onMissionAreaModified" );
189   }
190   mSavedDrag = false;
191}
192
193void GuiMissionAreaCtrl::onMouseDown(const GuiEvent & event)
194{
195   if(!bool(mMissionArea))
196      return;
197
198   RectI box;
199   getScreenMissionArea(box);
200
201   mLastHitMode = getHitHandles(event.mousePoint, box);
202   //if(mLastHitMode == Handle_Middle)
203   //   setCursor(GrabCursor);
204   mLastMousePoint = event.mousePoint;
205}
206
207void GuiMissionAreaCtrl::onMouseMove(const GuiEvent & event)
208{
209   if(!bool(mMissionArea))
210      return;
211
212   RectI box;
213   getScreenMissionArea(box);
214   S32 hit = getHitHandles(event.mousePoint, box);
215
216   // set the current cursor...
217   //updateCursor(hit);
218   mLastHitMode = hit;
219}
220
221void GuiMissionAreaCtrl::onMouseDragged(const GuiEvent & event)
222{
223   if(!bool(mMissionArea))
224      return;
225
226   if(mLastHitMode == Handle_None)
227      return;
228
229   // If we haven't already saved,
230   // save an undo action to get back to this state,
231   // before we make any modifications.
232   if ( !mSavedDrag )
233   {
234      submitUndo( "Modify Node" );
235      mSavedDrag = true;
236   }
237
238   RectI box;
239   getScreenMissionArea(box);
240   Point2I mouseDiff(event.mousePoint.x - mLastMousePoint.x,
241      event.mousePoint.y - mLastMousePoint.y);
242
243   // what we drag'n?
244   RectI area = getArea();
245   Point2I wp = screenDeltaToWorldDelta(mouseDiff);
246
247   if (mLastHitMode == Handle_Middle)
248   {
249      area.point += wp;
250   }
251
252   if (mLastHitMode & Handle_Left)
253   {
254      if ((area.extent.x - wp.x) >= 1)
255      {
256         area.point.x += wp.x;
257         area.extent.x -= wp.x;
258      }
259   }
260
261   if (mLastHitMode & Handle_Right)
262   {
263      if ((area.extent.x + wp.x) >= 1)
264      {
265         area.extent.x += wp.x;
266      }
267   }
268
269   if (mLastHitMode & Handle_Bottom)
270   {
271      if ((area.extent.y - wp.y) >= 1)
272      {
273         area.point.y += wp.y;
274         area.extent.y -= wp.y;
275      }
276   }
277
278   if (mLastHitMode & Handle_Top)
279   {
280      if ((area.extent.y + wp.y) >= 1)
281      {
282         area.extent.y += wp.y;
283      }
284   }
285
286   setArea(area);
287   mLastMousePoint = event.mousePoint;
288}
289
290void GuiMissionAreaCtrl::onMouseEnter(const GuiEvent &)
291{
292   mLastHitMode = Handle_None;
293   //setCursor(DefaultCursor);
294}
295
296void GuiMissionAreaCtrl::onMouseLeave(const GuiEvent &)
297{
298   mLastHitMode = Handle_None;
299   //setCursor(DefaultCursor);
300}
301
302//------------------------------------------------------------------------------
303
304void GuiMissionAreaCtrl::submitUndo( const UTF8 *name )
305{
306   // Grab the mission editor undo manager.
307   UndoManager *undoMan = NULL;
308   if ( !Sim::findObject( "EUndoManager", undoMan ) )
309   {
310      Con::errorf( "GuiRiverEditorCtrl::submitUndo() - EUndoManager not found!" );
311      return;           
312   }
313
314   // Setup the action.
315   GuiMissionAreaUndoAction *action = new GuiMissionAreaUndoAction( name );
316
317   action->mMissionAreaEditor = this;
318
319   action->mObjId = mMissionArea->getId();
320   action->mArea = mMissionArea->getArea();
321      
322   undoMan->addAction( action );
323}
324
325//------------------------------------------------------------------------------
326
327void GuiMissionAreaCtrl::updateTerrain()
328{
329   mTerrainBlock = getTerrainObj();
330   updateTerrainBitmap();
331}
332
333TerrainBlock * GuiMissionAreaCtrl::getTerrainObj()
334{
335   SimSet * scopeAlwaysSet = Sim::getGhostAlwaysSet();
336   for(SimSet::iterator itr = scopeAlwaysSet->begin(); itr != scopeAlwaysSet->end(); itr++)
337   {
338      TerrainBlock * terrain = dynamic_cast<TerrainBlock*>(*itr);
339      if(terrain)
340         return(terrain);
341   }
342   return(0);
343}
344
345void GuiMissionAreaCtrl::updateTerrainBitmap()
346{
347   GBitmap * bitmap = createTerrainBitmap();
348   if( bitmap )
349      setBitmapHandle( GFXTexHandle( bitmap, &GFXDefaultGUIProfile, true, String("Terrain Bitmap Update") ) );
350   else
351      setBitmap( "" );
352}
353
354GBitmap * GuiMissionAreaCtrl::createTerrainBitmap()
355{
356   if(!mTerrainBlock)
357      return NULL;
358
359   GBitmap * bitmap = new GBitmap(mTerrainBlock->getBlockSize(), mTerrainBlock->getBlockSize(), false, GFXFormatR8G8B8 );
360
361   // get the min/max
362   F32 min, max;
363   mTerrainBlock->getMinMaxHeight(&min, &max);
364
365   F32 diff = max - min;
366   F32 colRange = 255.0f / diff;
367
368   // This method allocates it's bitmap above, and does all assignment
369   // in the following loop. It is not subject to 24-bit -> 32-bit conversion
370   // problems, because the texture handle creation is where the conversion would
371   // occur, if it occurs. Since the data in the texture is never read back, and
372   // the bitmap is deleted after texture-upload, this is not a problem.
373   for(S32 y = 0; y < mTerrainBlock->getBlockSize() ; y++)
374   {
375      for(S32 x = 0; x < mTerrainBlock->getBlockSize(); x++)
376      {
377         F32 height;
378         height = mTerrainBlock->getHeight(Point2I(x, y));
379 
380         U8 col = U8((height - min) * colRange);
381         ColorI color(col, col, col);
382         bitmap->setColor(x, y, color);
383    
384      }
385   }
386
387   return(bitmap);
388}
389
390//------------------------------------------------------------------------------
391
392void GuiMissionAreaCtrl::setMissionArea( MissionArea* area )
393{
394   mMissionArea = area;
395   if( mMissionArea )
396   {
397      setArea(getArea());
398   }
399}
400
401const RectI & GuiMissionAreaCtrl::getArea()
402{
403   if( !bool(mMissionArea) )
404      return(MissionArea::smMissionArea);
405
406   return(mMissionArea->getArea());
407}
408
409void GuiMissionAreaCtrl::setArea(const RectI & area)
410{
411   if( bool(mMissionArea) )
412   {
413      mMissionArea->setArea(area);
414      mMissionArea->inspectPostApply();
415      //onUpdate();
416   }
417}
418
419//------------------------------------------------------------------------------
420
421void GuiMissionAreaCtrl::drawHandle(const Point2F & pos)
422{
423   Point2F pnt(pos.x-mHandleTextureHalfSize.x, pos.y-mHandleTextureHalfSize.y);
424   GFX->getDrawUtil()->drawBitmap(mHandleTexture, pnt);
425}
426
427void GuiMissionAreaCtrl::drawHandles(RectI & box)
428{
429   F32 fillOffset = GFX->getFillConventionOffset();
430
431   F32 lx = box.point.x + fillOffset, rx = box.point.x + box.extent.x + fillOffset;
432   F32 cx = (lx + rx) * 0.5f;
433   F32 by = box.point.y + fillOffset, ty = box.point.y + box.extent.y + fillOffset;
434   F32 cy = (ty + by) * 0.5f;
435
436   GFX->getDrawUtil()->clearBitmapModulation();
437   drawHandle(Point2F(lx, ty));
438   drawHandle(Point2F(lx, cy));
439   drawHandle(Point2F(lx, by));
440   drawHandle(Point2F(rx, ty));
441   drawHandle(Point2F(rx, cy));
442   drawHandle(Point2F(rx, by));
443   drawHandle(Point2F(cx, ty));
444   drawHandle(Point2F(cx, by));
445}
446
447bool GuiMissionAreaCtrl::testWithinHandle(const Point2I & testPoint, S32 handleX, S32 handleY)
448{
449   S32 dx = testPoint.x - handleX;
450   S32 dy = testPoint.y - handleY;
451   return dx <= Handle_Pixel_Size && dx >= -Handle_Pixel_Size && dy <= Handle_Pixel_Size && dy >= -Handle_Pixel_Size;
452}
453
454S32 GuiMissionAreaCtrl::getHitHandles(const Point2I & mousePnt, const RectI & box)
455{
456   S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1;
457   S32 cx = (lx + rx) >> 1;
458   S32 by = box.point.y, ty = box.point.y + box.extent.y - 1;
459   S32 cy = (ty + by) >> 1;
460
461   if (testWithinHandle(mousePnt, lx, ty))
462      return Handle_Left | Handle_Top;
463   if (testWithinHandle(mousePnt, cx, ty))
464      return Handle_Top;
465   if (testWithinHandle(mousePnt, rx, ty))
466      return Handle_Right | Handle_Top;
467   if (testWithinHandle(mousePnt, lx, by))
468      return Handle_Left | Handle_Bottom;
469   if (testWithinHandle(mousePnt, cx, by))
470      return Handle_Bottom;
471   if (testWithinHandle(mousePnt, rx, by))
472      return Handle_Right | Handle_Bottom;
473   if (testWithinHandle(mousePnt, lx, cy))
474      return Handle_Left;
475   if (testWithinHandle(mousePnt, rx, cy))
476      return Handle_Right;
477   if(mousePnt.x >= lx && mousePnt.x <= rx &&
478      mousePnt.y >= ty && mousePnt.y <= by)
479      return(Handle_Middle);
480
481   return Handle_None;
482}
483
484//------------------------------------------------------------------------------
485
486Point2F GuiMissionAreaCtrl::worldToScreen(const Point2F & pos)
487{
488   return(Point2F(mCenterPos.x + (pos.x * mScale.x), mCenterPos.y + (pos.y * mScale.y)));
489}
490
491Point2I GuiMissionAreaCtrl::worldToScreen(const Point2I &pos)
492{
493   return(Point2I(S32(mCenterPos.x + (pos.x * mScale.x)), S32(mCenterPos.y + (pos.y * mScale.y))));
494}
495
496Point2F GuiMissionAreaCtrl::screenToWorld(const Point2F & pos)
497{
498   return(Point2F((pos.x - mCenterPos.x) / mScale.x, (pos.y - mCenterPos.y) / mScale.y));
499}
500
501Point2I GuiMissionAreaCtrl::screenToWorld(const Point2I &pos)
502{
503   return(Point2I(S32((pos.x - mCenterPos.x) / mScale.x), S32((pos.y - mCenterPos.y) / mScale.y)));
504}
505
506Point2I GuiMissionAreaCtrl::screenDeltaToWorldDelta(const Point2I &screenPoint)
507{
508   return(Point2I(S32(screenPoint.x / mScale.x), S32(screenPoint.y / mScale.y)));
509}
510
511void GuiMissionAreaCtrl::setupScreenTransform(const Point2I & offset)
512{
513   const MatrixF & terrMat = mTerrainBlock->getTransform();
514   Point3F terrPos;
515   terrMat.getColumn(3, &terrPos);
516   terrPos.z = 0;
517
518   F32 terrDim = mTerrainBlock->getWorldBlockSize();
519
520   const Point2I& extenti = getExtent( );
521   Point2F extent( static_cast<F32>( extenti.x ), static_cast<F32>( extenti.y ) );
522
523   if(mSquareBitmap)
524      extent.x > extent.y ? extent.x = extent.y : extent.y = extent.x;
525
526   // We need to negate the y-axis so we are correctly oriented with
527   // positive y increase up the screen.
528   mScale.set(extent.x / terrDim, -extent.y / terrDim, 0);
529
530   Point3F terrOffset = -terrPos;
531   terrOffset.convolve(mScale);
532
533   // We need to add the y extent so we start from the bottom left of the control
534   // rather than the top left.
535   mCenterPos.set(terrOffset.x + F32(offset.x), terrOffset.y + F32(offset.y) + extent.y);
536}
537
538void GuiMissionAreaCtrl::getScreenMissionArea(RectI & rect)
539{
540   RectI area = mMissionArea->getArea();
541   Point2F pos = worldToScreen(Point2F(F32(area.point.x), F32(area.point.y)));
542   Point2F end = worldToScreen(Point2F(F32(area.point.x + area.extent.x), F32(area.point.y + area.extent.y)));
543
544   //
545   rect.point.x = S32(round_local(pos.x));
546   rect.point.y = S32(round_local(pos.y));
547   rect.extent.x = S32(round_local(end.x - pos.x));
548   rect.extent.y = S32(round_local(end.y - pos.y));
549}
550
551void GuiMissionAreaCtrl::getScreenMissionArea(RectF & rect)
552{
553   RectI area = mMissionArea->getArea();
554   Point2F pos = worldToScreen(Point2F(F32(area.point.x), F32(area.point.y)));
555   Point2F end = worldToScreen(Point2F(F32(area.point.x + area.extent.x), F32(area.point.y + area.extent.y)));
556
557   //
558   rect.point.x = pos.x;
559   rect.point.y = pos.y;
560   rect.extent.x = end.x - pos.x;
561   rect.extent.y = end.y - pos.y;
562}
563
564Point2I GuiMissionAreaCtrl::convertOrigin(const Point2I &pos)
565{
566   // Convert screen point to our bottom left origin
567   Point2I pnt = globalToLocalCoord(pos);
568   const Point2I& extent = getExtent( );
569   pnt.y = extent.y - pnt.y;
570   Point2I pt = localToGlobalCoord(pnt);
571   return pt;
572}
573
574void GuiMissionAreaCtrl::onRender(Point2I offset, const RectI & updateRect)
575{
576
577   RectI rect(offset, getExtent());
578   F32 fillOffset = GFX->getFillConventionOffset();
579
580   setUpdate();
581
582   // draw an x
583   if(!bool(mMissionArea) || !bool(mTerrainBlock))
584   {
585      GFX->setStateBlock(mSolidStateBlock);
586      PrimBuild::color3i( 0, 0, 0 );
587      PrimBuild::begin( GFXLineList, 4 );
588
589      PrimBuild::vertex2f( rect.point.x + fillOffset, updateRect.point.y + fillOffset );
590      PrimBuild::vertex2f( rect.point.x + updateRect.extent.x + fillOffset, updateRect.point.y + updateRect.extent.y + fillOffset );
591      PrimBuild::vertex2f( rect.point.x + fillOffset, updateRect.point.y + updateRect.extent.y + fillOffset );
592      PrimBuild::vertex2f( rect.point.x + updateRect.extent.x + fillOffset, updateRect.point.y + fillOffset );
593
594      PrimBuild::end();
595
596      return;
597   }
598
599   //
600   setupScreenTransform(offset);
601
602   // draw the terrain
603   if(mSquareBitmap)
604      rect.extent.x > rect.extent.y ? rect.extent.x = rect.extent.y : rect.extent.y = rect.extent.x;
605
606   GFXDrawUtil *drawer = GFX->getDrawUtil();
607   drawer->clearBitmapModulation();
608   drawer->drawBitmapStretch(mTextureObject, rect, GFXBitmapFlip_Y, GFXTextureFilterLinear, false);
609
610   GFX->setStateBlock(mSolidStateBlock);
611   drawer->clearBitmapModulation();
612
613   // draw the reference axis
614   PrimBuild::begin( GFXLineList, 4 );
615      PrimBuild::color3i( 255, 0, 0 );
616      PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset );
617      PrimBuild::vertex2f( rect.point.x + 25 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset );
618      PrimBuild::color3i( 0, 255, 0 );
619      PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset );
620      PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 25 + fillOffset );
621   PrimBuild::end();
622
623   RectF area;
624   getScreenMissionArea(area);
625
626   // render the mission area box
627   PrimBuild::color( mMissionBoundsColor );
628   PrimBuild::begin( GFXLineStrip, 5 );
629      PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + fillOffset);
630      PrimBuild::vertex2f(area.point.x + area.extent.x + fillOffset, area.point.y + fillOffset);
631      PrimBuild::vertex2f(area.point.x + area.extent.x + fillOffset, area.point.y + area.extent.y + fillOffset);
632      PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + area.extent.y + fillOffset);
633      PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + fillOffset);
634   PrimBuild::end();
635
636   // render the camera
637   //if(mRenderCamera)
638   {
639      CameraQuery camera;
640      GameProcessCameraQuery(&camera);
641
642      // farplane too far, 90' looks wrong...
643      camera.fov = mDegToRad(60.f);
644      camera.farPlane = 500.f;
645
646      //
647      F32 rot = camera.fov / 2;
648
649      //
650      VectorF ray;
651      VectorF projRayA, projRayB;
652
653      ray.set(camera.farPlane * -mSin(rot), camera.farPlane * mCos(rot), 0);
654      camera.cameraMatrix.mulV(ray, &projRayA);
655
656      ray.set(camera.farPlane * -mSin(-rot), camera.farPlane * mCos(-rot), 0);
657      camera.cameraMatrix.mulV(ray, &projRayB);
658
659      Point3F camPos;
660      camera.cameraMatrix.getColumn(3, &camPos);
661
662      Point2F s = worldToScreen(Point2F(camPos.x, camPos.y));
663      Point2F e1 = worldToScreen(Point2F(camPos.x + projRayA.x, camPos.y + projRayA.y));
664      Point2F e2 = worldToScreen(Point2F(camPos.x + projRayB.x, camPos.y + projRayB.y));
665
666      PrimBuild::color( mCameraColor );
667      PrimBuild::begin( GFXLineList, 4 );
668         PrimBuild::vertex2f( s.x + fillOffset, s.y + fillOffset );
669         PrimBuild::vertex2f( e1.x + fillOffset, e1.y + fillOffset );
670         PrimBuild::vertex2f( s.x + fillOffset, s.y + fillOffset );
671         PrimBuild::vertex2f( e2.x + fillOffset, e2.y + fillOffset );
672      PrimBuild::end();
673   }
674
675   // render the handles
676   RectI iArea;
677   getScreenMissionArea(iArea);
678   drawHandles(iArea);
679
680   renderChildControls(offset, updateRect);
681}
682
683//------------------------------------------------------------------------------
684
685DefineEngineMethod( GuiMissionAreaCtrl, setMissionArea, void, ( MissionArea* area ),,
686   "@brief Set the MissionArea to edit.\n\n")
687{
688   object->setMissionArea( area );
689}
690
691DefineEngineMethod( GuiMissionAreaCtrl, updateTerrain, void, ( ),,
692   "@brief Update the terrain bitmap.\n\n")
693{
694   object->updateTerrain();
695}
696
697//------------------------------------------------------------------------------
698
699void GuiMissionAreaUndoAction::undo()
700{
701   MissionArea *ma = NULL;
702   if ( !Sim::findObject( mObjId, ma ) )
703      return;
704
705   // Temporarily save the MissionArea's current data.
706   RectI area = ma->getArea();
707
708   // Restore the MissionArea properties saved in the UndoAction
709   ma->setArea( mArea );
710   ma->inspectPostApply();
711
712   // Now save the previous Mission data in this UndoAction
713   // since an undo action must become a redo action and vice-versa
714   mArea = area;
715
716   // Let the script get a chance at it.
717   Con::executef( mMissionAreaEditor, "onUndo" );
718}
719