Professional

Part I: Getting Started Chapter 1: Understanding Flash3D ...... 3 Chapter 2: Getting Started With Papervision3D ...... 33 Chapter 3: Rezzing Primitives...... 75 Chapter 4: Adding Materials ...... 127

Part II: Working with Models and Data Chapter 5: Creating 3D Models ...... 173 Chapter 6: Working with Particle Systems ...... 221 Chapter 7: Geocoding, XML, and Databases ...... 253 Chapter 8: Gliding on AIR...... 285

Part III: Building Games and Websites Chapter 9: Incorporating 3D Physics ...... 321 Chapter 10: Building 3D Games for Wii ...... 365 Chapter 11: Integrating the Flash Media Server ...... 417 Chapter 12: Developing 3D Websites ...... 459

Part IV: Extending PV3D and Beyond Chapter 13: Making 3D Movies ...... 491 Chapter 14: Taking Virtual Tours ...... 521 Chapter 15: Adding Services ...... 553 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond ...... 585

Appendix A: Pocket Reference ...... 623 Appendix B: Companion Website ...... 693 Index ...... 697

Professional Papervision3D

Michael Lively

A John Wiley and Sons, Ltd., Publication Professional Papervision3D This edition first published 2010 © 2010 John Wiley & Sons, Limited

Registered office John Wiley & Sons Ltd, The Atrium, Southern Gate, Chichester, West Sussex, PO19 8SQ, United Kingdom

For details of our global editorial offices, for customer services and for information about how to apply for permission to reuse the copyright material in this book please see our website at www.wiley.com.

The right of the author to be identified as the author of this work has been asserted in accordance with the Copyright, Designs and Patents Act 1988.

All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording or otherwise, except as permitted by the UK Copyright, Designs and Patents Act 1988, without the prior permission of the publisher.

Wiley also publishes its books in a variety of electronic formats. Some content that appears in print may not be available in electronic books.

Designations used by companies to distinguish their products are often claimed as trademarks. All brand names and product names used in this book are trade names, service marks, trademarks or registered trademarks of their respective owners. The publisher is not associated with any product or vendor mentioned in this book. This publication is designed to provide accurate and authoritative information in regard to the subject matter covered. It is sold on the understanding that the publisher is not engaged in rendering professional services. If professional advice or other expert assistance is required, the services of a competent professional should be sought.

A catalogue record for this book is available from the British Library.

ISBN 978-0-470-74266-2

Set in 9/11 Palatino Roman by Macmillan Publishing Solutions Printed in Great Britain by Bell & Bain, Glasgow To Carlotta, my gift from God . . . and Josh, Sam, James, Mary, Jonathan, Rachael, Abigail, Davy, and Naomi . . . God’s gifts to us.

About the Author

Michael Lively is presently working at the University of Central Florida simulating nano - technology in Papervision3D and CS4. He has worked for 22 years in government and academia and has been programming in Flash and Flex for the past 10 years, building learning management systems. He has worked as the Director of Instructional Design at Northern Kentucky University, Manager of Instructional Design at the University of Cincinnati, Department Chair at Palm Beach Community College in Electronics Engineering Technology, and as a freelance consultant. He holds a BA in Mathematics, BS in Chemistry, BSEE in Electrical Engineering, MS in Physics, and is presently completing his Ph.D. in Quantum Physics. Acknowledgments

It takes many people to publish a book, and first and foremost, I like to thank everyone at Wiley for their support: specifically I would like to thank Chris Webb, Associate Publisher, whose encouragement and professionalism guided me through the entire writing process, and Kenyon Brown, development editor, who kept everything moving on track and in the right direction.

Also, a big thanks to Dan Orlando, technical editor of the book who ’ s technical insight made this book possible. We were very fortunate to have Dan aboard and he played an integral part in the book ’ s creation.

Finally, many thanks to my lovely wife Carlotta who spent many hours helping me proof and edit the book. Credits

Associate Publisher Associate Production Director Chris Webb Martin Tribe

Assistant Editor Project Coordinator, Cover Colleen Goldring David Mayhew

Publishing Assistant Copy Editor Ellie Scott Anne O’Rorke

Development Editor Compositor Kenyon Brown Macmillan Publishing Solutions, Chennai, India

Technical Editor Proofreader Dan Orlando Martin Horsgood

Project Editor Indexer Juliet Booker Jack Lewis – j&j indexing

Production Editor Cover Image Claire Spinks © Punchstock/Score by Aflo

Editorial Director Michelle Leete

Contents

Acknowledgments viii Introduction xxi

Part I: Getting Started Chapter 1: Understanding Flash3D 3 3D Coordinates in Flash 10 4 Building a 3D Flash 9 Engine in 18 Lines of Code 5 Applying Thales Theorem 5 Deriving the Scaling Equation 6 Rendering to the Screen 8 Coding 9 Running the Code 11 Vanishing Point (Asymptotic ) 12 Adding a Camera 13 Using CS4 (3D Flash 10 Engine in 13 Lines of Code) 14 Vanishing Point 14 Using the Big 3 15 Translation 15 Rotation 21 Scaling (Basic Principles of Animation) 28 Summary 32

Chapter 2: Getting Started With Papervision3D 33 Getting Papervision3D 33 Diving into 3D 34 Shapes Versus Objects 35 Hidden Surface Drawing 37 Painter’s Algorithm 37 The View Frustum (Culling and Clipping) 38 Exploring the Guts of Papervision3D 39 DisplayObject3D 41 Running Papervision3D Four Different Ways (Hello World) 41 Using Flash or Flex 42 Contents

Building a Class 43 Writing an OOP (Object Oriented) Class 43 Formulating a Papervision3D Class 50 Running Papervision3D Applications 57 Document Class 57 Class Path 58 Flex Builder 59 Adding Motion 60 Setting the Stage 65 Align 65 Scale Mode 66 Quality 66 Using BasicView (Hello Plane) 67 Encapsulation 69 Undulating Sphere using BasicView 69 Incorporating CS4 72 Summary 73

Chapter 3: Rezzing Primitives 75 Understanding Primitives 75 Exploring the Guts of a Papervision3D Primitive 76 Rezzing Primitives to the Stage 79 Prim Template Code (Using a Wireframe Material) 80 Plane 81 Cube 83 Making Ellipsoids 95 Creating Custom Prims 96 Pinched Sphere 96 Pyramid 97 Geodesic 99 Curved Plane 100 Puckered Cube 101 Torus 102 Tube 105 Hourglass 107 Second Life Tree 108 Double-Sided Plane 110 Creating CS4 Primitives 110 Second Life Tree in Flash CS4 111 Plane 114 Cube 114 xii Contents

Pyramid 118 CS4 Super Prim (Plane, Cylinder, Cone, Sphere, Half Torus . . . ) 120 Dissecting the Big Three 125 Camera and Viewport 126 Summary 126

Chapter 4: Adding Materials 127 Applying Textures 127 Using Simple Materials (or just Materials) 129 Material Types 130 Adding Materials to a Prim 130 Bitmaps 134 Movie Clips 142 Understanding Light 147 Using 148 Adding Brightness 160 Bump Map 162 Summary 169

Part II: Working with Models and Data Chapter 5: Creating 3D Models 173 Modeling for Papervision3D 173 Bringing Down the Count 174 Gauging Performance in PV3D 175 Choosing a Modeling Application 176 Blender 177 3DSMax 178 Google SketchUp 178 Swift3D 179 Papervision3D Importers 179 What’s MD2? 180 Model Pack 181 Creating MD2 Files (MilkShape to the Rescue) 185 Making Things Move 186 What’s Up with KMZ? 187 Hello Collada 187 Single and Multiple Objects with Simple Images 188 Single Object with Multiple Images 191 Interactive Objects (or Vehicles) and Environments 194

xiii Contents

Animated Objects 198 What’s Up with SketchUp? 200 Binary versus XML 201 Writing Your Own Parsers 201 Papervision3D and Collada 202 Simple Blender Exporters 202 Simple 3DSMax Exporters 204 Understanding Collada Parsers 204 Processing Images (and Pixel Bender) 206 What’s Pixel Bender? 206 Getting Started with Pixel Bender 207 Writing a Pixel Bender Filter 208 Adding Parameters 210 Typical Point Processes 210 Grayscale 211 Complement 211 Brightness 211 Exposure 211 Color Balance 211 Binary Contrast 212 Simple Pixelate 212 Simple Blur 212 Sharpen 213 Simple Point Light 213 Bloom Brightness 214 Multiple Image Processing 214 CrossFade 214 Subtraction and Addition 214 Bringing Pixel Bender into Flash 215 Adding Pixel Bender to Papervision3D 215 Creating a Multiple Light 217 Animating Your Light Sources 218 Summary 219

Chapter 6: Working with Particle Systems 221 Particle Systems in PV3D 221 Creating Particles in PV3D 222 Putting Stars in a Skybox 223 Creating a Generic CS4 Particle Class 227

xiv Contents

Plug-in Media 244 Flint Particles 249 Viewing Particle Examples 249 Using Flint in Papervision3D 249 Getting More Bang Out of Flash 251 Summary 251

Chapter 7: Geocoding, XML, and Databases 253 Geocoding 253 Putting a Google Map on a PV3D Prim 254 Using Air (Flash Sandbox Security Issue) 255 Putting it on the Web with Flex 4 (Gumbo) 259 XML 262 What Is XML Anyway? 263 Taking a Historic Tour 265 Building a Planetarium in PV3D 268 Building a Flash CS4 Planetarium 273 Working with MySQL and PHP 275 What’s a Database Anyway? 275 Using Flex with a Database 276 Installing a Test Server 276 Creating a Map Maker Database 277 Automatically Generating PHP 278 Summary 284

Chapter 8: Gliding on AIR 285 Building a Simple Modeling Program 285 Placing Particles at Vertices 286 Modeling Terrains 287 Creating a Terrain Editor in AIR 293 Drawing Code 294 Using Radio Buttons 295 Adding Sliders 296 Using Buttons 296 Working with Perlin Noise 298 Adding Key listeners 299 Incorporating Adobe AIR 306 Accessing your Local File System 307 Saving BitmapData 307

xv Contents

Saving to Your Local PC Using Flash 10 310 Turning Your Webcam into a PV3D Heightmap 311 Using SQLite 312 Deploying AIR 312 Sculpting a Prim Using face3d 313 Summary 317

Part III: Building Games and Websites Chapter 9: Incorporating 3D Physics 321 Understanding the Guts of Physics 321 Adding Physics to Your Camera 322 Creating Camera Perspectives 323 Cruising the Stars Using Cameras 324 Adding Physics to the DisplayObject3D Class 326 Building Your Own Physics Classes 328 Simple Pendulums: Line, Ball, Animation 329 Double Pendulum: What’s pinning? 330 Torsion Pendulum: Cylinder on a Rod 331 Foucault Pendulum: A Little More Realism 331 Interacting with Objects in PV3D 332 Building an Interactive Cube: Face Level 332 Browser Double-Click Problems 337 Building Interactive Particles: Object Level 338 Rotating Small Sphere 338 Clicking on Particles 339 Rolling Over the x, y, z Gizmo 340 Taking it to the Next Level: 3D Wall Course 341 The Big Problem 344 Rapid Large-Scale Development 345 Using 3rd Party Physics Engines 346 WOW Physics 347 Jiglibflash 348 Implementing Jiglibflash 348 Building an Example Viewer 356 AS3Dmod 360 The Heart of AS3Dmod 361 Making National Treasure: Porting 361 Summary 363

xvi Contents

Chapter 10: Building 3D Games for Wii 365 Shooting Pool in Space 366 Five Game States: Start, Play, Win, Lose, Cancel 367 What’s Modal Control? 367 Adding a Background Image 368 Using View States 368 Building a Custom Skybox 373 Building a Jiglib Bounding Box 375 Creating Your Game Elements 377 Following Game Flow 386 Optimizing Your Game for the Web 390 Using Your Wii 393 Wii Starter Code 393 Drawing with Silly String 397 Adding Wii to Your Game 402 Playing Wii Jiglib Pong 404 Building the Bounding Box 406 Adding Jiglib 406 Moving at Constant Velocity 408 Adding AI 408 Making Game Levels and a Scoring System 408 Creating Textboxes 410 Adding Reflectivity and a SkyBox 411 Building a Wii Navigation System 411 Adding Mouse-Controlled Paddle Motion 414 Summary 415

Chapter 11: Integrating the Flash Media Server 417 Getting Started 417 Configuring Your Development Environment 419 Connecting to the FMS 419 The Big Mistake 421 Checking on Your Program’s Performance 422 Running a RSO Hello World 423 Dragging Objects 436 Building a Race Track 437 Creating a Track 438 Adding the FMS to Your Slot Car 445 Turning Your Code into a Game 456

xvii Contents

FMS Alternatives 456 Red 5 457 Wowza 457 Collaboration Service 457 Summary 458

Chapter 12: Developing 3D Websites 459 What You Need to Know 459 Why Use 3D 459 Building it Right in Flash 460 Learning Photoshop 461 Creating a Site Map 461 Converting CSIS to 3D 462 Making it 3D 463 Using Reflection 464 Making your Collage 465 Making Clouds 466 Making Trees 468 Placing your Panels with XML 470 Building a Nav System 474 Hey! Where’s the Preloader? 476 Flirting with Web Spiders 483 Using HTML 484 Using SWFObject 485 Summary 488

Part IV: Extending PV3D and Beyond Chapter 13: Making 3D Movies 491 Houston . . . We’ve Got a Problem 491 Using Flash CS4 492 Rebuilding CSIS Seven Revolutions in Flash CS4 492 Adding a Preloader 499 Bridging the Gap with Flash Catalyst 500 Laying it out in Photoshop 500 Creating Code Automatically in Flash Catalyst 501 Sending Your Work to Flash Builder 503

xviii Contents

Pushing the Envelope with Flash Builder 503 Birthing a Spark 504 Animating in 3D Using Flash Builder 505 Going 3D with Photoshop 511 Getting Started Tips 511 Unlocking the Power of Google 3D Warehouse 511 Recording 3D 512 Creating an Animation Recorder in PV3D 512 Storing Your Results Using the DOM 515 Animating Spark 516 Building a PV3D Studio 518 VizualPV3D 518 Summary 519

Chapter 14: Taking Virtual Tours 521 Hello “Uncle Sam” 521 Using Drones – the Yang of VR 522 Creating Augmented Reality 523 Playing a Game in Augmented Reality 527 Building 3D Worlds 550 Summary 551

Chapter 15: Adding Services 553 Tweeting 553 Creating Your Layout in Photoshop 554 Adding Interactivity in Flash Catalyst 554 Building Connectivity in Flash Builder 559 Adding 3D to Your Flickr Menu 565 Using Photoshop 565 Adding 3D in Flash Catalyst 566 Hooking it up in Flash Builder 566 Mining Yahoo’s Treasure Trove 570 Checking Your Local Weather 571 Building a CNN News Feed 577 Connecting Your Service 577 Binding Your Data 578 Configuring Your Data 578 Summary 583

xix Contents

Chapter 16: Flash & Math: Exploring Flash 10 and Beyond 585 Rebuilding the 3D Pool “Shooting” 586 Laying out Your Game’s Framework 586 Adding Code in Flash Builder 589 Optimizing PV3D for Flash 10 590 Rendering 3D Models in Flash 10 591 Creating a 3D Image Ball 591 Using drawTriangles to a Shaded TIE Fighter 594 An Orbiting Ship using drawTriangles 599 Taking drawTriangles to the Next Level 606 Building a Google Wormhole 606 Creating a Molecule Viewer 610 Mapping a Single Image onto an Octahedron 617 Using ILayoutElement 620 Summary 621

Appendix A: Pocket Reference 623

Appendix B: Companion Website 693

Index 697

xx Introduction

Flash have always enjoyed a freedom of expression unparalleled in other programming platforms. And with the release of AS3 and CS4, Adobe has propelled that freedom of expression into the third dimension.

But long before AS3, Flash developers were experimenting with 3D. And applications like Papervision3D formalized these endeavors into a robust object - oriented class structure. The acceptance and popularity of Papervision3D has become a driver of change in the Flash developer community.

Originally conceived by Carlos Ulloa in November 2005 and converted to Great White by Ralph Hauwert, Papervision 3D has grown from approximately 20 to over 300 classes. It ’ s maintained by a 10+ member core and committer team. And with the release of Papervision3D, 2.0, developers worldwide have been contributing to this phenomenal open source project.

But what ’ s so special about 3D? Why should you even care? Besides the “ wow! ” factor, 3D has the highest learning impact on your brain: driven by the most efficient information processor in your body – your visual cortex. If you want to inject information into your client rapidly – do it visually!

It ’ s not just visual stimulation that creates a good learning environment; it ’ s also tapping into your client ’ s emotions. This is the essence of brain - based learning; an emotionally driven 3D visual experience. That ’ s why 3D RPGs (role - playing games), like Second Life, have so much popularity on the web. They ’ ve tapped into their client ’ s emotionally driven visual cortex.

Creating complete web experiences like the one described previously required extending Papervision3D beyond its present framework. And one of the purposes of Professional Papervision3D is to provide you with the necessary tools to do so.

Flash 3D coding has come a long way since the early days of Flash. And the 3D coding architectures of the future will not only be fast (incorporating such technologies as Pixel Bender and Alchemy), but also self - generating. Full 3D websites including 3D models will be created using graphical methods very similar to those of Flash Catalyst.

This book and its website bridge the gap between the old and the new, showing how Papervision3D ’ s solid OOP (object oriented programming) structure can be extended to add additional features, and looking into the next generation of 3D architecture, which incorporates auto - code generation using tools such as Flash Catalyst and Flash Builder.

Who This Book Is For Professional Papervision3D and its companion website have been specifically written for beginning and intermediate developers. It ’ s been created for those who are seeking both basic knowledge and the tools required to extend Papervision3D beyond its present capabilities. Introduction

An emphasis has been placed on learning OOP throughout the book. And various class packages are built and the principles behind their construction explained. In addition, much starter code has been created that can be used for more advanced projects.

Many basic concepts are explained in simple understandable terms and illustrated through numerous code examples. In addition, each major topic in the book has a video associated with it found on the book ’ s website. This approach is designed to maximize your grasp of the content and convey concepts difficult to explain in text. Finally, a number of supporting topics such as the use of Photoshop, 3DSMax, and Blender are covered on the book ’ s website as well.

If you ’ re trying to learn PV3D for the first time, need to master OOP, want to learn PV3D supporting technologies or to extend PV3D to do something that ’ s not currently possible – this is the book for you!

What This Book Covers The book covers everything that you need to know to get started in Papervision3D, and how to extend Papervision3D and create your own Papervision3D classes. It covers how to work with 3D models and add data connectivity to your programs. It covers how to build Wii games and 3D websites. And shows you how to extend what you ’ ve learned to build 3D models in CS4.

How This Book Is Structured The chapters in Professional Papervision3D are divided into four major parts:

❑ Getting Started ❑ Working with Models and Data ❑ Building Games and Websites ❑ Extending PV3D and Beyond

Each part has four chapters and each chapter builds on the next with an emphasis on digging into PV3D ’ s classes, understanding important OOP principles, and creating your own supporting 3D classes. Each part (and their chapters) is described below.

Part I: Getting Started In Part I, you learn all the basics to get you up to speed in using PV3D. You learn how to build 3D engines, how to download and get PV3D running, how to build classes, and how to work with primitives and materials. Finally, you extend these concepts to build CS4 primitives with materials.

Chapter 1, “ Understanding Flash3D ” — Flash 3D is one of the fastest - moving areas in web technology. In this chapter, you learn how to build a simple 3D engine in both CS3 and CS4. Using what you learn

xxii Introduction

about 3D engines you create a torus worm, carousel, and image ball. Finally, you examine Disney ’ s rules for creating realistic animation, and convert a timeline animation into ActionScript.

Chapter 2, “ Getting Started With Papervision3D ” — In this chapter, you ’ re introduced to the basics of Papervision3d, Painter ’ s Algorithm, and the view frustum. You learn about culling and clipping and examine the guts of Papervision3d. You instantiate your first primitive (a sphere) and add a wireframe material. You extend your application using BasicView, and examine the different ways to run an application.

Chapter 3, “ Rezzing Primitives ” — In this pivotal chapter, you examine how prims are made in both Papervision3D and CS4. You create your own custom prim by examining the parametric equations of different prims. And in the case of creating a Second Life tree, you analyze the potential savings between using Papervision3D and CS4. You learn about the powerful new classes and methods in the CS4 library (such as Matrix3D, drawTriangles, and vectors). And learn to create your own CS4 super prim, using a switch case and timer.

Chapter 4, “ Adding Materials ” — In this chapter you turn the corner from 3D techno - babble to application building. You learn the basics of how materials are used to create objects and about their lighting. You learn how to add brightness to a Papervision3D light source, create shades, and make bump maps. And you extend these concepts to CS4.

Part II: Working with Models and Data In Part II, you learn how to create your own 3D models and import then into PV3D. You learn to create particle systems, bring Google Maps into PV3D and CS4, and build data - driven applications in Flash, Flex, and Air.

Chapter 5, “ Creating 3D Models — In this chapter, you start with modeling and end up with Pixel Bender. Regardless of how PV3D changes over time the principles presented in this chapter will be around for a while. You ’ ll still need parsers to bring in vertex data regardless of your software environment. And Pixel Bender, the new kid on the block, will obviously become the cornerstone of any new 3D package hitting the scene.

Chapter 6, “ Working with Particle Systems ” — This chapter gives a broad overview of particles in both PV3D and CS4. You start with the PV3D and add some of your own particles to it and create a starry panorama. You build a 3D particle system from scratch and create a Flash CS4 glowworm. You learn how to slice and explode particle systems, and how to use them to interact with video. You take a look at the great work that Plug - in Media is doing and learn how to incorporate FLINT into PV3D.

Chapter 7, “ Geocoding, XML, and Databases ” — In this chapter, you turn the corner from learning the inner workings of PV3D to using it to build data - driven web applications. You build a number of applications in Air, Flash CS4, and Flex, which illustrate the use of XML, PHP, and MySQL. You learn how to use the Flex data wizard to automatically create PHP code, which is used to make server requests.

Chapter 8, “ Gliding on AIR ” — In this chapter, you build your first editor in Adobe Air. During the development process you master the use of a few basic Flex components that you ’ ll use again to create other editors. Accessing your local PC file system using Air and Flash10, you save your editor results to

xxiii Introduction

your local hard drive. You learn about creating grayscale height maps and using the geometry.vertices property to bring those maps into PV3D. Using this you create a PV3D terrain viewer to view your heightmaps. Extending your editor you capture your webcam, programmatically change it into grayscale, and bring it into your PV3D terrain viewer.

Part III: Building Games and Websites In Part III you explore bringing physics into PV3D by creating custom physics classes, you modify the DisplayObject3D class, and you learn to use Jiglib. You build Wii controlled games, investigate creating multiplayer games using the Flash Media Server, and create a 3D website.

Chapter 9, “ Incorporating 3D Physics ” — In this chapter, you examine a number of approaches to bring physics into PV3D. You start by creating a spring camera, and beefing up the DisplayObject3D class to add gravity effects, to create orbiting planets. You create custom classes for oscillation and learn how to make particles interactive using the interactive scene manager (or ISM). You learn how to build large - scale applications using states, modules, and porting. You examine both WOW and Jiglibflash physics engines. Finally, you build a Jiglib Hello World example and a Jiglib example viewer.

Chapter 10, “ Building 3D Games for Wii ” — Building games is great fun and in this chapter you learn the basics of building game systems, which include game states and multi - levels. This chapter explores two different games: pool “ shooting ” and Pong. In the pool game you learn to build an entire game system based on Flex View States. And in the Pong game you learn to build a multi - level Wii controlled game. Both games are built in Flex, but the Pong game can easily be built in Flash, as it ’ s an ActionScript package. You build bounding boxes for your games by hacking the Jiglib PV3D plugin class and create a skybox.

Chapter 11, “ Integrating the Flash Media Server ” — Creating Rich Internet Applications has long been the goal of (now Adobe). An integral part of that has been using the Flash Media Server to create interactive web experiences. In this chapter, you learn how to get your users interacting with 3D objects using remote shared objects. You also create the starter code for a slot car racing game. Finally, you examine alternatives to the FMS such as Red 5, Wowza, and the Flash Collaboration Service.

Chapter 12, “ Developing 3D Websites ” — In this chapter you convert the CSIS 2D site to a 3D site. You create custom tree, cloud, and collage classes and build a 3D navigation system from scratch using PV3D components. You examine a few important reality checks when it comes to building websites: using a design doc, learning Photoshop, and combining 2D with 3D to get an optimized 3D experience. Finally, you learn how to optimize your website for search engines by adding html text to your html - wrapper file and by using the SWFObject.

Part IV: Extending PV3D and Beyond In Part IV, you learn to take PV3D to the next level of performance by using Flash CS4, Photoshop3D, Flash Catalyst, and Flash Builder. You add augmented reality, services, and examine a number of CS4 rendering engines.

Chapter 13, “ Making 3D Movies ” — This chapter highlights one of the greatest challenges developers face today – the rapid change in programming architecture and platforms. This is demonstrated by building the Seven Revolutions project from the previous chapter in Flash CS4. Next, you examine Flash Catalyst and its integration into Flash Builder. Finally, the new Animate Super Effects class is used to xxiv Introduction

create a panel animation, Photoshop3D is examined, and a PV3D animation engine that saves its results to the DOM is developed.

Chapter 14, “ Taking Virtual Tours ” — As technology advances, is playing a key role. In this chapter you examine using VR in the military, using augmented reality, and building 3D worlds. The majority of the chapter treats augmented reality using the FLARToolkit, which was created by Saqoosha. Saqoosha ’ s starter kit code is examined and extended to create PV3D BasicView starter code. Using this starter code a number of items are imported in FLAR including the Jiglib pong game created in the chapter on games.

Chapter 15, “ Adding Services ” — In this chapter you learn to hook up Web Services using Flash Catalyst and Flash Builder. You build a Twitter viewer, Weather checker, Flickr picker, and CNN news reader. Flash Builder has a full range of data connectivity options . . . WSDL, PHP, Coldfusion, BlazeDS, and LCDS. This makes data connectivity easier and gives you more time for creative development.

Chapter 16, “ Flash & Math: Exploring Flash 10 and Beyond ” — Flash 3D coding has come a long way, and over the next few years will transcend even optimistic expectations. In this chapter you scratch the surface of such technologies by rebuilding your 3D pool “ shooting ” game in Flash Catalyst, and visiting a number of CS4 rendering examples.

Appendices The book has two appendices:

Appendix A is a pocket reference that contains all the important formulas and code snippets from each chapter. It purpose is for quick reference when programming applications that may use similar code snippets.

Appendix B describes the book ’ s companion website in detail, describing its purpose and various training courses designed to help convey the book ’ s material and the use of supporting technologies.

What You Need to Use This Book Professional Papervision3D starts by emphasizing the use of Flash and moves to Flex 3 (Gumbo), Air, Flash Catalyst and Flash Builder. In addition, Papervision3D uses a number of supporting technologies such as Blender, Photoshop, 3DSMax, and so on.

The applications that you need to complete the exercises in this book fully are Flash CS4, Photoshop, Pixel Bender, Blender (or 3DSMax), Flash Catalyst, MilkShape, and Flash Builder.

To help you master a number of these supporting technologies the book ’ s website has the following video tutorials to help you get started:

❑ Photoshop ❑ Illustrator (Basics) ❑ 3DS Max

xxv Introduction

❑ Blender ❑ SketchUp ❑ Flash CS4 ❑ Flash Catalyst ❑ Flash Builder (Gumbo)

If you ’ re on a budget, Blender is free (at www.blender.org ) and the Adobe products can be downloaded for full 30 days trials from www.adobe.com , the Eclipse + Flex SDK version is free, and Pixel Bender is free at http://labs.adobe.com/technologies/pixelbender/ .

The Book ’s Companion Website The book ’ s companion website (at www.professionalpapervision.com ) is an integral part of conveying the book ’ s contents. The days when code was small and easily explained in a few written pages are gone. Today, many Flash applications are thousands of lines of code with large supporting libraries. Using video to help convey the concepts behind such applications is the primary goal of the book ’ s website.

You ’ ll find a large set of video resources on the book ’ s website designed to help you come up to speed on the book ’ s topics. The website consists of:

❑ Book chapter videos on each major topic ❑ Training videos on topics supporting PV3D ❑ Chapter code for download and viewing ❑ Additional topics from the book ❑ Bonus examples and new Flash 3D technologies ❑ Current 3D topics from UCF

Using the book and the book ’ s training videos together should help you come up to speed rapidly in Flash 3D and its supporting technologies.

How to Use This Book Each major topic in the book has a video associated with it. This approach is designed to maximize your grasp of the content and convey concepts difficult to explain in text. In some cases, portions of the book were written to go hand - in - hand with its counterpart video content. To maximize your absorption of the contents, first read a section in the book, watch its video, try the code examples, and then try extending the code ’ s capability.

xxvi Introduction Conventions To help you get the most from the text and keep track of what ’ s happening, we ’ ve used a number of conventions throughout the book:

❑ We show file names, URLs, and code within the text like so: persistence.properties . ❑ We present code in two different ways:

In code examples we highlight new and important code with a gray background. The gray highlighting is not used for code that’s less important in the present context, or has been shown before. Source Code Many Flash applications are thousands of lines of code and have large supporting libraries. Due to the size of many 3D applications you no longer have the luxury of showing every line of code that goes behind each application. And in many instances, in this book, after a discussion of how code works the reader is sent to the book ’ s website to download the entire application for review.

The amount of code written for the book was significant (about 119,000 files), and as the book was written the versions of PV3D and other supporting software changed. So as opposed to trying to keep track of every different version of PV3D, the version of PV3D (and other supporting software) used to create each application was included with each application.

All of the source code used in this book is available for download at http://www.wrox.com . Once at the site, simply locate the book ’ s title (either by using the Search box or by using one of the title lists) and click the Download Code link on the book ’ s detail page to obtain all the source code for the book.

Because many books have similar titles, you may find it easiest to search by ISBN; this book ’ s ISBN is 978 - 0 - 470 - 74266 - 2.

After you download the code, just decompress it with your favorite compression tool. Alternately, you can go to the main Wrox code download page at http://www.wrox.com/dynamic/books/download .aspx to see the code available for this book and all other Wrox books.

Errata We make every effort to ensure that there are no errors in the text or in the code. However, no one is perfect, and mistakes do occur. If you find an error in one of our books, like a spelling mistake or faulty piece of code, we would be very grateful for your feedback. By sending in errata, you may save other readers hours of frustration and at the same time you ’ ll be helping us provide even higher quality information.

To find the errata page for this book, go to http://www.wrox.com and locate the title using the Search box or one of the title lists. Then, on the book details page, click the Book Errata link. On this page you can view all errata that has been submitted for this book and posted by Wrox editors. A complete book

xxvii Introduction

list including links to each book ’ s errata is also available at http://www.wrox.com/misc-pages/ booklist.shtml .

If you don ’ t spot “ your ” error on the Book Errata page, go to http://www.wrox.com/contact/ techsupport.shtml and complete the form there to send us the error you ’ ve found. We ’ ll check the information and, if appropriate, post a message to the book ’ s errata page and fix the problem in subsequent editions of the book.

p2p.wrox.com For author and peer discussion, join the P2P forums at http://p2p.wrox.com. The forums comprise a web - based system for you to post messages relating to Wrox books and related technologies and to interact with other readers and technology users. The forums offer a subscription feature to email you topics of interest of your choosing when new posts are made to the forums. Wrox authors, editors, other industry experts, and your fellow readers are present on these forums.

At http://p2p.wrox.com you ’ ll find a number of different forums that ’ ll help you not only as you read this book, but also as you develop your own applications. To join the forums, just follow these steps:

1. Go to http://p2p.wrox.com and click the Register link. 2. Read the terms of use and click Agree. 3. Complete the required information to join as well as any optional information you wish to provide and click Submit. 4. You will receive an email with information describing how to verify your account and complete the joining process.

You can read messages in the forums without joining P2P, but in order to post your own messages, you must join.

Once you join, you can post new messages and respond to messages posted by other users. You can read messages at any time on the web. If you would like to have new messages from a particular forum emailed to you, click the Subscribe to this Forum icon by the forum name in the forum listing.

For more information about how to use the Wrox P2P, be sure to read the P2P FAQs for answers to questions about how the forum software works, as well as many common questions specific to P2P and Wrox books. To read the FAQs, click the FAQ link on any P2P page.

xxviii Part I: Getting Started

Chapter 1: Understanding Flash3D

Chapter 2: Getting StartedPart With Papervision3DXX

Chapter 3: RezzingPart Primitives Title

Chapter 4: Adding Materials Part 1: Getting Started

In Part I, you learn all the basics to get you up to speed for using Papervision3D (PV3D). You learn how to build 3D engines, download and get PV3D running, build classes, and work with primitives and materials. Finally, you extend these concepts to build CS4 primitives with materials.

2 Understanding Flash3D

Flash programmers have always enjoyed a freedom of expression unparalleled by other programming platforms. And with the release of CS4, Adobe has propelled that freedom of expression into the 3rd dimension.

But 3D didn ’ t start with AS3. Flash developers were experimenting with 3D long before. And applications like Papervision3D formalized these endeavors into a robust object - oriented class structure. The acceptance and popularity of Papervision3D has become a driver of change in the Flash developer community. But underneath the 300 classes that make up Papervision3D still beats the original algorithms used by the early developers of Flash 3D.

Understanding how to create 3D in Flash is essential to fully grasping Papervision3D and applications like it. As you learn how 3D engines were originally constructed, you ’ ll gain both an insight into Papervision3D ’ s core architecture and an appreciation of its robust structure. Its complexity will fade into a set of fundamental 3D algorithms.

Papervision3D, at its core, is a perspective projection engine, where projection simply means transforming a 3D “ object ” space into 2D Flash x and y screen space. And surprisingly, the whole process hinges on one equation: a perspective scaling equation derived from Thales Theorem:

T = scale = focal length/(focal length + z)

In the equation above, T is the perspective scale, and z is the distance from the projection plane. The focal length (or calculated “ screen ” location) determines the amount of perspective provided in the view. You use this equation to create an illusion of depth by scaling 2D surfaces.

In this chapter, you use Thales Theorem to create a simple 3D engine in just 19 lines of code. Then using Flash CS4 you rebuild your engine in just 13 lines of code. You cover the big 3: translation, rotation and scaling. And applying what you ’ ve learned you create a 3D torus worm, carousel, and image ball. Finally, you cover the basics of making believable animation, and how to turn a timeline animation into pure ActionScript. Part 1: Getting Started

But before you get started it ’ s important that you understand the coordinate system you ’ ll be rendering your objects in. It ’ s a little different to the way you learned it in math class, and well worth reviewing.

3D Coordinates in Flash 10 Understanding the Flash coordinate system is vital to rendering in Papervision3D or CS4. With the transition to Flash Player 10, every ActionScript display object has a z property. Adding a 3rd dimension allows an object to move towards and away from the viewpoint of the user using perspective scaling.

As a side note, this extra dimension is easily handled using a 3D transformation matrix that incorporates the three fundamental transformations found in all physical systems, such as translation, rotation, and scaling. Every 3D object within Papervision3D has a Matrix3D. The Matrix3D class supports complex transformations of 3D geometry, such as rotation, scaling, and translation.

But unlike other 3D systems, especially those you might have met in math class, the y and z - axes are reversed as shown in Figure 1.1.

Y

Z

X X

Z

Y Figure 1-1

From the figure you can see that:

❑ x increases as you move to the right along the x - axis ❑ y increases as you move down along the y - axis ❑ z increases as you move away from the viewpoint.

4 Chapter 1: Understanding Flash3D

In addition, in Flash 9 the coordinate origin (0,0) is not located at the center of the screen, but at the upper left corner of your screen. This becomes an issue when working with the asymptotic vanishing point, and you ’ ll learn how to adjust for this later in this chapter.

Building a 3D Flash 9 Engine in 18 Lines of Code In this example, you ’ re going to build a 3D engine based on perspective scaling in just 19 lines of code. Before Flash 10 there was no native support for 3D and all you had were x and y coordinates to play around with. So you needed to add another dimension to get 3D (a z - axis). The trick to creating a z - axis was to use perspective scaling. Which means that as an object moves away from you it gets smaller and as it moves towards you it gets larger. But we need to quantify this idea (make some math out of it) so we can program it. And that come from Thales Theorem.

Applying Thales Theorem The Greeks are famous for their geometry and Thales (a Greek Philosopher) was the first to propose similar triangles. From the concept of similar triangles you get simple linear projection: which is the heart of a Flash 3D engine. Of course perspective drawing really didn ’ t take off until the Renaissance, and now again in Flash 3D.

Imagine that you ’ re in your home looking out the window. As you approach the window objects outside look larger and as you get farther away from the window, objects outside look smaller. Your distance from the window is called your focal length, the window is your projection plane (or viewport), and your eye is the vanishing point.

Now remain stationary, this fixes your focal length; watch outside as objects move closer to and farther away from your window. As a bird flies closer to the window it looks larger and as it flies away it looks smaller. This is your z - axis: the distance between the outside object and your windowpane.

The equation that governs this behavior is:

T= scale = focal length/(focal length + z)

where T equals “ one ” when the outside object is at the window and “ zero ” when your object (bird) flies far away (off to infinity, also called vanishing point). This equation works well for Flash and is illustrated in the graphic below as a Blender monkey peers at a projection plane. Focal length is the distance from the vanishing point (monkey ’ s eye) to the projection plane (see Figure 1.2).

5 Part 1: Getting Started

Figure 1-2

Creating the illusion of depth (or 3D) using perspective scaling is sometimes referred to as 2.5D.

Another term used to describe perspective projection is 2.5D. The term is usually used with , especially video games, where a computer system uses to visually simulate . In Flash, you use the z perspective scale to create a perspective projection onto the Flash x, y screen.

Deriving the Scaling Equation You ’ re probably wondering how the scaling equation presented above was derived. As mentioned earlier, it came from Thales ’ idea of similar triangles. The figure below shows two nested (or similar) triangles. Small h is the size of an object at the computer screen and large H is the actual size of the object beyond the screen. And as described earlier fl (or focal length) is the distance from your computer screen to your eye, and z is the distance on the other side of your screen to your object as shown in Figure 1.3.

Computer Screen

H h

fl z

Vanishing Point

Figure 1-3

6 Chapter 1: Understanding Flash3D

From the law of similar triangles, it follows that

h H fl fl z

By cross - multiplying, you get the scaling equation:

h fl Scale H fl z

That ’ s all there is to its derivation, but examining the equation reveals an interesting phenomenon: a singularity (blow up) in z. As z approaches – fl your equation ’ s denominator goes to zero and your scale blows up to infinity. And as shown in the figure below after you pass – fl your figure goes through an inversion (it flips over).

You ’ re probably wondering how fl (focal length) is assigned. Is it actually the distance of your eye to the computer screen? No! It ’ s a virtual quantity that was used in the derivation of the scaling equation. But that doesn ’ t mean it doesn ’ t have meaning. It is the focal length of your camera, and its assignment changes the appearance of what you see on your computer screen.

The figure below illustrates a number of important occurrences in the life of the scaling equation:

❑ When z = 0 your image scale is 1 (image is its actual size) ❑ When z = fl your image scale is ½ (image is half its size) ❑ As z approaches fl your scale approaches infinity (image is infinitely large) ❑ As z approaches infinity your scale approaches zero (image has vanished)

Of the four conditions listed above, two are used most by mathematicians to quickly define the behavior of a function, the singularity (blow up point) and asymptotic behavior (as z approaching infinity). The asymptotic behavior is important here since as your object moves towards infinity it vanishes. This is referred to as the vanishing point and occurs at a specific x, y position of your screen. In Flash, that position is at the origin that occurs at the upper left corner of your computer screen. Later in this chapter you ’ ll find out how to change your vanishing point.

This may cause you a little confusion since you ’ ve already learned that the vanishing point occurs at the eye as shown in Figure 1.2 previously. So are there two vanishing points? Definitely, but the first one that occurred at the eye was used for derivation purposes and is where your singularity occurs (shown in Figure 1.4). The second vanishing point occurs at infinity. It ’ s the one that you ’ ll now be most interested in working with. It determines where your 3D object goes (on the x,y screen) as it gets further away from you.

7 Part 1: Getting Started

as z > fl, scale > infinity scale

1 1/2 as z > infinity, scale > 0 z z 0 z fl z fl is a singularity at vanishing point Inversion fl z

Figure 1-4

The curve above is very similar to the gravitational or electrical potential curves found in physics. And it further suggests that a size scaling force field could be constructed by taking the differential of the scaling equation. But that ’ s beyond the scope of this book.

Rendering to the Screen Using perspective scale you ’ ve picked up a 3rd dimension: z. But in order to render it to the Flash Stage you ’ ve got to get rid of it again. The process is called rendering and occurs by projecting a 3D object (created from perspective) to a 2D computer screen (or viewport).

Projection onto the Flash screen requires that you convert a 3D point (x, y, z) into a 2D (x, y) point. You accomplish this by using scaling to drop the z coordinate. A change in z affects the position and size of an object in 3D space as shown in the code below:

scale=focal length/(focal length + z); x=x*scale; y=y*scale; xscale=yscale=scale*100.

In the code, the scale (based on focal length and z) changes the x and y position and the x and y scale, resulting in a 2.5D effect described earlier. And it ’ s done the same way in Papervision3D. Throughout this book, you ’ ll be drilling down into Papervision3D ’ s classes and examining how it does what it does. Below is a code snippet taken from its Camera3D class.

8 Chapter 1: Understanding Flash3D

In Chapter 2, you ’ ll find out how to obtain the Papervision3D classes and how to drill down and examine its code. This particular code snippet is found in the org/papervision3d/camera folder.

if (screen.visible = ( focus + s_z > 0 )) { s_x = vx * m11 + vy * m12 + vz * m13 + view.n14; s_y = vx * m21 + vy * m22 + vz * m23 + view.n24;

//perspective scaling in Papervision persp = fz / (focus + s_z); screen.x = s_x * persp; screen.y = s_y * persp; screen.z = s_z. }

The code snippet above taken from Papervision3D ’ s Camera3D class demonstrates how Papervision3D uses perspective scaling. You ’ ll use this idea to create a 3D Flash engine in just 19 lines of code.

Coding Animation Okay, you should now understand enough about perspective scaling to begin coding your 3D engine. In this example, you create your code in the Flash API, but many Papervsion3D developers use either Flex or Eclipse for their code development work. Flex and Eclipse offer superior code debugging and code hinting capabilities over Flash, and in many instances their use speeds your code development by a factor of 4. It ’ s worth trying one of them out.

In this exercise, you ’ ll create a circle that oscillates in and out of the screen in the z perspective direction. Whether using the timeline or pure ActionScript, Flash in essence is an animation machine. When using purely scripted animation, the animation process is based upon a looping (or updating) procedure shown in Figure 1.5.

Grab Imports Set Initial Parameters Listener Create Graphical Elements

Render Display Alter Frame Frame Frame

Figure 1-5

9 Part 1: Getting Started

The typical flow of such a program involves first initializing the program and then using a listener to loop through a scripted animation routine. The whole process can be divided into three parts: initialize, listen and loop: Initialize: Steps 1.3 Initializing an AS3 program can be a little overwhelming at first. The question that most people ask is how do I know what imports to bring in? There is no magical pool of knowledge that can help you. It just takes experience (or reading the documentation, or paying attention to syntax). As you work with the different Flash and Papervision3D methods you ’ ll become experienced in spotting classes that need to be imported. For example, if you ’ re creating a Wii game, it ’ s obvious that you ’ ll need to import some Wii classes.

But, which ones? It depends on which methods you ’ re using to create your program. From experience, you ’ ll begin to recognize and associate the right classes with their methods. Or better yet, Flex builder has an auto - complete that automatically adds the classes you need upon auto - completion of a method.

In the example below, you create a ball sprite, so it makes sense that you ’ ll need to import the Sprite class. After importing the Sprite class, you initialize the position and angle of the ball and then set your focal length (from experience 300 works well for this example). Then in step 3, you create your ball, give it a color and set it on the stage using the addChild method.

1. Start by importing the Sprite class; this is where you ’ re going to draw a ball that you will animate in 3D. import flash.display.Sprite;//imports sprite class

2. Next declare your variable ’ s zposition, angle, and focal length. var zposition:Number = 0;//z position var myAngle:Number =0;//Angle of ball var fl:Number = 300; //focal length

3. Next create your ball and add it to the stage.

var ball:Sprite = new Sprite();//instantiates ball sprite ball.graphics.beginFill(0xFF0000);//Assigns a ball color ball.graphics.drawCircle(0, 0, 40);//draws your ball at (0,0) ball.graphics.endFill();//ends the fill addChild(ball);//adds the ball to the stage Listen: Step 4 In AS3, the event architecture is bigger, better, and badder (in a good way). Written from the ground up, it ’ s fast, powerful, and easy to use. It incorporates listener objects to listen for events. Since event listeners all work the same way, once you understand how to use one of them you understand them all. This is how they work:

10 Chapter 1: Understanding Flash3D

❑ Call the method addEventListener to listen for an event ❑ Name the event you want to listen for ❑ Name the function you want to execute when the event occurs

So in the code below, you want to iterate your animation periodically. This can be done using the enter frame event listener or a timer. In this case, you ’ ll listen for the enter frame event. Each time a frame event occurs the function onEnterFrame is called, as shown in step 4.

❑ Next create your onEnterFrame listener which loops through your equations of motion. This is the heart of all 3D engines. addEventListener(Event.ENTER_FRAME, onEnterFrame);//loops equations Loop: Step 5 Looping is the heart of creating a scripted animation. You ’ re probably familiar with creating frame loops on Flash ’ s timeline. But in Papervision3D, you don ’ t use the timeline. Everything is done in ActionScript. But you can still create graphical animation by creating a frame ripper, or use animation script created in Flash by grabbing the animation elements as classes.

In Step 5, the animation is created by incrementing the angle on each loop. As the angle is incremented the ball oscillates and the z position changes.

❑ Create the function that will be looped. These are your equations of motion that govern the perspective as it is changed and converts 3D to 2D (or projects onto the viewport). function onEnterFrame(event:Event):void{ var scale:Number = fl / (fl + zposition);//scale perspective myAngle=myAngle+.1;//iterates angle myB all.x = 300*Math.sin(myAngle)*scale; //ball orbit x ball.y = 300*Math.cos(myAngle)*scale; //ball orbit y ball.scaleX = scale;//scales perspective in x ball.scaleY = scale;//scales perspective in y zposition = 2000*Math.sin(myAngle/10);} //increments z and changes sign.

Upon every loop, the myAngle variable is iterated and the ball.x and ball.y positions oscillate sinusoidally. This creates the circling motion of the ball. In the last line of code the zposition variable oscillates sinusoidally as well, causing the ball to cycle in and out of the screen. By putting these two motions together your ball will spiral in and out of the screen in the z direction.

You now put it all together and run the code.

Running the Code The code (all 18 lines of it) is listed below. Just type it in the Flash ActionScript editor and click control test. You ’ ll see a red ball oscillating on the z - perspective axis. Not very exciting, but there are a number of concepts here that you ’ ll use throughout the book.

11 Part 1: Getting Started

import flash.display.Sprite;//imports sprite class var zposition:Number = 0;//z position var myAngle:Number =0;//Angle of ball var fl:Number = 300; //focal length var ball:Sprite = new Sprite();//instantiates ball sprite ball.graphics.beginFill(0xFF0000);//Assign a ball color ball.graphics.drawCircle(0, 0, 40);//draws your ball at 0,0 ball.graphics.endFill();//ends the fill addChild(ball);//adds the ball to the stage addEventListener(Event.ENTER_FRAME, onEnterFrame);//loops equations function onEnterFrame(event:Event):void{ var scale:Number = fl / (fl + zposition);//scale perspective myAngle=myAngle+.1;//iterates angle myB all.x = 300*Math.sin(myAngle)*scale; //ball orbit x ball.y = 300*Math.cos(myAngle)*scale; //ball orbit y ball.scaleX = scale;//scales perspective in x ball.scaleY = scale;//scales perspective in y zposition = 2000*Math.sin(myAngle/10);} //increments z and changes sign

The code, though it ’ s not Papervision3D, illustrates a number of important concepts that every 3D engine possesses:

❑ A frame looper/renderer ❑ Perspective (z - coordinate) ❑ Projection onto a viewport ❑ Primitive or basic shape ❑ Addition of a color (or material)

And all of this is done in just 19 lines of code. If only it had stayed this simple. Papervision3D started off with only 20 classes, now it ’ s in the hundreds and growing. But as they say, no pain no gain.

Vanishing Point (Asymptotic Zero) As your animation runs, the spiraling red ball approaches its vanishing point (asymptotic zero), which as mentioned earlier, is the Flash origin located at (0, 0) or upper left position of your screen. This is a little annoying, but can be easily adjusted by moving your vanishing point. This is done by adding a vanishing point coordinate value to your ball x and y positions. To center the vanishing point to the Flash stage you must import the stage class and use stage.stageWidth/2 (which returns the x center point) and stage.stageHeight/2 (which returns the y centerpoint) as shown here:

ball.x = 300*Math.sin(myAngle)*scale+stage.stageWidth/2; ball.y = 300*Math.cos(myAngle)*scale+stage.stageHeight/2;

The results of this change are shown in Figure 1.6.

12 Chapter 1: Understanding Flash3D

Vanishing at the origin (0, 0)

Adjusted Vanishing Point (stageWidth/2, stageHeight/2)

Figure 1-6

The stage class is very useful in positioning your Flash objects and will be addressed in more detail in the next chapter.

Adding a Camera Along with 3D comes the concept of a camera. Theoretically a camera is just a point in 3D space that acts as a point of view of that space. Changing the position of your camera lets you change your view in your 3D world. But in reality your camera isn ’ t moving, it ’ s just offsetting everything else in relation to itself. So for example, if you want your camera to move through a maze, you have to move the maze around your camera – not the camera around the maze.

Programmatically you accomplish this by creating a camera object and giving it x, y, and z values. You use these values to adjust the position of your objects in your scene (represented by the statement below).

//create your camera object camera = new Object(); camera.x = 10; camera.y = 10; camera.z = 100;

//loop through for all elements in your scene scale = focalLength/(focalLength + this.z - camera.z); this.x = (this.x - camera.x) * scale; this.y = (this.y - camera.y) * scale; this.xscale = this.yscale = 100 * scale;

It ’ s important to understand that by putting the camera.z in the numerator of your scale equation you ’ ve actually changed the position of your singularity. This can produce some weird effects since at the singularity your object will blow up in size and then flip.

You could continue to extend this train of thought adding many other features such as rotation and scaling, as was done with Papervision3D. But there is a better way!

13 Part 1: Getting Started

In CS4 the perspective scaling and rotation are automatically built into Flash. It ’ s still important to know the concepts above when working with Flash 10 (that ’ s why they were included), but it ’ s just not necessary to do all this work. To illustrate the point, rebuild your spiraling 19 line animation engine in CS4.

Using CS4 (3D Flash 10 Engine in 13 Lines of Code) Using CS4 you only need 13 lines of code to create the same animation that took 18 lines above. Saving 5 lines may not seem like much, but it enables you to drop your perspective scaling and let the Flash 10 engine do all the work.

import flash.display.Sprite;//imports sprite class var myAngle:Number =0;//Angle of ball var ball:Sprite = new Sprite();//instantiates ball sprite ball.graphics.beginFill(0xFF0000);//Assign a ball color ball.graphics.drawCircle(0, 0, 40);//draws your ball at 0,0 ball.graphics.endFill();//ends the fill addChild(ball);//adds the ball to the stage addEventListener(Event.ENTER_FRAME, myonEnterFrame);//loops equations function myonEnterFrame(event:Event):void{ myAngle=myAngle+.1;//iterates angle ball.x = 300*Math.sin(myAngle); //ball orbit x ball.y = 300*Math.cos(myAngle); //ball orbit y ball.z = 2000*Math.sin(myAngle/10);} //increments z and changes sign

Allowing Flash 10 to do the perspective scaling gives you much leverage in building 3D applications easily. Essentially, it ’ s what Papervision3D does, but now that power is built directly into the Flash 10 player with no complicated set up or massive imports. Not that you ’ re going to abandon Papervision3D . . . that ’ s what this book is about. But whenever a better way to do it exists in CS4, we ’ ll cover it as much as space allows.

Vanishing Point After running the program above, you ’ ll notice that the vanishing point moved. That ’ s because Flash 10 automatically set the vanishing point. It ’ s set upon initiation of the swf, based on the size set in the document properties panel. But there ’ s a glitch in Flex. Flex Builder ’ s default swf has a size of 500375 pixels, and you ’ ll probably never create an swf that size. As a result, all your vanishing points will be off. The solution is to use the PerspectiveProjection class to set your vanishing point. To set your display object to center stage, for example, you would use the following command:

myDisplayObject.transform.perspectiveProjections.projectionCenter = new Point(stage.stageWidth/2, stage.stageHeight/2);

Not only does it set the vanishing point for your display object (myDisplayObjec t in this case), but also its children. And that ’ s the power of CS4 ’ s new display object. When applying transforms to a CS4 display object, its children are transformed as well. Papervision3D does the same thing with its DisplayObject3D, but now it ’ s inherent in the Flash10 player. The flash.display.DisplayObject class contains the z property and new rotation and scaling properties for manipulating display objects in 3D space.

14 Chapter 1: Understanding Flash3D Using the Big 3 Fundamental to 3D graphics and physics are three fundamental motions: translation, rotation and scaling. All motions (at least for Newtonian motion) can be broken down into combinations of these three motions.

You can apply these 3D transformations all at once using a Matrix3D object. You can rotate, scale, and then move an object by applying three separate transformations or more efficiently by using one Matrix3D transformation. It ’ s important to remember that these matrix operations aren ’ t generally commutative: which means that applying a rotation and then translation won ’ t necessarily give the same results as applying the reverse order (a translation then rotation). The following code snippet shows how to cascade a series of transformations: rotation, scale, translation, and then rotation again.

var matrix:Matrix3D = myDisplayObject.transform.matrix3D; matrix.appendRotation(45, Vector3D.Y_AXIS); matrix.appendScale(2, 1, 3); matrix.appendTranslation(10, 150, -300); matrix.appendRotation(10, Vector3D.X_AXIS); myDisplayObject.transform.matrix3D = matrix;

Performing difficult matrix maths is unnecessary . . . that ’ s great news! Adobe has made it pretty easy to do this just by applying the simple transformations shown above. And in many cases it ’ s done automatically without the user even knowing it ’ s being performed. Consider translation, for example, when you explicitly set the z property of a display object to a numeric value, the object automatically creates a 3D transformation matrix. It all happens behind the scenes giving you more time to concentrate on building stellar 3D experiences.

Translation Adding a native z coordinate, in Flash 10, enables you to treat z just as you ’ ve treated x and y in the past. But using translation doesn ’ t just mean you ’ re traveling in a straight line. You can use z to constrain an element to a 3D trajectory path. As an example, consider a parametric path on a 3D surface such as a torus.

The parametric curves for a torus (and any other 3D surface) can be found at WolframMathWorld ( www.mathworld.wolfram.com ).

So what ’ s a parametric equation?

Parametric equations are a set of equations that define the coordinates of the dependent variables (x, y and z) of a curve or surface in terms of one or more independent variables or parameters. That ’ s a mouthful, but basically if you iterate over the range of your parameters (of the parametric equation) your torus will be plotted in 3D space. This is a very useful device as it gives you the vertices of your torus, and in Appendix A the parametric equation for a number of 3D objects are given.

15 Part 1: Getting Started

The parametric equations for a torus are:

x=(c+a*cos(v))cos(u) y=(c+a*cos(v))sin(u) z=a*sin(v)

where c is the donut radius, and a is the tube radius. And u is the parameter that takes you around the larger radius (the donut) and v around the smaller tube radius as shown in Figure 1.7.

Z

tube radius a u V

C donut radius

Figure 1-7

You now use these equations to create a parametric path on your torus. What you want to do is have your graphical element spiral around the torus. You can accomplish this by iterating the parameters u and v simultaneously adjusting v to get to the desired spiral velocity. The parametric path is extremely easy to execute. Just take the code from the 3D Flash 10 Engine in 13 lines and substitute your parametric equations for ball.x, ball.y, and ball.z as shown below:

import flash.display.Sprite;//imports sprite class var myAngle:Number =0;//Angle of ball var ball:Sprite = new Sprite();//instantiates ball sprite ball.graphics.beginFill(0xFF0000);//Assign a ball color ball.graphics.drawCircle(0, 0, 10);//draws your ball at 0,0 ball.graphics.endFill();//ends the fill addChild(ball);//adds the ball to the stage addEventListener(Event.ENTER_FRAME, myonEnterFrame);//loops equations function myonEnterFrame(event:Event):void{ myAngle=myAngle+.1;//iterates angle //ball parametric orbit x ball.x = (100+50*Math.cos(2*myAngle))*Math.cos(myAngle/4)+200; //ball parametric orbit y ball.y = (100+50*Math.cos(2*myAngle))*Math.sin(myAngle/4)+200; //ball parametric orbit z ball.z = 50*Math.sin(2*myAngle);}

It works flawlessly, but a single ball isn ’ t very exciting. Now add a few more balls to your parametric path.

16 Chapter 1: Understanding Flash3D

Creating a Parametric Particle System (Torus Worm) In the previous section, you learned how to get a single element orbiting a torus using the parametric equations of a torus. Here, you multiply that single element by 100 and create a parametric particle system, which orbits (or worms around) your torus.

You get a more formal treatment of particles in the “ Gaming ” section of this book. But for the most part, anything with more than one element can be treated as a particle system. Treating elements as particles has a number of advantages. Primarily, particles are easily handled mathematically. You ’ ll use particle systems extensively throughout this book.

Building a particle system in Flash is relatively easy, and in this section, you cover the basics. To build the orbiting worm around a torus (parametric particle system), follow the steps below:

1. Declare the number of particles to be created and create an array to place your particles in. This is the key to working with particles. Using the array “ particles_ary ” you ’ re able to control position, color, and alpha of each particle. var numOfParticles:uint = 100; var particles_ary:Array = [];

2. Use a “ for ” loop to run the updateStage() function, which creates your particles. Once the particles are created run the addEventListener method, which starts updating the loop for each particle on every frame event. for(var i:uint = 0; i < numOfParticles; i++) { updateStage(); numVar++; //Start Looping if(numVar==numOfParticles){ addEventListener(Event.ENTER_FRAME, myonEnterFrame);}

}

3. Draw your balls (particles) to the stage and place them in a particle array. A random color is assigned to each ball using the Math.random()*0xffffff method. //Draw a Ball var ball:Sprite = new Sprite //Assign a random ball color ball.graphics.beginFill(Math.random()*0xffffff); //draws your ball at 0,0 ball.graphics.drawCircle(0, 0, ballRadius); ball.graphics.endFill();//ends the fill //Add ball to the stage addChild(ball); //Push the ball into a particle array particles_ary.push(ball);

4. With each onEnterFrame loop you update all your particle positions using myAngle+i/20 that separates the different particles in angle by the ratio of i/20. This causes your particles to line up on the torus and move across its surface as the myAngle variable is iterated. Use the

17 Part 1: Getting Started

particles_ary[i].alpha method to change the alpha of each particle based on their ith position. Center the particle system on the stage by adding CenterX, and CenterY to the particle x, y positions. for(var i:uint = 0; i < particles_ary.length; i++) { //ball parametric orbit x var newAngle:Number=myAngle+i/20; particles_ary[i].alpha=1/(i+1)+.2; particles_ary[i].x = (100+60*Math.cos(2*newAngle))*Math.cos(newAngle/ 4)+CenterX; //ball parametric orbit y particles_ary[i].y = (100+60*Math.cos(2*newAngle))*Math.sin(newAngle/ 4)+CenterY; //ball parametric orbit z particles_ary[i].z = 60*Math.sin(2*newAngle); }

The results are shown in Figure 1.8 and result in a wormlike entity orbiting a torus. The key to creating particle systems and controlling them is to stuff the individual particles into an array upon creation. And then iterate over each particle during the frame loop of the animation sequence.

Figure 1-8

Putting it all together, the entire code is show below:

//imports sprite and stage class import flash.display.Sprite; import flash.display.Stage; //Add Particle Array var numOfParticles:uint = 100; var particles_ary:Array = []; var numVar:Number=0;

var ballRadius:Number=10; //Stage Center var CenterX:Number = stage.stageWidth/2; var CenterY:Number = stage.stageHeight/2;

18 Chapter 1: Understanding Flash3D var myAngle:Number =0;//Angle of ball

//Add Multiple Particles function updateStage():void { //Draw a Ball var ball:Sprite = new Sprite //Assign a random ball color ball.graphics.beginFill(Math.random()*0xffffff); //draws your ball at 0,0 ball.graphics.drawCircle(0, 0, ballRadius); ball.graphics.endFill();//ends the fill //Add ball to the stage addChild(ball); //Push the ball into a particle array particles_ary.push(ball); }

//Place Particles on the Stage for(var i:uint = 0; i < numOfParticles; i++) { updateStage(); numVar++; //Start Looping if(numVar==numOfParticles){ addEventListener(Event.ENTER_FRAME, myonEnterFrame);}

}

//Looping function function myonEnterFrame(event:Event):void{ myAngle=myAngle-.1;//iterates angle

for(var i:uint = 0; i < particles_ary.length; i++) {

//ball parametric orbit x var newAngle:Number=myAngle+i/20; particles_ary[i].alpha=1/(i+1)+.2; particles_ary[i].x = (100+60*Math.cos(2*newAngle))*Math.cos(newAngle/4)+CenterX; //ball parametric orbit y particles_ary[i].y = (100+60*Math.cos(2*newAngle))*Math.sin(newAngle/4)+CenterY; //ball parametric orbit z particles_ary[i].z = 60*Math.sin(2*newAngle);

}}

No discussion about particles would be complete without mention of Seb Lee Delisle, an expert in particle systems and one of the Papervision3D team members. Seb has a great site that covers particle creation in Papervision3D at http://www.sebleedelisle.com/. Next on the list of particle super stars is the

19 Part 1: Getting Started

Flint - Particle - System (at http://flintparticles.org/), a robust open source project found on Google Code, which can be used to create impressive particle systems for Papervision3D.

However, the particle landscape is changing. Having a native 3D and pixel bender with the release of CS4 greatly simplifies the process of building particle systems in 3D (as shown in the code above). Depth Sorting Shuffling Objects so that the ones closer to your viewpoint appear on top of objects farther away is called depth sorting (or z - sorting). One big advantage that Papervision3D has over CS4 is automatic depth sorting. In CS4, sorting algorithms must be written and unfortunately they ’ re dependent on object nesting.

When creating a 3D carousel for example you need to z -sort the objects as they spin to put the closest one on top of the stack. The technical name is transposition and in AS2 it was easily accomplished using the swapDepths method. But in AS3 it ’ s a little more complicated.

Objects on the Stage In AS3, the display list functions as an array and each display object has an index. The index starts at zero and goes up to the number of objects on your stage where index zero is the bottom object. So since the display object is a child you can change its position using the setChildIndex method.

As all your objects are in an array, you can sort that array by z and then set the indices of your array objects based on z. And that ’ s how it ’ s presently done! It uses the same code structure variables as the torus worm. Here ’ s a sort code snippet that illustrates the concept of sorting:

//Sorting function sortParticles():void { particles_ary.sortOn(“z”, Array.DESCENDING|Array.NUMERIC); for(var i:int = 0; i < particles_ary.length; i++) { addChildAt(particles_ary[i] as Sprite, i); } }

Using the pipe Array.DESCENDING|Array.NUMERIC forces the z value to be sorted in reverse numeric order. Particles are sorted from high to low based on their z value. Thus, the objects further away from your projection plane get a lower index value, placing them on the bottom of the stack.

This method needs to be called each time your 3D engine iterates the position of your objects. Typically this occurs on an onEnterFrame event. Unfortunately, you ’ re stuck with manual sorting. But expect sorting to be native to the next version of the Flash Player.

Objects in a Display Container In many examples in this book, you won ’ t render multiple objects directly to the stage, but inside a display container. This approach works well when you rotate multiple objects. Instead of creating a complex trig algorithm to rotate each individual object you can just rotate the display container.

20 Chapter 1: Understanding Flash3D

But in CS4 there ’ s a problem with this. When using the sorting algorithm (above) you sort the elements on the z - axis internal to the container. But when the container is rotated the objects rotate with it and are no longer sorted correctly. Essentially, there are two z values: z inside the container and z outside the container. To sort these correctly, you must sort with respect to the z axis outside your container. You must transform the local coordinates inside your container to the stage coordinates (or z outside your container).

Keith Peters in his advanced book on AS3 Animation proposed the following solution for the container - sorting problem.

//Sorting function sortParticles():void { particles_ary.sort(particleZSort); for(var i:int = 0; i < particles_ary.length; i++) { myDisplayObject.addChildAt(particles_ary[i] as Sprite, i); } }

// function particleZSort(particle1:DisplayObject,particle2:DisplayObject):int { var zpos1:Vector3D = particle1.transform.matrix3D.position; zpos1 = myDisplayObject.transform.matrix3D.deltaTransformVector(zpos1); var zpos2:Vector3D = particle2.transform.matrix3D.position; zpos2 = myDisplayObject.transform.matrix3D.deltaTransformVector(zpos2); return zpos2.z - zpos1.z; }

The particles_ary.sort method passes in the particleZSort function as a parameter. The particleZSort function is called multiple times with pairs of particles during a sort, returning a negative number if the first particle should be placed in front of the second. The Matrix3D and Vector3D methods are used to keep track of your objects ’ rotated positions, which we discuss in greater detail later in Chapter 3.

This method is computationally intensive, but works surprisingly well (as you can see from the Carousel and Image Ball examples next).

Rotation You ’ ve already met basic sinusoidal functions, such as sine and cosine, in this chapter: it ’ s these functions that give you the ability to rotate elements in space. Even though rotation is inherent in Papervision3D (and Flash 10), you still use these functions occasionally when manipulating elements in 3D space. For example, in the gaming section of this book, you use these equations to rotate a Rubik ’ s cube.

21 Part 1: Getting Started

3D Rotation To rotate x, y, z points in 3D space you use the following equations:

xnew = xold*cos(angleZ) - yold*sin(angleZ)

ynew = xold*sin(angleZ) + yold*cos(angleZ)

xnew = xold*cos(angleY) - zold*sin(angleY)

znew = xold*sin(angleY) + zold*cos(angleY)

ynew = yold*cos(angleX) - zold*sin(angleX)

znew = yold*sin(angleX) + zold*cos(angleX)

There are actually two ways to rotate elements in Papervision3D; using the preceding matrix equations or using quaternions. Most 3D engines start with the preceding matrix equations. Flash 10 Rotation In Flash 10, rotation around the x, y, and z - axis is clockwise as angle increases. Rotation around the x, y, and z - axis are accomplished using the rotationX , rotationY , and rotationZ commands respectively. Just as in Papervision3D, the rotational values are given in degrees as opposed to radians, which had been the practice in Flash previously. But when using sine and cosine functions you still use radians (see Figure 1.9).

Y

rotationY Z

rotationX

X X

rotationZ

Z

Y Figure 1-9

22 Chapter 1: Understanding Flash3D

The conversion between radians and degrees and vice - versa is given below:

Radians = Degrees*PI/180 Degrees = Radians*180/PI

Although these conversions are very simple, you use them all the time. Creating a Carousel and Image Ball Two of the most popular navigation devices created using Papervision3D are the carousel and image ball. As done in the torus worm example, you ’ ll treat the planes in the carousel and image ball as particles. Place these particles into a display object so you can rotate them with a single rotation command. But in this case (as opposed to the torus worm example), you ’ ll need to sort your planes according to their z - positions. Otherwise, as you rotate your display container, your planes will not overlap correctly.

Carousel Creating a carousel is very similar to creating the torus worm demonstrated earlier. As in the torus worm case, the planes of the carousel are treated as particles. And the planes are placed in Sprite display objects with the variable name myDisplayObject . Placing the planes in a display object gives you the ability to rotate all the planes at once using the rotationY method.

The individual planes are created using the drawRect method.

var plane:Sprite = new Sprite();//instantiates plane sprite plane.graphics.beginFill(Math.random()*0xffffff);//Assign a plane color plane.graphics.drawRect(-60, -60, 80, 80);//draws your plane at 0,0 plane.graphics.endFill();//ends the fill

Using a little trig to place your planes is at the heart of the code. The planes are set around a circle of radius 200 pixels using sine and cosine functions.

plane.x = Math.cos(angle) * 200; plane.z = Math.sin(angle) * 200;

But the orientation of each plane is not correct. Each plane is facing the same direction. To align the planes around the carousel correctly you use the rotationY method. Dividing - 360 by the number of particles, multiplying by the iteration variable i, and adding 90 degrees sets the planes to the right orientation.

plane.rotationY = -360 / numOfParticles * i + 90;

Once the planes are placed correctly in the carousel they ’ re sorted using the container - sorting algorithm discussed in the previous section (see Figure 1.10).

23 Part 1: Getting Started

Figure 1-10

Finally the display container is rotated based on mouse movement using a simple mouseX displacement algorithm.

function myonEnterFrame(event:Event):void { myDisplayObject.rotationY += (CenterX - mouseX) * .01; sortParticles();

}

The complete carousel code is listed here:

//imports classes import flash.display.DisplayObject; import flash.display.Sprite; import flash.display.Stage; import flash.geom.Vector3D; //Add Particle Array var numOfParticles:uint = 12; var particles_ary:Array = []; var numVar:Number=0; var myDisplayObject:Sprite= new Sprite();

var planeRadius:Number=20; //Stage Center var CenterX:Number = stage.stageWidth/2; var CenterY:Number = stage.stageHeight/2;

//Place Planes on the Stage for(var i:uint = 0; i < numOfParticles; i++) { //Draw a plane var plane:Sprite = new Sprite();//instantiates plane sprite plane.graphics.beginFill(Math.random()*0xffffff);//Assign a plane color plane.graphics.drawRect(-60, -60, 80, 80);//draws your plane at 0,0 plane.graphics.endFill();//ends the fill

//Add plane to the stage myDisplayObject.addChild(plane); var angle:Number = Math.PI * 2 / numOfParticles * i;

plane.x = Math.cos(angle) * 200; plane.z = Math.sin(angle) * 200; plane.y =0;

24 Chapter 1: Understanding Flash3D

plane.rotationY = -360 / numOfParticles * i + 90; plane.alpha=1; particles_ary.push(plane);

numVar++; //Start Looping if(numVar==numOfParticles) { addEventListener(Event.ENTER_FRAME, myonEnterFrame); addChild(myDisplayObject); myDisplayObject.rotationX=0; myDisplayObject.x=CenterX; myDisplayObject.y=CenterY;

} }

//Sorting function sortParticles():void { particles_ary.sort(particleZSort); for(var i:int = 0; i < particles_ary.length; i++) { myDisplayObject.addChildAt(particles_ary[i] as Sprite, i); } }

function particleZSort(particle1:DisplayObject, particle2:DisplayObject):int { var zpos1:Vector3D = particle1.transform.matrix3D.position; zpos1 = myDisplayObject.transform.matrix3D.deltaTransformVector(zpos1); var zpos2:Vector3D = particle2.transform.matrix3D.position; zpos2 = myDisplayObject.transform.matrix3D.deltaTransformVector(zpos2); return zpos2.z - zpos1.z; } //Looping function function myonEnterFrame(event:Event):void { myDisplayObject.rotationY += (CenterX - mouseX) * .01; sortParticles();

}

Image Ball An image ball is just a bunch of images equally distributed around a sphere. The trick to distributing the images correctly is to use the parametric equation for a sphere, which can be found from WolframMathWorld. Iterating through the different rows and columns of the parametric equations distributes the planes to their correct positions. But they won ’ t be oriented correctly.

plane.x = radius*Math.sin(i*pStep)*Math.sin(j*tStep); plane.z = -radius*Math.sin(i*pStep)*Math.cos(j*tStep); plane.y =-radius*Math.cos(i*pStep);

25 Part 1: Getting Started

You must orient the planes to the correct angles so that they uniformly hug the sphere as shown in Figure 1.11.

plane.rotationX=phiTilt[i]; plane.rotationY=-j*thetaStep[i];

For this particular example the vectors for the plane positions and angles were obtained from Flash & Math ( http://www.flashandmath.com/ ).

Figure 1-11

As in the previous example, you use the z - sorter and mouseX displacement algorithm code to sort the rectangles correctly in depth and move the entire container using the rotationY method.

The complete image ball code is listed below:

//imports sprite and stage class import flash.display.DisplayObject; import flash.display.Sprite; import flash.display.Stage; import flash.geom.Vector3D; //Add Particle Array var particles_ary:Array = []; var myDisplayObject:Sprite= new Sprite();

//Image Ball Parameters //Parameters adapted from Flash & Math // http://www.flashandmath.com/ var jLen:Vector. < Number >=new Vector. < Number > (); jLen=Vector. < Number > ([1,6,10,12,10,6,1]); var thetaStep:Vector. < Number >=new Vector. < Number > (); thetaStep=Vector. < Number > ([0,60,36,30,36,60,0]);

26 Chapter 1: Understanding Flash3D

//The vertical angle between circles. var phiStep:Number=30; var phiTilt:Vector. < Number >=new Vector. < Number > (); phiTilt=Vector. < Number >([-90,-60,-30,0,30,60,90]); //radius of the Sphere var radius:Number=180; //Stage Center var CenterX:Number = stage.stageWidth/2; var CenterY:Number = stage.stageHeight/2;

//Place Particles on the Stage var i:int; var j:int; var tStep:Number; var pStep:Number=phiStep*Math.PI/180; for(i=0;i < 7;i++){ tStep=thetaStep[i]*Math.PI/180; for(j=0;j < jLen[i];j++) {

//Draw a plane var plane:Sprite = new Sprite();//instantiates plane sprite plane.graphics.beginFill(Math.random()*0xffffff);//Assign a plane color plane.graphics.drawRect(-30, -30, 30, 30);//draws your plane at 0,0 plane.graphics.endFill();//ends the fill

//Add plane to the stage myDisplayObject.addChild(plane); //Use parametric equations of a sphere plane.x = radius*Math.sin(i*pStep)*Math.sin(j*tStep); plane.z = -radius*Math.sin(i*pStep)*Math.cos(j*tStep); plane.y =-radius*Math.cos(i*pStep); //Set rotations plane.rotationX=phiTilt[i]; plane.rotationY=-j*thetaStep[i]; particles_ary.push(plane); } }

//Start Looping and Set Display Object addEventListener(Event.ENTER_FRAME, myonEnterFrame); addChild(myDisplayObject); myDisplayObject.rotationX=0; myDisplayObject.x=CenterX; myDisplayObject.y=CenterY;

//Sorting function sortParticles():void { particles_ary.sort(particleZSort); for(var k:int = 0; k < particles_ary.length; k++) { (continued)

27 Part 1: Getting Started

(continued)

myDisplayObject.addChildAt(particles_ary[k] as Sprite, k); } }

function particleZSort(particle1:DisplayObject, particle2:DisplayObject):int { var zpos1:Vector3D = particle1.transform.matrix3D.position; zpos1 = myDisplayObject.transform.matrix3D.deltaTransformVector(zpos1); var zpos2:Vector3D = particle2.transform.matrix3D.position; zpos2 = myDisplayObject.transform.matrix3D.deltaTransformVector(zpos2); return zpos2.z - zpos1.z; }

//Looping function function myonEnterFrame(event:Event):void { myDisplayObject.rotationY += (CenterX - mouseX) * .01; sortParticles();

}

Scaling (Basic Principles of Animation) Many of the basic principles of animation, developed by Walt Disney, have their roots in scaling.

Scaling is the factor that adds realism to your 3D animations. For example, as a ball bounces against a hard surface it should exhibit a squash and stretch effect (accomplished by scaling). Walt Disney and his team of animators quantified the rules of cartoon animation. And no matter how great your images and 3D scenes are, if you don ’ t apply these principles, your 3D animations will appear lifeless.

Here ’ s a summary of Walt ’ s Rules of animation. Applying them will bring your 3D animations to life.

❑ Squash and Stretch occurs when a soft object comes into contact with a hard surface. It deforms proportionally to its velocity. The deformation exhibits both a squash and stretch, but its volume is conserved. In 3D, x and z are the stretch axis and y the squash axis. ❑ Exaggeration brings attention to an activity that ’ s being performed. Its motion is a little more flamboyant than in real life, but it draws the viewers in and adds character to your animation. ❑ Anticipation is the prep before the action. Prep - action before the action builds the excitement for what ’ s about to happen. It ’ s one of the great secrets of animation. Every animation should have anticipation injected in it – you should look for every opportunity to do so. ❑ Follow - Through is the action after the action. Once the action occurs, such as throwing a ball, or closing a car door, there ’ s follow - through. It establishes the personality of your character. It ’ s the aftermath of an action that really sells it.

28 Chapter 1: Understanding Flash3D

❑ Weight is the concept most forgotten in animation. Animation software doesn ’ t know how much something weighs. Communicating weight to your audience requires that your animated character shows strain when it picks up something heavy. An execution of squash and stretch principle is illustrated in Figure 1.12. It shows a bouncing ball squash and stretch as it hits the floor. As the ball approaches the floor, both shadow and ball stretch.

Squash and stretch

Figure 1-12

The bouncing ball above was created completely in the Flash CS4 motion editor and involves several motion edits. Here ’ s a really cool trick that enables you to apply this animation sequence to any object. You now turn this timeline - based animation completely into ActionScript. Turning Timeline Animation into ActionScript One of the amazing additions to Flash 10 is the motion editor, which gives you powerful control over the individual properties of your 3D animations. All animation can be done completely from the motion editor and motions created can be saved as custom presets and added to any object. In addition, there are tons of presets to choose from.

A full treatment of the motion editor is beyond the scope of this book. A good place to learn about the motion editor is to view the CS4 video tutorial series by Todd Perkins on http://www.Lynda.com. Lynda. com is a video tutorial service that specializes in Multimedia and Adobe products. They ’ re relatively inexpensive and fairly up to date.

Converting Your Animation You can convert 3D animation, created on the timeline or in the motion editor, into ActionScript. It ’ s pretty simple. Just right click on the timeline animation tween and choose Copy Motion As ActionScipt

29 Part 1: Getting Started

3.0. The ActionScript is then copied to your clipboard and you can paste this code wherever you want. If you try this with the bouncing ball example (shown above) you get the following code:

import fl.motion.AnimatorFactory; import fl.motion.MotionBase; import flash.filters.*; import flash.geom.Point; var __motion_Ball_16:MotionBase; //Is there animation running, if not start it if(__motion_Ball_16 == null) { import fl.motion.Motion; __motion_Ball_16 = new Motion(); __motion_Ball_16.duration = 10;

// Call overrideTargetTransform to prevent the scale, skew, // or rotation values from being made relative to the target // object’s original transform. // __motion_Ball_16.overrideTargetTransform(); // The following calls to addPropertyArray assign data values // for each tweened property. There is one value in the Array // for every frame in the tween, or fewer if the last value // remains the same for the rest of the frames.

__motion_Ball_16.addPropertyArray(“x”, [0,0,0,0,0,0,0,0,0,0]); __motion_Ball_16.addPropertyArray(“y”, [0,7.38399,37.4766,112.504,269.275,333.95,22 1.075,47.6064,8.68268,0]); __motion_Ball_16.addPropertyArray(“scaleX”, [1.000000,1.000000,1.000000,1.000000,1. 000000,1.250000,1.187556,1.092333,1.023972,1.000000]); __motion_Ball_16.addPropertyArray(“scaleY”, [1.000000,1.000000,1.000000,1.000000,1. 000000,0.750000,0.812444,0.907667,0.976028,1.000000]); __motion_Ball_16.addPropertyArray(“skewX”, [0,0,0,0,0,0,0,0,0,0]); __motion_Ball_16.addPropertyArray(“skewY”, [0,0,0,0,0,0,0,0,0,0]); __motion_Ball_16.addPropertyArray(“rotationConcat”, [-0.251785,-0.251785,- 0.251785,-0.251785,-0.251785,-0.251785,-0.251785,-0.251785,-0.251785,-0.251785]); __motion_Ball_16.addPropertyArray(“blendMode”, [“normal”]);

// Create an AnimatorFactory instance, which will manage // targets for its corresponding Motion. var __animFactory_Ball_16:AnimatorFactory = new AnimatorFactory(__motion_Ball_16); __animFactory_Ball_16.transformationPoint = new Point(0.746748, 0.778455);

// Call the addTarget function on the AnimatorFactory // instance to target a DisplayObject with this Motion. // The second parameter is the number of times the animation // will play - the default value of 0 means it will loop. // __animFactory_Ball_16.addTarget( < instance name goes here >, 0); }

The code above is relatively simple. If you ’ ve done this before in Flash 9, you ’ ll immediately notice that it ’ s not XML. The above code is built completely in ActionScript using the addPropertyArray method. This new approach is designed to work with the motion editor and uses property arrays of frame - by - frame motion tween data.

30 Chapter 1: Understanding Flash3D

Here ’ s how the code is put together:

❑ The code starts by bringing in a number of imports that set up the animation framework. ❑ The code uses an ‘ if ’ statement if(__motion_Ball_16 == null) to determine if animation is running or not. If it isn ’ t, it imports all the necessary parameters needed to get the animation going. The name Ball comes from the name of the movie clip in your Flash Library. ❑ The addPropertyArray method is then executed as many times as needed to bring all the data created in the motion editor.

For example, in the bouncing ball example there were ten frames of animation, and comparatively, in each addPropertyArray there are ten values. As shown below for the y argument there are ten values corresponding to the position of your ball in the animation:

__motion_Ball_16.addPropertyArray(“y”, [0,7.38399,37.4766,112.504,269.275,333.95,22 1.075,47.6064,8.68268,0]);

❑ Finally, to apply this animation to another object you need to uncomment the final line of code:

// __animFactory_Ball_16.addTarget( < instance name goes here >, 0);

❑ Create a movie clip, put it on your Flash stage, and give it an instance name. ❑ Then place that instance name in the < instance name goes here > spot in the code above.

You can now apply this custom animation to as many objects as you want using only ActionScripting. The motion editor allows you to work with a large number of properties.

The following properties can be included in your animation using the motion editor:

❑ Basic Motion (X, Y, Z, Rotation X, Rotation Y, Rotation Z) ❑ Transformation (Skew X, Skew Y, Scale X, Scale Y) ❑ Color Effect (Alpha, Brightness, Tint, Advanced) ❑ Filters (Drop Shadow, Blur, Glow, Bevel, Gradient Glow, Gradient Bevel, Adjust Color) ❑ Eases (Simple, Start and Stop, Bounce, Spring, Sine, Sawtooth, Square, Random, Damped, and Custom)

Adding these properties is easy in the Flash 10 motion editor, very much like using curve editors found in sound and graphic programs. Collada Files Handling animation and bringing 3D models into Papervision3D is an important topic. It ’ s also one of great frustrations to the Papervision3D user community. There ’ s nothing worse than spending a month creating an incredible 3D model and not being able to get it to run in Papervision3D. Presently, Papervision3D brings 3D objects into its program using primitives or Collada files.

31 Part 1: Getting Started

So what ’ s a Collada File?

Essentially, it ’ s an XML document that holds the parameters of your 3D model. It ’ s designed to be both human readable and platform independent. Its incorporation into Papervision3D as the primary way to bring in 3D models may have been premature. Not because there ’ s anything wrong with the Collada platform, but because most programs don ’ t export a version of Collada that works with Papervision3D.

At the time of writing of this book, there wasn ’ t a Blender Collada exporter for Papervision3D, and the 3DSMax exporters for Papervision3D only worked with certain versions of 3DSMax. But there are other options. In this book, you find out how to create XML exporters (essentially pared down Collada files) that work for both Papervision3D and Flash 10. Combining an XML exporter with the motion editor code presented earlier enables you to create a dynamic 3D animation engine.

Another way of dealing with 3D modeling is to use Papervision3D or Flash10 in which to create your 3D models. This requires you to create a modeling application in Papervision3D, but doing so allows you the freedom of working with your models natively. You can find out more about Collada and XML exporters in Chapter 5. But keep in mind that Flash offers great flexibility. If something isn ’ t working for you just try another way.

Summary In terms of innovation and “ coolness factor ” , Flash 3D is one of the fastest - moving areas in web technology. In this chapter, you discovered how to build a simple 3D engine in both CS3 and CS4. Using what you learned about 3D engines you created a torus worm, carousel, and image ball. Finally, you examined Disney ’ s rules for creating realistic animation, and converted a timeline animation into ActionScript.

You ’ ve begun the most important step in learning Papervision3D – understanding how 3D is made!

32 Getting Started With Papervision3D

Emanating from the AS3 are a host of 3D open source endeavors, such as , Sandy3D, Alternative3D, and Papervision3D. Of the numerous 3D platforms hitting the stage, Papervision3D is by far the most popular and enjoys an ever - growing user group and development team.

Originally conceived by Carlos Ulloa in November 2005 and converted to Great White by Ralph Hauwert, it has grown from approximately 20 classes to over 300. It ’ s maintained by a 10+ core and committer team. And with the release of Papervision3D 2.0, developers worldwide began downloading and contributing to this phenomenal open source project.

In this chapter, you start by downloading the basic Papervision3D classes, and examining the basics of 3D used in this book, and the guts of Papervision3D. Then, you ’ ll create a simple hello world application and build a simple Papervision3D class. Finally, you learn how to apply BasicView to simplify the creation of a Papervision3D application.

Getting Papervision3D Start by downloading the Papervision3D classes. Getting Papervision3D isn ’ t as simple as just downloading and unzipping it, you need a subversion client. Subversion is a great tool, which helps developers keep track of code changes. As so many developers are working on Papervision3D it ’ s essential to keeping it up to date and maintained. Here are the steps you need to follow to get the Papervision3D classes:

1. Download and install a SVN client. Tortoise for Windows ( http://tortoisesvn.tigris .org/ ) and for Mac - OS X Syncro SVN Client ( http://www.syncrosvnclient.com/ ) 2. For Windows, create an empty folder, name it Papervision3D, right click on it and choose “ SVN checkout ” . For Mac, just add a new SVN project. Part 1: Getting Started

3. Paste the address http://papervision3d.googlecode.com/svn/trunk/ in the URL box and press OK 4. Finish the download and examine the folder structure.

The repository has one trunk (most stable version) and several branches. Papervision3D is constantly changing. At the time of the writing of this book, the CS4 branch was being eliminated while a PapervisionX branch was being created. This brings up an essential point. As Papervision3D is being constantly updated, care should be taken when upgrading a project ’ s Papervision3D classes. Doing so can stop it from working or introduce instabilities. You should be careful to save all projects with their original Papervision3D files before upgrading especially if the project development spans more than a few weeks – that ’ s how fast things are changing.

If you have any difficulty with the steps above, make sure that you review the appropriate video on the book ’ s website. The getting - started video goes through the process step by step.

Now that you have the Papervision3D classes, you ’ ll find what you need for the first part of this book in the AS3 branch. Navigate to it following the Papervision3D\as3\trunk\src path.

From the src folder grab the org and nochump folders and place them into your Flash or Flex src project folders. The org folder contains the majority of classes needed for Papervision3D to run. The nochump folder is a set of zipping/unzipping classes that you use for more advanced projects. As your projects become more advanced, you add other folders as well, which contain packages for physics, the Wii, and tweening.

For your convenience, all the examples in this book already have the appropriate classes bundled with them. They ’ re ready to run. And you can skip the steps above. But to get the most up - to - date version of Papervision3D you must use an SVN.

Finally, make sure that you read the license contained in the src folder. Papervision3D is completely open source, licensed under MIT, which gives you full authority to modify and distribute it for commercial use – without restriction, including without limitation the rights to use, copy, modify, merge, and publish.

Diving into 3D Two different approaches to simulating 3D objects in ActionScript are as follows:

❑ Arranging and animating primitive objects in 3D space, which entails animating display objects using the x, y and z properties, or setting rotation and scaling properties using the DisplayObject3D class. ❑ Generating 2D triangles from 3D geometry, and rendering those triangles with textures. To use this approach you must first define and manage data about 3D objects and then convert that data into 2D triangles for rendering.

34 Chapter 2: Getting Started With Papervision3D

You accomplish this in Papervision3D by using :

❑ Primitives and materials ❑ 3D mathematics, and programming hacks ❑ 3D modeling software (such as 3DSMax, Blender, and Swift3D)

But before you jump too far into the specifics of building 3D applications, it ’ s important to cover a few important definitions.

Shapes Versus Objects Depending on where you look there are some variations in 3D definitions. In this book you ’ ll model 3D objects and shapes using 3DSMax (Max for short), Blender, and Swift3D. To keep things simple, you ’ ll use Max ’ s definitions of shapes and objects. In Max, a shape refers to something 2D and an object is 3D. This is the difference between a rectangle and a box. The first is a shape, the second an object. 2D shapes make up 3D objects. For example, a circle extrudes into a cylinder.

The pieces that make up objects are called sub - objects. And sub - objects connect together to form what you see on the screen.

Here ’ s the 3D terminology you ’ ll be using throughout the book.

❑ Vertex — a point in space (2D or 3D) that connects to other sub - object components to form a line or surface. It exists in a single x, y, and z coordinate. ❑ Vertices (Verts) — more than one vertex. ❑ Segment — a line between two vertices. ❑ Spline — a combination of vertices and segments. ❑ Shape — a 2D element. ❑ Object — a 3D element. ❑ Edge — a line connecting two vertices in 3D. Same as a segment but now in 3D. ❑ Polygon — a face on an object composed of edges and vertices. ❑ Sub - object — the pieces that make up an object.

The three most important sub -objects that you ’ ll be using throughout this book to create 3D models are the vertex, edge, and face. Both 3DSMax and Blender devote entire panels to these sub - objects and they ’ re key to modeling in 3D. Splines Splines are made up of vertices and segments as illustrated in Figure 2.1.

35 Part 1: Getting Started

segment vertex

Spline

Figure 2-1

You use splines to create paths and extrusions. Objects Objects consist of triangular sub - objects called faces, as illustrated in Figure 2.2.

vertex edge

face

Figure 2-2

You use vertex, edge, and face sub - objects shown above to model 3D objects. The majority of 3D objects are composed of triangles. Triangles Triangles are the most important shape in 3D. Since triangle math is easy to implement, the majority of 3D objects are created using triangles. Objects created from triangles are called meshes and the triangles in a mesh are called faces. You can create almost anything using triangles through a technique called box modeling. The book ’ s blog contains extensive examples of box modeling in both Blender and 3DSMax.

Once you ’ ve created your 3D object you need to get it rendered so it can be viewed. Rendering refers to the sequence of drawing faces so that they make visual sense. The backbone of Papervision3D ’ s rendering system uses a technique called hidden surface drawing.

36 Chapter 2: Getting Started With Papervision3D

Hidden Surface Drawing In Papervision3D, there ’ s a need for speed. Removing what is not visible is what makes a fast rendering algorithm work. Surfaces drawn that are hidden by other surfaces drawn over them subtract from your computer ’ s available CPU resources. By removing hidden surfaces you reduce the number of polygons and speed up the rendering process. You only want to draw those polygons visible to your viewer. Painter ’ s Algorithms is one of the easiest and most popular hidden surface methods.

Painter ’s Algorithm The idea comes from oil painting. Oil painters found it easiest to paint back to front. The background is painted first – distant objects, then nearer ones: hence the term Painter ’ s Algorithm. Rendering objects in Papervision3D uses a similar procedure.

Projecting a 3D scene onto a 2D viewport (the place where your image is projected) requires that you decide which polygons are viewable and which are not. The Painter ’ s Algorithm sorts all the polygons in a scene by their depth and then paints them in this order, furthest to closest, painting over the parts that are normally not visible, thus solving the visibility problems.

But it comes with the cost of painting redundant areas of distant objects. And the algorithm can fail in certain cases.

In Figure 2.3, the distant pyramid mountains are painted first, followed by the closer house then the closest objects in the scene, the trees, are painted.

Figure 2-3

So why is it called hidden? When painting on a surface you paint over anything that ’ s already there – hiding it. By arranging the polygons nearest the viewer at the end of a list, you paint every polygon into the framebuffer, overwriting the ones furthest away.

When does it fail?

When polygons overlap, as shown in Figure 2.4, you can ’ t determine which polygon is above the other. The problem occurs because in depth sorting, each polygon is represented by only one depth value. But polygons are not just single points in space – they cover large portions of the projection plane.

37 Part 1: Getting Started

Figure 2-4

Such polygons need to be cut to allow sorting; you ’ ll cover an algorithm designed for this purpose in a later chapter. Flaws in the Painter ’ s Algorithm have led to the development of Z - buffer techniques.

One major addition to Papervision3D to help overcome z - sorting issues has been the addition of the Quadrant Render Engine ported over by Andy Zupko from Away3D.

The View Frustum (Culling and Clipping) The view frustum contains everything that ’ s visible in a 3D scene. The view frustum is pyramid - shaped and at its apex is the camera. The viewable volume is bounded by a polyhedron. Its entry plane is called the near clip plane and the end of its viewing volume is called the far clip plane. As shown in Figure 2.5, objects outside of the viewable volume are culled from the scene.

far clip plane

near clip plane

camera not visible

inside viewing volume Figure 2-5

38 Chapter 2: Getting Started With Papervision3D

The cube inside the viewable volume shown above is projected onto the viewport while the pyramid outside is not drawn; it ’ s culled.

To successfully map x and y coordinates to the projection plane (or viewport) you must map the view frustum shown above to a cube. Without going into all the details, you accomplish this using a projection matrix. Most 3D operations are accomplished using matrix algebra, which is beyond the scope of this book but addressed on the book ’ s websites.

The depth in the view frustum is what determines the z - sorting values. Culling Culling reduces the number of calculations required by your CPU, removing complete polygons whose vertices lie completely outside of the view frustum. For example in Figure 1 - 5 the pyramid beyond the far clip plane isn ’ t drawn. Since objects outside of the view frustum aren ’ t seen by the observer drawing them is unnecessary and saves on required CPU resources. It ’ s simple to determine which polygons should be drawn and is solely based upon a polygon ’ s position with respect to the view frustum. It has nothing to do with the polygon ’ s face angle with respect to the viewer.

When objects are completely in or out of the view frustum, culling is a simple process. But when an object is partially in the view frustum it must be clipped to eliminate visual artifacts . Clipping Clipping eliminates artifacts by modifying those polygons that lie partially in the view frustum. Clipping is more involved than culling and requires that you get a vector normal to the polygon face, and then take the dot product of that vector normal with a vector from the camera position (at the vertex of the view frustum).

The dot product is a very important quantity in Papervision3D and is discussed in more detail later in this book. But in short, the sign of this dot product determines if the polygon is drawn or not. If the resulting scalar from the dot product is less than zero the polygon faces toward the camera and is drawn, otherwise it ’ s not drawn.

Of the two (clipping and culling), culling gives you the greater performance enhancement because it reduces the number of calculations required. Both are handled automatically in Papervision3D and usually don ’ t require adjusting.

Exploring the Guts of Papervision3D All Papervision3D applications have the following elements in common. They can be thought of as the guts of Papervision3D.

❑ Scene (Stuff) — The scene is where your objects are placed. It contains the 3D environment and manages all objects rendered to the screen in Papervision3D. It extends the DisplayObjectContainer3D class to arrange the display objects. ❑ Viewport (Place) — The viewport displays the rendered scene. It ’ s a sprite (rectangular region) that ’ s painted with a snapshot of all the stuff contained in the view frustum. The viewport sprite

39 Part 1: Getting Started

inherits all the sprite methods and can be moved, positioned, and have effects added just like any normal sprite in Flash. ❑ Camera (Eye) — The camera is your eye inside of the 3D scene and defines the view from which a scene is rendered. Different camera settings present a scene from different points of view. The camera is a DisplayObject3D and can be moved and rotated. And just like a digital camera you can change its zoom and focus. When rendering, the scene is drawn as if you were looking through the camera lens. ❑ Object (Body) — An object is a combination of vertices, edges, and faces that provide a meaningful form for display, such as a car, avatar, or box. Objects are created in Papervision3D using primitives, or from 3D modeling software (such as 3DSMax, Blender, or Swift3D) and are embedded or imported into Papervision3D. When imported into your scene, they become strongly typed objects that inherit from the DisplayObject3D class in the Papervision3D framework. ❑ Material (Clothes) — The material is the texture, which is applied to an object. Textures can consist of various formats such as bitmaps, , or video, and interact with light sources creating bump map effects and shaders. ❑ Renderer (Artist) — The renderer draws a sequence of faces onto the viewport so that they make visual sense. Using your selected camera, it takes the data from your scene and draws it to the viewport. Typically the renderer is set into a loop using onEnterFrame or Timer methods native to the Flash player.

Running a Papervision3D application is like simultaneously recording and projecting your 3D scene as shown in Figure 2.6. The scene is the middleman. It ’ s what the camera films and what the projector projects.

viewport material

projector

scene

renderer

camera object inside viewing volume Figure 2-6

40 Chapter 2: Getting Started With Papervision3D

You can think of the whole process as a combination of a movie camera and projector that both films and projects your 3D scene simultaneously. The scene is what the camera is pointed at and the objects are the items that the camera films. The materials are what your objects are dressed in and determine how the light interacts with the scene elements. The reels on your projector are your renderer and as your reels turn (using onEnterFrame or Timer methods) they cast the recorded scene onto your screen which is your viewport .

Finally, at the heart of Papervision3D is the DisplayObject3D class.

DisplayObject3D All objects placed in a Papervision3D scene are display objects. The DisplayObject3D class represents instances of 3D objects that are contained in the scene. That includes all objects in the scene, not only those that can be rendered, but also the camera and its target. The DisplayObject3D class supports basic functionality like the x, y and z position of an object, as well as rotationX, rotationY, rotationZ, scaleX, scaleY and scaleZ and visibility. It also supports more advanced properties of the object such as its transform Matrix3D.

DisplayObject3D is not an abstract base class; therefore, you can call DisplayObject3D directly. And in many instances you ’ ll create an empty DisplayObject3D object to act as a container for other 3D objects that are added to the scene. Invoking a new DisplayObject() creates a new empty object in 3D space, like when you used createEmptyMovieClip(). And most importantly, it ’ s the class where you ’ ll add your physics: mass, velocity, acceleration.

You can now put all of this into practice by creating your first Papervision3D Class.

Running Papervision3D Four Different Ways (Hello World) Since 1974 (thanks to Bell Labs), it ’ s been a tradition to start with a Hello World program when learning a new language. But Papervision3D provides four ways to say hello and two different programs to say it in.

In Flash you can run Papervision3D as a class package or as a timeline scripting application. Similarly for Flex you can run Papervision3D as an ActionScript package or as an MXML application with the code sandwiched in between ActionScipt tags. The four possible options are diagrammed in Figure 2.7.

41 Part 1: Getting Started

MXML & Script U si ng F le 1 x

4 Action Script Flash Package Hello World 2 Package

U s in g 3 Fl ex Timeline

Figure 2-7

You might ask which one you should use. And it really depends on the application that you ’ re writing. Some things are better done in Flash and others in Flex and in certain cases it ’ s easier to develop applications using timeline ActionScript instead of using packages. If you ’ re not familiar with writing class packages you ’ ll certainly find using timeline ActionScript in Flash or the script tags in Flex easier at first. But in this book, an emphasis is placed on developing ActionScript class packages.

Using Flash or Flex You ’ ve certainly heard the famous line ‘ parts are parts ’ but unfortunately it ’ s not true for Flex and Flash, which have different component sets. Adobe is continually working on streamlining the workflow between Flash, Flex and other CS4 programs. Mixing Flash code that uses Flash components with Flex and vice versa is still a daunting task. Flex and Flash can only interchange class packages that don ’ t contain elements of their specific component sets. As soon as you depart from the core language and start adding Flash or Flex components your program becomes non - transferable to the other platform. However, the Flex Component Kit, an mxp plug - in for Flash created by Adobe Labs to help extend interoperability between Flash and Flex, resolves some of the inherent issues that appear when moving across platforms.

At a fundamental level, the two platforms still differ considerably. It can be reasonably stated that the Flex framework further extends the Flash framework. For example, at a core level, any element that extends the Flex UIComponent class is essentially just a Sprite with added functionality that ’ s abstracted and encapsulated into it. It ’ s this fundamental difference however that keeps these two platforms separate and independent, which means you program Papervision3D a little differently based on the platform you ’ re working in. This makes writing a book on both a little challenging, but definitely more interesting.

To make things easier, you first learn to program Papervision3D in both Flash and Flex, developing classes generic to both. The first part of the book emphasizes Flash. And the second part Flex. And towards the end it really gets exciting. You learn how to get Flash, Flex, and Air working together to produce dynamic 3D Flash - Flex content. Finally, you learn to use Flash Catalyst and Flash Builder to create 3D data - driven applications.

42 Chapter 2: Getting Started With Papervision3D

An important difference exists between working with Flex applications where you ’ re using MXML and Script tags. The Flex framework includes a lot of abstracted code that handles layout for the developer. The hierarchical nature of the way that display objects are created on the stage in the Flash Player truly becomes apparent when working in Flex. In order to add children to the stage in Flex, you must put them inside a container component such as a Canvas, Panel, or TitleWindow. When programming with Papervision3D however, you must also use the rawChildren tag that ’ s shown here:

pv3Canvas.rawChildren.addChild(viewport);

This modification allows you to add non - native Flex display Objects to the Flex display stack. This is essential in getting your MXML and Script Tag application to run, otherwise nothing will happen.

For script editing, Flex has become the preferred vehicle. Its debugging capabilities, incorporating code completion and ctrl - click variable searching, are far superior to Flash. Writing code in Flex, as an ActionScript package, has become the preferred vehicle for many Papervision3D developers.

With that said, you ’ ll now build your “ Hello 3D World ” class package.

Building a Class To fully leverage the power of AS3 you must learn to build classes. Fundamental to Papervision3D is the class structure. In this section, you start at ground zero, building the framework for your first custom class. Then you ’ ll examine the basic Papervision3D class and get your first Papervision3D application running in Flash.

Writing an OOP (Object Oriented) Class If you ’ ve been a Flash for any amount of time, you ’ re probably used to putting items in the library and instantiating them to the stage. In a similar way classes are templates that you ’ ll use over and over again just like instances from a library. This becomes very apparent when you start working with particles in Chapter 3.

But what ’ s a class?

It ’ s a blueprint for how an object acts. For example, movie clips and buttons are classes. They respond and act with certain behaviors and have properties that are contained in their classes. Classes possess two important quantities: properties and methods. Properties hold information. They ’ re like variables. And methods, like functions, hold what a class can do or how it behaves. The best way to understand classes is to write your own. Writing your own class is called a custom class. This is how you get started:

❑ Open up Flash (or any text editor), and from its welcome screen under Create New, select ActionScript File. ❑ In the Flash IDE, go to File Save and give your file a name. Now this is important. The name of your file is the name of your class. In order to maintain consistency with conventions and best practices, always start your class name with a capital letter. In this case call it “ Papervision3DClass ” .

43 Part 1: Getting Started

❑ Select a folder. Select save. Note: The file saved is Papervision3DClass.as ❑ Place the following code into the Flash code window

package { class Papervision3DClass { function Papervision3DClass() { trace(“Hello 3D World”); } } }

All classes start with the package name. The package statement is a special name that wraps your class. Next you define your class just like you would define a variable or function. The class name must be the same name as your file name. In your case it ’ s Papervision3DClass. Then you create the constructor function. Your constructor function runs automatically when the class is created or instantiated. The constructor function is the same name as your class and doesn ’ t have a return class such as void or string. Now place any code inside the constructor that you want to run. In this first example, you put:

trace(“Hello 3D World”);

When your code runs it sends Hello 3D World to your Flash trace window. Congratulations, you ’ ve just written your first custom class!

Now you ’ ll extend it. Extending Your Class (Inheritance) Why reinvent the wheel? Extending your class allows you to use what ’ s already available from another class. That ’ s the real power of using classes. A class can inherit from properties and methods of another class (as long as those properties and methods aren ’ t private). The class that ’ s extended is called the subclass and the class it ’ s inheriting from is called the super class.

In the example below, you now extend your Papervision3D class with the Sprite class. In this case, the Sprite class is your super class and the Papervision3DClass is your subclass.

After the Papervision3DClass class statement, type extends Sprite as shown here:

class Papervision3DClass extends Sprite

This enables your Papervision3DClass to take everything from the Sprite class and use it. Essentially the Sprite class is a display object container that doesn ’ t carry the overhead of a movie clip. Its use reduces the resources needed to place your 3D objects on stage.

44 Chapter 2: Getting Started With Papervision3D

But your class will not run yet; you need to import the Sprite class.

import flash.display.Sprite;

Import statements go after the package curly bracket, and importing the Sprite class brings in all the information needed to allow your custom class to behave as a Sprite. Your code should look like this:

package { import flash.display.Sprite;

class Papervision3DClass extends Sprite { function Papervision3DClass() { trace(“Hello World”); } } }

In Papervision3D, you ’ ll be extending numerous classes. Chinese Hello Here ’ s another example that demonstrates how a sub - class can use a method of its super class. Consider the following two classes.

HelloClass The HelloClass has a chineseHello() method that when executed says hello in Chinese:

package { public class HelloClass{ public function HelloClass() {

} public function chineseHello():void { trace(“Ni Hao”); } } }

GoodByeClass The GoodByeClass has a chineseGoodBye() method that when executed says goodbye in Chinese.

45 Part 1: Getting Started

package { public class GoodByeClass{ public function GoodByeClass() {

} public function chineseGoodBye():void { trace(“Zai Jian”); } } }

But what if you want to say both hello and goodbye in one class? As opposed to rewriting your HelloClass to contain the chineseGoodBye() method you extend it using the extends method as follows:

package { public class HelloClass extends GoodByeClass{ public function HelloClass() {

} public function chineseHello():void { trace(“Ni Hao”); } } }

Placing the following code in the Flash ActionScript editor, you gain access to both functions from a single class using the extended HelloClass as follows:

var subclass:HelloClass = new HelloClass(); subclass.chineseHello(); subclass.chineseGoodBye();

Even though this is a simple example, it becomes extremely powerful when dealing with classes that are 1000s of lines long.

But what if you want to change a method of the super class (GoodByeClass ) in the sub class (HelloClass )? No longer do you want to say goodbye to your new Chinese friend, but something else. To do so you must override the chineseGoodBye() method using the override technique as follows:

46 Chapter 2: Getting Started With Papervision3D

package { public class HelloClassOverride extends GoodByeClass{

public function HelloClassOverride() {

} public function chineseHello():void { trace(“Ni Hao”); } override public function chineseGoodBye():void{ trace(“Hen gao xing ren shi nin”); //I am very glad to meet you. } } }

You can run this class from the Flash ActionScript editor by using the following code:

var subclass:HelloClassOverride = new HelloClassOverride(); subclass.chineseHello(); subclass.chineseGoodBye();

So now when you call the chineseGoodBye() function you get a totally different message (I am very glad to meet you) and are now ready for a longer conversation with your new Chinese friend. Of course, this technique only works if the super class method is public.

With this under your belt, you ’ re now ready to start building out your Papervision3D class. Creating Methods A method is a fancy name for a function, except now it ’ s in a class. You ’ ve probably written tons of functions. Put one in a class and it ’ s now a method. But with one twist: a method is a function attached to an object, which means it can be instantiated (or used over and over again).

You place your custom methods after the constructor class. For example, place the following initPV3D() custom method underneath your custom class. You ’ ll build out your code in the next section on formulating a Papervision3D Class.

function initPV3D():void { trace(“Initialize Papervision3D Here”); }

47 Part 1: Getting Started

Only your custom class can run your custom method. Your constructor calls the custom method. You do this by placing a function call inside of your constructor as follows:

package { import flash.display.Sprite;

class Papervision3DClass extends Sprite { function Papervision3DClass() { trace(“Hello World”);

initPV3D();

}

function initPV3D():void {

trace(“Initialize viewport here”);

}

} }

When the constructor runs, it calls the custom method initPV3D that executes your custom method. But first, you must set your scope. Scope defines the access level of your properties and methods. Defining Scope As discussed earlier, a public class is necessary if you ’ re going to override a super class ’ s method. When defining class methods and properties, you must define their scope or access modifiers. An access modifier governs the availability of variables, functions, classes, interfaces, and namespaces.

In building Papervision3D classes, you use access modifiers to control access to both properties and methods. Class properties use the following attributes to control property access:

❑ Internal (default) — visible to references inside the same package. ❑ Private — visible to references in the same class. ❑ P rotected — visible to references in the same class and derived classes. ❑ Public — visible to references everywhere. ❑ S tatic — specifies that a property belongs to the class, as opposed to instances of the class. ❑ UserDefinedNamespace — custom namespace name defined by user.

The four access modifiers used to control methods are: public, private, protected, and internal. Using public makes a method visible anywhere in your script. Private makes methods only visible within the defining class. Protected makes methods visible within the defining class and subclasses. And internal is the default attribute and allows a method to be called within its defining classes.

48 Chapter 2: Getting Started With Papervision3D

As an example of using access modifiers, the function initPV3D is called from the constructor function, but can ’ t be called outside of the class due to the private restriction.

package { import flash.display.Sprite;

public class Papervision3DClass extends Sprite { Public function Papervision3DClass() { trace(“Hello World”);

initPV3D();

}

private function initPV3D():void {

trace(“Initialize Papervision3D Here”);

}

} } Custom Properties You now finish up your class by adding some custom properties. Property is a fancy name for variable. For Papervision3D the four main properties/methods needed to create a 3D object for viewing are:

❑ Viewport3D ❑ Camera3D ❑ Scene3D ❑ Render

As discussed earlier, the viewport is where you see the projected scene, the camera is the observer ’ s position, the scene is where your objects live in 3D space, and the renderer is what paints the 3D onto a 2D sprite canvas called a viewport.

You find them in every Papervision3D movie. Without them Papervision3D won ’ t work. You ’ ll learn about each one in the next section. To place these properties into your package, paste them right after the class declaration.

private var scene:Scene3D; private var camera:Camera3D; private var viewport:Viewport3D; private var renderer:BasicRenderEngine;

49 Part 1: Getting Started

Don ’ t forget your import statements. For each property to work properly, you need its import statement as well.

In summary, custom classes consist of six elements:

❑ package name ❑ import statements ❑ properties ❑ class declaration ❑ constructor function ❑ methods

In addition to properties and methods, Papervision3D classes use a third, very important characteristic called an event. Events determine which instructions are carried out and when. An event gives Papervision3D the ability to listen and respond to elements in your program such as a button click, or enter frame occurrence. In essence, the (using events) just sits and waits for an event to occur, and executes the designated code when one does.

Using Papervision3D requires that you write your own classes and use events to execute behaviors. In the next section, you ’ ll start by formulating your first Papervision3D class.

Formulating a Papervision3D Class Starting with the code from the previous section, you ’ re probably wondering, “ Where do all those Papervision3D import statements come from? ”

Currently, you can download them from Google Code at http://code.google.com/p /Papervision3D/source/checkout

You need an SVN client, such as Tortoise, to download them as described earlier. If you have problems, just take a look at the book ’ s website.

package { import flash.display.Sprite;

import org.papervision3d.scenes.Scene3D; import org.papervision3d.cameras.Camera3D; import org.papervision3d.render.BasicRenderEngine; import org.papervision3d.view.Viewport3D;

public class Papervision3DClass extends Sprite { private var scene:Scene3D; private var camera:Camera3D; private var renderer:BasicRenderEngine; private var viewport:Viewport3D;

50 Chapter 2: Getting Started With Papervision3D

public function Papervision3DClass() { trace(“Hello World”);

initPV3D();

}

private function initPV3D():void {

trace(“Initialize Papervision3D Here”);

}

} }

Assuming that you ’ ve downloaded the Papervision3D files correctly, you have all you need to run your first 3D project.

Your goal is simple – to put a wire frame sphere on the stage, as shown in Figure 2.8. How difficult can that be?

Figure 2-8

So now you ’ ll finish your code. As you ’ re building a sphere you need a few more import statements such as those shown below for the sphere, its wire frame, and its material. Place this code after your other import statements.

//Primitives and Materials import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.core.proto.MaterialObject3D;

And add a sphere variable.

//Define your sphere variable. private var sphere:Sphere;

51 Part 1: Getting Started

The next set of code is the initPV3D function, which runs in the constructor function. This function follows the initPV3D constructor function. The initPV3D function contains the following behaviors:

❑ Instantiation of the viewport, camera, scene, and renderer ❑ Object instantiation ❑ Instantiation of the frame iteration mechanism

In this case you use ENTER_FRAME to create your looping action, but you can use a timer to create the loop function as well. The advantage of a timer is that you can throttle your application. You ’ ll become very familiar with throttling frame animation in this book.

//Initialize Papervision3D private function initPV3D():void {

// Create the viewport viewport = new Viewport3D(0, 0, true, false); addChild(viewport);

// Create the camera camera = new Camera3D();

// Create the scene scene = new Scene3D();

// Create the renderer renderer = new BasicRenderEngine();

//Create the Objects createObjects();

// Create the render loop this.addEventListener(Event.ENTER_FRAME, loop);

}

The declaration and instantiation of the viewport, camera, scene, and renderer objects is at the heart of what makes Papervision3D work. Viewport The viewport has four parameters. The first two are the width and height. These are critical parameters when it comes to optimizing your application.

Why? The viewport is your projection screen. And two thirds of the processing that occurs requires taking Papervision3D ’ s 3D objects and projecting them on the viewport. The viewport extends Sprite, one of the core base classes in Flash. The larger the viewport the larger the amount of work your processor needs to do.

If you set the height and width parameters to 0, 0 Papervision3D uses the entire stage to render. This is a big processor resource hit if you have a complex scene. Make the viewport smaller if you can. The next

52 Chapter 2: Getting Started With Papervision3D

two parameters are Booleans. The first Boolean, if set to true, resizes the viewport with the browser window (auto scale to stage), and the second sets the viewport interactivity.

// Create the viewport viewport = new Viewport3D(0, 0, true, false); addChild(viewport);

There are two more Booleans that aren ’ t shown here. They ’ re clipping and culling, which are set to true by default. Most of the time you leave these alone and accept the default values.

Then you add the viewport to the stage, but keep in mind that you can have multiple viewports. Creating multiple viewports is a way of solving the z - sorting problems that commonly arise with Papervision3D.

For example, when placing a house on a simple terrain you have sorting issues. Instead of placing the house and terrain in the same viewport, create two viewports and place your terrain in one viewport and your house in the other. Not only does this solve many of your sorting issues, it gives you the ability to render them differently.

Think of the viewport as the canvas that you paint your image on. It ’ s where Papervision3D meets the 2D world of Flash. Camera Papervision3D includes three cameras: free, frustum, and target. The code for instantiating a Camera3D object that ’ s already been declared is:

// Create the camera camera = new Camera3D();

The camera has a number of properties such as zoom and focus. When working with 3D carousels, for example, you ’ ll find it useful to use focus and zoom, especially when bringing panels in for close observation. What ’ s the difference between zoom and focus?

Focusing is equivalent to magnifying elements at the viewport, but you ’ re not really getting closer. Opening up the focus too large distorts your objects in the 3D scene.

Zooming essentially changes the distance from the camera to the view frustum. Zooming in too much puts you on the other side of the object and it will seem as if the object has disappeared. Scene This is where the 3D stuff lives, and assuming you ’ ve already imported and declared the object, it ’ s instantiated in Papervision3D by using the following command:

// Create the scene scene = new Scene3D();

In reality (or virtual reality that is), you never see this stuff in the scene. It ’ s being handled mathematically and when it ’ s rendered, it ’ s cast onto the viewport. This is about one third of the work

53 Part 1: Getting Started

on your CPU, and the other two thirds goes into the rendering process when the scene is culled, clipped, and painted onto your viewport. Renderer The renderer performs all the necessary calculations to paint your 3D data onto a 2D displayable sprite called the viewport, which is referred to as Viewport3D in Papervision even though it ’ s actually being painted in 2D.

It ’ s simple to create the sphere with a wire frame. Instantiate a material for the sphere (a wireframe in this case), instantiate a sphere, then add your new material to the sphere. Then you add the sphere to the stage by using scene.addChild(sphere) .

private function createObjects():void {

// Create a material for the sphere var sphereMaterial:MaterialObject3D = new WireframeMaterial(0xFFFFFF);

// Create/use wireframe material, radius 100, default 0,0,0 var sphere:Sphere = new Sphere(sphereMaterial, 100, 10, 10);

// Add Your Sphere to the Scene scene.addChild(sphere);

}

Finally, you need a looping function. The function below is very simple. You will use it to create complex interactions of primitives in the next section.

//Start rendering private function loop(evt:Event):void { renderer.renderScene(scene, camera, viewport);

}

By putting it all together, you have the final base code. Final Base Code After making all the changes above, your final code should look like the code below. Now it ’ s time to have some fun and run your code. Check out the next section “ Running Papervision3D Applications ” .

package { //Flash imports import flash.display.Sprite; import flash.events.Event;

54 Chapter 2: Getting Started With Papervision3D

//Basic Papervision3D Engine import org.papervision3d.scenes.Scene3D; import org.papervision3d.cameras.Camera3D; import org.papervision3d.render.BasicRenderEngine; import org.papervision3d.view.Viewport3D;

//Primitives and Materials import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.core.proto.MaterialObject3D;

//Define your class public class Papervision3DClass extends Sprite { //Define your properties private var scene:Scene3D; private var camera:Camera3D; private var renderer:BasicRenderEngine; private var viewport:Viewport3D;

//Define your sphere variable. private var sphere:Sphere;

public function Papervision3DClass() { trace(“Hello World”);

initPV3D();

}

//Define your methods

//Initialize Papervision3D private function initPV3D():void {

// Create the viewport viewport = new Viewport3D(0, 0, true, false); addChild(viewport);

// Create the camera camera = new Camera3D();

// Create the scene scene = new Scene3D();

// Create the renderer renderer = new BasicRenderEngine();

//Create the Objects createObjects(); (continued)

55 Part 1: Getting Started

(continued)

// Create the render loop this.addEventListener(Event.ENTER_FRAME, loop);

}

//Create your objects private function createObjects():void {

// Create a material for the sphere var sphereMaterial:MaterialObject3D = new WireframeMaterial(0xFFF FFF); // Create/use wireframe material, radius 100, default 0,0,0 var sphere:Sphere = new Sphere(sphereMaterial, 100, 10, 10);

// Add Your Sphere to the Scene scene.addChild(sphere);

}

//Start rendering private function loop(evt:Event):void {

renderer.renderScene(scene, camera, viewport);

}

} }

The final base code consists of two methods, which are initPV3D and createObjects . The initPV3D function sets up the Papervision3D engine, which renders your object discussed above. The createObjects function creates the object to be rendered. It consists of two parts: adding a sphere primitive and adding a material. The code to accomplish this is given here:

// Create a material for the sphere var sphereMaterial:MaterialObject3D = new WireframeMaterial(0xFFFFFF);

// Create/use wireframe material, radius 100, default 0,0,0 var sphere:Sphere = new Sphere(sphereMaterial, 100, 10, 10);

The next chapter goes into the details of adding primitives and materials. For now it ’ s enough to understand that every primitive requires a material to be seen. Materials in Papervision3D have a life of their own and are more like objects themselves as opposed to just static bitmaps. They can contain animation, video, and interact with light. It ’ s through the material that your object becomes interactive. And in addition, you can create your own materials and primitives – more on that in Chapters 3 and 4.

56 Chapter 2: Getting Started With Papervision3D

Finally, the sphere is added to the scene using the addChild method shown below:

// Add Your Sphere to the Scene scene.addChild(sphere);

The addChild method is inherited by Scene3D from the DisplayObjectContainer3D base class, and is responsible for adding a visual object to the scene.

Similarly you can remove an object from the stage using the removeChild command. Later on, you ’ ll examine numerous ways to work with children. These include referencing display objects by index number and name, controlling the display objects of different movies, and using addChild to change a display object ’ s parent.

Running Papervision3D Applications You have a number of options for running your custom class. In Flash, you can declare it as the document class, a movie clip, or class path. Flex Builder is an attractive alternative to Flash, as it contains a more powerful editor. Additionally, if you ’ re a student or teacher, Flex Builder is free from Adobe. You first run your code by declaring it as the application ’ s document class in Flash.

In order for the code to run, you must have the Papervision3D classes included in the same folder with the FLA as shown below. The Flash player looks for the import classes in the same folder as your FLA. Those folders are com, fl, nochump, and org as shown in Figure 2.9. You will learn more about them in later chapters.

If you want to place the Papervision3D classes in another folder you will need to set the class path to that folder. Setting the class path is covered in the section on class path.

Figure 2-9

Make sure that you ’ ve placed the Papervision3D class folders in the same folder as your FLA. Otherwise, your program won ’ t run.

Document Class In previous versions of Flash, the main timeline was a movie clip. The release of Flash CS brought with it the ability to define a main document class from within the Flash IDE. This approach is consistent with the way most object - oriented programming languages begin an application. To find the document class

57 Part 1: Getting Started

go to the property inspector in Flash. Note: if it ’ s not shown in the properties box click on the grey space off of your stage. Next click in the Document class area and type in the name of your class Papervision3DClass (see Figure 2.10). Click off the stage and test your movie.

Figure 2-10

Class Path Class path is a good way to organize your classes. Rather than placing your classes in your FLA folder every time you create a new Flash project, you can specify a folder outside of your FLA folder anywhere on your hard drive. And Flash can import your classes from that external folder at runtime. Using class path you can have several different Flash projects using the same external classes.

To test this out, create a folder on your desktop and place all your Papervision3D classes in that folder. To set your class path do the following:

❑ Go to the properties inspector. ❑ Select Publish setting. ❑ Select Flash. ❑ Select Settings next to the ActionScript version (ActionScript 3.0). ❑ In the Classpath area at the bottom of the screen, click the target button and browse to your folder on the desktop containing your Papervision3D class. ❑ Select the folder and select choose and click OK and OK again. You now have a class path specified. ❑ Make sure that all movie linkage properties, package names, and import names are set correctly. Test your movie.

If you don ’ t want to do this, just drop your Papervision3D classes into the same folder as your FLA. Flash knows to look first for the classes that it needs to import from the same folder that its FLA is in.

In this book, we provide the entire set of classes with each code sample. This will ensure that your downloaded code sample runs and you won ’ t have to go scampering for the version of Papervision3D that we used for the demo.

58 Chapter 2: Getting Started With Papervision3D

Flex Builder You can run the program you created above, Papervision3DClass, in Flex. As mentioned earlier, there are actually several ways to run and a Papervision3D program in both Flex or Flash. Flash and Flex programs aren ’ t always interchangeable because they don ’ t have the same component base.

Flex has two distinct advantages over Flash:

❑ A better code editor ❑ Streamlined data handling

The big disadvantage is the learning curve, but with AS3 the learning curve is already high. So why not learn Flex anyway? Chances are, in the course of your career you ’ ll need to know and use both.

If you ’ re running data - rich Papervision3D applications, Flex is the way to go. In this book, you find out how to use Flash assets to enhance Flex applications so that your Flex applications don ’ t have that ‘ Flex look ’ . You ’ ll also discover how to make a Flex application look like Flash using Flash Catalyst and Flash Builder.

To create your custom class in Flex, follow these steps:

1. Open up Flex. 2. Select new. 3. Create an ActionScript Project and name it Papervision3DClass as before. Note: Flex automatically creates the class package for you, shown here: package { import flash.display.Sprite;

public class Papervision3DClass extends Sprite { public function Papervision3DClass() { } } }

4. Add in the script that you created in the previous exercise. Essentially just replace what Flex generated with the Flash Papervision3DClass file created earlier in this chapter. 5. Make sure that you put all the Papervision3D classes in the Flex Papervision3DClass folder as shown in Figure 2.11 (or you ’ ll get a million errors). 6. Finally, hit the run button. Oops . . . your sphere appears at the lower - right of your screen. 7. Change the position of the sphere by changing its x, y coordinate in the createObjects() function:

sphere.x=-300; sphere.y=100;

59 Part 1: Getting Started

Figure 2-11

A better way to set the initial position is to use the Stage class, which is covered in the section on BasicView. Your sphere in Flex should look like the one that was shown in Figure 2.8 previously. You now add motion.

Adding Motion Your sphere is just sitting motionless on the screen. And you may be wondering if this is all there is to Papervision3D? Should you ask for a refund? Not yet . . . you ’ ll extend this code to do some pretty amazing things in the coming chapters. But for now you’ll add some simple motion.

Adding motion to your sphere is really easy. You add three types of motion in this section:

❑ rotation ❑ translation ❑ scaling

As you ’ re already iterating the renderer in order to get your sphere moving, all you have to do is put some code in the myLoop function in your base code.

But what do you add and how do you find that code?

There are three ways to discover the commands that are available to use. You can open up the DisplayObject3D class and look at all the available functions. Or you can open up the sphere class contained in the Papervision3D classes (org /Papervision3D /objects /primitives /Sphere.as) and in the comments you ’ ll read the following:

It includes x, y, z, rotationX, rotationY, rotationZ, scaleX, scaleY scaleZ and a user defined extra object.

The other way is to open your application in Flex and use code hinting to find the available commands. Typing a period after the instantiated name sphere brings up all the possible commands that you can use with your sphere. Figure 2.12 shows what you see in Flex.

60 Chapter 2: Getting Started With Papervision3D

Figure 2-12

From Flex you find that the commands for translation are x, y, and z. The commands for rotation are rotationX , rotationY , and r otationZ (later in the book you learn about pitch, yaw, and roll). The commands for scaling are scaleX , scaleY , and scaleZ .

So your render function should be modified to look like the code here:

private function myLoop(evt:Event):void { //Rotate around x-axis sphere.rotationX+=2;

//Increase angle myTheta += 2; //plot a circular orbit sin and cos myX = Math.cos(myTheta * Math.PI / 180) * 100-300; myZ = Math.sin(myTheta * Math.PI / 180) * 100; //Set X and Z of sphere sphere.x = myX; sphere.z = myZ;

//Fattening and shrinking myY = 1+Math.cos(myTheta * Math.PI / 180)*.7; sphere.scaleX=sphere.scaleZ= 1+Math.sin(myTheta * Math.PI / 180)*.7; //Elongate sphere.scaleY =myY;

//Render Scene renderer.renderScene(scene, camera, viewport);

}

In the first part you add rotation around the x - axis by using:

//Rotate around x-axis sphere.rotationX+=2;

61 Part 1: Getting Started

With each iteration of the loop, the sphere rotates around the x - axis 2 degrees. How do you know it ’ s degrees and not radians? While in Flex builder hold down the ctrl key and roll over the function you want to investigate. Click on that function and it takes you to its class. In this case, the rotation class states clearly in its code that you ’ re using degrees.

//Increase angle myTheta += 2; //plot a circular orbit sin and cos myX = Math.cos(myTheta * Math.PI / 180) * 100-300; myZ = Math.sin(myTheta * Math.PI / 180) * 100; //Set X and Z of spher sphere.x = myX; sphere.z = myZ;

Finally, you stretch and shrink the sphere by using trig functions (cosine and sine) and the scale functions.

In the next part, translation is added by doing a simple trig rotation around a circle by incrementing the myTheta variable.

//Fattening and shrinking myY = 1+Math.cos(myTheta * Math.PI / 180)*.7; sphere.scaleX=sphere.scaleZ= 1+Math.sin(myTheta * Math.PI / 180)*.7; //Elongate sphere.scaleY =myY;

Adding animation and interactivity to Papervision3D is loads of fun. And you ’ ll make use of the techniques developed in this section many times in this book.

The following code is your final code. It forms the basis for much of what you ’ ll do in this book.

package { //Flash imports import flash.display.Sprite; import flash.events.Event; import flash.display.StageAlign; import flash.display.StageScaleMode; //Papervision3D Imports import org.papervision3d.cameras.Camera3D; import org.papervision3d.core.proto.MaterialObject3D; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.render.BasicRenderEngine; import org.papervision3d.scenes.Scene3D; import org.papervision3d.view.Viewport3D;

//Define your class public class Papervision3DClass extends Sprite { //Define your properties private var viewport:Viewport3D;

62 Chapter 2: Getting Started With Papervision3D private var camera:Camera3D; private var scene:Scene3D; private var renderer:BasicRenderEngine;

//Define your sphere variable. private var sphere:Sphere; private var sphereMaterial:MaterialObject3D; private var myTheta:Number=0; private var myX:Number=0; private var myY:Number=0; private var myZ:Number=0;

public function Papervision3DClass() { trace(“Hello World”);

//Initiate Papervision3D initPV3D();

//Create Your Objects createObjects();

//Create Renderer createRenderer();

}

//Define your methods

//Initialize Papervision3D private function initPV3D():void {

// Create the viewport viewport = new Viewport3D(0, 0, true, false); addChild(viewport);

// Create the camera camera = new Camera3D();

// Create the scene scene = new Scene3D();

// Create the renderer renderer = new BasicRenderEngine();

//Initialize Stage stage.align = StageAlign.TOP; stage.scaleMode = StageScaleMode.NO_SCALE;

} (continued)

63 Part 1: Getting Started

(continued)

//Create your objects private function createObjects():void { // Create a material for the sphere sphereMaterial = new WireframeMaterial(0xFFFFFF);

// Create/use wireframe material, radius 100, default 0,0,0 sphere = new Sphere(sphereMaterial, 100, 10, 10);

sphere.x=-300; sphere.y=130;

// Add Your Sphere to the Scene scene.addChild(sphere);

}

//Loop Renderer private function createRenderer():void{ addEventListener(Event.ENTER_FRAME, myLoop); }

//Single Loop private function myLoop(evt:Event):void { //Rotate around x-axis sphere.rotationX+=2;

//Increase angle myTheta += 2; //plot a circular orbit sin and cos myX = Math.cos(myTheta * Math.PI / 180) * 100-300; myZ = Math.sin(myTheta * Math.PI / 180) * 100; //Set X and Z of sphere sphere.x = myX; sphere.z = myZ;

//Fattening and shrinking myY = 1+Math.cos(myTheta * Math.PI / 180)*.7; sphere.scaleX=sphere.scaleZ= 1+Math.sin(myTheta * Math.PI / 180)*.7; //Elongate sphere.scaleY =myY;

//Render Scene renderer.renderScene(scene, camera, viewport);

}

} }

64 Chapter 2: Getting Started With Papervision3D

You should have obtained your figure to distort from a disk to an ellipse as shown in Figure 2.13.

Figure 2-13

In addition to adding motion in the example above, you removed the sphere positioning and set up the stage so that your movie sizes as you change the stage size. You do this by using the following commands:

//Initialize Stage stage.align = StageAlign.TOP; stage.scaleMode = StageScaleMode.NO_SCALE;

The selection of no scale for the scaleMode property, keeps the display objects from scaling. It keeps the content the same size as you ’ ve previously specified. By setting the align property of the stage to TOP_ LEFT you force the stage to the (0,0) point. TOP_LEFT and NO_SCALE are generally chosen for most Papervision3D experiments that are commonly found on web blogs.

Setting the Stage When the screen is resized, the Flash Player automatically adjusts the Stage contents to compensate. You can control this process using the Stage class.

The Stage class represents the main drawing area. You can create and load multiple display objects into the display list, but the stage property of each display object refers to the same stage.

During the course of this book, you ’ ll be primarily using three stage methods: align, scaleMode, and StageQuality. Now take a look at each one in more detail.

Align You have many options for setting your align. The various options for stage align are given below:

❑ StageAlign.TOP (Top, Center) ❑ StageAlign.BOTTOM (Bottom, Center) ❑ StageAlign.LEFT (Center, Left) ❑ StageAlign.RIGHT (Center, Right) ❑ StageAlign.TOP_LEFT (Top, Left)

65 Part 1: Getting Started

❑ StageAlign.TOP_RIGHT (Top, Right) ❑ StageAlign.BOTTOM_LEFT (Bottom, Left) ❑ StageAlign.BOTTOM_RIGHT (Bottom, Right)

The best way to understand them is to just try different options and see what works best for your particular application.

Scale Mode Of the four possible scale modes, three are designed to scale contents to fit within their boundaries. The fourth one is NO_SCALE and keeps the contents from resizing.

❑ EXACT_FIT — scales the SWF proportionally. ❑ NO_BORDER — determines whether the content can be partially cropped. ❑ SHOW_ALL — determines whether a border appears. ❑ NO_SCALE — contents maintain their defined size.

Overall, having scaleMode set to StageScaleMode.NO_SCALE gives you greater control over how the screen contents adjust to the window resizing.

Quality Setting stage quality from low to best can significantly tax your processor. Most Papervision3D applications set stage quality to low to reduce processor requirements. But there are times when the higher quality is needed. Below are the four different stage quality settings and what they mean:

❑ LOW — graphics are not anti - aliased, and bitmaps are not smoothed. ❑ MEDIUM — graphics are anti - aliased using a 2 2 pixel grid, but bitmaps are not smoothed. ❑ HIGH — graphics are anti - aliased using a 4 4 pixel grid, and bitmaps are smoothed if the movie is static. ❑ BEST — graphics are anti - aliased using a 4 4 pixel grid and bitmaps are always smoothed.

For most Papervision3D projects setting quality to low doesn ’ t change the look of your graphics much, but greatly enhances your application ’ s performance – sometimes as much as 50 per cent. When it comes to optimizing a Papervision3D application for the web, quality should be the first item on your list to check – set it to low. In most cases, it won ’ t change the appearance of your graphics by much.

An alternative to directly coding your viewport, camera, scene, and render loop, is to use the BasicView class. BasicView is a great way to get a Papervision3D project up and running quickly, which we discuss next.

66 Chapter 2: Getting Started With Papervision3D Using BasicView (Hello Plane) In a nutshell, BasicView acts as a wrapper class for combining the viewport, camera, scene and renderer together. It was developed to make the creation of Papervision3D applications easier. It ’ s great for small projects (ones that don ’ t require multiple cameras and viewports). You ’ ll use it often in the coming chapters. The following example is a rotating perlin plane:

package { //Flash Imports import flash.display.BitmapData; import flash.events.Event; import flash.display.StageAlign; import flash.display.StageScaleMode; //Papervision3D Imports import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.view.BasicView;

//Declare the Hello Plane class that extends BasicView public class HelloPlane extends BasicView { //Declare the plane, bitmapdata, and material variables protected var myPlane:Plane; protected var planeBitmapData:BitmapData; protected var planeMaterial:BitmapMaterial;

//Create the Hello Plane Constructor function public function HelloPlane() { super(1, 1, true, false); initPV3D(); } //Set up stage and plane private function initPV3D():void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; opaqueBackground = 0; //Set up plane planeBitmapData = new BitmapData(512,256,false,0); planeBitmapData.perlinNoise(512,256,4, 123456, true,false); planeMaterial = new BitmapMaterial(planeBitmapData); myPlane = new Plane(planeMaterial,300,300, 10,10); scene.addChild(myPlane); startRendering(); } //Override protected function override protected function onRenderTick(event:Event=null):void { (continued)

67 Part 1: Getting Started

(continued)

//Rotate the hello plane primitive. myPlane.rotationY+=4; super.onRenderTick(event); } } }

This short application uses perlin noise to fill a plane as shown in Figure 2.14. Perlin noise is pseudo - random noise that creates fractal - like distortions on a data set. It has a large number of uses such as creating fire, marble, wood, clouds, and terrain. You ’ ll also be using it in the coming chapters.

Figure 2-14

As you run the rotating perlin plane example above, notice that as the plane rotates 180 degrees out of view, it disappears for half of a revolution. It hasn ’ t really disappeared though, it ’ s just invisible. This is because in order to see the plane (or any 3D object) its normal vectors from the surface must be facing you. When the plane disappears its normal vectors are facing away from you and you can ’ t see it.

The key differences between using BasicView and what you ’ ve done before (creating the Papervision3DClass ) are listed below:

❑ Missing declaration of scene, camera, and viewport — encapsulated in the BasicView class. ❑ Adding startRendering() — hidden in the AbstractView class and takes the place of the addEventListener(Event.ENTER_FRAME, myLoop) method. ❑ Adding onRenderTick — the event listener that corresponds to the startRendering method.

You should notice the use of the override statement which, as discussed in the section “ Extending Your Class (Inheritance) ,” is used here to extend the onRenderTick method. You ’ ve heard the term encapsulation a few times now; we ’ ll now encapsulate the entire preceding Hello Plane example.

68 Chapter 2: Getting Started With Papervision3D

Encapsulation The power of Papervision3D is in its use of OOP (object oriented programming) principles. If you ’ ve ever put a movie inside of a movie in Flash, then you already understand how encapsulation works. But in this case, you ’ re encapsulating classes (putting classes in classes), enabling you to provide a friendlier object interface, remove clutter, and isolate functionality. Encapsulation simplifies programming, allowing your program to become a collection of objects as opposed to a set of instructions.

package { //Flash Imports import flash.display.MovieClip; import flash.display.StageAlign; import flash.display.StageScaleMode;

//Not needed when in same directory as Main File import HelloPlane;

//Papervision3D Imports import org.papervision3d.view.BasicView;

//Extend Movie Class public class Main extends MovieClip { private var view:BasicView; public function Main() { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT;

//Setup the “view” view = new HelloPlane(); //Add the view to stage. addChild(view);

//Start rendering. view.startRendering();

}

} }

The Main class above calls your HelloPlane class (developed previously with stage moved to main). It ’ s much cleaner, and can be used to call any class. It ’ s a convenient mechanism that ’ s well worth understanding. Now that you ’ ve seen the basics of BasicView, you ’ ll apply it to your previous undulating sphere example.

Undulating Sphere using BasicView Applying BasicView to the undulating sphere example only requires a few changes. The code still uses the initPV3D method in its constructor to set everything into motion, the createObjects method to create

69 Part 1: Getting Started

your sphere, and the startRendering method to start the rendering process. However, it no longer needs to create a separate viewport, scene, camera, and renderer. That ’ s now being handled by BasicView.

To get BasicView operational, you need to make the following additions:

❑ Super — comes from extending your class. It ’ s essentially the instantiation of the class that ’ s being extended. ❑ setStage — used to orient your scene, but is not required for BasicView to run. ❑ onRenderTick — needed for BasicView to iterate the scene.

The addition of Super and onRenderTick were necessary to get BasicView operational. The setStage method was added to enhance the visual appeal. The finished code is listed here:

package { //Flash imports import flash.display.BitmapData; import flash.events.Event;

//Basic Papervision3d Engine import flash.display.StageAlign; import flash.display.StageScaleMode; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView; import org.papervision3d.core.proto.MaterialObject3D; import org.papervision3d.materials.WireframeMaterial;

// BasicViewTest extends BasicView, and automatically sets up Scene, Camera, Renderer and Viewport for you.

public class BasicViewTest extends BasicView {

protected var planeBitmapData:BitmapData; protected var planeMaterial:BitmapMaterial;

//Define your sphere variable.

private var sphere:Sphere; private var sphereMaterial:MaterialObject3D; private var myTheta:Number=0; private var myX:Number=0; private var myY:Number=0; private var myZ:Number=0;

//This allows for easy Papervision3d initialization. //Call to the BasicView constructor public function BasicViewTest() {

70 Chapter 2: Getting Started With Papervision3D

//Width and Height are set to 1, since scaleToStage is set to true, these will be overriden.

super(1, 1, true, false); initPV3D();

}

private function initPV3D():void { //Set the Stage Scale setStage();

//Color the background of this basicview / helloworld instance black. opaqueBackground = 0;

//Create the materials and primitives. createObjects();

//Call the native startRendering function, to render every frame. startRendering();

}

protected function setStage():void{ stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; }

//createObjects creates the needed primitives, and materials.

protected function createObjects():void { // Create a material for the sphere sphereMaterial = new WireframeMaterial(0xFFFFFF);

// Create anduse wireframe material, radius 100, default 0,0,0 sphere = new Sphere(sphereMaterial, 100, 10, 10);

// Add Your Sphere to the Scene scene.addChild(sphere); }

// onRenderTick can be overridden so you can execute code on a per render basis, using basicview.

override protected function onRenderTick(event:Event=null):void { //Rotate around x-axis sphere.rotationX+=2;

//Increase angle myTheta += 2; (continued)

71 Part 1: Getting Started

(continued)

//plot a circular orbit sin and cos myX = Math.cos(myTheta * Math.PI / 180) * 100; myZ = Math.sin(myTheta * Math.PI / 180) * 100; //Set X and Z of sphere sphere.x = myX; sphere.z = myZ;

//Fattening and shrinking myY = 1+Math.cos(myTheta * Math.PI / 180)*.7; sphere.scaleX=sphere.scaleZ= 1+Math.sin(myTheta * Math.PI / 180)*.7; //Elongate sphere.scaleY =myY;

//Render Scene renderer.renderScene(scene, camera, viewport); //Call the super.onRenderTick function, which renders the scene to the viewport using the renderer and camera classes. super.onRenderTick(event); }

} }

In using the BasicView class you make some savings in the code required to set up the application, and the renderer is now automatic. However, you still need to set up your objects and materials. The down side to using BasicView is that you lose control over the render process. So even though the BasicView wrapper class is good for small experiments, it won ’ t suffice when working with more complex applications, and you may find it more transparent to create your own scene using the earlier approach in this chapter.

Incorporating CS4 With the release of Flash Player 10, many improvements were made to the Flash Drawing AP1. Throughout this book, you find out about many of the improvements that can enhance your Papervision3D applications. The following list of changes have the potential for improving Papervision3D:

❑ Vectors or typed arrays provide enhanced performance for the drawing API. ❑ Perspective rendering is directly incorporated into triangle drawing and 3D transformation translation. ❑ Powerful drawing methods are now part of the Flash display graphics packages, such as drawTriangles and drawPaths. ❑ Bone animation capability through Inverse Kinematics.

72 Chapter 2: Getting Started With Papervision3D

❑ UV mapping of textures onto 3D triangulated structures at runtime. ❑ Non - zero winding rules for drawing a complex shape or gradients. ❑ Shaders and Pixel Bender.

Summary In Chapter 1, using CS4 you built a torus worm, carousel, and image ball. In Chapter 3, along with learning about primitives, you ’ ll build your first CS4 primitive.

In this chapter, you were introduced to the basics of Papervision3d, Painter ’ s Algorithm, and the view frustum. You learned about culling and clipping and examined the guts of Papervision3d. You instantiated your first primitive (a sphere) and added a wireframe material. You extended your application using BasicView, and examined the different ways to run an application. In the next chapter, you build upon what you ’ ve learned by adding a new host of primitives to your repertoire.

73

Rezzing Primitives

Chances are that when you were a child, you were given a box of building blocks that contained cubes, rectangles, half spheres, cylinders, and cones. And then you spent hours in play building fantastic structures. The experiences you gained from your many hours of play will now come in handy. The shapes you built with are called primitives and many drawing packages such as Bryce, Blender, and 3DSMax rely heavily on primitives to build with.

Second Life is an entire virtual world built with nothing but primitives: just like your world of blocks. A natural extension to Papervision3D would be to have such a primitive building set for constructing buildings, furniture, vehicles, and so on. In this chapter you focus on Papervision3D ’ s basic primitive set and then learn how to create your own custom primitives in both Papervision3D and CS4.

Primitives form the structure of your world. Materials placed on your primitives give primitives meaning and interactivity. You ’ ve probably already seen some pretty cool websites using interactive planes or cubes. In this chapter, you find out the basics required to create such sites.

Understanding Primitives Primitives are constructed by extruding basic two dimensional shapes. For example, a cube can be created by extruding a 2D square along a linear path. And similarly, a cylinder can be created by extruding a circle along the same linear path. Modifying the cylinder extrusion by narrowing the extrusion to a point creates a cone. However, the term primitive is used a little more loosely when it comes to modeling in programs such as 3DSMax: it ’ s anything used to build with which could include 2D components such as a plane. More generally, think of a primitive as a building block.

Borrowing from Second Life, primitives are called prims. And creating a prim (or bringing it to the stage) is called rezzing a prim (see Figure 3.1). Part 1: Getting Started

Figure 3-1

In the panel above, you can see all the available prims in Second Life. And using the Second Life magic wand allows you to place them on your stage for stacking or modeling.

In Second Life prims can be modified – shrunk, stretched, twisted, sheared, stacked, cut, and textured. The possibilities are endless. To find the available prims in Papervision3D navigate to the primitives folder found in org/ papervision3d/objects/primitives.

In the primitives folder, you ’ ll find the following set of available primitives:

❑ Plane ❑ PaperPlane ❑ Cube ❑ Sphere ❑ Cylinder ❑ Cone ❑ Arrow

In the list above, PaperPlane is not really a plane. It ’ s a simple colored airplane without a texture (thus, the name PaperPlane). The PaperPlane is useful in finding the direction that an object is facing and used by the Papervision3D team in code development. It ’ s also a good place to start when investigating how a prim is constructed.

Exploring the Guts of a Papervision3D Primitive The three most important elements of a primitive are vertices, face indices, and uv map coordinates. From vertices you create triangles that form your objects, and then you lay uv texture maps on those triangles to clothe your objects.

76 Chapter 3: Rezzing Primitives

Vertices Crucial to understanding how Papervision3D handles objects is to grasp how vertices are manipulated to create 3D computer graphics. The most important thing to remember when dealing with 3D graphics in Papervision3D is that “ a vertex is a vertex ” . No matter how complex your 3D objects become, it all eventually boils down to a group of vertices arranged in triangles.

Papervision3D ’ s vertex class is Vertex3D. It holds the 3D position of your vertices and a few methods for those vertices. Technically speaking a vertex is an instance of a data structure. That instance contains the attributes of your point in space … wherever it may be. Face Indices All Papervision3D objects are composed of vertices. Papervision3D uses the TriangleMesh3D class to construct them. The TriangleMesh3D class is found in the geom folder in org/papervision3d/ core/geom .

It allows you to create and display solid 3D objects made of vertices and triangular polygons. Vertices are put together in triangular polygon formations using indices. Three indices make up a single triangle. And triangles grouped together linearly form a triangular strip. For example consider a triangular strip below that has the following face indices:

Indices 1,2,0,2,3,0,4,5,0,5,1,0,7,6,5,7,5,4

Each vertex that appears in Figure 3.2 is labeled with a number. These numbers are assigned as the image is created. Each triangle below is represented by a triplet of indices, and is arranged so that each triangle is traced out clockwise. This is extremely important since, as the object rotates away from the camera, its faces become counter - clockwise giving an indication of which triangles should be culled using painters ’ algorithm.

Figure 3-2

You ’ ve probably noticed that the triangles above aren ’ t numbered sequentially. That ’ s because this comes from actual data that was generated from a strip created in Blender. You ’ ll learn more about 3D modeling in Chapter 5.

Creating high quality objects requires a large number of triangles. But with a higher number of triangles comes a higher CPU performance hit (more computational cycles to create the object). So the goal is to create low polygon objects that run quickly in Papervision3D. Many modeling software packages have

77 Part 1: Getting Started

polygon and vertices counters that help you keep track of how many of each you have as you model your object. You should use these counters as your model to keep your numbers down. UV Coordinates UV mapping is a method for applying textures to the triangles of an object. UV maps are two dimensional and based on percentages (or values from 0 to 1). U and V represent the horizontal and vertical texture axes respectively. As can seen in Figure 3.3, U goes from 0 to 1 in the horizontal direction and V from 0 to 1 in the vertical direction.

Figure 3-3

To understand how vertices, indices, and UV coordinates work, start by examining the plane primitive next. It ’ s the simplest of the textured primitives, as it ’ s based on an x - y grid. Constructing a Plane In Papervision3D, the vertices for the plane are generated from a double for loop that loops through an x and y coordinate grid (segmented from the height and width of your plane), as shown here.

for ( var ix:int = 0; ix < gridX + 1; ix++ ) { for( var iy:int = 0; iy < gridY1; iy++ ) { var x :Number = ix * iW - textureX; var y :Number = iy * iH - textureY; vertices.push( new Vertex3D( x, y, 0 ) ); } }

78 Chapter 3: Rezzing Primitives

These x - y grid points are placed in a Vertex3D object and pushed into a vertices array that ’ s used to calculate index and UV coordinate positions. Next the faces array of the triangles are generated using the following method:

faces.push(new Triangle3D(this, [ a, b, c ], material, [ uvA, uvB, uvC ] ));

Where a , b , c are your face indices, uvA , uvB , uvC are your coordinate indices, and the material parameter is your texture. Papervision3D actually does this iteration twice to generate complimentary triangles that connect to form your object. This doubling up is part of the mathematical generation of the indices and UV coordinates, but when importing XML Blender/3DSMax models into Papervision3D (see Chapter 5) this method will only be needed once.

This is just a brief introduction to vertices, indices, and uv mapping – so don ’ t worry if you didn ’ t get it all. You ’ ll be visiting this topic many times throughout the book in greater detail. Just keep in mind that vertices, indices, and uv coordinates are important quantities. And that certain methods use these quantities to create textured triangles that make up your 3D objects. That ’ s really all there is to it!

Now that you ’ ve got some of the basics you can start rezzing primitives to the stage. The vertices, indices, and uv coordinates are generated internal to the primitive, but the primitive material is added externally. You ’ ll learn more about texturing in the next chapter.

Rezzing Primitives to the Stage You ’ ve already been introduced to rezzing prims in Chapter 2 where you placed a sphere and a plane on the stage. In this chapter, using the same BasicView class and wireframe material, you ’ ll build the five prims that are shown in Figure 3.4.

Figure 3-4

Rezzing a prim is simple: in almost every case except for the cube, all you have to do is replace the bracketed statements in the following template code with the appropriate commands for the prim being rezzed. Item 1 imports the appropriate prim class for the object under consideration. Item 2 declares your prim name type. And item 3 instantiates your primitive. The instantiated primitive is then added to the scene using the addChild method in item 4. And finally, an action statement (animation sequence) is added to your prim in item 5.

For simplicity, use the wireframe material, but in the next section you learn how to dress your prim in colors, textures, shaders, and even video.

79 Part 1: Getting Started

Prim Template Code (Using a Wireframe Material) The code is a template for various types of primitives using a wireframe texture. In the sections that follow you learn how to apply this template.

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; //import Papervision3D import org.papervision3d.materials.WireframeMaterial;

1. [Your Primitive Import Statement Goes Here]

import org.papervision3d.view.BasicView;

//Create WirePlane class public class WirePlane extends BasicView { //Instantiate primitive and wireframe material

2. [Your Primitive Name Type Declaration Goes Here]

private var planeMaterial:WireframeMaterial;

public function WirePlane() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); }

//Initiate Papervision protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0; // Create a wireframe material for the plane planeMaterial = new WireframeMaterial(0xFFFFFF); planeMaterial.doubleSided=true; //Add your wireframe to the scene

80 Chapter 3: Rezzing Primitives

3. [Your Primitive is Instantiated Here]

// Add Your Primitive to the Scene

4. [Your AddChild Statement Goes Here]

}

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Apply an action to your primitive

5. [Your Action Statement Goes Here]

//Call the super.onRenderTick function super.onRenderTick(event); }}}

You start with the plane, the most popular of the Papervision3D prims. You might even say that it ’ s been overused.

Plane Using the template code above you can rez a rotating plane to the stage by replacing the bracketed statements with the appropriately numbered code:

1. Import the Papervision3D Plane class. import org.papervision3d.objects.primitives.Plane;

2. Declare variable name plane variable and data type it Plane . private var plane:Plane;

3. Instantiate a Plane using the variable name plane and add the material, height, width, horizontal and vertical segments to the Plane method. plane = new Plane(planeMaterial, 400,400, 10, 10);

4. Add your plane to the scene using the addChild command. scene.addChild(plane)

5. Start your plane rotating using the yaw command.

plane.yaw(2);

81 Part 1: Getting Started

The complete code is shown here:

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; //import Papervision3D import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.objects.primitives.Plane; import org.Papervision3d.view.BasicView;

//Create WirePlane class public class WirePlane extends BasicView { //Instantiate plane and wireframe material private var plane:Plane; private var planeMaterial:WireframeMaterial;

public function WirePlane() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); }

//Initiate Papervision protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0; // Create a wireframe material for the plane planeMaterial = new WireframeMaterial(0xFFFFFF); planeMaterial.doubleSided=true; //Add your wireframe to the scene plane = new Plane(planeMaterial, 400,400, 10, 10); // Add Your Plane to the Scene scene.addChild(plane); }

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Rotate plane around y-axis plane.yaw(2); //Call the super.onRenderTick function super.onRenderTick(event); }

82 Chapter 3: Rezzing Primitives

The main elements of the code are the import statement, plane declaration, creation of the wireframe material, and instantiation of the plane. The plane method shown below has five parameters: material, width, height, horizontal plane segments, and vertical plane segments.

plane = new Plane( material, width, height, segmentsW, segmentsH)

When rotating the plane beyond 180 degrees it becomes invisible. This is because the normal vector of the plane is no longer facing you. The normal vector of the plane tells Papervision3D which side of the plane to render. To tell Papervision3D to render both sides of the plane you set the doubleSided property of the material to true:

planeMaterial.doubleSided=true;

Next, you rez a cube to the stage using the materials list, a powerful method for handling multiple materials. Although the materials list is commonly used for the cube you can use it whenever you need multiple materials.

Cube The big difference between the cube and the other prims is the use of a materials list (containing many materials) instead of a single material. The code snippet below demonstrates how to institute a materials list to be used on a cube in the code template.

1. Import the Papervision3D Cube class and the MaterialsList class. import org.papervision3d.objects.primitives.Cube; import org.papervision3d.materials.utils.MaterialsList;

2. Declare the cube variable name and data type it Cube . cubeMaterial name and data type it Wireframe , and myMaterialList name and data type it MaterialsList . private var cube:Cube; private var cubeMaterial:WireframeMaterial; private var myMaterialsList:MaterialsList;

Apply your materials to a cube by placing your materials into a MaterialsList Object, and by replacing the planeMaterial = new WireframeMaterial(0xFFFFFF) statement in the template code with the code here. cubeMaterial = new WireframeMaterial(0xFFFFFF);

var myMaterialsList:MaterialsList = new MaterialsList(); myMaterialsList.addMaterial( wireframeMaterial, “front” ); myMaterialsList.addMaterial( wireframeMaterial, “back” ); myMaterialsList.addMaterial( wireframeMaterial, “left” ); myMaterialsList.addMaterial( wireframeMaterial, “right” ); myMaterialsList.addMaterial( wireframeMaterial, “top” ); myMaterialsList.addMaterial( wireframeMaterial, “bottom” );

83 Part 1: Getting Started

In the preceding code, the materials list is instantiated with the name my MaterialsLists and data typed MaterialsList . The front, back, left, right, top, and bottom terms are programmed into the prim code and identify which materials go where on your cube.

3. Instantiate a Cube using the variable name cube and add the myMaterialsList , height , width , horizontal and vertical segments to the Cube method. cube = new Cube(myMaterialsList, 400,400,400,4,4,4);

4. Add your cube to the scene using the addChild command. scene.addChild(cube);

5. Start your cube rotating using the yaw command. cube.yaw(2);

The complete code is shown here:

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event;

import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.primitives.Cube; import org.papervision3d.view.BasicView;

//Create WireCube class public class WireCube extends BasicView { //Instantiate cube and wireframe material private var cube:Cube; private var cubeMaterial:WireframeMaterial; private var myMaterialsList:MaterialsList;

public function WireCube() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); }

//Initiate Papervision3D protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0;

84 Chapter 3: Rezzing Primitives

// Create a wireframe material for the cube cubeMaterial = new WireframeMaterial(0xFFFFFF);

//Add Materials List myMaterialsList = new MaterialsList(); myMaterialsList.addMaterial( cubeMaterial, “front” ); myMaterialsList.addMaterial( cubeMaterial, “back” ); myMaterialsList.addMaterial( cubeMaterial, “left” ); myMaterialsList.addMaterial( cubeMaterial, “right” ); myMaterialsList.addMaterial( cubeMaterial, “top” ); myMaterialsList.addMaterial( cubeMaterial, “bottom” );

cubeMaterial.doubleSided=true; //Add your wireframe to the scene cube = new Cube(myMaterialsList, 400,400,400,4,4,4); // Add Your Cube to the Scene scene.addChild(cube); }

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Rotate plane around y-axis cube.yaw(1); cube.pitch(1); cube.roll(1); //Call the super.onRenderTick function super.onRenderTick(event); }

} }

The best way to understand how primitives are built is to navigate to them and examine their code. The cube prim is found in the primitives folder found in org/papervision3d/objects/primitives.

The cube prim is created from six planes using the buildPlane method shown in the following code (taken from the Papervision ’ s Cube class). The method is called six times, arranging each plane into the proper orientation to build a cube. A conditional if statement governs which planes are to be drawn (by default all six planes are drawn).

if ( ! (excludeFaces & FRONT) )buildPlane( “ front” , “ x” , “y” , width, height, depth2, ! Boolean( insideFaces & FRONT ) );

if ( ! (excludeFaces & BACK) )buildPlane( “ back” , “ x”, “y” , width, height, -depth2, Boolean( insideFaces & BACK ) );

85 Part 1: Getting Started

if ( ! (excludeFaces & RIGHT) )buildPlane( “ right”, “ z” , “y” , depth, height, width2, Boolean( insideFaces & RIGHT ) );

if ( ! (excludeFaces & LEFT) )buildPlane( “ left” , “ z” , “y” , depth, height, -width2, ! Boolean( insideFaces & LEFT ) );

if ( ! (excludeFaces & TOP) )buildPlane( “ top” , “ x” , “z” , width, depth, height2, Boolean( insideFaces & TOP ) );

if ( ! (excludeFaces & BOTTOM) )buildPlane( “ bottom”, “ x” , “z” , width, depth, - height2, ! Boolean( insideFaces & BOTTOM ) );

The materials list holds the material for each side of the cube. You apply materials to your cube by placing your materials into a MaterialsList Object. After instantiating the myMaterialsList object, you set up the cube ’ s materials list as follows:

var myMaterialsList:MaterialsList = new MaterialsList(); myMaterialsList.addMaterial( wireframeMaterial, “front” ); myMaterialsList.addMaterial( wireframeMaterial, “back” ); myMaterialsList.addMaterial( wireframeMaterial, “left” ); myMaterialsList.addMaterial( wireframeMaterial, “right” ); myMaterialsList.addMaterial( wireframeMaterial, “top” ); myMaterialsList.addMaterial( wireframeMaterial, “bottom” );

The MaterialsList class is simply a dictionary that allows you to store your materials by name for look up. The Dictionary class lets you create a dynamic collection of properties, which uses strict equality (===) for key comparison. When an object is used as a key, the object ’ s identity is used to look up the object.

The MaterialsList Object allows you to apply the material according to each face of the cube using the tags -front , back , left , right , top , and bottom . From the Cube method below, the first parameters are the materials list, width , length , height , and their perspective segmentation.

cube = new Cube(materials, width, depth, height, segmentsS, segmentsT, segmentsH, insideFaces, excludeFaces)

Two additional parameters are available that can come in handy. They are inside faces and exclude faces . Inside faces allows you to see faces on the inside (or switches the normals of the specified face), and exclude faces inhibits a face from being drawn. These parameters can be connected with a pipe and as a result multiple sides can be excluded from being drawn or have their normals switched. As shown in Figure 3.5, the top and bottom of a cube are excluded using the pipe command (Cube.BACK|Cube. FRONT ).

86 Chapter 3: Rezzing Primitives

Figure 3-5

The complete list of possible excludable planes follows the cube plane naming convention (ALL , NONE , BACK , FRONT , TOP , BOTTOM , LEFT , RIGHT ):

Cube(myMaterialsList,400,400,400,4,4,4,Cube.ALL,Cube.BACK|Cube.FRONT);

The materials list class is used whenever a list of materials needs to be stored. It ’ s not just for a cube; you can also use it on custom prims. Sphere The WireSphere class below requires an import statement, a sphere declaration, a wireframe material, and the instantiation of the sphere in the template code.

1. Import the Papervision3D Sphere class. import org.papervision3d.objects.primitives.Sphere;

2. Declare the sphere variable name and data type it Sphere . private var sphere:Sphere;

3. Set the sphere parameters and the material. Instantiate the Sphere class as sphere . sphere = new Sphere(sphereMaterial, 400, 10, 10);

4. Add your sphere to the scene using the addChild command. scene.addChild(sphere);

5. Start your sphere rotating using the yaw command. sphere.yaw(2);

87 Part 1: Getting Started

The complete code is shown here:

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; //import Papervision3D import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView;

//Create WireSphere class public class WireSphere extends BasicView { //Instantiate sphere and wireframe material private var sphere:Sphere; private var sphereMaterial:WireframeMaterial;

public function WireSphere() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); }

// protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0; // Create a wireframe material for the plane sphereMaterial = new WireframeMaterial(0xFFFFFF); sphereMaterial.doubleSided=true; //Add your wireframe to the scene sphere = new Sphere(sphereMaterial, 400, 10, 10); // Add Your sphere to the Scene scene.addChild(sphere); }

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Rotate sphere around y-axis sphere.yaw(2); //Call the super.onRenderTick function super.onRenderTick(event); }

88 Chapter 3: Rezzing Primitives

} }

As shown here, the Sphere prim takes 4 parameters: material , radius , width segments , and height segments .

sphere = new Sphere(material, radius, wSegments , hSegments); Slicing a Sphere One really cool thing you can do with prims is slice their mesh. In Figure 3.6, you slice off a piece of a sphere so you can peer inside of it. This is accomplished in the following code using the Plane3D and cutTriangleMesh method:

//Cut Sphere var normal:Number3D = new Number3D(1,.5,0); //Cut angle var point:Number3D = new Number3D(200,300,0); //Cut position... var cutPlane:Plane3D = Plane3D.fromNormalAndPoint(normal, point);

//Cut the sphere along the plane3D var cutMeshes:Array = MeshUtil.cutTriangleMesh(sphere, cutPlane); //Two meshes are returned //Add top Mesh tophemiSphere = cutMeshes[0]; tophemiSphere.x = 200; scene.addChild(tophemiSphere);

//Add bottom Mesh bothemiSphere = cutMeshes[1]; bothemiSphere.x = -100; scene.addChild(bothemiSphere);

Figure 3-6

89 Part 1: Getting Started

The complete source code is listed here:

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; //import Papervision3D import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView;

//For Cutting import org.papervision3d.core.geom.TriangleMesh3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.core.math.Plane3D; import org.papervision3d.core.utils.MeshUtil;

//Create WirePlane class public class WireSphereCut extends BasicView { //Instantiate plane and wireframe material private var sphere:Sphere; private var sphereMaterial:WireframeMaterial;

private var tophemiSphere:TriangleMesh3D; private var bothemiSphere:TriangleMesh3D;

public function WireSphereCut() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); }

protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0; // Create a wireframe material for the plane sphereMaterial = new WireframeMaterial(0xFFFFFF); sphereMaterial.doubleSided=true; //Add your wireframe to the scene sphere = new Sphere(sphereMaterial, 400, 30, 30); // Add Your Plane to the Scene //Cut Sphere var normal:Number3D = new Number3D(1,.5,0); //Cut angle var point:Number3D = new Number3D(200,300,0); //Cut position... var cutPlane:Plane3D = Plane3D.fromNormalAndPoint(normal, point);

90 Chapter 3: Rezzing Primitives

//Cut the sphere along the plane3D var cutMeshes:Array = MeshUtil.cutTriangleMesh(sphere, cutPlane); //Two meshes are returned //Add top Mesh tophemiSphere = cutMeshes[0]; tophemiSphere.x = 200; scene.addChild(tophemiSphere);

//Add bottom Mesh bothemiSphere = cutMeshes[1]; bothemiSphere.x = -100; scene.addChild(bothemiSphere);

}

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void {

//Rotate the spheres.

tophemiSphere.yaw(1); bothemiSphere.yaw(-1);

//Call the super.onRenderTick function super.onRenderTick(event); } } }

Next, you rez a cylinder. Cylinder The WireCylinder class (see Figure 3.7) requires an import statement, sphere declaration, creation of the wireframe material, and instantiation of the cylinder. Using the template developed earlier, follow these steps:

1. Import the Papervision3D Cylinder class: import org.papervision3d.objects.primitives.Cylinder;

2. Declare the cylinder variable name and data type it Cylinder ; private var cylinder:Cylinder;

3. Instantiate a Cylinder using the variable name cylinder and add the material , radius , height , horizontal and vertical segments and optional top radius : cylinder = new Cylinder(cylinderMaterial, 140,400, 10, 10);

91 Part 1: Getting Started

4. Then add the cylinder to the scene using the addChild command: scene.addChild(cylinder);

5. Start your cylinder rotating using the yaw command: cylinder.yaw(2);

Figure 3-7

The complete code is shown here:

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; //import Papervision3D import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.objects.primitives.Cylinder; import org.papervision3d.view.BasicView;

//Create CylinderPlane class public class WireCylinder extends BasicView { //Instantiate Cylinder and wireframe material private var cylinder:Cylinder; private var cylinderMaterial:WireframeMaterial;

public function WireCylinder() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); }

92 Chapter 3: Rezzing Primitives

// protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0; // Create a wireframe material for the Cylinder cylinderMaterial = new WireframeMaterial(0xFFFFFF); cylinderMaterial.doubleSided=true; //Add your wireframe to the scene cylinder = new Cylinder(cylinderMaterial, 140,400, 10, 10); // Add Your Cylinder to the Scene scene.addChild(cylinder); }

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Rotate plane around y-axis cylinder.yaw(2); cylinder.roll(2); //Call the super.onRenderTick function super.onRenderTick(event); }

} }

cylinder = new Cylinder(wireframeMaterial, radius, height, wSegments, hSegments, topRadius);

cylinder = new Cylinder(cylinderMaterial, 140,400, 10, 10,-1, true, false);

As demonstrated next, the cylinder can be turned into a cone by setting its top radius to zero. Cone The cone is generated by extending the Cylinder class as follows:

public class Cone extends Cylinder

This is only possible because the Cylinder class already contains a top radius component that ’ s set to zero to create a cone. The code snippet for the cone is almost identical to the cylinder snippet where the radius component set the base diameter of the cone.

1. Import the Papervision3D Cone class. import org.papervision3d.objects.primitives.Cone;

2. Create the cone variable and data type it Cone . private var cone:Cone;

93 Part 1: Getting Started

3. Instantiate a Cone using the variable name cone and add the material , radius , height , horizontal and vertical segments. cone = new Cone(coneMaterial, 140,400, 10, 10);

4. Add the cone to the scene using the addChild command. scene.addChild(cone);

5. Start your cone rotating using the yaw command. cone.yaw(2);

The complete code is shown here:

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; //import Papervision3D import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.objects.primitives.Cone; import org.papervision3d.view.BasicView;

//Create Cone class public class WireCone extends BasicView { //Instantiate Cone and wireframe material private var cone:Cone; private var coneMaterial:WireframeMaterial;

public function WireCone() { //Call super class super(1, 1, true, false); initPV3D(); startRendering(); }

// protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0; // Create a wireframe material for the Cylinder coneMaterial = new WireframeMaterial(0xFFFFFF); coneMaterial.doubleSided=true; //Add your wireframe to the scene

94 Chapter 3: Rezzing Primitives

cone = new Cone(coneMaterial, 140,400, 10, 10); // Add Your Cone to the Scene scene.addChild(cone); }

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Rotate plane around y-axis cone.yaw(2); cone.roll(2); //Call the super.onRenderTick function super.onRenderTick(event); }

} }

Making Ellipsoids Not every coffee table in the world is round: many are oval or ellipsoidal. The same holds for balloons. In addition to the ones used for parties or in Albuquerque at the Balloon Fiesta; there are also blimps. Making these ellipsoidal shapes can be accomplished by modifying the parametric equations (introduced in Chapter 1) of your primitive set. By applying the parametric equations below for the cylinder and sphere you can create your oval table or blimp. Cylindrical Ellipsoid (oval table) The addition of the parameters a and b to the parametric equations below give you the ability to change an ordinary cylinder into a stretched one along the x or z - axis. Thus, creating your oval coffee table:

x=a*cos(u) z=b*sin(u) y=v Spherical Ellipsoid (blimp) The addition of the parameters a , b and c to the parametric equations below gives you the ability to change an ordinary sphere into one stretched along its x, z or y - axis, creating your blimp.

x=a*cos(u)*sin(v) z=b*sin(u)sin(v) y=c*cos(v)

But there ’ s an easier way!

Every object in Papervision3D has a DisplayObject3D . And every DisplayObject3D supports basic functionality like position ( x , y , z ), rotation ( rotationX , rotationY , rotationZ ), scaling (scaleX , scaleY , scaleZ ), and visibility. So, as opposed to doing all the work above to create an ellipsoidal object, use the scaleX , scaleY and scaleZ instead to accomplish the same purpose.

95 Part 1: Getting Started

To turn a cylinder into an oval just scale the Cylinder in the x direction use:

cylinder.scaleX=2;

This gives you twice the scaling in the x direction. Or turn a sphere into a blimp using:

sphere.scaleX=2;

Or you can even turn a sphere into a flying saucer by compressing its y - axis and expanding its x and z - axis using the following code:

//Instantiate your sphere sphere = new Sphere(sphereMaterial, 400, 10, 10); //Add Your Plane to the Scene and its deformation scene.addChild(sphere); sphere.scaleX=1.2; sphere.scaleZ=1.2; sphere.scaleY=.4;

After instantiating your sphere, add it to the scene and apply your scale transformations. The results are similar to those obtained in Chapter 2, by squashing your sphere using scaling.

The only issue with scaling is that it doesn ’ t increase or decrease the number of segments in your prim. So if you greatly enlarge something you may have to increase the number of segments. And conversely if you shrink something, it reduces the number of triangles required to render it.

Now that you ’ ve rezzed the standard prims to the stage, discovered how to make ellipsoids, and seen a few applications, it ’ s time to learn how to make your own prims.

Creating Custom Prims Creating custom prims only requires a little knowledge of geometry and how they’ re made in Papervision3D. To illustrate the process, begin by building some very simple prims and then move to more advanced prim construction. In every case, you modify an existing prim to enhance or create a new one.

To avoid wrecking your base prims, create a new prim with a different name and modify it. Doing so only takes two steps:

1. Open up the prim you ’ re going to modify and save as a new name. 2. Change the prim class and constructor names to that new name.

To illustrate the point, you modify the Sphere prim by pinching its center.

Pinched Sphere The Pinched Sphere is one of the easiest custom prims to create (see Figure 3.8). First start by opening up the Sphere prim found in Papervision3D ’ s primitives folder. You can get there by navigating through the path org/papervision3d/objects/primitives. After you open up the Sphere prim resave it with the name PinchedSphere . Doing so keeps you from messing up your Sphere prim.

96 Chapter 3: Rezzing Primitives

Figure 3-8

Next, change the class and constructor name to PinchedSphere .

public class PinchedSphere extends TriangleMesh3D

Then change the constructor name to PinchedSphere .

public function PinchedSphere( material:MaterialObject3D=null, radius:Number=100, segmentsW:int=8, segmentsH:int=6, initObject:Object=null )

Changing the class name and constructor name is essential or your prim won ’ t run. All that ’ s left is to alter the sphere ’ s parametric equations so that they pinch the sphere in the center. You accomplish this by placing the following two lines of code in the buildSphere method of your newly created PinchedSphere class.

fX=fX*(fZ)/fRadius; fY=fY*(fZ)/fRadius;

The code simply pinches the fX and fY vertices to zero when fz equals zero, producing the pinched effect shown in the figure above. Next, use this same pinching idea to create a pyramid.

Pyramid The way to transform a cube into a pyramid is to remove its top and squeeze (or pinch) its sides into a top point. A cube has six sides and a pyramid five. As a result, your materials list only has five elements. The pyramid that you ’ ll create is shown in Figure 3.9.

97 Part 1: Getting Started

Figure 3-9

The best practice in this case would be to extend the cube and override the buildPlane method, giving you the advantage of code reuse. But in this case you don ’ t do that as you ’ re being encouraged to dig into the PV3D classes, play around with their code, and create your own classes from scratch using the PV3D model. Familiarizing yourself with how these classes work will enable you to build parsers and change code on the fly when needed.

A great example of code reuse is the PV3D Cone primitive that extends the Cylinder class. Later in the book (Chapter 10), you ’ ll extend classes to take advantage of code reuse. For now, you build your own classes from scratch.

Creating a custom pyramid is best done in Adobe Flex Builder due to its excellent error check abilities. Here are the steps required to transform a cube into a pyramid:

1. Open up the Cube prim found in org/papervision3d/objects/primitives and rename it Pyramid . Then rename the class and constructor to Pyramid as well. 2. Comment out all references to the top face including the buildPlane( “ top ” , “ x ” , “ z ” , … ) method. This keeps the top plane from being drawn. 3. Change the vertices generation equation so that the four side planes (LEFT , RIGHT , FRONT , BACK ) are squeezed from the BOTTOM plane to a central point, where the TOP plane used to be: // Vertices

for ( var iu:int = 0; iu < gridU1; iu++ ) { for ( var iv:int = 0; iv < gridV1; iv++ ) { var vertex:Vertex3D = new Vertex3D(); vertex[v] = iv * incV - textureV;

98 Chapter 3: Rezzing Primitives

vertex[u] = (iu*incU-textureU)*rev*Math.abs(1-(iv*incV)/height); vertex[w] = depth*Math.abs(1-(iv*incV)/height); vertices.push(vertex); planeVerts.push(vertex); } }

This is accomplished by the last parts of vertex[u], and vertex[w] equations that converge to (0,0) as the height and width grids are iterated through (u iterates over width, and v over height) as shown below.

(1-(iv*incV)/height, 1-(iv*incV)/height) goes to (0, 0)

And that ’ s all there is to it. Creating custom prims is pretty easy once you get the hang of it. The entire code for the Pyramid prim can be downloaded from the book ’ s website. You could literally write an entire book on creating custom prims. The purpose of this example is to show how easy it is. You ’ ll revisit this again in a later chapter when you learn about Collada files and how to build XML prims from Blender, and 3DSMax.Finally, you need a materials list and in this case all you have to do is reduce the cube materials list by one:

myMaterialsList = new MaterialsList(); myMaterialsList.addMaterial( pyramidMaterial, “front” ); myMaterialsList.addMaterial( pyramidMaterial, “back” ); myMaterialsList.addMaterial( pyramidMaterial, “left” ); myMaterialsList.addMaterial( pyramidMaterial, “right” ); myMaterialsList.addMaterial( pyramidMaterial, “bottom” );

The use of the materials list can get pretty elaborate. On the book ’ s website and blog you can find an example of an interactive carousel made completely of one prim. Its materials list is a little more elaborate than the one above.

Geodesic The geodesic sphere prim was created by Gabriel Putnam and can be downloaded from the open source Panosalado project found on Google Code. It starts with an octahedron and then builds triangles by breaking the edges.

Geodesic spheres handle image distortion better than the “ normal ” sphere in Papervision3D because all of their triangles are more uniform. However, there ’ s still very significant distortion at the poles, but arguably less . . . and the equator is definitely less distorted. Its advantage over using a Sphere prim is that its vertices are evenly spaced as shown in Figure 3.10.

99 Part 1: Getting Started

Figure 3-10

Next, you create a curved plane.

Curved Plane Using the geodesic sphere from above you can wrap a series of planes around it to create a 3D memory game. The positions of the planes can be placed using the geometry.vertices method covered later in the book (Chapter 8). But just using square planes looks a little disjointed as shown on the left of the image below. For a better visual appeal, you want to wrap the planes around the geodesic sphere so that their curvature matches the geo - sphere. You do this by creating a curved plane prim shown wrapped around the geodesic on the right side of the image that appears in Figure 3.11.

Figure 3-11

100 Chapter 3: Rezzing Primitives

It ’ s pretty easy to create a curved plane. Follow this procedure:

1. Open up the Plane primitive found in org/Papervision3d/objects/primitives and rename it CurvedPlaneXY . Then rename the class and constructor to CurvedPlaneXY as well. 2. Next add parabolic curvature for both the x and y iteration across the width and height of the plane as shown in the following code:

for ( var ix:int = 0; ix < gridX + 1; ix++ ) { for ( var iy:int = 0; iy < gridY1; iy++ ) { var x :Number = ix * iW - textureX; var y :Number = iy * iH - textureY; //Curvature var z :Number = -(myCurvatureX*x*x+myCurvatureY*y*y)/ ((width*width+height*height)/2); vertices.push( new Vertex3D( x, y, z ) ); }}

This is pretty easy to do and greatly enhances the visual appeal of the wrapped geo - sphere. You can download both the geodesic sphere and curved plane primitives from this book ’ s blog or website.

Another application of the curved plane is the puckered Cube where every side is puckered in or out depending on the sign of the curvature given to the pucker variable.

Puckered Cube By applying the curved plane to every side of a cube, you get the puckered cube (see Figure 3.12), which is the combination of a cube and a paraboloid.

Figure 3-12

101 Part 1: Getting Started

The heart of the code is a series of three vertex parabolic functions bound by the edges of a cube, which are shown here:

for( var iv:int = 0; iv < gridV1; iv++ ) { var vertex:Vertex3D = new Vertex3D(); vertex[ u ] = (iu * incU - textureU) * rev; vertex[ v ] = iv * incV - textureV; //top and bottom planes if(mat==”top”||mat==”bottom”){ vertex[ w ] = depth +curvature*(1-vertex[u]*vertex[u]/(textureU*textureU))* (1-vertex[v]*vertex[v]/(textureV*textureV))* rev ; } //right and left planes if(mat==”right”||mat==”left”){ vertex[ w ] = depth + curvature*(1-vertex[u]*vertex[u]/(textureU*textureU))* (1-vertex[v]*vertex[v]/(textureV*textureV))* rev ; } //front and back planes if(mat==”front”||mat==”back”){ vertex[ w ] = depth - curvature*(1-vertex[u]*vertex[u]/(textureU*textureU))* (1-vertex[v]*vertex[v]/(textureV*textureV))* rev ; } vertices.push( vertex ); planeVerts.push( vertex ); }

The first if statement creates the vertices for the top and bottom planes, the second for the right and left planes, and the third for the back and front planes.

The constructor function for the puckered cube is the same as that for a cube with the addition of a curvature parameter.

PuckeredCube( materials, curvature, width, depth, height, segmentsS, segmentsT, segmentsH, insideFaces, excludeFaces, initObject)

By increasing or decreasing the curvature value from positive to negative you can get a variety of effects: from a six - sided asteroid to an imploded gas can.

In the next example, you port the torus prim over from Away3D.

Torus Before you begin the tedious work of creating a torus, the first thing you want to do is search the web to see if one already exists – and it does. Away3D (another open source 3D Flash engine) has a more advanced primitive set than Papervision3D. And it does contain a torus. So your task is to convert an Away3D torus to a Papervision3D torus.

The process of hijacking someone else ’ s code is professionally referred to as porting. It sounds better to say you ported the Away3D torus to Papervision3D as opposed to saying you hacked the Away3D torus. And since Away3D is open source there ’ s no copyright problem here, so go ahead and hack away.

102 Chapter 3: Rezzing Primitives

Believe it or not, hacking in the open source community is a good thing. It ’ s associated typically with contributing to the evolution of technology.

However, open source is a give and take. If you ’ ve hacked from somewhere you should improve it and give it back. Or if you have something they don ’ t have, do an exchange. Later in this section you enhance the torus code and have the chance to contribute it back to Away3D and complete the open source cycle of giving back.

The port is actually (almost) set up so it can be generalized to accept any set of parametric equations with two parameters.

Many thanks to Gabriel Putnam once again; he created the geodesic sphere in both Papervision3D and Away3D, which demonstrates the porting process between the two. You ’ ll now use his approach to port the Away3D torus into Papervision3D, as shown in Figure 3.13.

Figure 3-13

To accomplish this, you must first find the parametric equations of a torus. As discussed in Chapter 1, parametric equations are a set of equations that define the coordinates of the dependent variables (x, y and z) of a curve or surface in terms of one or more independent variables or parameters. That ’ s a mouthful, but basically if you iterate over the range of your parameters (of the parametric equation) your torus will be plotted in 3D space.

This is a very useful device since it gives you the vertices of your torus. Also note that Appendix A contains the parametric equations for a number of 3D objects.

The parametric equations for a torus are:

x=(c+a*cos(v))cos(u) y=(c+a*cos(v))sin(u) z=a*sin(v)

103 Part 1: Getting Started

where c is the donut radius, and a is the tube radius. So looking at the Away3D Torus you must first identify where the parametric equations are:

for (i = 0; i < segmentsR; i++) { grid[i] = new Array(segmentsT); for (j = 0; j < segmentsT; j++) { var u:Number = i / segmentsR * 2 * Math.PI; var v:Number = j / segmentsT * 2 * Math.PI;

if (yUp)

grid[i][j] = createVertex((radius + tube*Math.cos(v))*Math.cos(u), tube*Math. sin(v), (radius + tube*Math.cos(v))*Math.sin(u)); else

grid[i][j] = createVertex((radius + tube*Math.cos(v))*Math.cos(u), -(radius + tube*Math.cos(v))*Math.sin(u), tube*Math.sin(v)); } }

You should immediately recognize the grid[i][j] array as the element collecting the vertices from the parametric iteration. Vertices are the place to start. Once you understand how the vertices are being generated everything else follows.

Follow these steps to port the Away3D torus over to Papervision3D:

1. Place the Away3D Torus primitive in the Papervision3D org/papervision3d/objects/primitives folder. 2. Change the package statement to package org.papervision3d.objects.primitives. 3. Replace the Away3D imports with the appropriate Papervision3D imports. import org.papervision3d.core.*; import org.papervision3d.core.proto.*; import org.papervision3d.core.geom.*; import org.papervision3d.core.geom.renderables.Triangle3D; import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.core.math.NumberUV;

4. Delete use namespace arcane in the constructor function. 5. Add super(material, new Array(), new Array(), null) to the constructor method. 6. Add to the buildTorus method the following lines of code:

this.geometry.ready = true; var aVertice:Array = this.geometry.vertices; var aFace:Array = this.geometry.faces; var aUV:Array = new Array();

104 Chapter 3: Rezzing Primitives

7. Add aVertice.push(grid[i][j]); to the parametric vertex generation under the if (yUp ) statement. 8. Change createVertex to Vertex3D. 9. Change all Vertex Type declarations to Vertex3D. 10. Change all UV Type declarations to NumberUV. 11. Replace the following code:

addFace(createFace(a, b, c, null, uva, uvb, uvc)); addFace(createFace(d, c, b, null, uvd, uvc, uvb)); with:

aFace.push( new Triangle3D( this, [a, b, c], material, [uva, uvb, uvc])); aFace.push( new Triangle3D( this, [d, c, b], material, [uvd, uvc, uvb]));

12. Delete all non - contributing statements (this is pretty easy if you ’ re editing in Flex - just delete the statements that give you an error when you run the torus wrapper program).

This demonstrates how easy it is to port Away3D into Papervision3D, and sets the framework for creating a generalized parametric prim to accept any form of parametric curve such as a sphere, torus knot, or cylinder. If you had difficulty following the discussion above, refer to the book ’ s video on this topic.

Now that you ’ ve ported your torus from Away3D, you enhance and give it back.

Tube The tube is an important primitive. If you want to give your virtual sweetheart a ring, put tires on your 3D car, or handcuff a criminal, you need a tube. Creating a tube requires fully implementing the parametric equations of the elliptical torus.

x=(c+a*cos(v))cos(u) y=(c+a*cos(v))sin(u) z=b*sin(v)

The only difference between these parametric equations and the ones for the regular torus is the b parameter of the z=b*sin(v) equation. Increasing b (tube length) increases the height of your torus. So the process is very simple.

1. Add a new parameter b (variable name tubelength ) to your Torus class. 2. Implement it at the vertex level.

The results of making these changes are shown in Figure 3.14.

105 Part 1: Getting Started

Figure 3-14

Start by opening up the Torus class, and rename it to Tube . Then change its class and constructor names from Torus to Tube . This process is illustrated in the hourglass example below and can be found on the book ’ s video examples.

From examining the parametric equations in the Torus (shown below), it ’ s apparent that by altering the tube*Math.sin(v) to another value such as tube length you can adjust the length of your Torus.

grid[i][j] = new Vertex3D((radius + tube*Math.cos(v))*Math.cos(u), tube*Math. sin(v), (radius + tube*Math.cos(v))*Math.sin(u));

By changing tube to tubelength in the longitudinal coordinate (b*sin(v) term) , you can now turn your torus into a tube using the following equation in your custom Tube class.

grid[i][j] = new Vertex3D((radius + tube*Math.cos(v))*Math.cos(u), tubelength*Math. sin(v), (radius + tube*Math.cos(v))*Math.sin(u));

Add the tubelength variable to the following places:

1. Create a tubelength variable and place it in your Tube custom class. private var tubelength:Number; 2. Add tubelength to your custom Tube class. public function Tube( material:MaterialObject3D=null, radius:Number=100, tube: Number=50,tubelength:Number=100, segmentsR:int=8, segmentsT:int=6, yUp: Boolean=true) 3. Place the tubelength variable in the constructor function of the custom Tube class. this.tubelength=tubelength; 4. Add the tubelength variable to the buildTorus method of your custom Tube class. buildTorus(_radius, _tube, tubelength, _segmentsR, _segmentsT, _yUp); 5. Add the tubelength to the buildTorus method of your custom Tube class.

106 Chapter 3: Rezzing Primitives

private function buildTorus(radius:Number, tube:Number,tubelength:Number, segmentsR:int, segmentsT:int, yUp:Boolean):void

Although this is a small change, it ’ s a very useful one and all thanks to Away3D. With this iteration in hand you need to complete the open source cycle by giving it back to the community: post it on the Away3D forum! Completing the Cycle Bring up the Away3D Torus prim, and as before, give it a new name TorusTube and change its constructor and class statements to match that name. Then follow the step above, reconfiguring this prim so it has a tube length parameter. Once the TorusTube prim is complete, bring up Away3D and test it to make sure that everything is working. When you ’ ve fixed the bugs, submit your iteration to the Away3D forum, with thanks.

Hourglass Earlier you met Papervision3D ’ s cone primitive, but it ’ s not really a cone: just a special case of a cylinder. The real equations for a cone are given below and involve a (h - u) parameter: where h is the height of your cone and r its radius.

x=((h-u)/h)*r*cos(v) y=((h-u)/h)*r*sin(v) z=u

As u iterates larger than h your h - u term gives you an inversion in x, and y. This inversion yields an hourglass that ’ s shown in Figure 3.15.

Figure 3-15

107 Part 1: Getting Started

Creating the Hourglass class is very similar to creating the Tube class demonstrated in the previous section so we ’ re not repeating the procedure here. To see the entire process check out the book ’ s website Chapter 3 video on this section.

The hourglass is of interest in certain mathematical fields. For example, all conic sections can be derived from intersections of a plane with the hourglass. And in relativistic physics the inversion of the hour glass represents the difference between moving forward or backward in time.

Moving forward in time, you now create a Second Life tree.

Second Life Tree A Second Life tree in Papervision3D is pretty easy to make (see Figure 3.16). It ’ s made up of two planes intersecting at 90 degrees. As you rotate around the tree, it appears to be 3D, as you can see its sister plane. Of course it ’ s just an optical illusion, and as you get closer to the tree the planes become more apparent. But given the processing savings of creating a tree with just one prim, it ’ s worth it! When you get to the chapter on virtual tours you use this trick to create an entire grove of trees on a Google Map.

Figure 3-16

The trick for creating this illusion is to take a cube, which is made up of six planes, eliminate four of the sides and translate two of them into an intersection point.

From the Cube prim available in Papervision3D, create a Tree prim, as was done in the hourglass case. Then navigate to the plane building functions of the cube (now named tree) and comment out the four unneeded planes. In this case, keep front and right planes as shown here:

if( ! (excludeFaces & FRONT) ) buildPlane( “front”, “x”, “y”, width, height, depth2, ! Boolean( insideFaces & FRONT ) );

//Remove if( ! (excludeFaces & BACK) ) //Remove buildPlane( “back”, “x”, “y”, width, height, -depth2, Boolean( //Remove insideFaces & BACK ) );

if( ! (excludeFaces & RIGHT) )

108 Chapter 3: Rezzing Primitives

buildPlane( “right”, “z”, “y”, depth, height, width2, Boolean( insideFaces & RIGHT ) );

//Remove if( ! (excludeFaces & LEFT) ) //Remove buildPlane( “left”, “z”, “y”, depth, height, -width2, ! //Remove Boolean( insideFaces & LEFT ) );

//Remove if( ! (excludeFaces & TOP) ) //Remove buildPlane( “top”, “x”, “z”, width, depth, height2, Boolean( //Remove insideFaces & TOP ) );

//Remove if( ! (excludeFaces & BOTTOM) ) //Remove buildPlane( “bottom”, “x”, “z”, width, depth, -height2, ! //Remove Boolean( insideFaces & BOTTOM ) );

Then navigate down to the vertex generation equations. Removing the rev term from the vertex(u) equation below brings the front and right planes together.

vertex[ u ] = (iu * incU - textureU)[Remove * rev]; vertex[ v ] = iv * incV - textureV; vertex[ w ] = depth;

Your prim is ready to run. But remember, the cube uses the materials list. In this case, as you have only two planes, you need to create a materials list of only two png image files. You do this by creating two bitmap file materials, adding your images to them, and then adding them to a two item materials list. Then you add your materials list to your Second Life tree.

Use png image files as your tree must have the area around its branches transparent and png files have a transparent channel.

//Create for your first png myBitmapFileMaterial = new BitmapFileMaterial( “assets/TreeBigFront.png”); myBitmapFileMaterial.doubleSided = true ;

//Create a BitmapFileMaterial for your second png myBitmapFileMaterial2 = new BitmapFileMaterial( “assets/TreeBigSide.png”); myBitmapFileMaterial2.doubleSided = true ;

//Add a materials list containing your two bitmaps var materialsList:MaterialsList = new MaterialsList(); materialsList.addMaterial(myBitmapFileMaterial, “ front” ); materialsList.addMaterial(myBitmapFileMaterial2, “right” );

//Instantiate your Tree and add it to the scene mySLTree = new Tree(materialsList,400,400,400,4,4,4); scene.addChild(mySLTree); startRendering();

Second Life trees actually have 3 planes. This example gives the lowest possible CPU hit for the biggest bang! It ’ s part of optimizing Papervision3D.

The rest is the same as before, just instantiate the tree prim and add it to your scene using the addChild method, as demonstrated in the code above. Other custom prims, which are included in the book ’ s

109 Part 1: Getting Started

source code but not discussed here, are PolyPlane , Triangle , Cylinder Tube , MultMatPlane , and Carousel .

Double -Sided Plane A natural extension of the approach presented above is to create a double - plane. This is different to having the same material on both sides of the plane. It ’ s putting different materials on each side of a plane; like two different photos, front of the face and back of the head. As the plane spins the face is revealed, then the back of the head – it ’ s a pretty cool effect.

And just as in the SL Tree prim, you ’ re going to take a cube, which is made up of six planes, and eliminate four of the sides. But in this case you eliminate all but the front and back planes and translate them 0.1 pixels apart.

vertex[ u ] = (iu * incU - textureU) ; vertex[ v ] = iv * incV - textureV; if (mat== ”front” ){ vertex[ w ] = 0.1; } else { vertex[ w ] = 0; }

Central to the double plane creation is a simple ‘if’ statement which shifts the front plane forward 1 pixel if the front panel is being created.

As the approach is identical to the SL Tree, the code is not presented here, but can be obtained from the book ’ s code download page.

Congratulations! You now have a larger prim set than Second Life!

Now that you have Papervision3D prims under your belt, you ’ ll learn how to create prims in CS4. As seen earlier, prims are built on parametric equations and that still holds true for CS4.

Creating CS4 Primitives The initial test of using CS4 versus Papervision3D hasn ’ t yielded the significant enhancement that was hoped for by the Papervision3D team. CS4 alone is far less developed than Papervision3D in its 3D functionality. But don ’ t give up on CS4 yet. Having a native z component does give CS4 a greater ease of use, and far less of a learning curve to produce 3D content. In addition, CS4 Flash has a host of new tools not available in Papervision3D such as Pixel Bender, motion editor, and 3D placement tools.

If all you want is to create a website full of 3D planes, which CS4 does natively, then there ’ s no setup at all. You just open the Flash interface and place the planes where you want them to go using the 3D placement tools in Flash. Then animate them in 3D on the timeline using the motion editor. Papervision3D has no such motion editor or ActionScript animation engine. You will however construct one in this book based on the CS4 Flash standard, which takes advantage of the many options that the Flash 10 player offers.

110 Chapter 3: Rezzing Primitives

As Flash developers begin to produce 3D using CS4, uses will be discovered that push the envelope of what was expected. Papervision3D needs to keep pace with these changes or it will become a blip on the screen! As you proceed in this book, you create 3D content using both Papervision3D and Flash CS4.

With that said, you ’ ll create a basic set of CS4 prims: plane, cube, cylinder, cone, sphere, torus, and tree.

To see the savings you gain in using CS4, rebuild the Second Life tree above in Flash CS4.

Second Life Tree in Flash CS4 When it comes to creating Second Life foliage in Flash 3D, Flash CS4 is far superior in ease of use, code required, and portability. Ease of Use We provide here the steps that are required for creating the Second Life tree graphic (see Figure 3.17) for use in the 3D environment for CS4. Flash CS4 requires no programming, where PV3D is programming intensive.

Figure 3-17

CS4, No Programming Required To create the Second Life Tree in Flash CS4, follow these steps:

1. Import the front and side tree “ png ” images onto the Flash Stage. 2. Turn them both into movie clips. 3. Rotate one 90 degrees with the 3D rotation tool and use the 3D translation tool to overlap them at their centers. 4. Turn both into a movie clip and check the linkage of that movie clip for export.

You can do this in about five minutes, but with Papervision3D (PV3D) it takes a little longer.

111 Part 1: Getting Started

PV3D, Programming Intensive To create the Second Life Tree in Papervision3D (PV3D), follow these steps:

1. Create a Tree Primitive. 2. Import the Tree Primitive into a BasicView wrapper program. 3. Instantiate two BitmapFileMaterials and load your images into them. 4. Create a MaterialsList and load your Image materials into it. 5. Load your MaterialsList into your Tree method.

Counting lines of code was the real eye opener; it really shows the advantage of having the native z component in Flash 10. Code Required All things considered, CS4 beats Papervision3D hands down in the number of lines (fewer) required to code the same user experience: 23 to 243, a factor of 10 (see Figure 3.18)!

Figure 3-18

Portability Like Papervision3D, CS4 runs on Windows, Mac, and Linux. But with CS4 you can run your Second Life Tree with Papervision3D, Away3D, Sandy3D, or any other 3D package running in the Flash 10 player.

The code for the CS4 Second Life Tree is explained below: CS4 Tree Code After programming 243 lines of code to create a tree, it ’ s amazing to do the same thing in just 23 lines. But don ’ t give Papervision3D up right yet. There are a large number of things that PV3D can do that CS4 can ’ t. To create your tree in CS4 first create a package and name it SLTreePackage . If you create an

112 Chapter 3: Rezzing Primitives

ActionScript Project in Flex, it automatically creates a generic package structure for you. From there all you have to do is fill in the blanks.

package { import flash.display.Sprite;

public class SLTreePackage extends Sprite { public function SLTreePackage () { } } }

The key to the program is getting the Tree movie clip you created earlier from the Flash Library to the stage. You do this by instantiating the SLTree and then using the addChild method as follows:

private var tree:SLTree= new SLTree(); addChild(tree);

This is a technique that you ’ ll use over and over again. Once your tree is on the stage you can position it and start an onEnterFrame listener to get it animating. Your looping function contains the simple equation tree.rotationY+=1, which rotates your tree 1 degree every frame.

Putting it all together, you get:

package { import flash.display.*; //Create the Second Life Tree Package public class SLTreePackage extends Sprite { private var tree:SLTree= new SLTree(); public function SLTreePackage() { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; addChild(tree); //Position your tree on the stage tree.x=stage.stageWidth/2; tree.y=stage.stageHeight/4; //Start Tree Rotating addEventListener(Event.ENTER_FRAME, onEnterFrame); } //Looping Function private function onEnterFrame(event:Event):void { tree.rotationY+=1; } } }

113 Part 1: Getting Started

To run the code just place its class name in the Document Class found in the Properties Panel of your Flash application. If you experience any difficulties make sure that your Flash library contains the SLTree , and that your Flash fla and class are in the same folder.

Plane As shown in the case above, the plane is a native element in CS4 Flash. So there ’ s no need to create a plane using intensive iteration as is done in Papervision3D. Just use the one in Flash CS4. Use the 3D rotation and translation tools to position it. If need be, convert the timeline animation into ActionScript. Just keep in mind that you must take sorting into account as discussed in Chapter 1.

Cube You now depart from the simplicity of flying post cards (the code name for Flash 10) and move into the computationally intensive methodology that you learned creating Papervision3D prims.

CS4 has a host of methods designed to help you draw in 3D. They include objects such as the Vector class, drawTriangles method, Matrix3D class, Utils3D class, and the projectVectors method:

❑ Regular arrays carry extra processing baggage with them since they ’ re designed to handle any data type. Using the Vector class jettisons this excess baggage by storing only one type of data, allowing your 3D application to run faster. ❑ The drawTriangles method is at the heart of the 3D drawing process. Its method receives vertices, indices, uvt data, and culling. which allows you to create complex, fully textured 3D objects. ❑ The Matrix3D class determines the position and orientation of a display object. It translates, rotates, scales, and projects your object to other 3D or 2D spaces. And the Matrix3D class simplifies the tasks of performing transformation and projection by using appendTranslation() , appendRotation() , and interpolateTo() . ❑ The Utils3D class contains static methods which simplify the implementation of certain Matrix3D operations, such as pointTowards , projectVector , and projectVectors . ❑ Using a projection Matrix3D object, projectVectors projects a Vector of three - dimensional space coordinates (verts) to a Vector of two- dimensional space coordinates (projectedVerts ). The projectVectors() method also sets the t value of the uvt data.

Armed with these methods, you begin by creating a simple cube based on vertices and indices. But where does one get such info? You could eyeball it, use an algorithm to calculate it, or obtain it from modeling software such as Blender or 3DSMax. It doesn ’ t really matter where you obtain it, as said earlier – a vertex is a vertex! The easiest way to get this data, devoid of human eyeball error, is to use a 3D modeling application.

114 Chapter 3: Rezzing Primitives

In Chapter 5, you cover XML exporters for Blender and 3DSMax, which give you this vertex and index info you need. But for now you just use the results below that were obtained from Blender by placing a cube on the stage, triangulating it, and doing an XML export of its data:

Vertices : 1.000000, - 1.000000,1.000000,1.000000, - 1.000000, - 1.000000, - 1.000000, - 1.000000, - 1.000000, - 1.000000, - 1.000000,1.000000,1.000000,1.000000,0.999999,0.999999,1.000000, - 1.000001, - 1.000000, 1.000000, - 1.000000, - 1.000000,1.000000,1.000000 Indices : 4,0,3,4,3,7,2,6,7,2,7,3,1,5,2,5,6,2,0,4,1,4,5,1,4,7,5,7,6,5,0,1,2,0,2,3

You should first check your data. Examining the vertices data yields 24 numbers corresponding to the x, y, and z values of the 8 points of a cube: 8x3=24. When the cube was triangulated it doubled its faces (from 6 to 12), giving you 36 indices (since each face requires 3 index values: 12x3=36). The data is correct and the vertices do correspond to the vertex positions of a cube. But there ’ s a problem.

The cube vertices above are normalized to 1 (or in some cases 0.999999). In order to get anything larger than a unit cube you need to scale your cube once it ’ s created. Figure 3 - 19 shows the triangulated cube to be scaled.

Figure 3-19

Your first task is to import the display, events, and geometry classes.

import flash.display.*; import flash.events.*; import flash.geom.*;

You should notice that the import statements use the wild card *, which imports all classes associated with display, events, and geometry. The use of the wild card is highly discouraged by many flashmaticians, since name conflicts can occur. For highly complex applications the wild card should be avoided, but for simple ones like this, it ’ s typically not a problem.

Create your variables next: matrix, verts, indices, uvData, projectedVerts, and cube.

115 Part 1: Getting Started

private var matrix:Matrix3D = new Matrix3D(); private var verts:Vector. < Number > ; private var projectedVerts:Vector. < Number > ; private var uvData:Vector. < Number > ; private var indices:Vector. < int > ; private var cube:Sprite; //Set the Oscillation variable private var theta:Number=0;

The verts (vertices), indices , uvData , and projectedVerts are all data - typed Vector arrays. This reduces the processing required to execute them. You now have to load your vertices and indices into these Vector arrays, and load the remainder of your variables, and instantiate your cube. After that you must scale your cube (since you ’ re using a unit cube) with the matrix appendScale method.

Create Cube

//Load vertices from Blender verts = Vector. < Number >([1.000000,-1.000000,1.000000,1.000000,-1.000000,-1.000000, -1.000000,-1.000000,-1.000000,-1.000000,-1.000000,1.000000,1.000000,1.000000,0.9999 99,0.999999,1.000000,-1.000001,-1.000000,1.000000,-1.000000,-1.000000,1.000000, 1.000000 ]);

//Load indices from Blender indices = Vector. < int > ([4,0,3,4,3,7,2,6,7,2,7,3,1,5,2,5,6,2,0,4,1,4,5,1,4,7,5,7,6,5 ,0,1,2,0,2,3]);

//Declare uvData and projectedVerts uvData = new Vector. < Number > ; projectedVerts = new Vector. < Number > ;

//Instantiate the cube and add it to the stage cube = new Sprite(); addChild(cube); //Scale the cube and set it to the center upper quarter of the stage matrix.appendScale(100,100,100); cube.x=stage.stageWidth/2; cube.y=stage.stageHeight/4; //Start Animation Loop addEventListener(Event.ENTER_FRAME, frameLoop);

Notice the verts (or vertices) are declared as Number and the indices as int . In the indices case, this saves processing space by not requiring more space than needed to handle the index numbers. The final addEventListener command starts the looping process of your animation.

As theta is iterated in the animation loop your cube oscillates in x , y , z , using the respective scaleX , scaleY , and scaleZ methods. You then rotate the cube using the Matrix3D appendRotation method, and its vertices are updated using projectVectors method. Animate Cube In the animation loop, the Matrix3D object is used to appendRotation to your prim and then the projectVectors method is used to find the new projected vector values resulting from the rotation.

116 Chapter 3: Rezzing Primitives

Once the Matrix3D methods are completed you then redraw the cube. Both the lineStyle and beginFill methods draw both edges and filled faces.

The projected vectors are sent to the drawTriangles method, which does the work of matching your vertices with your indices and drawing the triangles that make up your object, are shown in the code here:

//Iterate animation variable theta++; //Scale cube in x, y, and z cube.scaleX=cube.scaleY=cube.scaleZ=Math. abs(Math.cos(theta/100)); //Rotate the cube using matrix3D matrix.appendRotation(.4,Vector3D.X_AXIS); matrix.appendRotation(.4,Vector3D.Z_AXIS); matrix.appendRotation(.4,Vector3D.Y_AXIS);

//Calculate projected vectors Utils3D.projectVectors(matrix,verts,projectedVerts,uvData);

//Redraw cube cube.graphics.clear(); cube.graphics.beginFill(0x004444); cube.graphics.lineStyle(1,0x000000,1); cube.graphics.drawTriangles(projectedVerts, indices); cube.graphics.endFill();

Putting it all together into a single package yields:

package { import flash.display.*; import flash.events.*; import flash.geom.*;

public class Cube extends Sprite { private var matrix:Matrix3D = new Matrix3D(); private var verts:Vector. < Number > ; private var projectedVerts:Vector. < Number >; private var uvData:Vector. < Number > ; private var indices:Vector. < int > ; private var cube:Sprite; private var theta:Number=0;

public function Cube () { //Load vertices from Blender verts = Vector. < Number > ([1.000000,-1.000000,1.000000,1.000000,-1.000000,- 1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,1.000000,1.000000,1.0000 00,0.999999,0.999999,1.000000,-1.000001,-1.000000,1.000000,-1.000000,-1.000000,1.00 0000,1.000000 ]); //Load indices from Blender (continued)

117 Part 1: Getting Started

(continued)

indices = Vector. < int > ([4,0,3,4,3,7,2,6,7,2,7,3,1,5,2,5,6,2,0,4,1,4,5,1,4,7,5 ,7,6,5,0,1,2,0,2,3]); //Declare uvData and projectedVerts uvData = new Vector.< Number > ; projectedVerts = new Vector. < Number > ; //Instantiate the cube and add it to the stage cube = new Sprite(); addChild(cube); //Scale the cube and set it to the center upper quarter of the stage matrix.appendScale(100,100,100); cube.x=stage.stageWidth/2; cube.y=stage.stageHeight/4; //Start Animation Loop addEventListener(Event.ENTER_FRAME, frameLoop); }

private function frameLoop(event:Event):void { //Iterate animation variable theta++; //Scale cube in x, y, and z cube.scaleX=cube.scaleY=cube.scaleZ=Math.abs(Math.cos(theta/100)); //Rotate cube matrix.appendRotation(.4,Vector3D.X_AXIS); matrix.appendRotation(.4,Vector3D.Z_AXIS); matrix.appendRotation(.4,Vector3D.Y_AXIS);

//Calculate projected vectors Utils3D.projectVectors(matrix,verts,projectedVerts,uvData);

//Redraw cube cube.graphics.clear(); cube.graphics.beginFill(0x004444); cube.graphics.lineStyle(1,0x000000,1); cube.graphics.drawTriangles(projectedVerts, indices); cube.graphics.endFill(); } } }

Creating a pyramid is almost an identical process, but in this case you ’ ll learn how to throttle an animation.

Pyramid The process of creating a pyramid is pretty simple, you just create one in Blender and export its vertices and indices (using our XML exporter) and put them in the code above.

118 Chapter 3: Rezzing Primitives

//Load Pyramid vertices from Blender verts = Vector. < Number >([1.000000,-1.000000,1.000000,1.000000,-1.000000,-1.000000,- 1.000000,-1.000000,-1.000000,-1.000000,-1.000000,1.000000,-0.000000,1.000000,-0.000000 ]); //Load Pyramid indices from Blender indices = Vector. < int > ([0,4,1,1,4,2,2,4,3,4,0,3,0,1,2,0,2,3]);

This method works for any object that you make in Blender and want to bring into Flash 10, not just pyramids and cubes.

Throttling refers to starting and stopping an animation only when you need it. It ’ s often used in Papervision3D to optimize scenes. For example, if part of an animation isn ’ t being used (a waterfall, or flock of birds) don ’ t recalculate its vertices. There are a number of ways to do this. In this simple case you only throttle the frame when the mouse moves. To accomplish this you remove the Enter Frame listener and add a Mouse Move Listener.

stage.addEventListener(flash.events.MouseEvent.MOUSE_MOVE, frameThrottle);

If your mouse isn ’ t moving, the cube is not drawn, saving your processor work. The following code snippet is very useful and remembers where your mouse last moved to, using diffX and diffY variables.

private function frameThrottle(e:MouseEvent) { // Store stageX/stageY before modifying box var diffY:Number = savedStageY - e.stageY; var diffX:Number = savedStageX - e.stageX; savedStageX = e.stageX; savedStageY = e.stageY;

//Iterate animation variable matrix.appendRotation(-diffY,Vector3D.X_AXIS); matrix.appendRotation(diffX,Vector3D.Z_AXIS);

//Calculate projected vectors Utils3D.projectVectors(matrix,verts,projectedVerts,uvData);

//Redraw cube cube.graphics.clear(); cube.graphics.beginFill(0x000099); cube.graphics.lineStyle(1,0x8c8c8c,1); cube.graphics.drawTriangles(projectedVerts, indices); cube.graphics.endFill();

}

Managing resources is very important in optimizing 3D applications and the process can get pretty elaborate. Typically you want to remove listeners when you ’ ve completed a throttled event. The complete code can be obtained from the book ’ s website.

119 Part 1: Getting Started

CS4 Super Prim (Plane, Cylinder, Cone, Sphere, Half Torus . . . ) In this section, you create an all - in - one super prim which contains the plane, cylinder, cone, sphere, and half torus, as shown in Figure 3.20. You accomplish this by creating a “ vertex /indices ” builder that accepts the parametric equations of your prim set.

Figure 3-20

The photo of the earth above is Courtesy of NASA/JPL - Caltech at http://maps.jpl.nasa.gov/ . It ’ s a great place to get photos of planets. Images obtained from this web site may be used for any purpose without prior permission and with few restrictions; make sure you check out the copyright info that ’ s posted on this site before you use the photo – just to be on the safe side.

The heart of the super prim is the Matrix3D object. Essentially the Matrix3D object can be used for everything you want your prim to do – translate, rotate, scale, and project.

One of the early pioneers in CS4 Flash was Petri Leskinen whose blog Pixelero is well worth visiting at http://pixelero.wordpress.com/ .

In this particular example, you rotate through the set of primitives shown above. With a few modifications you can change the code to render only one of the primitives. So as opposed to having five different programs (as is done in Papervision3D) holding each primitive separately, you need only one, which gives you the choice of any of the five. Adding an Image to a Prim You can add images to a prim in CS4 by using the code below. The bitmap used in this case was embedded using:

//Embed your texture [Embed(source=”worldmapCourtesyNASAJPL.jpg”)] private var YourImage:Class;

120 Chapter 3: Rezzing Primitives

This is actually a Flex method and to get it to work in CS4 Flash you must include the Flex SWC in the library path of your Flash program found in File/Publish Settings/Flash/Settings/Library Path. Once the path is set you can use the embed statement in Flash.

clear(); beginBitmapFill(bitmapData,null, false, false); drawTriangles(projectedVerts, indices, uvtData,TriangleCulling.NEGATIVE); endFill();

But there ’ s more to adding images than just adding a bitmap to the stage. The bitmap needs to be cut into triangles and placed on your 3D object using the drawTriangle method. You can do this by using the following properties of the drawTriangle Method:

❑ projected vertices — results of your Matrix3D operation on your vertices (the 3D points in space that make up your 3D object) ❑ indices triplet integers — represent the vertex numbers that make up your triangles ❑ uvt Data — the percentage map of your texture to your object

Triangle Culling uses the normal vector generated by the triangle orientation to eliminate triangles not facing the viewport. The main culling options are Negative and Positive, which reverse the orientation of culling respectively.

The essential thing to keep in mind when preparing image data to be rendered is that only one uvtData value (u,v) is allowed for each vertex (x,y,z)! This becomes very important when, in Chapter 5, you use the drawTriangles method to bring Blender and 3DS Models into CS4Flash.

Blender and 3DSMax produce more than one uvtData value per point and as is, won ’ t work with the drawTriangles method. In order to get them to work with the drawTriangles method you must reduce uvtData , and add vertex info. You learn how to do this in Chapter 5. Timer You can switch the prims (in the demo) by using a timer class. In this case, the timer is fired every 1800 milliseconds (1.8 seconds), as shown in the code snippet below:

//Declare and start Timer var myTimer = new Timer(1800); myTimer.addEventListener(“timer”, timerHandler); myTimer.start();

The switch case, in the following code, holds all the parametric equations needed to create the vertex information for the different prims.

121 Part 1: Getting Started

switch (number) { case 0: //Plane paraVec = new Vector3D(-.5*(2-ix), .5*(iy),0); break; case 1: //Cylinder paraVec = new Vector3D( Math.cos(ix), iy, Math.sin(ix)); break; case 2: //Cone paraVec = new Vector3D( (iy)*Math.cos(ix), Math.sin(iy), (iy)*Math.sin(ix)); break; case 3: //Sphere paraVec = new Vector3D( Math.cos(iy)*Math.cos(ix), Math.sin(iy), Math.cos(iy)*Math.sin(ix)); break; case 4: //Torus paraVec = new Vector3D( (1+.4*Math.cos(iy))*Math.cos(ix), .4*Math.sin(iy), (1+.4*Math.cos(iy))*Math. sin(ix)); break; default: trace ( “ no case tested true “ ) }

The heart of the code runs on the Matrix3D class, which does all the perspective scaling for you automatically.

Putting it all together results in the following code:

package {

import flash.display.*; import flash.geom.*; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent;

//Create Generalized Primitive public class GeneralizedPrimitive extends Sprite {

//Embed your texture [Embed(source=”worldmapCourtesyNASAJPL.jpg”)] private var YourImage:Class;

//Primitive Declarations private var primitive:Sprite; private var myTimer:Timer; private var myCount:int=0;

122 Chapter 3: Rezzing Primitives private var paraVec:Vector3D; private var bitmapData:BitmapData; private var rows:int=32; private var cols:int=16; private var ix:Number; private var iy:Number;

//Elliptical Parameters private var radx:Number=1; private var rady:Number=1; private var radz:Number=1;

//Vertex Data private var verts:Vector. < Number > ; private var indices:Vector. < int > ; private var uvtData:Vector. < Number > ; private var uvtShadingData:Vector. < Number > ; private var projectedVerts:Vector. < Number > ; private var perspective:PerspectiveProjection; private var projectionMatrix:Matrix3D; private var myAngle:Number = 0;

//Contructor Function public function GeneralizedPrimitive():void {

//Declare and start Timer var myTimer = new Timer(1800); myTimer.addEventListener(“timer”, timerHandler); myTimer.start(); }

//Handle Timer Event private function timerHandler(event:TimerEvent):void {

//Iterate through primitives myCount=myCount%5; init(myCount); myCount++; }

//Initiation private function init(number:Number):void { //Instantiate parameters paraVec = new Vector3D(); verts = new Vector. < Number > (); indices= new Vector. < int > (); projectedVerts = new Vector. < Number > (); uvtData = new Vector. < Number > (); uvtShadingData = new Vector. < Number > (); bitmapData = new YourImage().bitmapData;

//Remove primitive from stage before adding the next one if(primitive){ (continued)

123 Part 1: Getting Started

(continued)

removeChild(primitive);} addChild(primitive= new Sprite()); //Set position on stage primitive.x=stage.stageWidth/2; primitive.y=stage.stageHeight/3; //Set perspective perspective= new PerspectiveProjection(); perspective.fieldOfView = 35.0; // camera angle, in degrees

//Calculate Vertices for (var i:int = 0 ; i!=rows; i++) { ix= i/(rows-1)*Math.PI*2.0; for (var j:int =0 ; j!=cols; j++) { iy= (j/(cols-1)-0.5)*Math.PI; //Primitive Selection Switch Case switch (number) { case 0: //Plane paraVec = new Vector3D(-.5*(2-ix), .5*(iy),0); break; case 1: //Cylinder paraVec = new Vector3D(Math.cos(ix), iy, Math.sin(ix)); break; case 2: //Cone paraVec = new Vector3D((iy)*Math.cos(ix), Math.sin(iy), (iy)*Math.sin(ix)); break; case 3: //Sphere paraVec = new Vector3D(Math.cos(iy)*Math.cos(ix), Math.sin(iy), Math. cos(iy)*Math.sin(ix)); break; case 4: //Torus paraVec = new Vector3D((1+.4*Math.cos(iy))*Math.cos(ix), .4*Math.sin(iy), (1+.4*Math.cos(iy))*Math.sin(ix)); break; default: trace ( “ no case tested true “ ) }

//Collect vertices in the verts Vector array verts.push(radx*paraVec.x,rady*paraVec.y,radz*paraVec.z);

//Load uvt data uvtData.push( i/(rows-1),j/(cols-1), 0.0); //Initialize projected vertices projectedVerts.push(0.0,0.0); } }

124 Chapter 3: Rezzing Primitives

//Create indices var ii:int =0; for (var ik:int=0 ; ik!=rows-1; ik++) { for (var jk:int=0 ; jk!=cols-1; jk++) { indices.push( ii,ii+cols+1,ii+1, ii+cols,ii+cols+1,ii++); } ii++; }

stage.addEventListener(Event.ENTER_FRAME, loop); }

//Looping Function private function loop(event:Event =null):void {

// Set up a viewpoint: projectionMatrix = perspective.toMatrix3D(); projectionMatrix.prependTranslation(0,0,0.35); myAngle+=2;

//Rotate Primitive projectionMatrix.prependRotation( myAngle ,new Vector3D(0,1,0.0));

//Update projection vectors Utils3D.projectVectors(projectionMatrix, verts, projectedVerts, uvtData);

//Draw primitive with (primitive.graphics) { clear(); beginBitmapFill(bitmapData,null, false, false); drawTriangles(projectedVerts, indices, uvtData,TriangleCulling.NEGATIVE); endFill(); } } } }

The Matrix3D object plays an essential role in the program above and represents a simplistic way of performing 3D operations. You don ’ t have to necessarily understand the math underneath the Matrix3D object in order to use it. Adobe has created a number of methods that are intuitively easy to use.

Dissecting the Big Three In Chapter 1, you learned about the big three: translation, rotation, and scaling. But how does Flash handle these operations? Using Matrix3D. The Matrix3D class uses a 4x4 square matrix, as shown in Figure 3.21. Using a Matrix3D object, you can apply many 3D transformations at once. For example, if you want to rotate, scale, and then translate a torus, you can apply three separate matrix transformations. All of the calculations are performed in the matrix first and then applied to the torus, when its transform.matrix3D property is set.

125 Part 1: Getting Started

M11 M12 M13 Tx

M21 M22 M23 Ty

M31 M32 M33 Tz 0 0 0 1 Figure 3-21

The compact form above can be used to represent all three forms of motion. Scaling is accomplished in x , y , and z using the diagonal elements M11 , M22 , and M33 . Translation is accomplished using Tx , Ty , and Tz . And rotation is accomplished using a combination of non - diagonal sinusoidal M elements discussed in Chapter 1.

Camera and Viewport As discussed in Chapter 1, it ’ s not the camera that moves but the objects that move around the camera (or view perspective). For example, as a camera moves down the world z - axis, the object moves toward you up the z - axis. One way to accomplish this is to set the Matrix3D object of the camera class to the inverse of the display object ’ s transformation. When dealing with multiple display objects, the Matrix3D object of the camera class can be set to the inverse of the root display object. You can also set the display objects as children of the camera object.

Moving a camera through space to view objects will be extended to even higher dimensions (greater than 3) when you investigate creating objects using Knowledge Space that ’ s discussed on the book ’ s companion website.

Summary In this pivotal chapter, you examined how prims were made in both Papervision3D and CS4. You created your own custom prim by examining the parametric equations of different prims. And in the case of creating a Second Life tree, you analyzed the potential savings between using Papervision3D and CS4. You learned about the powerful new classes and methods in the CS4 library (such as Matrix3D , drawTriangles , and vectors ). You also created your own CS4 super prim, using a switch case and timer.

You ’ ve turned the corner from elementary 3D engines to the implementation of parametric equations to build your own super prim. The “ smart use math ” in 3D programming is your most powerful ally and in many cases, will save you thousands of lines of code.

126 Adding Materials

Applying materials to prims is not unlike your childhood experience of coloring with crayons, finger painting, or playing dress up. In this chapter, you get to relive those experiences . . . from a programmer ’ s perspective.

Without a material, prims are lifeless and invisible. Dressing them in materials makes them both visible and interactive. Materials give your prims meaning; a thin long cylinder could be a column, a pencil, or a rope depending on its texture. In Papervision3D, shaders enable materials to interact with light, providing greater realism by determining how an object reflects or transmits light.

But in Papervision3D, it’ s much more than just exterior adornment. Materials actually draw your prims and objects. They lace your triangles together and then fill them with a color, material, or shade. Without them you have nothing on the screen. They’ re at the heart of what’ s happening in Papervision3D.

In this chapter, you learn about what really makes Papervision3D tick: the material classes. You investigate the basics of how materials are used to create objects. You learn about lighting and material types, and how to add brightness to a Papervision3D light source, create shades, and make bump maps. You ’ ll also extend these concepts to CS4.

At this point in the book, you ease out of the technical aspect of 3D into application building, by cons- tructing applications like a planet viewport viewer, video on a primitive, and bump map in a panorama.

Applying Textures Papervision3D contains a large number of material classes. Materials add life to your objects; it ’ s the material that interacts with your environment and light allowing and effects. But most importantly the material draws your objects. And as complex as Papervision3D may seem, it ’ s really a very simple process. This is how it works:

As already discussed, objects are made up of triangles. The vertices and triangular association of these vertices are created mathematically or in a modeling program such as Blender, or 3DSMax. Part 1: Getting Started

These triangles are then drawn onto the screen by the material class and then filled with a color, material, or light. It ’ s really just a draw -and - fill process as shown in Figure 4.1: only two steps.

The code for drawing a triangle in Flash is extremely simple, and is provided here for the color material:

graphics.beginFill( fillColor, fillAlpha ); graphics.moveTo( x0, y0 ); graphics.lineTo( x1, y1 ); graphics.lineTo( x2, y2 ); graphics.lineTo( x0, y0 ); graphics.endFill();

This code uses the moveTo , and lineTo methods to draw the triangle from vertices (x0, y0 ), ( x1, y1 ), and (x2,y2 ). The triangle is then filled with a color and an alpha. Each material has its own twist on the process, but generally it ’ s always the same: draw-and-fill as shown in Figure 4.1.

Light

Material

Color

Draw and Fill

Figure 4-1

There are several material types. In this section you ’ re introduced to simple materials, materials, special materials, and utils materials.

But how do you find out what material types there are?

In Papervision3D, they ’ re grouped in the following folders under the org/papervision3d/ :

❑ Materials — simple materials (for example, bitmap and movie) ❑ Materials/shadermaterials — shader materials (flat, cell, gouraud, phong, environment) ❑ Materials/shaders — bump maps (for example, phong, environment) ❑ Materials/special — (for example, composite and fog)

128 Chapter 4: Adding Materials

Using Simple Materials (or just Materials) Simple materials (or just materials) don ’ t require anything more than just throwing them onto a triangle. They ’ re found in the materials folder. By examining the MaterialObject3D class (the abstract base class for all materials), you can determine the properties of these simple materials.

Every material in Papervision3D extends this class, and contains the properties found inside it. To find these properties, open the MaterialObject3D class in Flex and drill down to the org/papervision3d/ core/proto/MaterialsObject3D folder.

Inside this class you find the following properties: id, name, oneSide, doubleSided, interactive, smooth, tiled, opposite, invisible, bitmap, maxU, and maxV. The ones you use most commonly are:

❑ Name (String) — The name of the material. ❑ DoubleSided (Boolean) — A Boolean value that indicates whether the faces are double sided. OneSide and doubleSided perform the same tasks placing a material on front and back of a triangle mesh. ❑ Interactive (Boolean) — A Boolean value that allows listeners to be assigned. ❑ Smooth (Boolean) — A Boolean value that determines whether the BitmapData texture is smoothed when rendered. ❑ Tiled (Boolean) — A Boolean value that determines whether the texture is tiled when rendered. Defaults to false. ❑ Bitmap (BitmapData) — Grants access to the material ’ s bitmap data.

These properties are generic to all material types. But Wireframe and Color materials possess a few additional non - generic properties. Wireframe Material Only These properties are used exclusively with Wireframe material:

❑ LineThickness (Number) — A value for the thickness of the triangle face line. ❑ LineAlpha (Number) — An 8 - bit alpha value for the triangle face outline. If zero, no outline is drawn. ❑ LineColor (Number) — A RGB color value to draw the triangle face outline. Color Material Only These properties are used exclusively with Color material:

❑ FillAlpha (Number) — An 8 - bit alpha value to fill the triangle face with. ❑ FillColor (Number) — A RGB color value to fill the triangle face with.

Above you examined two possible material types, Wireframe and Color. But there are others.

129 Part 1: Getting Started

Material Types You can discover what material types are available by navigating to the materials folder found in Papervision3D by drilling down through the following path: org/papervision3d/materials .

Using Flex, you’ll find the following materials list as shown in Figure 4.2.

Figure 4-2

At first, the list above may seem a little overwhelming. But from the draw-and-fill perspective (discussed earlier), the difference between the many materials is primarily in the fill method.

Adding Materials to a Prim Adding materials to a prim can be done using the following code template. In the code template, the three bracketed items show where the appropriate commands need to be inserted. Item 1 imports the appropriate material class. Item 2 declares the material instance. Item 3 instantiates the material to be used. The instantiated material is then declared as double - sided and placed in the sphere primitive, which is then instantiated. Texture Template Code (Using Sphere Primitive) In the following code, you place a material on a simple sphere. The same approach can be used for any prim.

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event;

//Import Papervision3D classes // 1. [Your Material Import Statement Goes Here]

import org.papervision3d.objects.primitive.Sphere;

130 Chapter 4: Adding Materials

import org.papervision3d.view.BasicView;

//Create WirePlane class

public class MaterialSphere extends BasicView { //Instantiate primitive and material

// 2. [Declare Your Material Instance and Data Type]

private var sphere:Sphere; public function MaterialSphere() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); }

//Initiate Papervision protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0;

// Create a material for the Sphere // 3. [Instantiate Your Material Here and Add Parameters]

material.doubleSided=true;

//Add your material and primitive to the scene

sphere = new Sphere(material, 100, 8, 8);

// Add Your Primitive to the Scene

scene.addChild(sphere);

}

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Apply an action to your primitive sphere.yaw(2);

//Call the super.onRenderTick function super.onRenderTick(event); }}}

You now use this template to place several simple materials on a sphere. 131 Part 1: Getting Started

WireframeMaterial You ’ ve already seen a number of primitives dressed in a wire frame material. The WireframeMaterial draws only the outlines of the object ’ s faces. The WireFrameMaterial constructor has three properties: color, alpha, and thickness, as shown in its signature:

WireframeMaterial( color, alpha, thickness)

The following code is contained in the wireframe method and demonstrates the use of the line drawing method:

param3.lineStyle(0, lineColor, lineAlpha); param3.moveTo(_loc_7, _loc_8); param3.lineTo(_loc_9, _loc_10); param3.lineTo(_loc_11, _loc_12); param3.lineTo(_loc_7, _loc_8); param3.lineStyle();

In CS4, this approach has changed. A new GraphicsPath class has moveTo , lineTo , curveTo , wideLineTo , and wideMoveTo utility methods that are designed to ease the pain of trying to define or update the commands and data values directly. The CS4 graphics classes can store complete drawings in a list (such as Vector. < IGraphicsData > ) that can be passed around as data and reused with any target, making it easy to save drawings for use at a later time. ColorMaterial ColorMaterial is seldom used in Papervision3D as it only affords a single solid color across your object, and therefore doesn ’ t allow for any definition or contrast. Without contrast (variation in color intensity), an object looks washed out and pretty boring. Contrast falls under the topic of lighting and is addressed in further detail in the following sections.

The ColorMaterial method only allows three parameters: color , alpha , and interactive . It has the following signature:

ColorMaterial( color, alpha , interactive)

To place a solid color on an object, just use the material template provided above. Import your color material class, declare its data - typed variable, instantiate it, and place it on your sphere as shown in the following code:

package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event;

//Import Papervision3D classes

//1. [Your Material Import Statement Goes Here] //import the ColorMaterial Class

132 Chapter 4: Adding Materials

import org.papervision3d.materials.ColorMaterial; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView;

//Create WirePlane class

public class ColorMaterialSphere extends BasicView { //Instantiate primitive and material

//2. [Declare Your Material Instance and Data Type] private var colormaterial:ColorMaterial; private var sphere:Sphere; public function ColorMaterialSphere() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); }

//Initiate Papervision protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0;

// Create a material for the Sphere

//3. [Instantiate Your Material Here and Add Parameters] colormaterial = new ColorMaterial(0x888888, .8); colormaterial.doubleSided=true;

//Add your material and primitive to the scene sphere = new Sphere(colormaterial, 100, 8, 8);

// Add Your Primitive to the Scene scene.addChild(sphere); }

// override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Apply an action to your primitive sphere.yaw(2);

//Call the super.onRenderTick function super.onRenderTick(event); }}}

133 Part 1: Getting Started

Both the wireframe and color materials use the drawTriangles class, which is simply an encapsulated form of the moveTo and lineTo methods in Flash. Don ’ t confuse Papervision3D ’ s drawTriangles with CS4 ’ s drawTriangles . They ’ re different methods, but do pretty much the same thing.

You now investigate one of the most important material types: bitmaps.

Bitmaps At the heart of using bitmaps in Papervision3D is the beginBitmapFill method. The method fills an area (a triangle in this case) with a bitmap and has the added benefit that it can be tiled. The method ’ s signature is:

beginBitmapFill(bitmap, matrix, repeat, smooth)

As you can see, it has four parameters: bitmap (bitmap fill), matrix (transform matrix), repeat (tiling), and smooth (bilinear interpolation). This method is used in conjunction with the bitmapData method to fill triangles with a texture: a bitmap is embedded and then a piece of it is grabbed using the bitmapData method, matrix transformation, and its UV data. You find out more about how this is accomplished in the next section. BitmapMaterial Before you dive into the BitmapMaterial class, it ’ s important to understand how Flash puts textures on a 3D object.

When it comes to , it ’ s important to remember that Flash can ’ t distort bitmaps. Its ability to transform images on a screen is limited to what are called affine transformations: changes in position, scaling, rotation, and skewing. In other words, no tapering or perspective distortion is allowed.

As polygons become distorted due to perspective scaling, Papervision3D places textures on those distorted areas by cutting them up into triangles. Furthermore, the portion of the image to be mapped onto each triangle is skewed onto it using the matrix method of beginBitmapFill . These skewed images are then masked and put together (actually the masking is inherent to the beginBitmapFill method). Of course, more triangles create higher quality, but this is also much more resource intensive, particularly on the CPU (see Figure 4.3).

Figure 4-3

134 Chapter 4: Adding Materials

So here ’ s the dirty secret: in Flash, bitmaps don ’ t get transformed to have perspective as 3D textures. Triangular pieces of bitmaps are individually transformed (with affine transformations) to make up the textures applied to 3D objects. These pieces are fitted as best as possible within the 3D space . . . shocking isn ’ t it! The affine transformations are applied in Flash using the Matrix method of the geometry class.

The affine transformation matrix shown in Figure 4.4, is used with the draw method of the BitmapData object, as well as the beginBitmapFill() , the beginGradientFill() , and the lineGradientStyle() methods of a graphics object.

a b tx

c d ty 0 0 1 Figure 4-4

The transformation equations for x and y are given by:

Xnew = Xold*a + Yold*c + tx Ynew = Xold*b + Yold*d + ty

Where a , b , c , d , tx , and ty are defined as:

❑ a — scaleX ❑ b — skewY ❑ c — skewX ❑ d — scaleY ❑ tx — translate (move) X ❑ ty — translate (move) Y

Using combinations of these parameters you can now translate, scale, rotate, and skew your image bit into the right place for your triangle fill. And that is exactly what Papervision3D is doing in the BitmapMaterial class.

Essentially, the BitmapMaterial class, using the Matrix method above, creates texture pieces from the BitmapData object to be placed on an object ’ s triangular mesh. It ’ s the super class for the BitmapAssetMaterial , BitmapFileMaterial , BitmapViewportMaterial , and BitmapColorMaterial classes. It also allows for the re - initialization of textures, which enables texture swapping. The BitmapMaterial constructor method has two parameters: asset and precise, as shown in its method signature:

BitmapMaterial(asset, precise)

135 Part 1: Getting Started

The asset variable is your BitmapData . The precise variable, if set to true, adaptively renders triangles to conquer texture distortion by setting the tessellation per pixel ratio.

You ’ re now going to examine the remaining Bitmap classes. BitmapAssetsMaterial The BitmapAssetsMaterial allows you to grab a bitmap from your Flash library and bring it into Papervision3D. This doesn ’ t work in Flex, only Flash. It ’ s easily implemented by adding a bitmap to your Flash library, right clicking it and setting its linkage properties to export as a BitmapData as shown in Figure 4.5.

Figure 4-5

BitmapFileMaterial The BitmapFileMaterial is probably one of the most useful and easiest to use of the material classes. It creates a texture by loading a bitmap from an external file. Just give it a path or link to your image and just like magic you have an external image applied to your object. Its constructor class only requires a URL locator string – now that ’ s cool!

The signature for BitmapFileMaterial is as follows:

BitmapFileMaterial(url, precise)

Between the two, BitmapFileMaterial is more versatile than BitmapAssetsMaterial , as it works in both Flash and Flex and only requires that you point to the image file to import. However, there are loading delays since the material is not embedded and as a result you may be looking at an empty object for a few seconds as the material loads.

To avoid this, you can build an image preloader, as shown below. Unfortunately, although this is better than staring at an empty object, you ’ re still stuck waiting for the material. The preloader is just a bit friendlier, in that at least you know what ’ s going on and why you don ’ t see the material yet. In other words, the preloader provides feedback that the application is still working on getting the material for the object loaded.

136 Chapter 4: Adding Materials

// Add an image for your environment map var img:Loader = new Loader(); img.load(new URLRequest(“assets/envmap15.jpg”)); img.contentLoaderInfo.addEventListener(Event.COMPLETE, initenv);

With the BitmapAssetsMaterial , this is not the case because the materials are already embedded in the Flash Library. If you need immediate imagery on your prims, but can ’ t use the BitmapAssetsMaterial , try embedding your image. We discuss the process of embedding images in further detail later. BitmapViewportMaterial Viewport Material is one of the unsung heroes of Papervision3D. It allows you to take a bitmap snapshot of a viewport and then place that snapshot onto another object in Papervision3D. The applications of this class are endless. It ’ s like having a handheld TV that you can use to keep track of anything happening in your Papervision3D environment.

The BitmapViewportMaterial can be computationally intensive though, because you must process your scene twice to create a viewport projection. In CS4 you get it for free just by placing your scene in a display object. And as usual, the transition from Papervision3D to CS4 reduces the amount of code required to render a scene, as you can see in the coming chapters. Putting it all Together In the following example, you put these three materials together into a planet-swapping application. The application demonstrates the use of BitmapFileMaterial , BitmapAssetMaterial , BitmapViewportMaterial , and the swapping of materials of different types (see Figure 4.6).

Figure 4-6

Photo skins used for the planets above are Courtesy of NASA/JPL - Caltech

137 Part 1: Getting Started

The following code is a Flash package found in the book ’ s Chapter 4 code folder named swappingPlanets . Its code is divided into six parts:

❑ Import statements — includes the appropriate material classes. ❑ Variable declarations — declares planets, materials and viewports. ❑ Program initiation — sets the swapping timer, and stage initialization. ❑ Scene initiation — instantiates planets, materials, viewports, and sets the planet pivots and Bitmap Viewport position. ❑ Switch case swapping function — uses the planet.material method to change materials for five different cases. ❑ Animation loop — animates the yaw of the planets and double iterates over both viewports.

package { //Flash Imports import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent; import flash.display.MovieClip; import flash.display.StageAlign; import flash.display.StageScaleMode;

//Papervision Imports import org.papervision3d.view.BasicView; import org.papervision3d.scenes.Scene3D; import flash.events.Event; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.materials.BitmapAssetMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.materials.BitmapFileMaterial; import org.papervision3d.view.BitmapViewport3D;

import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.BitmapViewportMaterial; import org.papervision3d.materials.BitmapFileMaterial; import org.papervision3d.materials.BitmapViewportMaterial;

public class SwappingPlanets extends BasicView { //Declare your timer private var myTimer:Timer; private var myCount:int=0;

//Declare Earth’s material private var earth:Sphere; private var earthMaterial:BitmapAssetMaterial;

//Declare your planet materials

138 Chapter 4: Adding Materials

private var pivot:DisplayObject3D; private var planet:Sphere; private var planet1Material:BitmapFileMaterial; private var planet2Material:BitmapAssetMaterial; private var planet3Material:BitmapFileMaterial; private var planet4Material:BitmapAssetMaterial; private var planet5Material:BitmapFileMaterial;

//Declare your bitmap viewport, scene, and plane private var plane:Plane; private var viewport2:BitmapViewport3D; private var scene2:Scene3D;

//Create your constructor function public function SwappingPlanets() { //Setup stage. stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE;

//Initialize Timer var myTimer = new Timer(3000); myTimer.addEventListener(“timer”, timerHandler); myTimer.start();

//Set the size to scale to stage and initialize our scene super(0,0,true); initScene(); }

private function initScene():void { //Create a new material, use a texture from the Flash library. earthMaterial = new BitmapAssetMaterial(“earth512”);

//Place the earth material on a Sphere earth = new Sphere(earthMaterial, 400,10,10);

viewport2 = new BitmapViewport3D(); scene2 = new Scene3D();

//create a material of the above scene var material:BitmapViewportMaterial = new BitmapViewportMaterial(viewport2, true); material.doubleSided = true;

plane = new Plane(material);

plane.x=-800; plane.y=500;

scene.addChild(plane); (continued)

139 Part 1: Getting Started

(continued)

//Add your earth to your scene and bitmap viewport scene scene2.addChild(earth); scene.addChild(earth);

//Create materials for your orbiting planet planet1Material = new BitmapFileMaterial(“assets/moon255.jpg”); planet2Material = new BitmapAssetMaterial(“neptun640”); planet3Material = new BitmapFileMaterial(“assets/jupiter640.jpg”); planet4Material = new BitmapAssetMaterial(“phobos640”); planet5Material = new BitmapFileMaterial(“assets/venus640.jpg”);

//Create a new sphere for your planet planet = new Sphere(planet1Material, 80,6,6);

//Create a new pivot point for your planet pivot = new DisplayObject3D();

//Set Planet to x 700 planet.x = 700;

//Add moon to pivot. pivot.addChild(planet);

//Add pivot to scene scene2.addChild(pivot); scene.addChild(pivot);

//position your bitmap viewport plane plane.yaw(-30); plane.z=100;

// Start rendering the scene startRendering();

}

//Handle Timer Event private function timerHandler(event:TimerEvent):void {

myCount=myCount%5; init(myCount); myCount++;

}

//Swap planet materials using .material private function init(number:Number):void {

switch (number) { case 0: planet.material = planet1Material; break; case 1:

140 Chapter 4: Adding Materials

planet.material = planet2Material; break; case 2: planet.material = planet3Material; break; case 3: planet.material = planet4Material; break; case 4: planet.material = planet5Material; break; default: trace ( “ no case tested true “ ) }}

override protected function onRenderTick(event:Event=null):void { //Rotate earth, and call normal rendering. earth.yaw(1);

//Rotate planet around earth. pivot.yaw(1/2);

//Rotate planet around its own axis planet.yaw(1);

//Render both viewport and bitmapviewport (viewport2) renderer.renderScene(scene2, camera, viewport2); renderer.renderScene(scene, camera, viewport);

super.onRenderTick(event); }}}

To reiterate, you can easily swap planet materials by resetting the respective material property as follows:

planet.material = planet1Material

Just dot your object ’ s instance name with the material method and set it equal to the new material you want on your object. You don ’ t even have to do this in the render loop. This simply couldn ’ t be any easier. The best part is that this allows for the creation of some really cool effects.

Among the material classes presented so far, you ’ ll find yourself using the BitmapAssetMaterial and BitmapFileMaterial the most to bring materials into Papervision3D. As mentioned earlier, another way to bring material into Papervision3D is to use the Embed tag. Bitmap Color and Bitmap Wireframe The least interesting materials are bitmap color and bitmap wireframe. The bitmap color is similar to the color material and gives you the ability to change pixel color; you can use it to create a virtual chalkboard. The bitmap wireframe, similar to the wireframe, also gives you access to the bitmap data of a material. The big difference between the two is that BitmapColorMaterial uses the BitmapMaterial

141 Part 1: Getting Started

super class where BitmapWireframMaterial uses the TriangleMaterial super class. Both use the beginBitmapFill method to fill their triangles.

Movie Clips Well this is Flash, so you should be able to use animated materials – right? You bet! You can import your Flash Movie Clips directly into Papervision3D using their linkage identifier. You can also embed movie clips into Flex and create animated skins in your Flex projects as well. This is a very powerful technique as it allows for animated skins that can be controlled using ActionScript. But to this point animated movie clips have not been used very much in Papervision3D . This is mostly due to the heavy load that ’ s put on the processor as a result.

One of the reasons that this might be so, is that animated movie clips in Flash require more processor resources than just bitmaps. One way around this is to build a frame - ripper (bitmap image sequencer run by ActionScript). However, for small projects using animated swf ’ s, it shouldn ’ t be an issue.

You find out how to apply animated movie materials later in the book. But for now, start with the basic MovieMaterial class. MovieMaterial Not unlike the BitmapMaterial class, which is the super class for all bitmap materials, the MovieMaterial class is the super class for MovieAssetsMaterial and VideoStreamingMaterial . It has a number of important methods, such as:

❑ Animated — enables your animated movie clip to play. ❑ Transparent — enables transparency. ❑ Precise — avoids distortion by adaptively rendering triangles. ❑ Smooth — applies antialiasing.

And before you start setting all these variables to true, it ’ s important to realize that each method places an additional tax on the CPU and the necessity for the options is usually marginal and most often not even noticeable. Therefore, it ’ s best to keep them set to false.

For example, one of the big CPU hits is transparency; by working your web site to exclude the need for transparent images, you gain a significant performance improvement. Now this isn ’ t always possible, and only use it if you absolutely need to. MovieAssetsMaterial The MovieAssetsMaterial class only works with Flash, which means it will accept a linkage ID from the Flash Library. The signature for the constructor function is:

MovieAssetMaterial( linkageID, transparent, animated, createUnique, precise)

Up to this point, very little has been said about interactivity, and you ’ re probably wondering how you interact with your movie clips. Don ’ t worry; you ’ ll get there soon. If you can ’ t wait, check out the chapter on gaming.

142 Chapter 4: Adding Materials

VideoStreamMaterial One of the most exciting elements of Papervision3D is the ability to play video on a primitive. The VideoStreamMaterial gives you the ability to do this. The object is instantiated with the following signature:

VideoStreamMaterial (video, stream, precise, transparent)

As you can see, the constructor takes 4 parameters – the video object, the netStream object, the precise Boolean value, and the transparent Boolean value. The Boolean values are optional, and are set to false by default. As previously stated, it ’ s usually a safe bet to leave them set to their default value of false.

Video is played on a primitive by drawing the current video frame onto a bitmap as the video plays. The respective bitmap becomes the primitive ’ s material for the duration of that frame. The Papervision3D snippet that accomplishes this is given below.

public override function updateBitmap ():void { try { // copies the scale properties of the video var myMatrix:Matrix = new Matrix(); myMatrix.scale( this.video.scaleX, this.video.scaleY );

// Fills the rectangle with a background color this.bitmap.fillRect ( this.bitmap.rect, this.fillColor );

// Due to security reasons the BitmapData can’t access RTMP content like a NetStream using a FMS server. // The next three lines are a simple but effective workaround to get past the Flash security sandbox. this.video.attachNetStream ( null ); this.bitmap.draw( this.video, myMatrix, this.video.transform .colorTransform ); this.video.attachNetStream ( this.stream ); }catch(e:Error) { // }}

Central to the code above (and most of Papervision3D), is the use of the BitmapData class. The process of placing video on a primitive or object is a relatively simple one. It uses the draw method of the BitmapData class to draw the video onto the respective BitmapData object. This gives you pixel level access. However, you ’ ve only got one frame to work with (you need many more). To mitigate this dilemma, set up an onEnterFrame loop and draw the video at the application ’ s current frame rate.

You ’ re now drawing your video onto your BitmapData object on every frame tick. You have access to it as a bitmap and you can do anything you want with it, like wrap it around a primitive, for example. If you bump your video down to 320x240, you won ’ t be able to tell that your bitmap is not video. (For those of us who started way back in Flash 5, this is totally amazing!)

You ’ re now going to find out how to put video on the curved prim that was developed in Chapter 3.

143 Part 1: Getting Started

Video on a Papervision3D Prim The real power of Papervision3D is its strong OOP (object oriented) structure. It lets you do some very robust things, such as put video on any prim. And in this case, you place a simple video on a curved Plane (developed in Chapter 3), as shown in Figure 4.7.

Figure 4-7

The entire code for this project can be downloaded from Chapter 4 of the book ’ s companion website labeled Curved Video. Here you get the basics of how it was created.

At the heart of the program is the NetStream class that allows you to bring a video into Flash. We discuss this class in greater detail in Chapter 11, which deals with the Flash Media Server. The three classes that must be imported here are: Video , NetStream , and NetConnection . After importing the classes, declare the objects as follows:

//net stream assets private var myConnection : NetConnection; private var myStream : NetStream; private var myVideo : Video;

In addition to setting up Papervision3D and bringing in your curved video prim, you must create a net connection and set it’s URL value to null because you’re playing the video from the local disk. The NetConnection is needed for the NetStream to work though, so after making the call to NetConnection.connect, you instantiate the NetStream and pass it your NetConnection object. Lastly, you instantiate the Video object that you declared earlier, make the call to the NetStream.play, pass it the url path to the video, and attach the NetStream to the Video with the Video.attachNetStream method. The following code demonstrates this process:

private function loadMyVideo():void { //Set up Net Connection myConnection = new NetConnection(); //Not on the media server myConnection.connect(null); myStream = new NetStream(myConnection); //Set buffer time - at least 2 seconds for some machines MyStream. bufferTime = 2; //Set up My Stream for client myStream.client = new Object(); //Instantiate the video

144 Chapter 4: Adding Materials

myVideo = new Video(320, 240); myStream.play(“assets/eternity.flv”); //Attach to local client side video myVideo.attachNetStream(myStream); }

After attaching your net stream to your instantiated video object you can now place it on your curved prim.

videoMaterial = new VideoStreamMaterial(myVideo, myStream); //videoMaterial.interactive = true; videoMaterial.doubleSided = true ;

//plane = new Plane(material applied to object, width, height, wSegments, hSegments); planeCurve = new CurvedPlane(videoMaterial,32,24,8,8,10); scene.addChild(planeCurve);

You ’ re finally ready to use your VideoStreamMaterial .

First, place your video and stream in the VideoStreamMaterial . Make sure that you declare it double - sided so when you spin your curved plane you see the video on the other side. Place that videoMaterial on your curved plane, then add the plane to your scene. Ta da . . . video on a prim!

You now extend this concept to place video on the CS4 Super Prim you created in Chapter 3. Video on a CS4 Prim Get ready to eliminate 1000s of lines of Papervision3D code. That ’ s right! By using CS4 you no longer need the MovieMaterial class, the DisplayObject3D class, the VideoStreamMaterial class, and a plethora of Triangle/UV parsing classes. It ’ s amazing how easy it is to do this in CS4.

Placing video on the CS4 Super Prim requires only four steps:

1. Import the Video, NetStream , and NetConnection classes. 2. Declare your variables for the three objects as follows: //net stream assets private var myConnection : NetConnection; private var myStream : NetStream; private var myVideo : Video;

3. Instantiate the NetConnection , NetStream , and Video the same way you did previously, in the loadMyVideo() method. 4. Create a bitmapData object and in the animation loop, draw your video to it. Then attach the net stream to your video: //Draw Video into Bitmap bitmapData.draw( this .myVideo); this .myVideo.attachNetStream ( this .myStream );

145 Part 1: Getting Started

The results yield a video being played among the various prims contained in the super prim (see Figure 4.8). You can change the video simply by calling the play() method of the NetStream class again and changing the file path that was initially passed to the NetStream , as follows:

myStream.play(“assets/eternity.flv”)

Figure 4-8

Before you leave this section, you add video from your local web cam to your CS4 super prim. You do this in four steps:

1. Declare your camera and matrix variables: private var myCam:Camera; private var matrix:Matrix;

2. Remove the loadMyVideo() method and add the following code to connect to the local camera: myCam=Camera.getCamera(); myCam.setMode(320, 240, 15); myVideo = new Video(320, 240); myVideo.attachCamera(myCam);

3. Add a matrix method to flip your camera around the x - axis so you get your mirror image back. Otherwise, your video is reversed: matrix = new Matrix(-1,0,0,1,bitmapData.width,0);

4. Add this matrix method to the bitmapData.draw method (where – 1 in the matrix flips your video on the x - axis, and the other parameters reposition it). bitmapData.draw( this.myVideo, matrix);

After implementing the code, you end up with a web cam video of yourself (or your Guinea pig shown in Figure 4.9) switching from prim to prim.

146 Chapter 4: Adding Materials

Figure 4-9

Keep in mind that when activating the program, your video might not play. Before you go back to the drawing board though, right - click (or control click for a MAC) on your screen. Then click on settings , and then the camera icon, and make sure that your webcam is selected. Once the correct webcam is selected, your image should appear on the super prim. The complete code can be found in the Chapter 4 code downloads on the book site. Congratulate yourself – you ’ ve made it through materials! But before you dive right into shaders it ’ s important to first understand light.

Understanding Light Lighting is one of the most complex subjects in computer graphics. One of the simplest lighting systems that ’ s typically constructed in minimal games is a point light that has both brightness and position. But PV3D ’ s light system is even simpler and doesn ’ t have brightness, but casts the same illumination on an object, independent of its point source distance from that object. The process of adding a point light to PV3D is as follows:

1. Create a point light source with brightness and position. The point light position is only used to calculate the angle of the light with respect to an object ’ s individual triangles. 2. Use the light source ’ s angle to an object ’ s faces to change the value of the object ’ s individual triangle color. The color change referred to as shading is based on the triangle ’ s face angle with respect to the light source. As the triangle rotates away from the light, its pixel values darken depending on the transform algorithm being applied to it. 3. Calculate the normal vector of each triangle of your object. To do this, take the cross product of two triangle vector sides, which is constructed from the vertex differences of the points on your triangle. The normal vector is positioned at the center of your triangle perpendicular to your triangle plane as shown in figure 4.10. 4. Take the dot product of your light vector with that of your normal vector. This dot product is the heart of the whole process. It ’ s used to return the angle between the light source and the triangle under consideration. The cosine of this angle gives the amount that your triangle should be illuminated. As your object rotates, this angle is calculated dynamically for each triangle and the cosine of this angle determines the amount that each triangle is illuminated, giving the appearance that your light source moves across your object ’ s surface.

147 Part 1: Getting Started

Technically this is accomplished in PV3D by using two vectors: your light vector which is the vector between your object and your light source, and your triangle - face normal vector as shown in Figure 4.10. In Papervision3D, the light ’ s vector is held by the light matrix and the dot product of this vector with the triangle - face normal vector returns the cosine of the angle between them. This dot product determines the individual triangle illumination (or color) given by the equation below:

zd = face3D.faceNormal.x * lightMatrix.n31 + face3D.faceNormal.y * lightMatrix.n32 + face3D.faceNormal.z * lightMatrix.n33

In the formula, zd is the returned cosine of the angle between the light and normal vector. Each shader uses this function in its algorithm to create its particular shade by illuminating each triangle differently depending on its angle from the point source (see Figure 4.10). It ’ s the key element to shading.

Normal vector

Angle

Dot product - produces Angle

Cross product - produces normal vector

Figure 4-10

In Papervision3D the point - light brightness parameter is missing, but how can you have a sunrise without brightness? Worry not . . . you can find out in the coming sections how to add brightness so your days can be sunny! In Chapter 5, you discover how to make your sun rise over a Blender city.

Next, you apply what you ’ ve learned about light to create shade materials.

Using Shaders The use of shaders greatly enhances the illusion of depth. Shaders depict levels of darkness by applying media more densely for darker areas, and less densely for lighter areas. In Papervision3D, you accomplish this by altering a color based on its angle to and distance from a light source. Typically, shading uses multiple light sources. However, in Papervision3D there ’ s only one point light source; a point light originates from a single point, and spreads outward in all directions.

Using just one light source is somewhat limited and is a compromise due to CPU performance issues; more lights slow processor speed. However, even with just one point source, the shading effects are pretty amazing! For Flash, this is a first. Before Papervision3D, most “ flashmaticians ” could not even conceive that shading would ever be possible in Flash.

Shaders give objects a photorealistic effect. In Papervision3D, there are five shades: flat, gouraud, cell, phong, and environment (shown in Figure 4.11 from left to right respectively). Each requires a different mathematical algorithm to produce.

148 Chapter 4: Adding Materials

Figure 4-11

A description of each shade material is given here:

❑ Flat shading — shades each polygon of your object based on the angle between the polygon ’ s surface normal and the direction of the light source. It takes the least expensive hit on the CPU of all the shaders, but gives the object a boxy look. ❑ — applies lighting values to each vertex of an object, and interpolates between them. The light intensity values are obtained using a bi - linear interpolation to any point within the triangle under consideration from the intensity at the vertices of that triangle. This works well for high polygon models, but can produce undesirable specular (shiny) artifacts for low polygon models. This is common to Papervision3D. ❑ Cell shading — sometimes referred to as cartoon shading, uses a number of discrete lighting levels to produce its cartoon effect. Lighting values are calculated for each pixel and then mapped to a small number of discrete shades to create the characteristic flat look. ❑ — the most computationally intensive of the shaders. This is due to the fact that its algorithm uses non - linear terms in applying light values to each pixel of the object ’ s surface. In Phong shading, you linearly interpolate a triangle ’ s three normals across its surface. But in Papervision3D, this process is greatly simplified by using light maps and fast shading techniques. ❑ Environment Map Shading — sometimes called reflective shading, adds lighting from the surrounding environment by using a single texture. Typically, this texture contains the reflected environment from a mirrored surface such as a metallic ball. This efficient approach avoids the need for computationally intensive calculations. However, it lacks self - reflection.

Coding shaders is pretty straightforward, and requires the following steps:

1. Import your shade class. 2. Instantiate and position your light source. 3. Create the materials to be used with your shader. 4. Instantiate the shade material. 5. Create the shade materials. 6. Add the shade material to your object.

149 Part 1: Getting Started

Shaders Code Template The preceding steps are implemented in the following shaders code template:

package { // Flash imports import flash.display.BitmapData; import flash.display.Loader; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.net.URLRequest;

// Papervision3D imports import org.papervision3d.lights.PointLight3D; import org.papervision3d.core.proto.MaterialObject3D;

//[Your Shade Import Statement Goes Here]

import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.*; import org.papervision3d.view.BasicView;

public class FiveShadersSphere extends BasicView {

// Declare Cylinder variables private var cylinder:Cylinder;

//Constructor Statement goes here public function FiveShadersSphere() { super(0, 0, true, false);

// Initialise Papervision3D initPV3D();

// Create your objects createObjects();

}

private function initPV3D():void {

// Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; // Set the background to black opaqueBackground = 0xffffff;

// Place your camera camera.x = 0; camera.y = 400; camera.z = -100; }

private function createObjects():void {

150 Chapter 4: Adding Materials

// Create your point light source var light:PointLight3D = new PointLight3D(true); light.x = 100; light.y = 1000; light.z = -400;

// Create your flat shade material and apply it to your cylinder

// 2. [Your Shade type Material Statement Goes Here]

// Place Shade on Cylinder cylinder = new Cylinder(yourShadedMaterialhere, 50, 10, 20, 10); cylinder.x = -240;

// 3. [If Using Environment Map, Required Statements go Here]

// Add your Cylinder and light to the scene scene.addChild(cylinder); scene.addChild(light);

// Start rendering the scene startRendering(); }

override protected function onRenderTick(event:Event=null):void {

cylinder.yaw(2);

super.onRenderTick(event); }

} }

You ’ re probably wondering: what does the light source really do? Don ’ t get confused; the Papervision3D light source doesn ’ t do very much. Papervision3D doesn ’ t use ray tracing to set intensities and calculate shadowing.

Ray tracing traces light along rays propagating from the eye back into the scene towards the light source. The process is often visualized as a tree where each node is a surface hit point. At each hit point you spawn a light ray, a reflected ray and/or transmitted ray. Ray tracing is computationally intensive and presently beyond the ability of Flash to handle rapidly in real time for complex scenes. Although real - time pixel manipulation (using ray tracing) is getting faster, it still needs to be faked in AS3.

Papervision3D ’ s point light source has only one real purpose: to provide the x, y, and z point value for angle calculation (that ’ s fed into Papervision3D ’ s light matrix as described earlier). No brightness, no distance fall off, no targeting, no light color, no conical areas, and so on . . . and only one light source at a time can be used. But even with these limitations, the shades in Papervision3D are pretty impressive.

You can now examine how they work and extend them to add a brightness factor.

151 Part 1: Getting Started

FlatShadeMaterial The FlatShadeMaterial constructor function accepts four parameters: the light object, the diffuse color, the ambient color, and the specular level. Its constructor function is:

FlatShadeMaterial(light, lightColor, ambientColor, specularLevel)

At this point, you ’ re probably ready to whip out your super computer and start calculating light rays – as you ’ re dealing with ambient and specular terms. But there ’ s a trick; you ’ re going to skip over all the complexity by using the beginGradientFill method.

Here ’ s a brief explanation of the input variables of your constructor function:

❑ Light parameter — your point light, which gives you the position of your light source. ❑ LightColor variable — the color of your light source. ❑ AmbientColor variable — not true ambient light. It ’ s the first and second parameter in a gradient fill (the third one is your light color), and it governs the color that your object will take on. ❑ SpecularLevel variable — adjusts the number of shades between your middle ambient color and light color. This gives the appearance of making your surface shiny at low numbers.

Here ’ s how the gradient fill works.

The beginGradientFill method has several parameters but only the ones used in Papervision3D are listed here: type , color , alphas , ratios , and matrix . The method ’ s signature is:

beginGradientFill(type, colors, alphas, ratios, matrix)

The parameters do the following:

❑ Type — specifies which gradient type to use — linear or radial. ❑ Colors — an array of color values used in the gradient. ❑ Alphas — an array of alpha values corresponding to the colors. ❑ Ratios — defines the percentage of the width where the color is sampled at 100 per cent. ❑ Matrix — a transformation matrix that controls scale, skewing, and location of your gradient appearance.

Here ’ s how it ’ s implemented: a BitmapData object is created and named tempmap, which is just a simple strip 255 pixels long and 1 pixel wide; the gradient is calculated using the ambient and light colors, and then drawn into the tempmap and returned.

This is demonstrated in the code below, which comes from Papervision3D ’ s LightMaps class, found in the org/papervision3d/materials/utils folder. Flat, cell, gouraud, and phong all use this class to create their light maps:

152 Chapter 4: Adding Materials

var tempmap:BitmapData = new BitmapData(255,1, false,0); var s:Sprite = new Sprite(); var m:Matrix = new Matrix(); m.createGradientBox(255,1,0,0,0); s.graphics.beginGradientFill(GradientType.LINEAR, [ambientCo lor,ambientColor,lightColor],[1,1,1],[0,255-specularLevel,255],m); s.graphics.drawRect(0,0,255,1); s.graphics.endFill(); tempmap.draw(s); return tempmap;

Now that you understand how a simple light map is created, the steps to creating a flat shade are easy (see Figure 4.12):

1. Create your light map _colors = LightMaps.getFlatMapArray(lightColor, ambientColor, specularLevel );

2. Return a cosine (zd ) from the dot product, which gives the cosine of the angle of your triangle face normal to your light source. If your light source is at 90 degrees in relation to your triangle (that is, no light hitting it) then the value is zero. If your triangle is directly facing the light source then your cosine is 1 (completely lighted). zd = face3D.faceNormal.x * lightMatrix.n31 + face3D.faceNormal.y * lightMatrix.n32 + face3D.faceNormal.z * lightMatrix.n33;

3. Calculate the index value from your zd by multiplying it by 255 (hex 0xff) and grab the respective color from your _colors array . Essentially, as your triangle turns away from your light source, the hex value is reduced, effectively darkening your pixels. zAngle = zd*0xff; //0xff if hex for 255 currentColor = _colors[zAngle];

4. Render your triangle and fill it with your calculated light map color. graphics.beginFill(currentColor,fillAlpha); graphics.moveTo(x0, y0); graphics.lineTo(tri.v1.x, tri.v1.y); graphics.lineTo(tri.v2.x, tri.v2.y); graphics.lineTo(x0, y0); graphics.endFill(); renderSessionData.renderStatistics.shadedTriangles++;

The preceding code is taken from Papervision3D ’ s FlatShadeMaterials class that is found in the org/ papervision3d/materials/shadematerials folder.

153 Part 1: Getting Started

Find Light Map Render Triangle Index and Color

Calculate Cosine Create Light Map

Figure 4-12

This probably seems a lot to absorb, but the good news is that the rest of the shaders follow pretty much the same process (with the addition of a few matrix methods). GouraudMaterial The Gouraud Material has the same constructor variables as the Flat shader. The big difference, however, is that you ’ re now dealing with three surface normals, not one face normal.

GouraudMaterial( light, lightColor, ambientColor, specularLevel )

As discussed earlier, gouraud applies lighting values to each vertex of an object (a triangle in this case), and interpolates between them, as shown in the code below:

var p0:Number = (face3D.v0.normal.x * lightMatrix.n31 + face3D.v0.normal.y * lightMatrix.n32 + face3D.v0.normal.z * lightMatrix.n33)+1; var p1:Number = (face3D.v1.normal.x * lightMatrix.n31 + face3D.v1.normal.y * lightMatrix.n32 + face3D.v1.normal.z * lightMatrix.n33)+1; var p2:Number = (face3D.v2.normal.x * lightMatrix.n31 + face3D.v2.normal.y * lightMatrix.n32 + face3D.v2.normal.z * lightMatrix.n33)+1;

But what ’ s a vertex normal?

As opposed to a surface normal (which you saw in the previous case), a vertex normal is the normalized average of the surface normals of the faces that contain that vertex, and is calculated using the calculateNormal method in the Vertex3D class that is found in Papervision3D ’ s org/ papervision3d/core/geom/renderables folder. This normal value is only an approximation and does not take into account the size of your triangles (see Figure 4.13).

154 Chapter 4: Adding Materials

Figure 4-13

As shown (in the code) above, the dot product of these normals with the light matrix returns the cosine values of the vertices, which are then interpolated across your triangle to produce Gouraud shading. Papervision3D handles this by concatenating two Matrices - one that transforms the pixel values for fast shading, and the other, which handles triangle mapping of those pixel values. This concatenated matrix is then applied to the beginBitmapFill method, as shown in the following method signature:

graphics.beginBitmapFill( gouraudMap, transformMatrix, true, false);

The gouraudMap variable comes from your light map, and consists of a 3 pixel high gradient fill. You can find out more detail about gouraud shading on the book ’ s website.

As cell and phong shaders extend the EnvMapShader class, you ’ ll learn about the EnvMapShader first. EnvMapMaterial In Papervision3D, EnvMap, Cell, and Phong are all on the same computational level since Cell and Phong shaders extend the EvnMap shader. Shading in Papervision3D uses approximations that are often referred to as fast shading techniques. The inner workings of this are beyond the scope of this book, but are explained in further detail on the book ’ s website.

The EnvMapMaterial object takes four parameters: the light object, light map, back environment bitmap data and ambient light. The signature for the constructor is as follows:

EnvMapMaterial(light, lightMap, backEnvMap, ambientColor)

Typically in Papervision3D, the same material is used for lightMap and backEnvMap . Its implementation follows the same scheme as the Gouraud Matrix: by concatenating two matrices, one which transforms the pixel values for fast shading, and the other which handles triangle mapping of those pixel values. This matrix is then applied to the beginBitmapFill method. However, now there

155 Part 1: Getting Started

are six parameters needed for the pixel matrix to accomplish the environment mapping. Thus, you see a harder CPU hit when applying these shaders. PhongMaterial As mentioned earlier the Phong Material extends the EnvMap Material . Its constructor function has four properties: light , lightColor , ambientColor , and specular .

PhongMaterial(light, lightColor, ambientColor, specular)

The light map (or fill gradient method) for the phong shader is very similar to flat and gouraud light maps with the difference being that the phong light map is square in dimension (lw, lw ): as opposed to single or triple strips.

mat.createGradientBox(lw,lw,0,0,0); s.graphics.beginGradientFill(GradientType.RADIAL, [lightColor,ambientColor ,ambientColor], [1,1,1], [0,255-specularLevel,255], mat); s.graphics.drawRect(0,0,lw,lw);

The light map technique is a powerful approach to creating fast shaders. But with the release of Flash player 10, you have an even more powerful tool, called Pixel Bender. Pixel Bender gives you the ability to create animated shades at the pixel level, which we discuss in the next chapter. CellMaterial The CellMaterial light map is a little different than the previous ones. The CellMaterial remaps the Phong light map using a palletteMap . In other words, it doesn ’ t use the beginGradientFill method to return its final bitmap, but instead returns a blur - filtered bitmap constructed from a palette Map . The color arrays for the paletteMap are returned based upon your color steps:

for ( var i:int = 0; i < = 255; i++) { rlut[i] = (i-(i % Math.round(256/steps))) < < 16; glut[i] = (i-(i % Math.round(256/steps))) < < 8; blut[i] = (i-(i % Math.round(256/steps))); }

where rlut , glut , and blut are the arrays that hold your red, green, and blue color values.

The constructor function accepts four values: your light source, two color values, and the number of steps between the two color values. The CellMaterial signature is as follows:

CellMaterial(light, color_1, color_2, steps)

Calculating the color array for the palette map requires that you break a color up into its color components (red, green, blue, alpha). The component can be extracted using the following code:

alpha = yourColor > > 24; red = yourColor > > 16 & 0xFF; green = yourColor > > 8 & 0xFF; blue= yourColor > > & 0xFF;

156 Chapter 4: Adding Materials

where > > is the bitwise right shift operator and is used to shift to the required color position. Fractional bits (or bits shifted to far right) are discarded and the “ & 0xFF ” zeroes out the first part of the color. However, there ’ s a better way to go about this. In Pixel Bender, you don ’ t use the bit shift operator. Instead, you just refer to the color by dot syntax.

That ’ s a lot, and it ’ s going to be changing, especially with Pixel Bender, as you ’ ll see in the next chapter.

You now put all five of the shaders together into a single application. As you ’ ve already seen how each one of the shaders works, there ’ s no need to go through the code line by line. The code is well documented, but if you have any difficulties with it, have a look at the book ’ s website video that corresponds to this section.

package { // Flash imports import flash.display.BitmapData; import flash.display.Loader; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.net.URLRequest;

// Papervision3D imports import org.papervision3d.lights.PointLight3D; import org.papervision3d.core.proto.MaterialObject3D; import org.papervision3d.materials.shadematerials.CellMaterial; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.materials.shadematerials.GouraudMaterial; import org.papervision3d.materials.shadematerials.PhongMaterial; import org.papervision3d.materials.shadematerials.EnvMapMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.*; import org.papervision3d.view.BasicView;

public class FiveShadersSphere extends BasicView {

// Declare Cylinder variables private var cyl1:Cylinder; private var cyl2:Cylinder; private var cyl3:Cylinder; private var cyl4:Cylinder; private var cyl5:Cylinder; // Declare Cylinder holder private var myHolder:DisplayObject3D; private var vpX:Number = stage.stageWidth/2; private var vpY:Number = stage.stageHeight/2;

public function FiveShadersSphere() { super(0, 0, true, false);

// Initialize Papervision3D initPV3D();

(continued)

157 Part 1: Getting Started

(continued)

// Create your objects createObjects();

}

private function initPV3D():void {

// Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; // Set the background to black opaqueBackground = 0xffffff;

// Place your camera camera.x = 0; camera.y = 400; camera.z = -100; }

private function createObjects():void {

// Create your point light source var light:PointLight3D = new PointLight3D(true); light.x = 100; light.y = 1000; light.z = -400;

// Create your flat shade material and apply it to your cylinder var flatShadedMaterial:MaterialObject3D = new FlatShadeMaterial(light, 0x7799FF, 0x000522); cyl1 = new Cylinder(flatShadedMaterial, 50, 10, 20, 10); cyl1.x = -240;

// Create your gouraud material and apply it to your cylinder var gouraudMaterial:MaterialObject3D = new GouraudMaterial(light, 0x7799FF, 0x000522); cyl2 = new Cylinder(gouraudMaterial, 50, 10, 20, 10); cyl2.x = -120;

// Create your cell material and apply it to your cylinder var cellMaterial:MaterialObject3D = new CellMaterial(light, 0x7799FF, 0x000522, 5); cyl3 = new Cylinder(cellMaterial, 50, 10, 20,10); cyl3.x =0;

// Create your phong material and apply it to your cylinder var phongMaterial:MaterialObject3D = new PhongMaterial(light, 0x7799FF, 0x000522, 150); cyl4 = new Cylinder(phongMaterial, 50, 10, 20, 10); cyl4.x = 120;

158 Chapter 4: Adding Materials

// Add an image for your environment map var img:Loader = new Loader(); img.load(new URLRequest(“assets/envmap15.jpg”)); img.contentLoaderInfo.addEventListener(Event.COMPLETE, initenv);

// Create your environment material and apply it to your cylinder function initenv(event:Event):void{ var envMap:BitmapData = event.target.content.bitmapData; var environment:EnvMapMaterial = new EnvMapMaterial(light, envMap, envMap, 0x555555); cyl5 = new Cylinder(environment,50,10,20,10); cyl5.x = 240; myHolder.addChild(cyl5);}

// Create a holder container for your cylinders myHolder = new DisplayObject3D(); myHolder.addChild(cyl1); myHolder.addChild(cyl2); myHolder.addChild(cyl3); myHolder.addChild(cyl4);

// Add your Cylinders and light to the scene scene.addChild(myHolder); scene.addChild(light);

// Start rendering the scene startRendering(); }

override protected function onRenderTick(event:Event=null):void {

// Rotate your cylinders cyl1.yaw(-2); cyl2.yaw(-2); cyl3.yaw(-2); cyl4.yaw(-2); cyl5.yaw(-2);

cyl1.roll(-2); cyl2.roll(-2); cyl3.roll(-2); cyl4.roll(-2); cyl5.roll(-2);

// Rotate Holder Container var angleX:Number = (mouseY - vpY) * .5; var angleY:Number = (mouseX - vpX) * .5;

myHolder.rotationZ = -angleY; myHolder.rotationX = -angleX; (continued)

159 Part 1: Getting Started

(continued)

// Call the BasicView renderer super.onRenderTick(event); }

} }

As cool as Papervision3D is, it lacks an essential ingredient to having even a nominal lighting system: brightness. In the following section, you find out how to add brightness to Papervision3D.

Adding Brightness If you want a sunrise you must have brightness control. The PV3D point light source only has a position parameter, used primarily to calculate the angle of the triangle normals (as discussed earlier) – no brightness factor. However, adding brightness is easy to do; the main issue is where to put it. Brightness can be viewed as a physical parameter like mass, velocity, and acceleration. The problem is that where the quantities go is not always intuitive. Some programs create duplicate physical primitives (like the WOW physics engine) and you end up double processing your primitive to get physics to work. That ’ s a bad idea, resulting in a major processor drain. To be efficient, the physical parameters need to be incorporated directly into Papervision3D. The other option is to put them on each and every primitive, but then you ’ ve got a ton of coding to do and you end up with some that have it and some that don ’ t. Updating DisplayObject3D Every object in Papervision3D is a DisplayObject3D, so the best place to put physical parameters is in the DisplayObject3D class. That way every object in Papervision3D possesses the ability to have physical parameters, avoiding double processing, and special objects with and without physics. Making this decision really is the hardest part of all. All you have to do now is open up the DisplayObject3D class (found in the org/papervision3d/ folder) and add brightness.

First create a brightness property and place it with the other properties. Set brightness equal to 1.

private var _brightness:Number=1;

Next place the following getter/setter method with the other getter setters.

public function get brightness():Number { return this._brightness; }

public function set brightness(value:Number):void { this._brightness = value; }

Because your light is a DisplayObject3D , it immediately inherits brightness.

160 Chapter 4: Adding Materials

You can use this method to add any physical property that you want to your DisplayObject3D class, such as mass, velocity, or acceleration. Updating Shaders All you have to do now is update all the shaders with your new brightness parameter and you ’ re done. As discussed earlier, Papervision3D has five shaders: flat, cell, gouraud, phong, and environment.

Open up the shaders, which are located in the org/papervision3d/materials/shadermaterials, and make the following modifications:

Flat Open up the FlatShadeMaterial and multiply the light brightness by the zd factor.

zAngle = zd*0xff*light.brightness;

Gouraud Open up the GouraudShadeMaterial and multiply the light brightness by the p0 , p1 , and p2 factors.

p0 *= 127*light.brightness; p1 *= 127*light.brightness; p2 *= 127*light.brightness;

Cell, Phong, Environment (three -in-one) Open up the EnvMapMaterial class and make the following changes to the p0 , q0 , p1 , q1 , p2 , and q2 equations. It may seem like a lot, but all you ’ re really doing is multiplying the dot product function by (2 - light.brightness) . The changes are shown in bold below.

p0 = lightmapHalfwidth*(face3D.v0.normal.x * lightMatrix.n11 + face3D.v0.normal.y * lightMatrix.n12 + face3D.v0.normal.z * lightMatrix.n13)*(2-light.brightness) +lightmapHalfwidth; q0 = lightmapHalfheight*(face3D.v0.normal.x * lightMatrix.n21 + face3D.v0.normal.y * lightMatrix.n22 + face3D.v0.normal.z * lightMatrix.n23)*(2-light.brightness)+ lightma pHalfheight; p1 = lightmapHalfwidth*(face3D.v1.normal.x * lightMatrix.n11 + face3D.v1.normal.y * lightMatrix.n12 + face3D.v1.normal.z * lightMatrix.n13)*(2-light.brightness)+ lightm apHalfwidth; q1 = lightmapHalfheight*(face3D.v1.normal.x * lightMatrix.n21 + face3D.v1.normal.y * lightMatrix.n22 + face3D.v1.normal.z * lightMatrix.n23)*(2-light.brightness)+ lightma pHalfheight; p2 = lightmapHalfwidth*(face3D.v2.normal.x * lightMatrix.n11 + face3D.v2.normal.y * lightMatrix.n12 + face3D.v2.normal.z * lightMatrix.n13)*(2-light.brightness)+ lightm apHalfwidth; q2 = lightmapHalfheight*(face3D.v2.normal.x * lightMatrix.n21 + face3D.v2.normal.y * lightMatrix.n22 + face3D.v2.normal.z * lightMatrix.n23)*(2-light.brightness) +lightmapHalfheight;

That ’ s it. Just run your shaders like you normally do, adjusting your brightness in your animation loop and Ta da . . . instant sunrise!

161 Part 1: Getting Started

Changing the brightness of a shaded object is now as easy as just setting its light brightness property, as follows:

oscbright++; light.brightness=Math.abs(Math.sin(oscbright/20));

As oscbright increments the sine function, the brightness of your light source oscillates. The results are shown in Figure 4.14.

Figure 4-14

To see the entire code, download it from the ActionScript Project folder under Chapter 4, FiveShaderBright.as .

Finally, keep your brightness value between 0 and 1, or your program won ’ t perform correctly. In the book ’ s downloadable source, the shader material classes that were changed have a Brightness name added to the end of them. You don ’ t have to do this. It ’ s a precautionary measure. If you mess up, you can always go back to the original class. In the next chapter, you extend this concept and add brightness to a CS4 Blender model.

Bump Map No discussion about materials would be complete without Bump Maps. They ’ re about the coolest thing in Papervision3D, but require much CPU energy to function – and that ’ s a bummer. Bump mapping is essentially an optical illusion; a technique where each pixel has a heightmap applied to it, giving the appearance of a detailed surface representation without actually creating that complex surface using vertices.

In this section, you put a bump map inside a panorama. Sounds like a lot to do. But don ’ t freak. It ’ s just a combination of a bump map plus a pano. And a pano is just an image on a sphere (that you sit inside of). Start by creating a bump map. Creating a Bump Map You can find the shader class for creating a bump map in the org/papervision3d/materials/shaders folder. Two good shaders to choose from are the PhongShader or EnvMapShader . For this demo you use EnvMap, but the procedure is similar for Phong, the exception being that you ’ re dealing with colors.

The constructor function for the EnvMapShader class takes six input parameters:

162 Chapter 4: Adding Materials

❑ Light — a light source, primarily used for position and angle information. ❑ Envmap & backenvmap — two textures (typically the same) environment maps, front and back. ❑ AmbientColor — shadow color for when your object rotates out of the light source. ❑ BumpMap — uses a convolution filter to create a height map effect to augment your object ’ s pixels, giving your object an enhanced “ indented or bumpy ” surface look. ❑ SpecularMap — a specular map.

You ’ ve already encountered a number of these parameters. The object ’ s signature is as follows:

EnvMapShader(light, envmap, backenvmap, ambientColor, bumpMap, specularMap)

The code snippet for creating a bump map is:

//Create Assets for EnvMap and Bump Map var bumpmap:BitmapAsset = new graphic() as BitmapAsset; var envmap:BitmapAsset = new envmap() as BitmapAsset; var bumpmapc:BitmapData = bumpmap.bitmapData.clone();

//Add Blur to bump map clone bumpmapc.applyFilter(bumpmapc, bumpmapc.rect, new Point(), new BlurFilter(3,3,3));

//Create shade material for the bump Sphere var envShaded:EnvMapShader = new EnvMapShader(pointLight, envmap.bitmapData, envmap.bitmapData,0,bumpmapc); var shadedMaterial:ShadedMaterial = new ShadedMaterial(new BitmapMaterial(bumpmap. bitmapData),envShaded);

//Instantiate your sphere and place your shaded Material on it. bumpSphere = new Sphere(shadedMaterial, 400, 16,16); scene.addChild(bumpSphere);

The bump map is essentially created from two materials; a surface material (used twice, top and bottom) and a bump map. The bump map is enhanced by using a blur filter, which though computationally expensive, really adds to the visual experience. (Try commenting out the blur filter and you ’ ll definitely see the difference). The environment map and bump map are combined using the ShadeMaterial class. And finally, this new ShadeMaterial is placed on your sphere – wow that ’ s a bunch to do to get a bump map. It ’ s great for small experiments, but not practical for large projects as it ’ s so computationally intensive.

You ’ re now going to do something a little more interesting. You place a bump map inside of a panorama. Putting Your Bump Map in a Panorama Starting with good images makes all the difference when working with bump maps and panoramas.

Creating great looking spherical panorama images is beyond the scope of this book (see Figure 4.15), but is treated extensively on the book ’ s website and blog.

163 Part 1: Getting Started

Figure 4-15

Putting a bump map inside of a panorama is an easy four - step process:

1. Create your bump map using the code shown above. 2. Create a Panorama using the following code: //Create a Movie Material to place on your pano Sphere sphereMat = new MovieMaterial(movieAsset, false); sphereMat.opposite = true; sphereMat.animated = true;

//Smooth - heavy on the processor - but adds to the luster sphereMat.smooth = true;

//Create the panosphere. panoSphere = new Sphere(sphereMat, 25000, 30,30); scene.addChild(panoSphere);

Using this code you essentially create a sphere at coordinates (0,0 ) and place a movie (swf) on it. A Flash movie is used in this case because it gives you the ability to include animation in your pano and control the animation with ActionScript. Otherwise, you could ’ ve used a bitmap image. 3. Position your bumpSphere and camera and create a rotation function which lets you rotate your camera and bumpSphere symmetrically. This takes a few tries with different parameter adjustments. Your pano actually remains stationary as your bumpSphere and camera rotate. The rotation code for the bumpShere is shown below and involves a combination of sines and cosines. private function rotateXY(ball:Sphere, angleX:Number, angleY:Number):void { var cosX:Number = Math.cos(angleX*Math.PI/180); var sinX:Number = Math.sin(angleX*Math.PI/180);

var cosY:Number = Math.cos(angleY*Math.PI/180); var sinY:Number = Math.sin(angleY*Math.PI/180);

var myX:Number = 1000 * sinY*cosX; var myZ:Number = 1000 * cosY*cosX-1000 ;

164 Chapter 4: Adding Materials

var myY:Number = 1000 * sinX;

ball.x = myX; ball.z = myZ; ball.y = -myY; }}}

4. Use mouse listeners to throttle your application as you rotate around your environment.

Putting it all together you get the eye candy results shown in Figure 4.16. It ’ s more than just eye candy though. It ’ s an adventure that stimulates the optical cortex in a way that ’ s somewhat transcendental.

Figure 4-16

The complete code is listed here:

package { //Import statements import flash.display.BitmapData; import flash.events.Event; import flash.events.MouseEvent; import flash.filters.BlurFilter; import flash.geom.Point;

import mx.core.BitmapAsset; import mx.core.MovieClipAsset;

import org.papervision3d.cameras.CameraType; import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.*; import org.papervision3d.materials.shaders.EnvMapShader; import org.papervision3d.materials.shaders.ShadedMaterial; (continued)

165 Part 1: Getting Started

(continued) import org.papervision3d.materials.utils.*; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView;

//Bump Pano Class public class PanoBumpDemo extends BasicView { //Embed image resources [Embed(source=”assets/library2.swf”)] private var panoAsset:Class; [Embed(source=”image/LibraryPano.jpg”)] public static var graphic:Class; [Embed(source=”image/env2.png”)] public static var envmap:Class; //Declare point light and Bump Sphere public var pointLight:PointLight3D = new PointLight3D(); public var bumpSphere:Sphere; //Declare pano Sphere private var panoSphere:Sphere; private var sphereMat:MovieMaterial; //Set mouse condition private var myMouseDown:Boolean=false;

//Pano Bump Class public function PanoBumpDemo() { super(1, 1, true, false, CameraType.FREE); init3D(); startRendering(); opaqueBackground = 0; } //Initiate scene protected function init3D():void { //Create Assets for EnvMap and Bump Map var bumpmap:BitmapAsset = new graphic() as BitmapAsset; var envmap:BitmapAsset = new envmap() as BitmapAsset; var bumpmapc:BitmapData = bumpmap.bitmapData.clone(); //Add Blur to bump map clone bumpmapc.applyFilter(bumpmapc, bumpmapc.rect, new Point(), new BlurFilter(3,3,3));

//Create shade material for the bump Sphere var envShaded:EnvMapShader = new EnvMapShader(pointLight, envmap .bitmapData, envmap.bitmapData,0,bumpmapc); var shadedMaterial:ShadedMaterial = new ShadedMaterial(new BitmapMaterial(bumpmap.bitmapData),envShaded);

bumpSphere = new Sphere(shadedMaterial, 400, 16,16); scene.addChild(bumpSphere);

166 Chapter 4: Adding Materials

//Create the pano material var movieAsset:MovieClipAsset = new panoAsset(); sphereMat = new MovieMaterial(movieAsset, false); sphereMat.opposite = true; sphereMat.animated = true;

//Smooth - heavy on the processor - but adds to the luster sphereMat.smooth = true;

//Create the panosphere. panoSphere = new Sphere(sphereMat, 25000, 30,30);

scene.addChild(panoSphere); //Set up your camera camera.x = camera.y = 0; camera.z =-1000; camera.focus = 300; camera.zoom = 2;

//Add your mouse listeners

addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); addEventListener(MouseEvent.MOUSE_UP, onMouseUp); addEventListener(MouseEvent.ROLL_OVER, onMouseUp);

//Mouse controls function onMouseDown() : void { myMouseDown = true;

}; function onMouseUp() : void { myMouseDown = false;

};

}

//Animation Loop override protected function onRenderTick(event:Event=null):void { if (myMouseDown) { camera.rotationY += (mouseX - stage.width / 2) / 50; camera.rotationX += (mouseY - stage.height / 2) / 50; rotateXY(bumpSphere, camera.rotationX, camera.rotationY); } super.onRenderTick(event); }

(continued)

167 Part 1: Getting Started

(continued)

//Rotation equation for the Bump Mapped Sphere private function rotateXY(ball:Sphere, angleX:Number, angleY:Number):void { var cosX:Number = Math.cos(angleX*Math.PI/180); var sinX:Number = Math.sin(angleX*Math.PI/180);

var cosY:Number = Math.cos(angleY*Math.PI/180); var sinY:Number = Math.sin(angleY*Math.PI/180);

var myX:Number = 1000 * sinY*cosX; var myZ:Number = 1000 * cosY*cosX-1000 ; var myY:Number = 1000 * sinX;

ball.x = myX; ball.z = myZ; ball.y = -myY; }}} Using Fog and Composite Materials Both Fog and composite materials (like shaders) are processor intensive items and therefore we don ’ t address them in great detail here. However, we discuss them further in the next chapter, in which you find out about Pixel Bender.

Fog requires multiple layers of colors. The way the Fog Filter works (in Papervision3D) is by generating simple colored fill layers during the render process. Unfortunately this bogs down your CPU as it tries to treat enough layers to make your foggy scene look realistic. These layers are added at slightly increasing alpha transparencies over the range of depth of your current view. To make things worse, most practical applications are difficult to execute in Papervision3D using its built - in FogFilter.

The use of composite materials results in a similar situation as the FogFilter. Composite materials are combinations of materials, such as a Wireframe and a ColorMaterial :

//Instantiate your ColorMaterial and WireframeMaterial wireMaterial = new WireframeMaterial(0x676767); colorMaterial = new ColorMaterial(0x434343);

//Instantiate your CompositeMaterial as myCompMaterial myCompMaterial = new CompositeMaterial();

//Add your Composite Material using the addMaterial method myComposite.addMaterial(colorMaterial); //Add the Wireframe Material using the addMaterial method myComposite.addMaterial(wireMaterial);

//Add your Composite Material to your 3D object mySphere = new Sphere(composite, 200, 8, 8);

168 Chapter 4: Adding Materials

From the code above, you can see that composite materials work by adding one material to another in a composite material. As a result, you ’ re double processing both materials as you instantiate your primitive. Double processing is something you generally want to avoid whenever possible.

Summary Congratulations, you ’ ve turned the corner from 3D - techno babble to building “ real world ” applications. Although some of the material presented in this chapter may seem dry, it ’ s essential to know how to improve your application ’ s performance. Digging into the next level of Papervision3D and adding just a few lines of code can really set your application apart from the rest. In the next chapter, you crank this up a notch as you bring in 3D models from 3DSMax, and Blender.

169

Part II: Working with Models and Data

Chapter 5: Creating 3D Models

Chapter 6: Working with Particle Systems

Chapter 7: Geocoding, XML, and Databases

Chapter 8: Gliding on AIR Part II: Working with Models and Data

In Part II, you learn how to create your own 3D models and import them into PV3D. You learn how to create particle systems, bring Google Maps into PV3D and CS4, and build data - driven applications in Flash, Flex, and Air.

172 Creating 3D Models

The most incredible news about Flash is its ability to import 3D models from programs like Blender and 3DSMax. With the advent of programs like Papervision3D, a world of ubiquitous 3D web content has appeared on the horizon. But with any new world, there are new challenges: ActionScript driven animation, 3D modeling, texture preparation, and more. Flash3D is a very broad area and your journey begins here with modeling.

In this chapter, you cover the basics of modeling and how to import those models into Papervision3D. You examine commonly used modeling packages such as Blender, 3DSMax, and SketchUp. You ’ ll develop a technique for creating Quake 2 models, and create simple parsers for Blender and 3DSMax. Finally, you examine the powerful Collada format, and learn how to enhance your models by incorporating Pixel Bender.

Modeling for Papervision3D Remember you ’ re modeling for the web. You don ’ t have the luxury of a large supportive ; everything bounces off the Flash player. Keeping your polygon (triangle) count low is essential to great performance. This is achieved in part by low polygon modeling. You might be asking at this point: how low? You ’ ll be rendering entire scenes with less than 2,000 polygons.

Now, before that statement sends you packing, you ’ re going to learn some tricks to enhance this number significantly; especially when you get to portal engines and Pixel Bender. If you ’ re coming from a Flash background, you ’ re probably cheering right now: bringing in 3D models is a leap for Flash – even low polygon ones. It ’ s interesting how different things look depending on where you sit though, because this is a catastrophe from the standpoint of those who come from a 3D game development background. Part II: Working with Models and Data

But if you ’ re a cell phone developer, then you ’ re probably thinking: “ bring it on ” . That ’ s because you ’ re looking for a fluid multi - media interaction with your user. In other words, no stops, no downloads, no slow downs, and high visuals. Furthermore, as technology rapidly changes, applications continue to migrate to the web. So even if you ’ re used to creating 120,000 - polygon models, the techniques you learn here will help you become more efficient.

Bringing Down the Count Most modeling software packages have some way to keep track of the number of polygons in your model. 3DSMax, for example, by pressing the 7 key, lets you keep track of polygon and vertex number as you model. Keeping this on as you model is a great help; you ’ ll be surprised how applying certain modifiers can bloat your triangle count (sometimes by an order of magnitude). By using this simple tool you get a feel for what keeps triangle count down. In addition, here are some tips that can help you create low polygon models:

❑ Reduce face count by simplifying geometry such as windows and doors. For example, it ’ s more important to show position and proportion than the trim. Use images of windows and doors as opposed to trying to model them exactly. Whenever possible, use entire building photos to model building sides. ❑ Use simplified structures. Rather than trying to create a grainy surface (or wood lattices) based on vertex manipulation (or multi - primitive placement), just use a texture that has a grainy look (or PNG lattice). This can go a long way, so use it whenever you can. Use PNG (for transparency) or JPG images at 72ppi. ❑ Keep curved surfaces to a minimal number of faces. Reduce the number of sides of a polygon. Many times you can get away with using a 3 - sided polygon to draw a column as opposed to a 12 - sided polygon. The savings are huge. ❑ Avoid hidden structures. If you can ’ t see it, don ’ t draw it. Many models have internal structures that are revealed when opening a door or hatch (like the inside of a tank). Separate those structures using a portal engine and don ’ t draw them until you need them. For example, why draw the bottom of a house or its internal walls if you can ’ t see it. Delete them. ❑ Be aware of levels of detail. Focus on where your user will be looking. If your user ’ s eye isn ’ t drawn to a certain area of the screen, approximate detail with an image. Or if an object is always going to be viewed from far away, don ’ t detail it. ❑ Use single faces when possible. Many times a single face (or combination of single faces) can be used to approximate structures. A tree, for example, can be created from two or three single faces (as was shown in Chapter 3), as opposed to drawing multiple polygons to represent the branches and leaves. ❑ Turn repeated geometry into instantiated components, in which case your computer only has to remember a single definition. And optimize the component definition itself (such as removing backsides that aren ’ t seen). ❑ Paint small or insignificant structures with colors as opposed to using a texture. ❑ Use Picasa, Gimp, or Photoshop to optimize your images. Bringing down image size and file size will increase your model ’ s performance on the web. There ’ s a tradeoff between image quality and web performance. Also, use mip - mapping (discussed in the next chapter) whenever possible.

174 Chapter 5: Creating 3D Models

❑ Avoid lines. Drawing lines in Papervision3D is expensive; make sure that you have the lines turned off. And avoid using composite components and transparency in PV3D; it taxes your processor. ❑ When shading, use Pixel Bender or the Fast Shading techniques developed in this book.

Also, CS4 lets you use large polygon faces without getting PV3D image sag. This greatly reduces the number of polygons needed to model buildings.

Gauging Performance in PV3D In addition to watching your polygon count, Papervision3D has a built - in mechanism for judging its ability to process a scene. It ’ s called StatsView, and includes the following performance indicators:

❑ FPS — Frames Per Second ❑ Tri - Triangles, Sha - Shaded Triangles ❑ Lin - Lines ❑ Par - Particles ❑ Cob - Culled Objects ❑ CTr - Culled Triangles ❑ CPa - Culled Particles ❑ Fob - Filtered Objects ❑ Mem - Memory Used

Depending on what you ’ re working on, any number of these parameters can be useful. At a minimum you want to track frames - per - second and memory usage.

Using the BasicView class to implement StatsView is a simple two - step process:

1. Import the StatsView class, import org.papervision3d.view.stats.StatsView; 2. Declare and add StatsView to your scene: addChild(new StatsView(renderer));

The result of implementing the code is a state window in the upper left corner of your screen (see Figure 5.1), which provides all the performance parameters listed above.

Figure 5-1

But how does Papervision3D come up with these parameters?

175 Part II: Working with Models and Data

How it Works You can actually implement this to optimize your scene programmatically by making changes to your scene and models based upon the output of these parameters. To accomplish this task you need to understand how they ’ re created. Fps StatsView is found in the org/papervision3d/view/stats folder and extends the AbstractStatsView class. In this super class, there ’ s a very simple method called onFrame , which enables you to calculate the actual frames per second. It ’ s shown here:

protected function onFrame(event:Event): void { currentFrameTime = getTimer(); fps = 1000/(currentFrameTime-lastFrameTime); lastFrameTime = currentFrameTime; }

The onFrame method is based on the getTimer() method, which returns the number of milliseconds that have elapsed since Flash Player was initialized. Subtracting the getTimer value between the ENTER_FRAME events allows you to calculate fps. Fps is a measure of how well you ’ re mathematically processing your vertices. Later in the book you find out how to use this number to throttle your scene. Memory Usage Memory usage is not a Papervision3D entity. It ’ s a Flash player System method and is implemented (in MB to two decimal places) using this code:

System.totalMemory/1024/1024).toFixed(2)

Memory usage is a big item, it ’ s what makes your machine run hot, and as you add and remove items from your scene you want to keep your eye on this number. All the Others In the RenderStatistics class (found in the org/papervision3d/core/render/data folder) StatsView picks from a host of properties to create its output list. You can easily create your own StatsView by modifying this list. There ’ s nothing magical about StatsView and understanding how it works allows you to use it to optimize your scene programmatically. Now that you understand a little about optimizing your models, it ’ s time to learn how to build them.

Choosing a Modeling Application Before you get ready to spend a million bucks on the newest and coolest 3D modeling application, you should keep in mind that low polygon modeling really levels the playing field when it comes to which application to use. As mentioned earlier in this book, “ a vertex is a vertex, ” which means that it doesn ’ t matter which application you use, as long as you can use it to create a low polygon model and export that model to PV3D.

176 Chapter 5: Creating 3D Models

Expanding on this idea, the purpose of modeling (especially in Papervision3D) is to position vertices in space, create a triangular face array, unwrap it, and place a texture on your object. Most modeling packages do that . . . so the next consideration, which is the most important, is being able to export your model into a form that PV3D can use.

Blender There ’ s some negative chatter out there in cyberspace about Blender. In reality, Blender is a great 3D modeling tool, and it ’ s free! Once you get over the hump of learning Blender, you ’ ll find that it ’ s fast, crashes less than other 3D modeling apps, runs on Windows or Mac, and has a large supportive user community. You can download Blender from http://www.blender.org .

Here are some important points to learning Blender:

❑ Saving and Opening Models — when saving or opening a Blender model, a Linux - like interface comes up that gives you access to your computer ’ s tree structure. It ’ s easy to use, but looks really strange at first. ❑ Using the Space Bar — by pressing the space bar (when the main stage is selected) a menu comes up that lets you add primitives for box modeling, and edit or transform your models (see Figure 5.2).

Figure 5-2

❑ Short Cuts — like many modeling apps, tons of keyboard short cuts are available that accelerate your ability to create models. Some of the essential ones are: e for extrude, s for shrink, r for rotate, b for select, and more . . . check out the book ’ s website for a full listing.

Also on the book ’ s blog and website are extensive video tutorials on using Blender. They ’ re designed to get you over the hump of learning Blender and into producing PV3D content. Once you ’ ve mastered the basics, you ’ ll find it easy to produce textured objects for PV3D or CS4 using Blender.

177 Part II: Working with Models and Data

3DSMax Besides being the industry standard, 3DSMax is one of the easiest 3D modeling packages to learn and use. If you ’ re only going to learn one modeling package (and can afford it) 3DSMax is the one. It ’ s entirely menu driven, has a host of modifiers, and a robust material editor, which works on a tree modification structure. There ’ s one problem though; it only works on Windows. If you only have a Mac, your next best option is Maya, which runs on both Mac and Windows and is similar to 3DSMax.

In addition, 3DSMax has a tree - like materials editor, which gives you the ability to cascade multiple effect (or filters) and create a high degree of realism. Like Blender, Max has a host of shortcut keys that can be used to accelerate model creation.

The book ’ s blog and website contain extensive video tutorials on learning 3DSMax. They ’ re designed to get you into the core of architectural modeling. You ’ ll find box modeling a dream in 3DSMax with many approaches designed to help you build that perfect model for PV3D. However, it ’ s important to do multiple saves of your work with 3DSMax as it has a tendency to crash. Also, make sure that you save different version names, since a crash may render your present saved version unusable.

Google SketchUp SketchUp modeling is a little different than 3DSMax and Blender. As opposed to modeling with vertices, edges, or faces, SketchUp just uses edges and faces. SketchUp is both fun and easy to learn with numerous tutorials available on the web to assist you in mastering it. And the coolest thing is that there are tons of free models and textures available on Google Warehouse found at http://sketchup .google.com/3dwarehouse/ .

SketchUp is a large “ free ” unharnessed resource that you can use in both PV3D and CS4. When it ’ s high quality, drawn by someone else, and free – that ’ s the best of all worlds. Typical of what you may find in Google Warehouse is the model of Westminster Abbey as shown in Figure 5.3.

Figure 5-3

178 Chapter 5: Creating 3D Models

SketchUp handles its textures differently than the uvmaps typically used in Blender and 3DSMax. It assigns individual bitmaps to objects. This approach is similar to how Second Life assigns its textures and sidesteps the need for complex uv modeling.

Swift3D Before PV3D, Swift3D was the only way of producing hiqh quality 3D Flash content for the web. It was used to create 3D swfs for use in Flash, but has upgraded to allow export to PV3D. You can import a 3DSMax model into Swift3D and export it as acceptable PV3D Collada files. If you ’ re having problems getting your 3DSMax exporters to work, going through Swift3D is a definite option. Swift3D also has a modeler based on triangle manipulation.

The major upgrade needed to make Swift3D a major league hitter in Flash3D is a material editor that lets you unwrap models, create exportable uv maps, and Collada animation.

Once you ’ ve created your models you need to bring them into Papervision3D using a parser. A parser extracts from a file the parameters required to render your model. Papervision3D has a bunch of them.

Papervision3D Importers PV3D has a host of parsers designed to import model data from a handful of modeling tools: 3DSMax, Maya, SketchUp, and Quake (MD2 format). They ’ re found in the org/papervision3d/objects/ parsers folder. At first, this list may seem a little overwhelming. You can figure out which one to use simply by understanding what each one is (see Figure 5.4).

Figure 5-4

Here ’ s a short description of the various parsers:

❑ Ase — lets you load and parse ASE format objects exported from 3DS Max. This format has no animation channel. ❑ Collada — lets you parse Collada scenes. This format has no animation channel. ❑ DAE — most advanced Collada parser; designed to parse COLLADA 1.4.1 files (Maya and Max settings in the documentation). This format includes an animation channel.

179 Part II: Working with Models and Data

❑ KMZ — lets you load and parse KMZ files; has no animation channel. ❑ Max3DS — a Max parser based on Away3D ’ s class; has no animation channel. ❑ MD2 — loads Quake 2 (MD2 file) with an animation channel. ❑ SketchUp — helper file that assists in bitmap loading for SketchupCollada . ❑ SketchupCollada — Collada class that lets you load and parse Collada scenes with no animation channel

The most commonly used parsers are: Collada, DAE, ASE, and MD2. Both the DAE and MD2 parsers have animation channels (ability to bring in animation data and use it in PV3D), where the rest do not.

In the following sections, you find out about the MD2 and Collada parsers.

What ’s MD2? The MD2 format was created for the Quake II engine and has since gained popularity due to its simple, easy - to - use format. But finding a model that works with Papervision3D ’ s MD2 importer can sometimes be a challenge.

The overall MD2 flow is shown in Figure 5.5.

Skin Header Frames Triangles UV Data Names

Vertices

Figure 5-5

In the diagram above you should recognize the familiar vertices, triangles, and UV data that ’ s required for all models to render. And as you ’ re putting textures on your models, you must also call your skin names. The procedure for putting MD2 models into PV3D is as follows:

1. Import the MD2 parser class. 2. Instantiate your MD2 texture as a BitmapFileMaterial. 3. Instantiate the MD2 object. 4. Load your MD2 model and its texture. 5. Add your MD2 object to your scene using addChild.

The documented code snippet that enables you to do that ’ s provided here:

180 Chapter 5: Creating 3D Models

private function initMD2():void {

// Instantiate your skin as a BitmapFileMaterial var myMD2Skin:BitmapFileMaterial = new BitmapFileMaterial(“assets/mySkin.jpg”, true);

// Instantiate an MD2 object (use true for animation, false otherwise) var myMD2Object:MD2 = new MD2(true);

// Give your object a name so you can control it in the display list myMD2Object.name = “md2Object1”;

//Load your MD2 model and its skin myMD2Object.load(‘assets/myModel.md2’, mySkin, framerate, scale);

// Add your MD2 object to your scene using addChild scene.addChild(myMD2Object); }

The preceding code snippet really highlights the beauty of PV3D. It uses the same procedure that you ’ ve seen many times before: after importing your MD2 class, you declare a skin, an object, and add it to your scene using the addChild method.

Using MD2 in PV3D was seen, at first, as the best of all worlds. MD2 models are low polygon and work well in PV3D. However, finding models that work in PV3D, or creating your own, is a specialized process. It could even be considered a lost or dying art. You can still find many great websites out there though, which use MD2.

A number of model packs can be found at http://www.md2.sitters-electronics.nl/index.html and http://telias.free.fr/models_md2_menu.html. But keep in mind not all of them work and in the long run you need to be able to make your own models and modify them to be successful.

Model Pack To help you get started, included in the book ’ s chapter code is a small MD2 model pack. These models are released freely under the GNU license (see Figure 5.6).

Figure 5-6

181 Part II: Working with Models and Data

The code required to import these models into PV3D is provided here. The code is fully documented and closely follows the code snippet above. When you get to the chapter on gaming, you ’ ll do more with MD2 models. But for now they just spin in a circle.

package {

import flash.events.Event; import org.papervision3d.materials.BitmapFileMaterial; import org.papervision3d.objects.parsers.MD2; import org.papervision3d.view.BasicView;

public class ModelPack extends BasicView {

// Create your MD2 types (car, airplane, helicopter, and van). private var myCar:MD2 = new MD2(); private var myAir:MD2 = new MD2(); private var myCop:MD2 = new MD2(); private var myVan:MD2 = new MD2(); private var osc:Number=0;

public function ModelPack() {

super(400, 400, true); opaqueBackground=”0xffffff”;

initPV3D(); buildModels(); }

// Initiate PV3D, with BasicView this is easy private function initPV3D():void {

// Add ENTER_FRAME listener addEventListener(Event.ENTER_FRAME, render); }

// Build your models private function buildModels():void {

// Load skin/texture of your model from an external Bitmap var myCarSkin:BitmapFileMaterial = new BitmapFileMaterial(‘modelpack3/car/car .png’, true); var myAirSkin:BitmapFileMaterial = new BitmapFileMaterial(‘modelpack3/ airplane/air.jpg’, true); var myCopSkin:BitmapFileMaterial = new BitmapFileMaterial(‘modelpack3/heli/ helicopter1.png’, true); var myVanSkin:BitmapFileMaterial = new BitmapFileMaterial(‘modelpack3/van/van .png’, true); var myBrdSkin:BitmapFileMaterial = new BitmapFileMaterial(‘modelpack3/bird/ bird_final.jpg’, true);

// Setting model name, with this you can use methods as, getChildByName in scene object.

182 Chapter 5: Creating 3D Models

myCar.name = ‘myCar’; myAir.name = ‘myAir’; myCop.name = ‘myCop’; myVan.name = ‘myVan’;

// The load method includes (url string, material, framerate, scale)

myCar.load(‘modelpack3/car/car.md2’, myCarSkin, 10, 1); myAir.load(‘modelpack3/airplane/body.md2’, myAirSkin, 10, 2); myCop.load(‘modelpack3/heli/helicopter.md2’, myCopSkin, 10, 2); myVan.load(‘modelpack3/van/van.md2’, myVanSkin, 10, .8);

// Setup x, y, and z initial rotation and position//Position Car myCar.rotationX = -90; myCar.x=-540; //Position AirPlane myAir.rotationX = -90; myAir.x=-200; //Position Van myVan.rotationX = -90; myVan.x=200; //Position Helicopter myCop.rotationX = -90; myCop.x=600;

// Add models to your scene scene.addChild(myCar); scene.addChild(myAir); scene.addChild(myCop); scene.addChild(myVan);

// Start rendering. startRendering(); }

private function render(event:Event):void { // Add rotation to the models

myCar.roll(1); myAir.roll(1); myVan.roll(1); myCop.roll(1);

super.onRenderTick(event); }}}

In addition to the preceding code, on the book ’ s website and blog is a MD2 model viewer with downloader (see Figure 5.7). From this application you can view various MD2 models and download them. It ’ s worth mentioning some of the highlights of that program here since it solves the basic problem of handling multiple models in PV3D.

183 Part II: Working with Models and Data

Figure 5-7

The model downloader that ’ s shown has a populated data grid on the right, which allows you to click on, view, and download MD2 models. The trick to handling multiple models is to treat them as particles. The Big Trick to Handling Multiple Models In the application shown above, you ’ re switching from model to model seamlessly. The big trick to handling multiple models in PV3D is to treat your models as a particle system. Each model has its own display object, which is loaded into an array and is annihilated or created when needed as shown here:

for(var j:uint = 0; j < markersCount; j++) { var item:MD2 = new MD2(); particles_ary.push(item); }

In the code snippet above all you do is create a unique MD2 instance for each model by iterating over them using a for loop. If you don ’ t do this your models become distorted as you try to populate the same container with each one of them. The MD2 containers are then pushed into an array called particles_ary , which is similar to what you would do with a particle system.

As you move forward in the book you learn more about optimization. Here you discover how to remove the object from the stage, but its listener is still attached. To truly make it available for Flash trash pick up, you need to remove its event listener as well . . . but more on that in Chapter 6.

movieParent.removeChild(particles_ary[prevIndex]); particles_ary[prevIndex].load(myDataGrid.selectedItem.address, mySkin, 15, 1); movieParent.addChild(particles_ary[prevIndex])

The models are indexed by the data grid and can easily be removed and added to the stage using the removeChild and addChild methods. We cover how to treat applications as a particle system in great detail in Chapter 6. To see the entire code for the model viewer, check out the book ’ s chapter code section and website.

184 Chapter 5: Creating 3D Models

Using models from other sources can be problematic, especially if you can ’ t change them, aren ’ t sure what ’ s in the byte code, and can ’ t get copyright permission to use them. So next you find out how to make your own models in Blender and 3DSMax. And with a little practice, you ’ ll learn how to create great models quickly.

Creating MD2 Files (MilkShape to the Rescue) When it comes to working with or creating MD2 models, MilkShape really comes to your rescue. Initially designed for Half - Life, MilkShape is a low - polygon modeler, which now supports over 70 different file formats.

Creating MD2 models is a 4 step process as shown in Figure 5.8.

Texture and Prepare in Export to Create Model Export MilkShape Model Viewer

Figure 5-8

The steps are outlined as follows:

1. Create Your Model 2. Texture and Export Your Model 3. Prepare Your Model in MilkShape 4. Export to a Model Viewer

At the center of the process of creating a MD2 model is the use of MilkShape to create the MD2 (Quake 2) format from a 3DSMax (or Maya) import. MilkShape (and a MD2 Viewer) can be downloaded from

http://chumbalum.swissquake.ch/

MilkShape has many of the standard features of other 3D modelers:

❑ Select, move, rotate, scale ❑ Extrude, turn edge, subdivide ❑ Low - level editing with the vertex and face tool ❑ Primitives like sphere, box, cylinder, plane ❑ Numerous plug - ins

185 Part II: Working with Models and Data

The true power of MilkShape is in its ability to import and export such a wide variety of file formats (especially Quake formats). In the following discussion, you use it to convert a 3DSMax file into the MD2 format, but the same procedure works for Maya as well. The steps for creating a MD2 model for PV3D starting with 3DSMax (or Maya) are as follows:

1. Create your model in 3DSMax (or Maya). 2. Export it as a 3DS file (fdx file). 3. Import this file into MilkShape by choosing Autodesk 3DS . . . (or Maya). 4. Click the model tab, then joint button and insert a joint by clicking on a vertex. 5. Select all vertices (control a). 6. Go to the joints tab and click assign. 7. Check to make sure the animation is correct (single frame for no animation). 8. Export as an MD2 file (ignore the no md3.qc warning not loading into Quake). 9. Check it out using the MD2 model viewer found on the MilkShape site or the PV3D viewer provided on the book ’ s website.

An extensive video tutorial of the process can be found on the book ’ s blog and website.

Before you leave MD2 you must investigate how MD2 objects are animated in PV3D. Currently, animation in PV3D can be accomplished in two ways: by using scripting to animate, or parsed external data xml (or binary file). What follows is an introduction to how PV3D takes information from an external file and creates animation. You ’ ll be amazed at how easy it is!

Making Things Move Animation in MD2 essentially involves drawing frames one after another. To smooth out animation, interpolation between key frames is used. In PV3D, this is accomplished by interpolating between frames with respect to time. Doing this lets you automatically generate new frames (between the key frames) that represent your model ’ s position at any given time. Now here ’ s the trick: as with bone animation, you ’ re not really moving your model, but rather you ’ re moving its vertices. It ’ s all based upon the following equation:

Vnext=V0+ frameAlpha* (V1-V0)

where Vnext is the position of your next vertex in your keyframe - to - keyframe animation sequence. V0 is your first keyframe vertex. V1 is your second keyframe vertex, and frameAlpha is a value between 0 and 1 indicating position between startTime and endTime .

The simple equation above is implemented in PV3D ’ s MorphChannel3D class found in the animation channel folder. Its code snippet is shown below:

for( var i:int = 0; i < target.geometry.vertices.length; i++){ var u:Vertex3D = target.geometry.vertices[i]; var v:Vertex3D = curOutput[i]; var w:Vertex3D = nxtOutput ? nxtOutput[i]: curOutput[i];

186 Chapter 5: Creating 3D Models

//Implement Vnext = V0+ frameAlpha*(V1-V0)

u.x = v.x + frameAlpha * (w.x-v.x); u.y = v.y + frameAlpha * (w.y-v.y); u.z = v.z + frameAlpha * (w.z-v.z); } }

The u vector is your present frame (in between keyframe vertex positions), the v vector is your starting keyframe vertex and the w frame is your ending key frame vertex. The frameAlpha parameter is calculated in the AbstractChannel3D class (which is the super class of the MorphChannel3D class) using the following code snippet:

frameAlpha = (currentTime-currentKeyFrame.time) / frameDuration;

The equation goes from 0 to 1 as the currentTime – currentKeyFrame.time approaches frameDuration . If you ’ re a flashmatician you ’ re very familiar with keyframe animation. Luckily the animation channel code used for MD2 and Collada formats are the same in PV3D. In other words, what you ’ ve learned here about MD2 animation applies to Collada as well.

What’s Up with KMZ? KMZ is the Google Earth format and can be exported directly from the free version of Google SketchUp. It would make sense to provide an extensive discussion of this format right about now. However, there ’ s a major problem with KMZ in PV3D. The KMZ parser was submitted by a third party, and isn ’ t supported by the PV3D team. In other words, there ’ s no support for it and at the time of this writing, the parser has been broken for quite a while. The PV3D team advises developers that they use it at their own risk and states that the library is incomplete.

No need to worry though, you haven ’ t lost your ability to use SketchUp yet. SketchUp Pro exports Collada (dae) files, and when downloading a model from Google Warehouse, a Collada file is part of the standard download package. With this in mind, you now start your journey into the wonderful world of human - readable formats created in XML.

Hello Collada Started by Sony for their PlayStation 3, COLLADA stands for COLLAborative Design Activity, and was introduced with the intent of creating a standardized, portable 3D format. It has since grown into a consortium of companies and is managed by the non - profit technology consortium, the Khronos Group. Collada ’ s original goal of creating a useful tool for the widest possible audience continues to evolve.

All of the major modeling applications such as Blender, 3DSMax, Maya, SketchUp, Swift3D, and PV3D use Collada. In this section, you find out how to bring a simple Collada model into PV3D. It ’ s written in XML and is therefore human - readable. When you first see a Collada file however, you may think it ’ s not human - readable (it can be a little intimidating at first glance). In comparison to binary format, it ’ s a cake - walk when it comes to editing it.

187 Part II: Working with Models and Data

PV3D contains two Collada parsers: Collada and DAE. The DAE parser is actually an improved version of the Collada parser. One big difference is that the DAE parser supports animation, where the Collada parser does not. Five commonly used scenarios, found in PV3D when working with Collada files, are:

❑ Single Objects with UVMaps ❑ Multiple Objects with UVMaps ❑ Single Objects with Multiple Images ❑ Interactive Objects (or Vehicles) and Environments ❑ Animated Objects

You go through each one in the following section. You start with the Collada parser, as it ’ s the simpler of the two.

Single and Multiple Objects with Simple Images This is obviously the simplest of all the scenarios. In the example below of the moon orbiting the earth, 3DSMax is used. But the procedure presented is similar for other modeling software packages.

An object is created by a modeling program, such as 3DSMax, and a simple image is placed on that object. In 3DSMax this is done using the material editor. Each material and object is given a name so that it can be controlled by the PV3D program (after you ’ ve imported the Collada file, created by your modeling program).

In Figure 5.9, two spheres are created in 3DSMax and given the names earth and moon. Their image files are also given similar names (earth and moon).

Figure 5-9

Once the Collada file is exported from 3DSMax and brought into PV3D, the object can be easily manipulated by referring to the object names contained in the exported Collada file. These names can be viewed by opening the Collada file in an editor such as Dreamweaver or Flex and navigating to the geometries library and geometry tags:

< geometry id=”earth-mesh” name=”earth”> < geometry id=”moon-mesh” name=”moon” >

188 Chapter 5: Creating 3D Models

Manipulating objects in a Collada file using AS3 is key to creating dynamic game content. The planets are set in orbit using the following steps:

1. Import the Collada parser: import org.papervision3d.objects.parsers.Collada;

2. Declare a variable named collada and datatype it to Collada : private var collada:Collada;

3. Instantiate the Collada object and fill in the appropriate parameters: collada = new Collada(“MoonEarth.DAE”,null,.1);

In this case, the material parameter of the Collada parser is set to null , as the material is imported from the reference inside of the Collada file, found in the images library shown below:

< image id=”earth.jpg” name=”earth_jpg” > < init_from > ./assets/earth.jpg < /init_from > < /image > < image id=”moon.jpg” name=”moon_jpg” > < init_from > ./assets/moon.jpg < /init_from > < /image >

You actually need to open up the Collada file and make sure that you reference your images correctly. If you don ’ t, you ’ ll render a black object with purple lines, which is an indication that your image reference is wrong. You ’ re almost there; just correct your image reference! Make sure that you select relative paths when exporting your Collada file.

Once the images are loaded, the “ onColladaDone ” method is executed, which sets the planets into place and motion.

collada.addEventListener(FileLoadEvent.COLLADA_MATERIALS_DONE, onColladaDone);

The code snippets that accomplish this are explained in the next section. Setting Planets in Place The planets are accessed in the Collada files using the getChildByName method. The moon is removed from the Collada display object and placed in a pivot display object. The positions of the moon and earth are dictated by the position created in the 3DSMax modeling program.

//Get a reference to earth earth = collada.getChildByName( “earth” );

//Get a reference to moon moon = collada.getChildByName( “moon” );

(continued)

189 Part II: Working with Models and Data

(continued)

//Remove collada from the collada object. collada.removeChild(moon);

//Create a new pivot point for the moon. pivot = new DisplayObject3D();

//Add the moon to the pivot point pivot.addChild(moon);

//Add the pivot point to collada collada.addChild(pivot);

//Addcollada to the scene. scene.addChild(collada);

Setting the planets in motion is easily done using the PV3D yaw commands. Setting Planets in Motion The pivot , which contains the moon, is set into motion around the earth. Then the earth and moon are set into motion around their own pivot.

//Rotate the moon around the earth. pivot.yaw(1/3); //Rotate the earth around its axis. earth.yaw(1); //Rotate the moon around its axis. moon.yaw(1);

The results are the moon orbiting around the earth, and both planets rotating around their own axis. The full code can be downloaded from the book ’ s website. The results are shown in Figure 5.10.

Figure 5-10

Controlling objects inside of a Collada file is a technique that you use often, especially when you start moving game vehicles around your screen.

190 Chapter 5: Creating 3D Models

As opposed to programming your own pivots manually in PV3D, you could have set linkage in your 3D modeling code. You do that in the section on Interactive Objects. Next, you learn how to place multiple images on a single object.

Single Object with Multiple Images Multiple images on a single object is the common scenario that you find in SketchUp and Second Life. In 3DSMax, it ’ s called “ applying a multi/sub - object material modifier ” . When adding multiple materials in PV3D, you use the MaterialsList.addMaterial method. The MaterialsList acts as an array that holds all the related materials. The code snippet for working with multiple images is as follows:

1. Import the Collada parser: import org.papervision3d.objects.parsers.Collada;

2. Declare the variable collada and datatype it to Collada : private var collada:Collada;

3. Set up the MaterialsList Method. This method defines the material to be applied to the multi/ sub object and each image is tagged to the appropriate polygon set using the Collada file.

var materialsList:MaterialsList = new MaterialsList(); materialsList.addMaterial( bitmapFileMaterial1, “image1” ); materialsList.addMaterial( bitmapFileMaterial2, “image2” ); materialsList.addMaterial( bitmapFileMaterial3, “image3” );

4. Instantiate the Collada class and fill in the appropriate parameters, like so: collada = new Collada(“my.dae file”, materialsList, scale);

5. Add your collada to the scene using the addChild method. scene.addChild(collada);

To illustrate the code snippet above, you create a cubic panorama using a multi/sub - object material modifier found in 3DSMax. Creating a Cubic Panorama Using Multi/Sub -Objects Adding multiple images to a single polygon object is pretty easy. To create the cubic panorama that ’ s shown in Figure 5.11, first render a box primitive to the stage (you learn it here in Max, but the process is similar in other modeling applications).

191 Part II: Working with Models and Data

Figure 5-11

The trick to understanding Collada files is to create them in your favorite modeling application and examine them as you make changes. As each change is made in your modeling program, go back and see how your Collada file changed. Experiment, experiment, experiment . . . that ’ s how the experts do it! It ’ s also the best way to learn. Creating Your Pano Object Once you ’ ve created your pano box, you need to edit it. In Max, that means converting it to an editable poly. The modifier gives you access to the polygon level of your Cube. You go through the steps here, but extensive tutorials on working with 3DSMax are on the book ’ s website and blog.

1. After bringing your cube to the stage and converting it to an editable poly, center it so that your pivot point is in the middle of your cube (visually place the center of your cube at 0,0,0). 2. From your Editable Poly modifier select polygon and then select the six sides of your cube individually and set their Polygon Material IDs (found in the scrollable menu below the poly modifier). This procedure works for any box - modeled entity. After your Material IDs are set, you ’ re ready to add your images. 3. Hit the m - key and bring up your material editor. Place the six sides of your cubic panorama in six separate material spheres (see Figure 5.12).

Figure 5-12

192 Chapter 5: Creating 3D Models

4. Once the six images are on your material sphere, select the 7th sphere and click on its Standard button and when the Material/Map Browser comes up, select Mult/Sub - Object. An IDs menu then appears below your spheres. Drag the appropriate images to the appropriate IDs. 5. At this point you need to adjust your top and bottom images to align with your pano. Do this by using the UVWmap modifier and Shift+ to check your render. 6. To create a pano you need to be inside the cube, which means you need to flip your normal vectors (or once inside your cube you ’ ll see nothing). Do this by the Normal modifier and make sure that Flip Normals is checked. Your box should turn black since your image normals are facing away from you. 7. At the moment, your simple cube doesn ’ t have enough polygons to support your image. When you run it in PV3D, you get image sag (this is not an issue in CS4), so you need to tessellate your cube a few times (twice should work fine). Tessellation just subdivides your cube. You want to do it all at once or you get some weird triangulation. 8. Finally, export your cube as a Collada file selecting Relative Paths, Normals, and Triangulate. As you ’ re not doing an animation in this case, make sure that animation is turned off by setting it to false.

Now that you have your Collada pano, it ’ s time to put it into PV3D. Putting It into PV3D The code snippet for bringing the Collada pano into PV3D is given below. It follows the previous discussion on MaterialsList very closely. This example below doesn ’ t use BasicView, but it could have. As you get into more complex examples, you ’ ll need the code snippet (which was introduced in chapter 2) below:

public function PV3DCollada():void { //Set up your Viewport as introduced in Chapter 2. viewport = new Viewport3D(800, 600, false, true); addChild(viewport); //Set the Stage stage.align=StageAlign.TOP_LEFT; stage.scaleMode=StageScaleMode.SHOW_ALL;

//Instantiate your Camera, Renderer, and Scene3D scene = new Scene3D(); camera = new Camera3D(); camera.z=0; camera.y=0; renderer = new BasicRenderEngine();

//Set your BitmapFileMaterial make sure your reference is correct bitmapFileMaterial1 = new BitmapFileMaterial(“sky4/sky1/Side1.png”); bitmapFileMaterial2 = new BitmapFileMaterial(“sky4/sky1/Side2.png”); bitmapFileMaterial3 = new BitmapFileMaterial(“sky4/sky1/Side3.png”); bitmapFileMaterial4 = new BitmapFileMaterial(“sky4/sky1/Side4.png”); bitmapFileMaterial5 = new BitmapFileMaterial(“sky4/sky1/Side5.png”); bitmapFileMaterial6 = new BitmapFileMaterial(“sky4/sky1/Side6.png”); (continued)

193 Part II: Working with Models and Data

(continued)

//Set Double-Sided to true bitmapFileMaterial1.doubleSided = true; bitmapFileMaterial2.doubleSided = true; bitmapFileMaterial3.doubleSided = true; bitmapFileMaterial4.doubleSided = true; bitmapFileMaterial5.doubleSided = true; bitmapFileMaterial6.doubleSided = true;

//Instantiate and load your MaterialsList make sure that it references correctly your Collada images var materialsList:MaterialsList = new MaterialsList(); materialsList.addMaterial( bitmapFileMaterial1, “Side1_png” ); materialsList.addMaterial( bitmapFileMaterial2, “Side2_png” ); materialsList.addMaterial( bitmapFileMaterial3, “Side3_png” ); materialsList.addMaterial( bitmapFileMaterial4, “Side4_png” ); materialsList.addMaterial( bitmapFileMaterial5, “Side5_png” ); materialsList.addMaterial( bitmapFileMaterial6, “Side6_png” );

//Instantiate your collada object and add it to your scene collada = new Collada(“theFlipCubePano2.DAE”, materialsList, .1); scene.addChild(collada);

//Set up onEnterFrame event addEventListener(Event.ENTER_FRAME, onEnterFrame);

//Define your animation loop function onEnterFrame(e:Event):void { collada.rotationY += .2; renderer.renderScene(scene, camera, viewport); }}

You now move into the wonderful world of vehicles and environments. In addition, you ’ ll transition from using the Collada parser to using the DAE parser.

Interactive Objects (or Vehicles) and Environments In this section, you find out how to create a basic Flintstone “ Barney Rubble car ” as an example of an interactive object. The techniques you learn apply to creating any motion - controlled Collada object, such as a boat with a propeller, or an airplane with a propeller and flaps.

Building a car requires that you control its motion and wheels. And just as in the planets example, you need to name each of your car parts so you can control them; such as the wheels, car body, and steering mechanism. This provides you with the ability to reference the parts later by their respective names when adding the interactivity code (see Figure 5.13).

194 Chapter 5: Creating 3D Models

Figure 5-13

You create the car by assembling its parts in 3DSMax just as you would put a model together. The important thing is that you give each part a name so you can control them with ActionScript. In this particular example, the following naming conventions are used:

❑ CarBody - your “ Barney Log ” car body ❑ FRWheel - your front right wheel which links to your steerFR (disk brake) ❑ FLWheel - your front left wheel ❑ steerFR - your front right disc brake which acts as the linkage container for your FRWheel ❑ steerFL - your front left disc brake which acts as the linkage container for your FLWheel ❑ RRWheel - your rear right wheel ❑ RLWheel - your rear left wheel

After creating and naming all the elements of your “ Barney car ” , export your car as a Collada file.

When you look at a Collada file for the first time, you may be shocked. It looks somewhat horrific at first. But actually Collada files are very simple. They ’ re organized into libraries. Each library typically contains some important aspect of 3D, such as lights, cameras, geometry, animation, physics, and so on.

In the case of the “ Barney car ” above, you ’ re interested in examining the visual_scenes library. Collada files can be opened in Dreamweaver or Flex. When examining the visual_scenes library nodes you find all the elements created by the 3D exporter:

< node id=”CarBody-node” name=”CarBody” type=”NODE”> < node id=”RRWheel-node” name=”RRWheel” type=”NODE”> < node id=”RLWheel-node” name=”RLWheel” type=”NODE”> < node id=”steerFR-node” name=”steerFR” type=”NODE”> < node id=”FRWheel-node” name=”FRWheel” type=”NODE”> < node id=”steerFL-node” name=”steerFL” type=”NODE”> < node id=”FLWheel-node” name=”FLWheel” type=”NODE”>

195 Part II: Working with Models and Data

When importing the “ Barney car ” Collada file into PV3D, you use these names to control your car. Grabbing Names from Collada Your first task, after importing the Collada file into PV3D, is to grab these names from the Collada file so you can use them in your program:

// Steering disc for front wheels steerFR = collada.getChildByName( “ steerFR” , true ); steerFL = collada.getChildByName( “ steerFL” , true ); steerFR.rotationX=steerFL.rotationX=-90;

// Rotation of wheels wheelFR = collada.getChildByName( “ FRWheel” , true ); wheelFL = collada.getChildByName( “ FLWheel” , true ); wheelRR = collada.getChildByName( “ RRWheel” , true ); wheelRL = collada.getChildByName( “ RLWheel” , true );

These names are stored in appropriate variables and used to control your car. First, you investigate the steering mechanism. Creating a Steering Mechanism The key to creating a steering mechanism is linking . The steering mechanism is created in 3DSMax by linking the front wheels to their corresponding disc brakes. Since the tires are children of the disc brakes, when the disc brakes are rotated the tires rotate with them. And when the car is moved forward, all the tires rotate together in sequence (see Figure 5.14).

Figure 5-14

Putting this all together in the code below illustrates how the car ’ s wheels are maneuvered in PV3D. The steering code is surprisingly simple and consists of only two lines of code:

steerFR.rotationY = steer; steerFL.rotationY = steer;

196 Chapter 5: Creating 3D Models

As the steering parameter is changed, the rotationY value of the disc is changed, which then changes the angle of the wheel. Next, you learn how to make the wheels roll. Rolling Wheels and Moving the Car As the speed of the car is increased, the wheel yaw value is also increased, which makes the wheels turn:

var roll:Number = speed*.4; wheelFR.yaw( roll ); wheelFL.yaw( roll ); wheelRL.yaw( roll ); wheelRR.yaw( roll );

// Turn your car car.yaw( speed * steer / 500 ); // Move car forward car.moveLeft( speed );

Finally, the car itself is turned using the yaw command and moved forward (or backwards) using moveLeft , where the parameter in the move method is the distance to move in pixels. You may have been expecting moveForward to be used, and in other cases it is. It just depends on the geometry of how your car was created in 3DSMax, and in this case moveLeft actually took you forward.

Now, you examine the six possible move methods. Moving Around the Screen In the DisplayObject3D class there are actually six important move functions, which are used many times to navigate an object in 3D space. The six moves are:

❑ moveForward — translates the display object in the direction it ’ s facing, that is, its positive z - axis. ❑ moveBackward — translates the display object in the opposite direction it ’ s facing, that is, its negative z - axis. ❑ moveLeft — translates the display object laterally to the left of the direction it ’ s facing, that is, its positive x - axis. ❑ moveRight — translates the display object laterally to the right of the direction it ’ s facing, that is, its negative X axis. ❑ moveUp — translates an object upwards, with respect to the direction it is facing, that is, its positive Y axis. ❑ moveDown — translates an object downwards, with respect to the direction it ’ s facing, that is, its negative Y axis.

What ’ s important here is that using these commands allows your object to translate with respect to itself naturally (using its local coordinates), as opposed to its global (or Flash screen) coordinates, which makes sense. But how does it work?

197 Part II: Working with Models and Data

public function translate( distance:Number, axis:Number3D ):void { var vector:Number3D = axis.clone();

if( this ._transformDirty ) updateTransform();

Matrix3D.rotateAxis( transform, vector );

this .x += distance * vector.x; this .y += distance * vector.y; this .z += distance * vector.z; }

The code above just takes the direction you specify and multiplies it by the distance you specified. But the coordinates are your actual Flash screen coordinates. So the code is actually transforming between your display object local coordinate to your Flash screen coordinate system using the Matrix3D method.

Another good application of these functions would be to build a teleporter, like the ones found in Second Life . . . but more on that later. The remainder of the “ Barney Car ” code can be viewed in the Chapter 5 book code. The remaining part of the “ Barney Car ” code is keyboard navigation, which can be viewed in the Chapter 5 book code.

Animated Objects To animate or not to animate, that is the question . . . You have two options when it comes to animating objects in PV3D: (1) you can animate them in a 3D modeling software package and attempt to import that into PV3D – and many great websites do this, or (2) you can bring in non - animated 3D objects into PV3D and animate them using AS3.

To animate objects in Blender use IPO curves, which readily exports into a PV3D - readable Collada file (see Figure 5.15).

Figure 5-15

IPO stands for InterPOlation and is used to animate objects by creating keyframes as you move your object around the screen. The example in Figure 5.15 is just a simple spinning head and could easily have been animated using AS3. In this example however, it ’ s used to illustrate how the DAE parser is utilized for this purpose. Once the IPO curves are created in Blender then you export your animation as a

198 Chapter 5: Creating 3D Models

Collada file. Animating objects using IPO curves is beyond the scope of this book, but for a good place to learn more about using IPO curves, consult the Blender Noob to Pro manual on Wikipedia found at http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro .

When you ’ ve exported your Collada file you need to bring it into PV3D using the DAE parser. The steps for using the DAE parser are similar to that of using the Collada parser and are given here:

1. Import the DAE parser: import org.papervision3d.objects.parsers.DAE;

2. Declare collada and dae vars and data type them to Collada and DAE respectively: private var collada:Collada; private var dae:DAE;

3. Set up dae and your FileLoadEvent.ANIMATIONS_COMPLETE event listener. You can load your images in two ways; by using the DAE reference to those images, or by using a MaterialsList. The code snippets for both methods are provided. Loading Images from DAE This method uses the image references already present in your Collada file to load its images.

dae = new helperDAE( false, null ); dae.addEventListener(FileLoadEvent.ANIMATIONS_COMPLETE, onAnimationsComplete); dae.load( “MyAnimated.DAE” );

Note that helperDAE is a helper class that manages the animation sequence of the Collada file, similar to what the MD2 morphing class does. Loading Images from a Materials List This method uses a MaterialsList to load the images onto the object. In the snippet below, the materials are first added to a composite material and then that ’ s added to the object. This is a great way to enhance an image skin, but as mentioned earlier, composite materials result in a double hit on the processor.

//Add Materials bitmapFileMaterial1 = new BitmapFileMateria( “MyTexture.png”); wireframe:WireframeMaterial = new WireframeMaterial(0); //Use a Composite Material to enhance your surface composite.addMaterial(bitmapFileMaterial); composite.addMaterial(wireframe); //Add your DAE and Initiate materialsList.addMaterial(composite, “ all” ); dae = new DAE( true ); dae.scale = 100; dae.load( “MyAnimated.DAE”, materialsList);

199 Part II: Working with Models and Data

The fourth step, upon load completion, is to add your dae to the scene using the addChild method:

scene.addChild(dae); Spinning Monkey Head After you ’ ve animated your object (using IPO curves in this case) and exported your Collada file, you then bring the Collada file into PV3D using the following code snippet. The snippet follows the steps previously discussed:

public function MonkeyAnimation() { view = new BasicView(600, 400, false , true , CameraType.FREE);

//Add Materials bitmapFileMaterial = new BitmapFileMaterial( “monkeyskin.png”); materialsList = new MaterialsList(); var composite:CompositeMaterial = new CompositeMaterial(); var wireframe:WireframeMaterial = new WireframeMaterial(0); composite.addMaterial(bitmapFileMaterial); composite.addMaterial(wireframe);

//Add DAE and Initiate materialsList.addMaterial(comp, “ all” ); dae = new DAE( true ); dae.scale = 100; dae.load( “SpinMonkey.dae”, materialsList); dae.rotationX=90; dae.rotationZ=180; dae.rotationY=180;

//Add the DAE file view.scene.addChild(dae); addChild(view);

//Add Load Listener dae.addEventListener(FileLoadEvent.LOAD_COMPLETE, onComplete);

}

After everything is loaded, the onComplete method is executed and your animation is rendered. This is an area of study that can easily fill an entire book. The above code snippet is presented as an aid to get you started with animating objects. To see the full code, consult the book ’ s code download page on the web.

What ’s Up with SketchUp? SketchUp is the most promising, and yet most under - utilized resource in all of PV3D. The reason for this is that most SketchUp files need to be hand - edited in order to get them to work in PV3D. Part of the problem is that SketchUp has a lines component, which PV3D doesn ’ t accept. The result is that PV3D is fed too many parameters at runtime.

200 Chapter 5: Creating 3D Models

Besides the fact that SketchUp is extremely easy to use, it also runs on both Mac and Windows machines. SketchUp comes in two versions: SketchUp and SketchUp Pro. However, only SketchUp Pro directly outputs Collada files (.dae), whereas the standard edition of SketchUp only output KMZ (Google Earth) files.

There ’ s a trick to getting Collada files out of KMZ files. Just change the .kmz extension to a .zip extension and unzip it. Within its resource files, you ’ ll find the Collada file!

In Chapter 13 you are presented with one possible scheme for converting SketchUp files into other formats for import into PV3D using the Photoshop 3D extension.

Binary versus XML Before you leave this section, it ’ s important to understand the trade - offs between using binary data versus XML to encode your models. There ’ s a misconception out there that a binary format is better than XML, but that ’ s not necessarily always the case. It depends on three factors: processing time, storage space, and numeric precision. When deciding whether to use binary or XML format, you should keep the following in mind:

❑ If you ’ re processor - constrained, then binary reduces the overhead of loading data, because stored data exactly matches the in - memory data. But remember that binary data isn ’ t compressed so it takes up more space. ❑ In most cases, where precision is the same, binary and XML encoding are similar in size. Due to the way that most data is accessed in the pipeline, actual differences in speed are usually unnoticeable when using binary over XML. However, this often depends on the multi - threading capabilities of the processor being used. ❑ If you ’ re using Collada you have the added benefit of adding binary into your XML structure if it ’ s needed.

Here ’ s the real kicker – currently the size of the models being brought into PV3D isn ’ t large enough to make binary encoding worth it (if it ’ s not already binary). Most importantly, Collada is cross platform (talks to everything), whereas binary isn ’ t. It is also important to remember that once your models are loaded, it does not matter what was used to import it. This is because everything is now on the vertex level.

Of course, this facet of PV3D is changing, courtesy of Pixel Bender and Alchemy (for more information on Alchemy, refer to Adobe Labs at: http://labs.adobe.com ).

In the following section you get your first look at creating parsers.

Writing Your Own Parsers This is an area of Papervision3D that ’ s sometimes frustrating for people. You expect that after creating your model, or finding a really cool one on the web, that it ’ ll parse in PV3D, and your model will be floating on the web in all its splendor. Unfortunately, more times than not, that ’ s not the case. To become

201 Part II: Working with Models and Data

successful you ’ ve got to do more than scratch the surface when dealing with parsers. More specifically, you must understand how parsers work and how to write your own when PV3D ’ s aren ’ t working for you.

If you don ’ t love XML, you should. It ’ s a uniform method of communication supported by almost all languages. It ’ s the web middle man that enables various platforms to talk to each other. Collada is an advanced 3D asset descriptor created in XML to be human - readable and product - independent. Life would be a lot simpler if all elements in the content pipeline could talk to each other in a common format. Collada ’ s goal is to serve as a standardized transport mechanism between various tools in the content pipeline. It should, in theory, reduce the need for writing a different import and export mechanism for each new application and platform.

Papervision3D and Collada Importing Collada into PV3D, in many cases, just doesn ’ t work well. However, the potential for great things using Collada and PV3D is there. According to the PV3D Core team, the problem is not with PV3D, but rather with the implementation of the Collada format by modeling applications such as 3DSMax. This is a moot point though, considering the fact that if your team has spent months putting together a model set and can ’ t import it into PV3D, you ’ re going to be pulling your hair out. All you have to do is search the PV3D community blog, and you ’ ll see that this topic has caused a great deal of frustration among members of the community. In reality, Collada is overkill when it comes to most Flash projects. Many developers just want to bring their models into PV3D and then manipulate them using Flash: that only requires vertices, triangles, and UV - data arrays. But Collada offers so much more, and as Flash 3D grows, Collada will most likely become the preferred format.

For those who ’ ve been frustrated by the process and only want to export simple models into PV3D so they can be manipulated using AS3, here are some options:

Simple Blender Exporters It ’ s commonly believed that Blender doesn ’ t export a Collada format that ’ s compatible with PV3D, but that ’ s not always true as shown in the Monkey animation example earlier. However, when the Collada exporter doesn ’ t work, you have two other options: an AS3 exporter, and an XML exporter (created by the Author). Both are written in Python. In both cases, a primitive had to be created that receives the data and is rendered in PV3D. Creating an AS3 or XML exporter is a two - step process (here the XML exporter is demonstrated). In step 1, you use Python to grab the needed data: verts, triangle indices, uv data, and normals. In step 2, create an XML primitive to parse the data and create your objects just like you would a sphere or cylinder in PV3D (see Figure 5.16).

Step 1 Step 2

Grab Create an XML Verts, Indices, UV Primitive that Parses and convert to XML the Data

Figure 5-16

202 Chapter 5: Creating 3D Models

Typical XML data for the case of a pyramid is given below; where the XML myVertices tag holds your verts data; the myFaces holds your triangle indices, uv data; and the myParams tag holds position, rotation, and scaling of your object:

< myPrimSet >

[1. Vertices] < myVertices > -0.182136,-0.641629,2.526386,-0.182136,-0.641629,0.526386,-2.182136, -0.641629,0.526386,-2.182136,-0.641629,2.526387,-1.182136,1.358371,1.526386, < /myVertices > [2. Triangle & UV Data] < myFaces > 0,4,1,0.500000,0.809017,0.000000,1.000000,0.190983,0.500000,0.894427,0.000000,0.447 214,1,4,2,0.190983,0.500000,0.000000,0.000000,0.500000,0.190983,-0.000000,-0.894427 ,0.447214,2,4,3,0.500000,0.190983,1.000000,0.000000,0.809017,0.500000,-0.894427,0.0 00000,0.447213,4,0,3,1.000000,1.000000,0.500000,0.809017,0.809017,0.500000,0.000000 ,0.894427,0.447214,0,1,2,0.500000,0.809017,0.190983,0.500000,0.500000,0.190983,0.00 0000,0.000000,-1.000000,0,2,3,0.500000,0.809017,0.500000,0.190983,0.809017,0.500000 ,0.000000,-0.000000,-1.000000,1 < /myFaces >

[3. Position, Rotation, & Scaling] < myParams > 0.025086,0.000000,0.075257,0.000000,0.000000,-0.000000,1.000000,1.000000,1.000000 < /myParams > < /myPrimSet >

This data is easily parsed in AS3 using e4x to refer to your XML tags by name. The data format of the XML above is commonly referred to as CSV or comma separated values. And it looks like it sounds; the data is separated by commas. Using the split command, this data is separated into an array so it can be referred to, using indices. As shown in the following code snippet, mySlitVert is your vertices data, and mySlitFace is your triangle and uv data:

//Create geometry vertices and faces ve = this.geometry.vertices; fa = this.geometry.faces; //Place your vertex and face data into arrays using the split command var mySlitVert:Array=myData_xml.myPrimSet[0].myVertices.split( “,” ); var mySlitFace:Array=myData_xml.myPrimSet[0].myFaces.split( “,” ); this .geometry.ready = true ; //Load your vertices into a 3D vertex point var j:int; var iVerNum:int=(mySlitVert.length-1)/3; for (j=0;j

}

An example of the Blender exporter can be obtained from the book ’ s chapter code.

203 Part II: Working with Models and Data

Simple 3DSMax Exporters In theory, 3DSMax has a working exporter. There ’ s a lot of theory running around PV3D. But theory doesn ’ t cut it at three in the morning when you ’ ve got a major project due at 8 A.M. As a result, Max has two other options: an AS3 Exporter, and an XML exporter (created by the Author). Both are written in MaxScript . The 3DSMax Exporter is very similar to the Blender exporter. The main difference between the two is the arrangement of the Triangle Indices and UV data. We discuss this in further detail on the book ’ s website and blog.

The exporters discussed above are great in a pinch, but if the Collada files are parsed correctly, then they aren ’ t needed. Every major modeling program outputs a Collada format. So why not take advantage of that, and create a system that takes Collada variations into account? To do that, you need to first understand how Collada files are put together.

Understanding Collada Parsers In reality, you don ’ t need the exporters. All the information you need can be extracted from the Collada file, given that an appropriate receptacle for that information is created (see Figure 5.17).

Figure 5-17

Collada documents are organized into a sequence of libraries. The information needed to create a simple model is contained in the geometries library. The data needed to create the pyramid in Figure 5.17 is as follows:

< library_geometries > < geometry id=”Cube-Geometry” name=”Cube-Geometry”> < mesh >

[1. Vertex Position] < source id=”Cube-Geometry-Position” > < float_array count=”15” id=”Cube-Geometry-Position-array”> -0.18214 2.52639 -0.64163 -0.18214 0.52639 -0.64163 -2.18214 0.52639 -0.64163 -2.18214 2.52639 -0.64163 -1.18214 1.52639 1.35837 < /float_array > < technique_common > < accessor count=”5” source=”#Cube-Geometry-Position-array” =”3”>

204 Chapter 5: Creating 3D Models

< param type=”float” name=”X” > < /param> < param type=”float” name=”Y” > < /param > < param type=”float” name=”Z” > < /param > < /accessor > /technique_common > < /source >

[2. Vector Normals] < source id=”Cube-Geometry-Normals” > < float_array count=”18” id=”Cube-Geometry-Normals-array”> 0.89443 0.00000 0.44721 - 0.00000 -0.89443 0.44721 -0.89443 0.00000 0.44721 0.00000 0.89443 0.44721 0.00000 0.00000 -1.00000 0.00000 -0.00000 -1.00000 < /float_array > < technique_common > < accessor count=”6” source=”#Cube-Geometry-Normals-array” stride=”3”> < param type=”float” name=”X” > < /param> < param type=”float” name=”Y” > < /param > < param type=”float” name=”Z” > < /param> < /accessor > < /technique_common > < /source >

[3. UV Data] < source id=”Cube-Geometry-UV” > < float_array count=”36” id=”Cube-Geometry-UV-array”> 0.50000 0.80902 0.00000 1.00000 0.19098 0.50000 0.19098 0.50000 0.00000 0.00000 0.50000 0.19098 0.50000 0.19098 1.00000 0.00000 0.80902 0.50000 1.00000 1.00000 0.50000 0.80902 0.80902 0.50000 0.50000 0.80902 0.19098 0.50000 0.50000 0.19098 0.50000 0.80902 0.50000 0.19098 0.80902 0.50000 < /float_array > < technique_common > < accessor count=”18” source=”#Cube-Geometry-UV-array” stride=”2”> < param type=”float” name=”S” > < /param > < param type=”float” name=”T” > < /param> < /accessor > < /technique_common > < /source >

< vertices id=”Cube-Geometry-Vertex” > < input semantic=”POSITION” source=”#Cube-Geometry-Position”/> < /vertices >

[4. Triangle Data] < triangles count=”6” material=”Material” > < input offset=”0” semantic=”VERTEX” source=”#Cube-Geometry-Vertex”/> < input offset=”1” semantic=”NORMAL” source=”#Cube-Geometry-Normals”/> < input offset=”2” semantic=”TEXCOORD” source=”#Cube-Geometry-UV”/> < p > 0 0 0 4 0 1 1 0 2 1 1 3 4 1 4 2 1 5 2 2 6 4 2 7 3 2 8 4 3 9 0 3 10 3 3 11 0 4 12 1 4 13 2 4 14 0 5 15 2 5 16 3 5 17 < /p > < /triangles > < /mesh > < /geometry > < /library_geometries >

205 Part II: Working with Models and Data

The visual_scenes library contains translation, rotation, scaling, and naming information:

< translate sid=”translate” > 0.02509 0.07526 0.00000< /translate > < rotate sid=”rotateZ” > 0 0 1 0.00000 < /rotate > < rotate sid=”rotateY” > 0 1 0 -0.00000 < /rotate > < rotate sid=”rotateX” > 1 0 0 0.00000 < /rotate > < scale sid=”scale” > 1.00000 1.00000 1.00000 < /scale >

Understanding the data output and how it ’ s organized is essential to building your own Collada parsers. If you pick up a book on Collada, you may be blown away by how complicated it sounds at first, but in reality it ’ s a very simple format. Referring to the code above, here ’ s how it works:

❑ Collada documents are organized into a sequence of libraries. Example libraries include: animation, camera, effect, light, material, physics, controller, geometry, and more. In Chapter 13, you ’ ll be adding a library for CS4 as well. ❑ The library that you ’ re presently interested in is the geometry library, which contains all the information that describes the object. ❑ The < mesh > element is the parent of the < source > elements, which contain the raw data for vertices, normals, uv data, and triangle indices.

With this information, you can build a simple Collada parser for PV3D that works with various modelers, including 3DSMax, Blender, and SketchUp.

Once your object is parsed and its vertices are set into arrays, a texture needs to be applied to it. Texturing models in PV3D requires some knowledge of image processing.

Processing Images (and Pixel Bender) Learning how to process good - looking textures for your 3D objects is an essential ingredient to realistic - looking models. Without textures, your 3D models are lifeless, and in PV3D, they don ’ t even exist: as the image classes conduct the primitive construction. If you ’ re going to learn only one image - processing software application, your application of choice should be Photoshop. With the release of Creative Suite 4, you now have a hefty 3D component. The open source alternative to Photoshop is called Gimp.

A great many great books are available out there on processing images for models. Additionally, resources for learning Photoshop and Gimp are provided on this book ’ s website and blog.

However, when it comes to image processing in AS3, there ’ s a new kid on the block, called Pixel Bender. You ’ ll be doing a lot of work with Pixel Bender in the coming chapters.

What ’s Pixel Bender? Pixel Bender is arguably the greatest thing that ’ s happened to Flash3D to date. It ’ s a tool that works with CS4, and lets you create your own pixel shaders or bitmap filters, but not like the slow ones in the previous version of Flash. Instead, Pixel Bender works at the machine level. Pixel Bender is a separate

206 Chapter 5: Creating 3D Models

program that has its own IDE and C - like programming language, which is compiled and imported into Flash. Its single purpose is to modify the value of a pixel, and it does so incredibly fast.

Pixel Bender takes advantage of multi - threaded, parallel processing to render an entire bitmap almost instantly, rather than processing each individual pixel within a given bitmap one at a time. This single characteristic of the rendering engine makes Pixel Bender the fastest gun in the west, so expect to see amazing things as it makes its way into Flash3D. You can use Pixel Bender to:

❑ Create your own custom filters ❑ Create your own blend modes ❑ Create your own custom fills ❑ Do generic number crunching

In the last chapter, you already saw the importance of bitmapFill . In this section, you discover how to create and apply your own pixel shader fill to Papervision3D and CS4 objects.

Getting Started with Pixel Bender Getting started with Pixel Bender is easy. First, go to the Adobe labs website and download Pixel Bender at http://labs.adobe.com/technologies/pixelbender/

The installation is pretty straightforward; just follow the installation instructions provided on the webpage. The next step is very important: when pixel bender downloads, it includes a docs folder, which has two very important documents: PixelBenderGuide and PixelBenderReference (see Figure 5.18). These guides help demystify the Pixel Bender process and save you hours of work, so let ’ s get started:

Figure 5-18

207 Part II: Working with Models and Data

After clicking on the pixel bender icon, a very simple interface appears, consisting of three UI panels:

❑ Image View — upper panel for loading images and examining shaders. ❑ Source Editor — lower panel for writing code. ❑ Filter Parameter UI — right panel for testing parameter “ slider ” effects. This allows you to see how your filter is affecting your image in real time.

Writing a Pixel Bender Filter The Pixel Bender language is not AS3. It ’ s a low level scripting language that ’ s similar to C, and it is very simple to use. Creating a filter or shader in Pixel Bender is accomplished through the following three steps:

1. Create a new, or open an existing, Pixel Bender filter. 2. Select the input images you ’ d like to use. 3. Click Run and use the parameter options to see your filter in action.

You now go through the details of accomplishing each step. Step 1 When you have Pixel Bender up, go to the file menu and click New Kernel. This populates your source code window with the simplest filter you can write – one that does nothing, but has all the essential elements of a pixel filter or shader.

[Part 1 Identifier and Kernel] < languageVersion: 1.0; >

//Kernel Definition kernel NewFilter < namespace: “Your Namespace”; vendor: “Your Vendor”; version: 1; description: “your description”; > [Part 2 Your filter code] { input image4 src; output pixel4 dst; void evaluatePixel() { dst = sampleNearest(src,outCoord()); } }

208 Chapter 5: Creating 3D Models

A Pixel Bender program is divided into two parts. The first part is your version identifier that includes your kernel definition followed by the name of your filter, and some meta data about your filter. The next part, contained between the curly braces, is your filter code.

In the preceding pixel code are two important commands: input and output . The input grabs your source image and has – in this case – the data - type image4, which means it has four color channels: red, green, blue, and alpha. Each channel is a 32 - bit floating point value. The output has the data - type pixel4, which means you ’ re outputting a four - channel pixel with a 32 - bit floating point value. So your input is an entire image (thus image4), but your output is a single pixel (thus pixel4), and the code is run for every single pixel in your image with the input and output variables named src and dst for “ source ” and “ destination ” respectively.

A noteworthy point is that Pixel Bender supports four data types: integer, float, pixel, and Boolean. Pixel Bender also has matrix support and a number of built in mathematical functions, such as sine, cosine, arctan, and so on. Consult the Pixel Bender reference documentation for more information on the functions available from within the Pixel Bender scripting language.

Next, the heart of your application is the evaluatePixel function. The code in this function is run for every single pixel in your image. This is where your processing algorithm goes:

evaluatePixel() { dst = sampleNearest(src,outCoord()); }

In this example, you ’ re only setting the destination pixel to the current value of your present pixel using the sampleNearest function. This method takes two parameters: your source image, and the outCoord property, which gives you the current x and y values of the pixel you ’ re currently evaluating. Therefore, sampleNearest returns the color of the pixel4 value of the present x, y coordinate of your src .

At the heart of Pixel Bender is the outCoord() property, which is the midpoint of the output pixel. Step 2 Go to the File menu and load an image. By default a samples images folder opens up, but you can pull any image from your hard drive. It ’ s not the images that matter, but the processing; remember you ’ re creating an algorithm that processes images. But what ’ s cool is that you can see how your pixel algorithm is affecting your photo as you make changes to the processing algorithm. Step 3 You now write a simple algorithm, which enhances the green channel of your image. Test it using the run button found in the Build menu. Within the evaluatePixel function, make the following changes:

evaluatePixel() { pixel4 colorChannel = sampleNearest(src,outCoord()); colorChannel.g = colorChannel.g*8.0; dst = colorChannel;

}

209 Part II: Working with Models and Data

In the code, a new pixel4 variable is created called colorChannel and is set equal to the present (x,y) pixel position being evaluated. The green value (grabbed by using dot syntax “ .g ” ) of your colorChannel .g is then multiplied by 8.0 (8.0 is used instead of 8 as you ’ re working with floating point numbers). You then set your destination (dst) equal to your adjusted colorChannel . Hitting the run button gives you an enhanced green image.

After creating your Pixel Bender, save it as a PBK file, which can then be loaded and played as a filter by the Flash player.

You now create a variable parameter to control the amount of green you enhance your green channel with.

Adding Parameters Using the parameter command allows you to add variables to your filters. In the following code, you learn to adjust your green color channel using a slider bar. The parameter is created by declaring a parameter (greenParam ), then setting its min , max and default , and multiplying it by your colorChannel variable:

parameter float greenParam < minValue:1.0; maxValue:10.0; defaultValue:1.0; > ; void evaluatePixel() { pixel4 colorChannel = sampleNearest(src,outCoord()); colorChannel.g = colorChannel.g*greenParam; dst = colorChannel; }

Because greenParam is a parameter, when you bring this filter class into Flash, you can control it using ActionScript!

Typical Point Processes In filter language, a point process modifies your source pixels, one pixel at a time. The values of your new pixels are independent of each other. Using relatively simple algorithms, you can make some very significant changes, such as brightness, color, balance, and contrast. A whole host of standard pixel transformations exist, which you now take a look at.

210 Chapter 5: Creating 3D Models

Grayscale Converting an image from color into shades of gray is one of the simplest image - processing tasks. It ’ s accomplished by averaging the three color channels, and setting each channel to that average. The code looks like this:

pixel1 shadeVal=(colorChannel.r+colorChannel.g+colorChannel.b) /3.0; colorChannel.r = colorChannel.g = colorChannel.b = shadeVal;

Complement To create the complement of an image, subtract each pixel color channel from 255 or in the case of Pixel Bender, subtract from 1.0, since RGB values in Pixel Bender have a value from 0 to 1, which represents an RGB channel value of 0 to 255. The Pixel Bender code is shown here:

colorChannel.r = 1.0-colorChannel.r; colorChannel.g = 1.0-colorChannel.g; colorChannel.b = 1.0-colorChannel.b;

Brightness To add to the brightness of a color, you add the percentage increase in brightness times the difference in its complement to each pixel ’ s component. . Check out the algorithm below:

colorChannel.r += brightParam*(2.0-colorChannel.r); colorChannel.g += brightParam*(2.0-colorChannel.g); colorChannel.b += brightParam*(2.0-colorChannel.b);

Note . A factor of 2.0 was introduced in the code above instead of 1.0. This is a hack solely for the purpose of visual appeal).

Exposure Exposure is similar to brightness, but you split your color channel into RGB and alpha and apply a pow function to your RGB color channel:

dst.rgb = pow( colorChannel.rgb, float3(1.0-exposureParam)); dst.a = colorChannel.a;

Color Balance Color balance is very similar to brightness, except that you adjust each color individually:

colorChannel.r += redParam*(1.0-colorChannel.r); colorChannel.g += greenParam*(1.0-colorChannel.g); colorChannel.b += blueParam*(1.0-colorChannel.b);

211 Part II: Working with Models and Data

Binary Contrast Contrast measures the difference between two colors. Binary contrast adjusts your color values to only two possible values: black or white. In this case, you just calculate the grey scale and convert it to black or white depending on a shade parameter:

pixel1 shadeVal=(colorChannel.r+colorChannel.g+colorChannel.b)/3.0; if(shadeVal > shadeParam) { colorChannel.r = colorChannel.g = colorChannel.b = 1.0; } else{ colorChannel.r = colorChannel.g = colorChannel.b = 0.0; }

Simple Pixelate Simple Pixelate uses the floor function to widen your color box region. It ’ s really just a trick, but demonstrates a good use of the floor function:

float2 pixelCorner = floor(outCoord()/dimAsFloat); pixelCorner = dimAsFloat*pixelCorner; outputPixel = sampleNearest(inputImage, pixelCorner);

Simple Blur A blur filter works by including a bit of surrounding pixel color into the color of your current pixel. This is typically accomplished by using some type of weighting function that drops off over distance, such as a Gaussian distribution. You design a simple blur filter using a linear drop off. The algorithm below adds two pixel colors beyond your center pixel and drops off linearly:

color = color + 0.75*color(blurParam/2) + 0.25*color(blurParam)

The algorithm works well for a blurParam value between 0 and 10, but then breaks down. To improve it, you need to add the necessary terms. . The Pixel Bender snippet is shown here:

float2 pos = outCoord(); pixel4 color = sampleNearest(src,pos); color+=0.75*sampleNearest(src, pos+float2(0.5*blurParam, 0))+0.25*sampleNearest(src, pos+float2(blurParam, 0)); color+=0.75*sampleNearest(src, pos- float2(0.5*blurParam, 0))+0.25*sampleNearest(src, pos-float2(blurParam, 0));

color+=0.75*sampleNearest(src, pos+float2(0, 0.5*blurParam))+0.25*sampleNearest(src, pos+float2(0, blurParam));

color+=0.75*sampleNearest(src, pos-float2(0, 0.5*blurParam))+0.25*sampleNearest(src, pos-float2(0, blurParam));

dst = color/5.0;

212 Chapter 5: Creating 3D Models

Sharpen The sharpen filter is similar to the blur filter in that you must displace the colors (created by Ryan Phelan on Adobe Pixel Bender exchange). After displacement, you must add and subtract them in such a way that you expose the outlines:

float4 left = sampleLinear(src, coord-float2(radius, 0.0)) * amount; float4 right = sampleLinear(src, coord + float2(radius, 0.0)) * amount; float4 top = sampleLinear(src, coord-float2(0.0, radius)) * amount; float4 bottom = sampleLinear(src, coord + float2(0.0, radius)) * amount; dst.rgb += (top.rgb); dst.rgb -= (bottom.rgb); dst.rgb += left.rgb; dst.rgb -= right.rgb;

The filter uses two parameters, radius and amount : radius increases the displacement, and amount increases the amount of color displacement.

Simple Point Light The equation places your light at the center position, since at that position there is a singularity in your equation (coord — center = 0 ). At that singularity, everything goes white. The attnDecay variable determines the spread of the light ’ s decay across your image. The attnMult variable multiplies your light source by a value and the brightness variable exponentiates the entire expression (see Figure 5.19).

Figure 5-19

The Pixel Bender code snippet is given here:

float2 outcoord = outCoord(); float attn = pow(attnMult/pow(distance(outcoord, center), attnDecay), brightness);

dst = attn* sampleNearest(src, outcoord);

213 Part II: Working with Models and Data

Bloom Brightness Bloom brightness is used in Away3D and was developed by Der Schmale. It filters the bright parts of the image and caps the darker parts at black:

float4 current = sample(src, outCoord());

if (length(current.xyz) < threshold) { dst = pixel4(0.0, 0.0, 0.0, 1.0); } else { current.xyz *= exposure; dst = current; }

The code uses two parameters: threshold and exposure . When the distance is less than the threshold, your pixel RGB channels are set to zero. If it ’ s greater than the given threshold, your exposure kicks in. You ’ ll see this again when you add Pixel Bender to PV3D.

Multiple Image Processing Multiple image processing involves combining two or more images in some way. A kernel can take any number of input images, each of which can have a different number of channels. There are a number of standard multiple - image algorithms. Here are a couple of them:

CrossFade Crossfade grabs the pixels from both images and combines them proportionately using an intensity parameter:

// Grab the pixel values from both images float4 topPixel = sampleNearest(topImage, outCoord()); float4 bottomPixel = sampleNearest(bottomImage, outCoord()); dst = (1.0- intensity)*topPixel+ bottomPixel*intensity;

An intensity of 0 reveals your first image and an intensity of 1 reveals the second one. Anywhere in between 0 and 1 is a combination of the two, weighted proportionately.

Subtraction and Addition To subtract images just subtract the color components of the corresponding images:

// Grab the pixel values from both images pixel4 topPixel = sampleNearest(topImage, outCoord()); pixel4 bottomPixel = sampleNearest(bottomImage, outCoord()); dst.rgb=topPixel.rgb-(1.0-intensity)*bottomPixel.rgb; dst.a=1.0;

214 Chapter 5: Creating 3D Models

Similarly, average is just adding the two corresponding image pixel values together and dividing by 2:

dst.rgb=(topPixel.rgb-(1.0-intensity)*bottomPixel.rgb)/2.0;

Subtraction has traditionally been used for edge detection and addition for noise reduction.

You now learn how to bring Pixel Bender into Flash.

Bringing Pixel Bender into Flash Before you get started on bringing Pixel Bender into Flash, you should be aware of a few limitations:

❑ Flash player always uses 1x1 square pixels. ❑ Pixel Bender images have 32 bits per channel, but graphics in Flash 10 only has 8 bits per channel. When the kernel is run in Flash, the input image data is converted to 32 bits per channel and then converted back to 8 bits per channel when kernel execution is complete. ❑ Arrays, region function, custom libraries, dependent values, and graph language aren ’ t supported in Flash. ❑ Control structures other than if and else (for example, Loops) aren ’ t supported.

If you get an error while trying to import a Pixel Bender filter to Flash it most likely means you ’ ve attempted one of the unsupported items above. Exporting Pixel Bender After creating and testing your filter, go to the Pixel Bender file menu and click on File and then Export Filter for Flash Player . That exports your filter as a Flash filter. Notice your file extension has changed. Pixel Bender has a file extension of .pbk and Flash filters have a file extension of .pbj. You can now download this filter into Flash and use it just like any other filter you would use in Flash. The key point is that it doesn ’ t result in the same processor load that the stock filters in Flash do.

Adding Pixel Bender to Papervision3D At the time of this writing, PV3D was in need of a CS4 overhaul. In this section, you find out how to put Pixel Bender into PV3D, by creating an algorithm that shades a PV3D surface.

Adding Pixel Bender to Papervision3D is a three - step process:

1. Creation of a Pixel Bender Filter 2. Creation of a Pixel Bender BitmapFileMaterial Object 3. Creation of a Pixel Bender BitmapMaterial Object

However, before you start coding, make sure that you ’ ve switched to the Flash 10 player, otherwise you ’ re going to get an error.

215 Part II: Working with Models and Data

Step 1: Creating a Pixel Bender Filter Adding dynamic shading to PV3D first requires that you create a more efficient light source, such as the simple point light filter that we presented in the previous section. The only problem with that filter is that there are so many exponent calculations, so you ’ re going to simply throw out all the exponents.

You can use inverse distance to mimic exponential behavior and thus eliminate all your exponents; a huge processor saver. Your new algorithm is shown below:

The Pixel Bender algorithm is easily implemented using the code below:

float2 outcoord = outCoord(); float attn = (brightness/((distance(outcoord, center)+radius))); dst = attn* sampleNearest(src, outcoord); dst.a=1.0

Also, note that the alpha component is split off and set to one. This keeps the image from losing its alpha channel as you reduce the brightness. Step 2: Creating a Pixel Bender Bitmap File Material Class Next you must create a bitmap file material class for Pixel Bender. This can be done by simply making a copy of PV3D ’ s BitmapFileMaterial class and renaming it to BitmapBendMaterial . Then, after you change the constructor and class name to match, use this class to extend the BitmapPixelMaterial class. You create the BitmapPixelMaterial in the following step:

BitmapBendMaterial extends BitmapPixelMaterial Step 3: Creation of a Pixel Bender Bitmap Material Class Make a copy of the PV3D BitmapMaterial class and name it BitmapPixelMaterial . Add the appropriate import statements and the PBJ and image embed methods and create a shader as shown below:

//Embed your images [Embed (source=”filters/livelyLight.pbj”, mimeType=”application/octet-stream”)] private var ShaderClass:Class; [Embed (source=”assets/images/gainesHouse512.jpg”)] private var myImage:Class; private var shader:Shader

Next, incorporate the code below to get your shader and image working together:

shader = new Shader(new ShaderClass()); shader.data.center.value = [400, 306]; shader.data.brightness.value = [150]; shader.data.radius.value = [100]; var image:Bitmap = new myImage(); image.filters = [new ShaderFilter(shader)]; shader.data.src.input = image.bitmapData

216 Chapter 5: Creating 3D Models

Finally, swap out the beginBitmapFill with the beginShaderFill method (which contains a matrix method shown in the following code). Be sure to check out the book ’ s website video on this topic for further explanation.

graphics.beginShaderFill(shader,_localMatrix)

The results of using the new shading are illustrated in Figure 5.20.

Figure 5-20

Figure 5.20 shows a dynamically shaded house using Pixel Bender. The house on the left shows no contrast and is washed out, whereas the house on the right appears much more realistic through the dynamic shading you just implemented.

The big advantage here is that you don ’ t have to “ texture bake ” your images. Texture baking applies lighting non - dynamically through an additional image - processing step. It ’ s more efficient than other lighting because it ’ s baked into the texture. However, once it ’ s baked into the texture, it ’ s static and is therefore not capable of changing dynamically based on changes to the scene, camera, or viewport properties. For example, if you wanted a sun to rise and set on your model house, your contrast (or shading) can change dynamically to correspond with the position of the sun at any given moment. This is a facet of 3D that makes 3D scenes appear so realistic. Check out the book ’ s chapter code for the working demo.

You can also extend your simple light to create multiple lights by adding additional terms.

Creating a Multiple Light Source Having multiple lights in PV3D is a desirable effect. However, after going through the Lightmap classes in Chapter 4, you may conclude that it ’ s impossible without a major rewrite of PV3D. With Pixel Bender, all you need to do is add another term to your PBK filter.

217 Part II: Working with Models and Data

Simply add another term to your Pixel Bender light equation as shown below:

The preceding code only shows two lights, but you can have as many lights as you want just by adding additional light terms.

The Pixel Bender code is provided here:

float2 outcoord = outCoord(); float attn = (brightness1/((distance(outcoord, center1)+radius1)))+(brightness2/ ((distance(outcoord, center2)+radius2))); dst = attn* sampleNearest(src, outcoord); dst.a=1.0;

Using Flash 10 reduces the amount of code you need to write significantly, which is why you can create multiple light sources with only five lines of code.

Animating Your Light Sources Once you ’ ve set up your dynamic Pixel Bender shader, then you can animate it fairly easily. Here are the steps for animating your lights:

❑ Increment your oscillation parameter with osc++ ❑ Calculate sine and cosine based upon your incremented osc ❑ Update your light positions ❑ Apply the update to your image

The documented code is given here:

private function loop(e:Event):void{ //Increment your oscillation parameter osc++; //Calculate sine and cosine var cos:Number=150*Math.cos(osc/10); var sin:Number=150*Math.sin(osc/10); //Update your light positions shader.data.center1.value = [sin+400, cos+180]; shader.data.center2.value = [cos+200, sin+180]; //Apply the update to your image image.filters = [filter]; //Rotating your image holder holder.rotationY+=.25; }

Dynamic shading in PV3D is difficult, but in Pixel Bender it ’ s a breeze!

218 Chapter 5: Creating 3D Models Summary Wow, that was a lot of material for just one chapter! You started with modeling and ended up with Pixel Bender. Regardless of how PV3D changes over time, the principles presented in this chapter will be around for a while. You ’ ll still need parsers to bring in vertex data regardless of your software environment. And Pixel Bender, the new kid on the block, will obviously become the cornerstone of new 3D packages that come about as the Adobe Flash Platform continues to evolve.

For further explanation of the material presented in this chapter, be sure to consult the book ’ s website and blog. Those resources have been created to help you become a 3D AS3 ninja and master this material .

219

Working with Particle Systems

No 3D animation is complete without a particle system. Fire, smoke, water, a swarm of bees, or flock of birds can all be created using particles. Particles add an element of interactive realism. If you ’ re on a star quest mission and your ship gets hit with a beam, your CGI (computer generated imagery) is using particles to create the plasma. Each particle has its own set of physical parameters such as position, velocity, and acceleration, which requires that PV3D incorporate physics into its classes.

PV3D ’ s particle system, at present, isn ’ t very robust. In this chapter you bring it up a notch using a few new OOP principles and a little bit of Newtonian physics. You ’ ll build a 3D particle system from scratch; learn how to create particles in PV3D, and create a particle glowworm in Flash CS4. You get your particles interacting with video, and exploding. And finally, you find out how to use bill - boarding to create large particle systems; how to create a scanning particle system, and how to incorporate the FLINT particle system into PV3D.

So, what ’ s a particle system anyway?

Particle Systems in PV3D A particle system is a large collection of similar particles acting in a similar way, like rain drops, smoke, and bees to name a few. PV3D itself can be thought of as a particle system (where the display objects are treated as particles). Also, like a particle system, PV3D keeps track of many points in space (vertices for example) and updates them periodically through rendering. Particle systems typically have the following properties:

❑ Initial Particles are generated. ❑ Each particle has unique attributes. Part II: Working with Models and Data

❑ Particles have a life time (or designated space) and afterwards are removed or reused. ❑ Particles are governed by scripts (classes that determine their behavior). ❑ Particles are rendered.

Particles add life to your scenes – fire, smoke, vapor, and explosions. And if you want your 3D avatar to enjoy sitting next to her virtual fireplace for a cozy evening, then light that fireplace up using particles. Understanding how particles are created and react will greatly enhance your understanding of PV3D.

Creating Particles in PV3D PV3D has a very limited particle system. Its ParticleMaterial class only has two particles: a circle and a square, each with an alpha and color property. Like other special materials, the ParticleMaterial class is found in the org/papervision3d/materials/special folder. But this class alone will not create particles in PV3D. You need the ParticleField class (found in org/papervision3d/objects/ special ), which uses polymorphism to grab your particle material and add number , size , and dispersion properties to it.

This isn ’ t enough to create particles in PV3D though. You need one more class: the Particles class (found in org/papervision3d/core/geom ), which lets you add and remove particles from the stage.

Particle Material Particle Field Particle Create a single Disperse Add and remove particle particles particles

Figure 6 -1

The steps and code for creating a particles system in PV3D is given here:

1. Import the required classes.

import org.papervision3d.materials.special.ParticleMaterial; import org.papervision3d.objects.special.ParticleField;

2. Declare a particle material, a particle field variable, and data type.

private var particlemat:ParticleMaterial; private var stars:ParticleField;

3. Instantiate your particle material and particle field and then add it to your scene by using the addChild method.

particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_CIRCLE); stars = new ParticleField(particlemat, 300,3, 2000,2000,2000); scene.addChild(stars);

As can be seen from the code above, the constructor function for the particle material contains three parameters: color , alpha , and shape ; at present you ’ re limited to only two shapes but in the following section, you extend that to include a few more.

222 Chapter 6: Working with Particle Systems

ParticleMaterial(color, alpha, shape);

The Particle Field constructor contains the particle material, number of particles to create, size, and x, y, and z dispersion areas.

ParticleField(particle material, number, size, fieldWidth, fieldHeight, fieldDepth)

Start with putting stars (your two particles and more) in a skybox.

Putting Stars in a Skybox Because PV3D only has two particles in its class, you can only generate a two - star universe (a circle and a square), which would be a pretty boring place. So first you need to extend the number of stars in PV3D and rewrite the ParticleMaterial class to make it a little more efficient. Currently, you only have a square and circle particle to put in your skybox so you add a star, gear, wedge, poly, and burst to make your universe a little more complete. These simple shapes are easily drawn in Flash using the following two steps:

1. Open up PV3D ’ s ParticleMaterial class and in the public class add the following static properties: public static var SHAPE_STAR:int = 2; public static var SHAPE_GEAR:int = 3; public static var SHAPE_WEDGE:int = 4; public static var SHAPE_POLY:int = 5; public static var SHAPE_BURST:int = 6;

2. In the drawParticle method, get rid of the else if statements and add a switch case . In the code below only the code for the Star shape is shown, the other shape codes can be found in the Chapter 6 book code (in the PV3DParticles folder in the ParticleMaterials class):

switch (shape) { case SHAPE_SQUARE: graphics.drawRect(renderrect.x, renderrect.y, renderrect.width, renderrect.height); break;

case SHAPE_CIRCLE: graphics.drawCircle(renderrect.x+renderrect.width/2, renderrect.y+renderrect.width/2, renderrect.width/2); break;

case SHAPE_STAR: var points:int=6; var innerRadius:Number=renderrect.width/10; var outerRadius:Number=renderrect.width*2; var count:int = Math.abs(points); if (count > =2) { // calculate distance between points var step:Number = (Math.PI*2)/points; (continued)

223 Part II: Working with Models and Data

(continued) var halfStep:Number = step/2;

// calculate starting angle in radians var start:Number = (20/180)*Math.PI;

graphics.moveTo(renderrect.x+(Math.cos(start)*outerRadius), renderrect.y- (Math.sin(start)*outerRadius));

// draw lines for ( var i:int=1; i < =count; i++) { graphics.lineTo(renderrect.x+Math.cos (start+(step*i)-halfStep)*innerRadius, renderrect.y-Math.sin(start+(step*i)-halfStep) *innerRadius); graphics.lineTo(renderrect.x+Math.cos(start+(step*i))*outerRadius, renderrect.y-Math.sin(start+(step*i))*outerRadius); } } break; case SHAPE_GEAR:

[ Gear code goes here] break ;

case SHAPE_WEDGE:

[Wedge code goes here] break ;

case SHAPE_POLY:

[Poly code goes here] break ;

case SHAPE_BURST:

[ Burst code goes here] break ; default : trace ( “warning-Particle material has no valid shape.”); break; }

As shown above, the switch case is more efficient and makes it easy to add as many different particles as you want. With this change made to your ParticleMaterials class, you ’ re now ready to put some stars in your skybox. This process involves three steps. Step 1: Adding Stars around the Earth Adding stars is really simple. After bringing in the appropriate import statements, and declaring your star ’ s variable name, just create your particle material and add it to your particle field. Then you add your stars using the addChild method as shown below:

224 Chapter 6: Working with Particle Systems

//First set of stars particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_CIRCLE); stars = new ParticleField(particlemat, 300,3, 2000,2000,2000); scene.addChild(stars);

//Second set of stars particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_STAR); stars = new ParticleField(particlemat, 300,3, 2000,2000,2000); scene.addChild(stars);

//Third set of stars particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_BURST); stars = new ParticleField(particlemat, 300,3, 2000,2000,2000); scene.addChild(stars);

In the preceding case, three different types of stars are added. Now, you add a cube that will act as a panorama. Step 2: Adding a SkyBox with Motion A skybox is just a large (10,000x10,000x10,000) cube that you sit inside of. You create one by placing six pano images on a cube and positioning your camera inside of it. It ’ s that easy.

The first thing you want to do is embed your pano images, then create your materials to be added to your materials list, and then add those materials to your materials list as shown in the code below:

First, embed your images.

[Embed (source=”assets/Side1.png”)] private var BitmapFront: Class; [Embed (source=”assets/Side2.png”)] private var BitmapRight: Class; [Embed (source=”assets/Side3.png”)] private var BitmapBack: Class; [Embed (source=”assets/Side4.png”)] private var BitmapLeft: Class; [Embed (source=”assets/Side5.png”)] private var BitmapDown: Class; [Embed (source=”assets/Side6.png”)] private var BitmapUp: Class;

Create the variable names for your materials list: front, back, top, bottom, left, and right:

var matFront: BitmapMaterial = new BitmapMaterial( new BitmapFront().bitmapData); var matLeft: BitmapMaterial = new BitmapMaterial(new BitmapLeft().bitmapData); var matBack: BitmapMaterial = new BitmapMaterial(new BitmapBack().bitmapData); var matUp: BitmapMaterial = new BitmapMaterial( new BitmapUp().bitmapData); var matRight: BitmapMaterial = new BitmapMaterial( new BitmapRight().bitmapData); var matDown: BitmapMaterial = new BitmapMaterial(new BitmapDown().bitmapData);

225 Part II: Working with Models and Data

Add the materials to your materials list.

var myMat: MaterialsList = new MaterialsList();

myMat.addMaterial(matFront, “ front” ); myMat.addMaterial(matLeft, “ left” ); myMat.addMaterial(matBack, “ back” ); myMat.addMaterial(matUp, “ top” ); myMat.addMaterial(matRight, “ right” ); myMat.addMaterial(matDown, “ bottom” );

In addition, make sure that you set your material “ double - sided ” property to true. That way your normal vectors face both ways and you can see the cube image inside and outside of the cube.

Finally, you place your materials list on your cube (instantiated as skybox) and you position your camera appropriately, which may take a little trial and error to get it right.

myWidth=myDepth=myHeight=10000; skybox = new Cube(myMat,myWidth,myDepth,myHeight,4,4,4); scene.addChild(skybox); camera.z=1000; camera.zoom = 6; camera.focus = 100;

Once everything looks okay – add some motion to make everything come to life. Step 3: Adding Motion In adding motion you have several options. There ’ s no set rule here. It all depends on what you want to accomplish. In the following case, you just want simple rotation.

Figure 6-2

So in the code below you rotate three things – the earth, the camera, and the skybox. You could have rotated the stars as well, but with everything else rotating it looks as if the stars are already rotating.

226 Chapter 6: Working with Particle Systems

//Rotate the earth sphere.yaw(1); myOsc++; //Rotate the camera camera.z = 1500*Math.cos(myOsc/300); camera.x = 1500*Math.sin(myOsc/300); //Rotate the skybox skybox.rotationY+=.1; skybox.rotationX+=.1;

Another option is to use the moveForward command discussed in the previous chapter. You can actually move your skybox forward and have your camera follow, or vice versa. The options are limitless and it just takes a little experimentation to produce something really cool. Check out the book ’ s chapter code parts 1, 2, and 3 to see the full code listing. In the next chapter, you take this a step further and create a Planetarium.

Now that you know a little about creating particles in PV3D, you can delve a little deeper into the theory of particles, which enables you to create a generic Particle3D class and add motion to your individual particles.

Creating a Generic CS4 Particle Class For the longest time particle systems in Flash have been 2D. But Flash CS4 now has a native z component. This is so cool. To get a third dimension into your particle system all you have to do is just add z, then throw out all your scaling as z is scaled automatically, and add a z -sort function (as discussed in Chapter 1).

In a particle system, every particle has it own unique attributes, many of which are rooted in Newtonian physics. Typical particle attributes are: life span, position, velocity, acceleration, mass, color, rotation, alpha, size, gravity, friction, and bounding geometry.

Of course this list isn ’ t complete. No particle list ever is. But it ’ s easy to add new particle properties. By creating a new particle property and adding it to your Particle class, you can essentially add whatever parameters you want to match your particle ’ s situation. That ’ s the power of encapsulation; you place all of these properties in a particle class and attach that class to a Flash object.

Figure 6-3

227 Part II: Working with Models and Data

In Figure 6.3, cascading a series of small 2D images in 3D creates a glowworm. The program consists of two parts; timeline ActionScript, and a Particle Class. First, you construct the Particle class. Building a Particle3D Class Taking a subset of the physical properties listed above (position, velocity, particle life, gravity, friction, and alpha), you can create a Particle3D class. The class has three basic parts:

❑ In the first part of the code, the various properties are defined. ❑ Second, the constructor function defines and sets the values for these properties. ❑ Finally, a method function is created which changes the property values as an update function is iterated in the wrapper code animation loop.

A noteworthy point is that the Particle3D class has been placed in the org/lively3d/particles/ folder and uses reverse domain syntax . The purpose of reverse domain syntax is to avoid naming conflicts. For example, in this case, instead of using papervision3d , lively3d is used. This avoids the potential for name conflicts with PV3D classes that may also be named Particle3D .

As you can see, the Particle3D class below is extremely simple.

package org.lively3d.particles { import flash.display.*;

public class Particle3D extends Sprite { //Part 1, Define Properties public var particleLife:Number; public var velocityX:Number; public var velocityY:Number; public var velocityZ:Number; public var gravity:Number; public var friction:Number; public var myAlpha:Number;

public function Particle3D() {

//Part 2, Add and define property values

particleLife=0; velocityX = 0; velocityY = 0; velocityZ = 0; gravity = 0; friction = 1; myAlpha = 1;

}

228 Chapter 6: Working with Particle Systems

//Part 3, Update Properties by looping public function myUpdate():void { //Add Looping variables plus native z component

this.x += velocityX; this.y += velocityY; this.z += velocityZ; particleLife+=1;

velocityX *= friction; velocityY *= friction; velocityZ *= friction; velocityY += gravity;

this.alpha *= myAlpha;

}}}

The particle is updated by running the myUpdate method (above) repeatedly in your animation loop function from the main wrapper code. We address this process in the next section.

Your particle is essentially naked. It ’ s like the emperor without any clothes. You need a movie clip from your Flash Library to represent your particle (that is, put some clothes on it). Connecting to the Movie Clip After creating a movie clip in Flash, you need to connect it to the Particle3D class that you created in the previous section. Here are the steps:

1. Create a single particle movie clip in Flash and call it smallParticle 2. Remove the particle from the stage so it remains only in the Flash Library 3. Right - click on the particle in the Library and choose Linkage 4. Call the Class name smallParticle and link it to the particle class as shown in Figure 6.4.

Your class path may be different.

Figure 6-4

229 Part II: Working with Models and Data

Once your linkage is set you ’ re now ready to start scripting your particle field. Open up Flash ’ s ActionScript panel and enter in the code in the next section. ActionScript Main Code You now build the wrapper or main code for your particle system. The following code is built on the Flash ActionScript timeline and therefore doesn ’ t have the formal class structure presented earlier in this book (because it runs inside of Flash CS4 on the timeline and isn ’ t a class). When converting it to a class, the class structure must be added. It ’ s important to recognize this difference, as you ’ ll most likely be converting code back and forth between timeline ActionScript and classes. The code has four parts:

❑ The first part brings in the required display class, sets the particle number and particle life span, defines the particle array, and creates an oscillation parameter. ❑ The second part sets the particle parameters for the methods defined in the Particle3D class for the individual particles. ❑ The third part updates your particles and refreshes the particles when they ’ ve reached the end of their lifetime. ❑ The fourth part creates a regenerative loop by placing a listener for Event.ENTER_FRAME , which triggers the myLoop method, causing the particle to be updated (and regenerated if need be) in every frame.

//First Part, import in Flash display class and declare variables

import flash.display.*;

var numParticles:uint = 30; var myLifeLived:Number=20; var particleArray:Array = new Array(); //Oscillation parameter var myOsc:int=0;

//Second Part, set your particle parameters

function myLoop(event:Event):void { if(particleArray.length < numParticles) { //myColor.color = Math.random() * 0xFFFFFF;

var myPart:smallParticle= new smallParticle();

addChild(myPart); myPart.gravity=.5; myPart.friction=1.1;

myPart.velocityX = 10*Math.cos(myOsc/10)*Math.cos(myOsc/10); myPart.velocityY = 10*Math.sin(myOsc/10); myPart.velocityZ = 10; myPart.x = (stage.stageWidth/2)*Math.cos(myOsc/100)*Math.cos(myOsc/100); myPart.y = (stage.stageHeight/2)*Math.sin(myOsc/100);

230 Chapter 6: Working with Particle Systems

myPart.z = (1000); myPart.myAlpha=.98; particleArray.push(myPart); myPart.scaleX=myPart.scaleY=3;

}

//Third Part, refresh particles

for(var i:uint = 0; i < particleArray.length; i++) { var myParticle:smallParticle = particleArray[i]; particleArray[i].myUpdate(); if(myParticle.particleLife > 40) { myOsc++; myParticle.velocityX = 10*Math.cos(myOsc/10)*Math.cos(myOsc/10); myParticle.velocityY = 10*Math.sin(myOsc/10); myParticle.velocityZ = 10; myParticle.x = (stage.stageWidth/2)*Math.cos(myOsc/100)*Math .cos(myOsc/100); myParticle.y = (stage.stageHeight/2)*Math.sin(myOsc/100); myParticle.z = (1000); myParticle.scaleX=myParticle.scaleY=1; myParticle.alpha=1; myParticle.particleLife=myLifeLived; myParticle.scaleX=myParticle.scaleY=3;

} }

sortZ(); } //Sort function function sortZ():void { particleArray.sortOn(“z”, Array.DESCENDING | Array.NUMERIC); for(var i:uint = 0; i < particleArray.length; i++) { var myParticle:smallParticle = particleArray[i]; setChildIndex(myParticle, i); } }

//Fourth Part, set event listener to update particles on enter frame

addEventListener(Event.ENTER_FRAME, myLoop);

A number of very important things are happening in the code above, which are generic to all particle systems no matter how many dimensions. Only the prescribed number of particles can be created, and once those particles exceed their lifetime or bounding box, they ’ re recycled. Central to particle systems is the particle array, which you must iterate over and continually update using the myUpdate method, created previously in the Particle3D class.

231 Part II: Working with Models and Data

Key Particle Concepts There are a number of key properties that you commonly encounter when working with particle systems. We describe them briefly below:

Emitter Creating an emitter is simple – you just tell the particles where to be created. In the code, we generate them at the mouse pointer with a small variation on velocity.

myParticle.ypos = mouseY-vpY; myParticle.xpos = mouseX-vpX;

Regenerator When a particle has outlived its lifetime we regenerate it by sending it back to the source. Regenerating particles is usually preferable due to the way in which Flash deals with memory resources. In PV3D particle sorting is done for you automatically, but in CS4 you must create a sorting algorithm.

z -Sorter As you ’ re dealing with multiple particles in CS4 you must sort them based on their z - position, otherwise they ’ ll overlap incorrectly. The sorter presented in the code above is one of many possible z - sorters. Later in the book (Chapter 16), you learn how to create a spherical image ball, for which you ’ ll use a different sorter.

Velocity Velocity is added by incrementing it in the loop. To get a 3rd dimension all you have to do is add a z component.

myParticle.xpos += myParticle.vx; myParticle.ypos += myParticle.vy; myParticle.zpos += myParticle.vz;

Gravity Gravity can get rather complex, but in many of the animated examples presented all you have to do is add a little more value to your y velocity, and your particle falls in the y - direction. Iterating the y - velocity creates gravity. For example, to get smoke you just change the sign of the gravity term and iterate upwards.

myParticle.vy += gravity;

Bounce Bounce adds a bit of physical reality to your particle. When the particle hits the floor, bounce is created by reversing velocity and doing a decimal multiplication on velocity.

232 Chapter 6: Working with Particle Systems

myParticle.bounceNum++; myParticle.vy *= bounce =-.6;

In the example above, bounce is equal to - .6, which reverses the velocity and reduces it exponentially. This list can go on and on, depending on your particular scenario. Lifetime or Bounding Box Particles have a limited existence or lifetime. The lifetime of a particle can be based on geometry or time. In the glow worm code, you limited your particle ’ s lifetime by using the particleLife variable. When your particle exceeded its particleLife variable of 20 (found in the if statement), it was regenerated (or reinitialized). But you could have limited your particle ’ s lifetime by using a bounding box as well. When it hit the bounding box it would be regenerated.

Next, take your Particle3D class, make a few changes, and use it to make things explode in 3D! Making Things Explode (in Flash CS4) Exploding an object is serious gaming fun. There ’ s just something about the human psyche that loves to see things blow up. The first step in making things explode is to add a spin (x, y, z) property to your Particle3D class. You can accomplish this by following these three steps:

1. To keep things straight, rename your Particle3D class to ParticleBlowUp and add a spin(x, y, z) property

public var spinX:Number; public var spinY:Number; public var spinZ:Number;

2. In the constructor function, set the spin(x, y, z) equal to zero.

spinX = 0; spinY = 0; spinZ = 0;

3. In your animation method myLoop , update the rotation of the particle by iterating the spin(x, y, z) amount.

this.rotationX += spinX; this.rotationY += spinY; this.rotationZ += spinZ;

That ’ s all you have to do to your Particle3D (now named ParticleBlowUp ) class.

In the discussion above, you added the new spin(x, y, z) property to your ParticleBlowUp class. The process of adding any property to your particle class is always the same. Add your property, initialize it in your constructor, and add its rule to your animation loop as shown in Figure 6.5.

233 Part II: Working with Models and Data

Add A Property Initialize it in Add its rule to your Constructor your Animation or Init Method Loop

Figure 6-5

This is so cool, since as mentioned earlier, it ’ s so easy to add as many properties as your particles need. However, you ’ re only halfway done. In this case, just adding spin isn ’ t enough. As things are now, only a single individual particle will spin.

To get your particle system to explode you need to transfer a multi - part movie clip into your Particle class (using polymorphism). Then iterate over the elements of that movie clip, so that you can spin those elements off into space in different directions, simulating an explosion.

To do this, make the following changes to your ParticleBlowUp class shown here:

1. Remove the extends Sprite statement from your ParticleBlowUp class; this allows you to bring an object into your ParticleBlowUp class. 2. Create a public property obj and data type it as a DisplayObject :

public var obj:DisplayObject

3. Add obj to your constructor method, declare obj as an Object in your constructor method. 4. Finally, change all the this keywords to object so that the transformations are applied to the object passed in through the constructor. The completed code is provided:

package org.lively3d.particles { import flash.display.*; //Particle Parameters public class ParticleBlowUp { public var velocityX:Number; public var velocityY:Number; public var velocityZ:Number; public var gravity:Number; public var friction:Number; public var fade:Number; public var autoRotate:Boolean; public var spinX:Number; public var spinY:Number; public var spinZ:Number; public var object:DisplayObject;

234 Chapter 6: Working with Particle Systems

public function ParticleBlowUp(obj:DisplayObject) { //Set Parameters object = obj; velocityX = 0; velocityY = 0; velocityZ = 0; gravity = 0; friction = 1; fade = 1; spinX = 0; spinY = 0; spinZ = 0;

} //Update Parameters public function myUpdate():void { velocityX *= friction; velocityY *= friction; velocityY += gravity; object.x += velocityX; object.y += velocityY; object.z += velocityZ; //Update rotation object.rotationX += spinX; object.rotationY += spinY; object.rotationZ += spinZ; } } }

Next, open up Flash and make a few movies (3D card houses) and make sure that those movies are made up of various movies. You iterate over these pieces to simulate the explosion. Figure 6.6 shows three card houses that explode when you click on them. To get this to happen, you need to give each house an appropriate instance name ( house1_mc , house2_mc , house3_mc ).

Figure 6-6

235 Part II: Working with Models and Data

You then attach listeners to each one of these instance names, so when you click on them the explode method is executed.

//Set your click listeners cards1_mc.addEventListener(MouseEvent.CLICK, explode); cards2_mc.addEventListener(MouseEvent.CLICK, explode); cards3_mc.addEventListener(MouseEvent.CLICK, explode); text_mc.addEventListener(MouseEvent.CLICK, explode);

The explode method is pretty straightforward; each particle is given a random velocity and spin (in x, y, z), and gravity is set to 1, which brings the particles to the ground over time.

The full code is listed here:

import org.lively3d.particles.*; //Import blowup class import org.lively3d.particles.ParticleBlowUp; //Declare particle array and particle var particles:Array = []; var particle:ParticleBlowUp;

function explode(event:MouseEvent):void { var element:MovieClip = MovieClip(event.currentTarget); for(var i:uint = 0; i < element.numChildren; i++) { //Set random velocity and spin particle = new ParticleBlowUp(element.getChildAt(i)); particle.velocityX = Math.random() * 20-10; particle.velocityY = Math.random() * -20; particle.velocityZ = Math.random() * 20-10; particle.gravity = 1; particle.friction = .98; particle.spinX = Math.random() * 40-20; particle.spinY = Math.random() * 20-10; particle.spinZ = Math.random() * 20-10; particles.push(particle); } addEventListener(Event.ENTER_FRAME, myLoop); }

function myLoop(event:Event):void { for(var i:uint =0; i < particles.length; i++) { particles[i].myUpdate(); } } //Add Listeners cards1_mc.addEventListener(MouseEvent.CLICK, explode); cards2_mc.addEventListener(MouseEvent.CLICK, explode); cards3_mc.addEventListener(MouseEvent.CLICK, explode); text_mc.addEventListener(MouseEvent.CLICK, explode);

236 Chapter 6: Working with Particle Systems

In this section, you ’ ve learned a very important technique that you ’ ll use over and over in coming chapters. You ’ re not just passing variables to your methods, but movies and objects . . . and that opens up a whole new world of possibilities.

You ’ re now going to use this new technique in another way – to interact with sliced images. Slicing an Image Passing an object to a particle class can be used for more than just blowing things up. In this section, you slice up an image and turn it into particles. There are a number of reasons why you may want to slice up an image – to make a puzzle, game, or portal engine. But slicing images in a photo - editing tool such as Photoshop, Gimp, or Illustrator can get frustrating. Many developers don ’ t know that you can do this programmatically, which saves you time and a big headache if you have to re - slice when the project parameters change.

Figure 6.7 shows a slice map of Walton County, Kentucky in the 1800s. The sliced map was created for a historic tour of the city, which has a rich history. The entire map rotates in 3D space around the x - axis and when you click on a section it elevates and rotates and then sinks back to its original position. In addition, there ’ s a drag - and - drop static method, which lets you drag the map pieces.

Figure 6-7

The slicing procedure consists of three important parts. The last two use the OOP principles discussed earlier:

❑ Slicing an Image ❑ Using Polymorphism & Encapsulation ❑ Using Static Properties/Methods & Interactivity

These key OOP concepts, previously discussed, are essential in understanding PV3D.

Slicing an Image Slicing an image is pretty easy using copyPixels . You just bring in your image and copy the portion of the image that you need to turn into a piece of the map.

237 Part II: Working with Models and Data

// loop through all pieces for(var x:uint=0;x < 6;x++) { for (var y:uint=0;y < 4;y++) {

// create new map piece bitmap var newmapPieceBitmap:Bitmap = new Bitmap(new BitmapData(pieceWidth,pieceHeight)); newmapPieceBitmap.bitmapData.copyPixels(image.bitmapData,new Rectangle(x*pieceWidth,y*pieceHeight,pieceWidth,pieceHeight),new Point(0,0));

// create new sprite and add bitmap data to it var newmapPiece:Sprite = new Sprite(); newmapPiece.addChild(newmapPieceBitmap); mapPiece=new MapPiece(newmapPiece,x,y); DragDropStatic.DragDrop(mapPiece);

// set location mapPiece.x = x*(pieceWidth+5)+20; mapPiece.y = y*(pieceHeight+5)+20; mapPiece.addEventListener(MouseEvent.CLICK,clickmapPiece);

// add to stage movie.addChild(mapPiece);

}}

You ’ ve got to loop twice to get both column and row as you use copyPixels to grab the individual slices of your map. If you check out Gary Rosenzweig ’ s book ActionScript 3.0 Game Programming University you ’ ll find that he uses a similar slicing technique. But in addition to Gary ’ s approach, you use polymorphism and encapsulation typically found in PV3D. This use is briefly discussed next.

Polymorphism & Encapsulation The heart of PV3D is encapsulation, and by using polymorphism you can create a MapPiece class that enables you to easily work with your map pieces. Polymorphism is used to grab the map pieces and treat them as instantiated objects as shown in the MapPiece class:

package objects.primitives { import flash.display.Sprite;

public class MapPiece extends Sprite { private var mapPiece:Sprite; private var locX:uint; private var locY:uint; private var myXPos:Number; private var myYPos:Number; //Transfer map piece into function public function MapPiece(mapPiece:Sprite,locX:uint,locY:uint) { this.mapPiece=mapPiece; this.locX=locX; this.locY=locY; addChild(mapPiece); } 238 Chapter 6: Working with Particle Systems

So what is polymorphism anyway?

It ’ s often joked that polymorphism is a word that will impress friends at parties; most likely it ’ ll get them running the other way. Polymorphism is the concept that an object that inherits from a class can be used in place of an instance from that class.

So to cut through all the formalities, it ’ s all about data types. If I ’ ve extended a class by the Movie class, then that class can be treated as a Movie object. Think of it as the king ’ s son. If the king is allowed into the throne room, so is his son, because in a sense he is an extended version of the king. There ’ s much more to polymorphism of course (or it wouldn ’ t have such a big name), but this is enough to get you through particles.

Interactivity Interactivity is accomplished in two ways: adding a listener and using Static Properties and Methods:

❑ Listener — The instantiated mapPiece has a mouse click listener added to each mapPiece using the code below:

mapPiece.addEventListener(MouseEvent.CLICK,clickmapPiece);

❑ Static Properties and Methods — By creating a static properties and methods class you can create reusable code. This is done for the drag - and - drop class shown here:

package utils { import flash.display.Sprite; import flash.events.MouseEvent;

public class DragDropStatic { public function DragDropStatic() {

} //Drag and Drop static method using mouse listeners public static function DragDrop(mySprite:Sprite):void { mySprite.addEventListener(MouseEvent.MOUSE_DOWN, dragMe); mySprite.addEventListener(MouseEvent.MOUSE_UP, dropMe); function dragMe(event:MouseEvent):void { mySprite.startDrag(); } function dropMe(event:MouseEvent):void { mySprite.stopDrag();

}}}}

239 Part II: Working with Models and Data

Once you ’ ve created the class you can easily execute it using the command below:

DragDropStatic.DragDrop(mapPiece);

The full code can be found in the book ’ s chapter code download for this chapter. Now you ’ re going to create a little smoke. Integrating Particles and Video The key to getting particles to interact with video is to trigger them on pixel color. The secret is to use the BitmapData class, which gives you access to pixel data. Then you can search for a color in your bitmap and whenever that color matches your search, you generate a particle (see Figure 6.8). This works with video, as you can grab the frames of the video with the BitmapData class and access the pixel data in that way.

Figure 6-8

The full code can be obtained from the book ’ s chapter code. Handling Your Particle Trash Once a particle is extinguished it hangs around in your computer ’ s memory and needs to be removed. The removal process is often referred to as trash collection. This occurs when the Flash player removes unused objects. Unlike AS2, in AS3 your particles hang around a bit, which can create a lag effect. There are three things that you must do in order for an object to be eligible for trash collection:

❑ It must be removed from all arrays. ❑ It must be removed from the stage and all display objects. ❑ It must have all event listeners removed.

Unfortunately, once you do all these things it doesn ’ t mean that your trash is going to be collected. You ’ ve only made it available for trash collection. In typical PV3D applications you don ’ t have control over trash collection.

240 Chapter 6: Working with Particle Systems

The way trash collection works is that as memory usage gets to a certain point Flash performs a mark and sweep operation. The Flash player scans your application, looking for elements eligible for trash collection and then sweeps them away.

Remember: as long as any reference to an object exists, the garbage collection system won ’ t recover the memory that the object occupies. If the value of an object changes such that it points to a different object or is set to null, the memory occupied by the original object becomes available for trash collection, but only if there are no other references to it.

So performing the three tasks above doesn ’ t mean that the item is immediately removed from your memory. Trash collection is sporadic, with no guarantees as to when it ’ s going to happen. But it does eventually happen so make sure that you keep your unused particles ready to go out with the rest of the garbage. Below are a few code snippets to help you out:

1. Use the shift command to remove a particle from the particles array.

myParticle = particles.shift();

2. Use the removeChild command to remove a particle from the stage.

stageContainer.removeChild(myParticle);

3. Use null to make sure that all references are removed from your particle.

myParticle = null;

Use the commands above to make your unused particles available for trash collection. If you don ’ t want to worry about collecting your unused particles, reinitialize as shown in the earlier code example. In addition, if you ’ ve added an event listener to your particle you need to remove that as well, using the following code snippet as a guide:

❑ Add: myParticle.addEventListener(MouseEvent.CLICK,clickmapPiece); ❑ Remove: myParticle.removeEventListener(MouseEvent.CLICK,clickmapPiece);

The key to removing an event listener is to use the exact same code as you used to create it, but use removeEventListener instead of addEventListener . Treating PV3D like a big particle system will improve your efficiency, especially when throttling your 3D objects with a timer. More on this later . . . when you build a portal system.

Using Dictionaries Recycling particles in Flash is an efficient way to tackle memory issue problems. And you do this by sticking them in an array and checking to see if a needed particle is available before creating a new one. So now you ’ ve disabled your particle when not needed (which just turns off the movie clip) and it just waits to be recycled. If you now want to recycle more than one type of particle you ’ ll need a dictionary object to keep track of them. Dictionaries are used extensively in PV3D, and in Second Life where every object and item created has a unique key.

241 Part II: Working with Models and Data

Dictionaries act like associative arrays, or maps, and use keys instead of numeric indices to organize stored values. Each key corresponds to a property name. Your dictionary is an unordered collection of key value pairs. Dictionary objects are similar to generic objects except that they can use keys of any data type. As discussed earlier, as long as any reference to an object exists, the garbage collection system won ’ t recover the memory that the object occupies. To find out more about dictionaries check out the AS3 Flash documentation.

No discussion of particles would be complete without a look at billboarding. It ’ s a way of making 2D particles look 3D and therefore reducing your CPU usage. Making 2D Surfaces Look 3D (Billboarding) Billboarding is how you make a 2D polygon look 3D by applying a 3D looking texture, and keeping that 2D polygon always oriented towards the camera. It ’ s an effective illusion and is used by many particle systems. In Figure 6.9, you see 400 particles orbiting a sphere. It uses billboarding for the individual particles. Such a system would be undoable in PV3D using real 3D display objects.

Figure 6-9

But how do you make those particle images?

You start with a filled circle and turn it into something that looks 3D. You can do this in many programs – Photoshop, Illustrator, Flash, or 3DSMax. For this example, use 3DSMax. Max has a secular material function that allows you to adjust the material reflection property. And Max comes with a large library of premade materials. Here are the steps for making a Billboard particle:

1. Open up 3DS Max and draw a sphere on the perspective viewport. 2. Hit the M key to bring up the material editor and hit the get material button. 3. In the Material/Map Brower choose Mtl Library and under the File options Select Open and navigate to the 3DSMax resources found at C:/Program Files/Autodesk/3dsMax 2009/ materiallibraries/3dsmax.

242 Chapter 6: Working with Particle Systems

4. After selecting the 3DSMax file, the material will be brought into the Material/Map Browser. 5. Clicking on the different materials shows the material in the preview window, or choose the small icon option to view an icon of all materials. 6. Choose a material by double clicking on it. The material then appears on one of the spheres of your material editor. Drag that material to your sphere. 7. Click on the Show Standard Map in the Viewport to make the texture show up. Also hit shift+Q to quick render your object. 8. From the quick render screen choose the save image icon and choose the png format. Give your texture a name and hit Save . 9. An options panel appears. Make sure you check the Alpha channel and choose an appropriate color palette. Then hit Save . 10. Open up your new image in an image processing program such as Photoshop or Gimp and make sure that it ’ s cropped and mipmapped . Resave it as a png. To find out more about mipmapping see the next section.

Figure 6.10 shows a composite of the different places you need to go in 3DSMax to accomplish the steps below. In addition, all this is covered in a video tutorial on the book ’ s website.

Figure 6-10

243 Part II: Working with Models and Data

Finally, place your new texture in PV3D by importing it into the Flash library or placing it in an assets folder in Flex. Then apply it to the appropriate material and place that material on your particle as follows:

//Instantiate the MovieAssetParticle Material from the Library var particleMat:MovieAssetParticleMaterial = new MovieAssetParticleMaterial(“mySphere”,true);

//Apply your material to the ith particle var particles3D:Particles = new Particles(“ParticleContainer”+i); var p:Particle = new Particle(particleMat,1,0,0,0);

In the preceding code block, you first instantiate a MovieAssetParticleMaterial and then apply it to the ith particle.

This may seem a lot just to create a simple texture, but it ’ s worthwhile being familiar with 3DSMax or Blender. You ’ re going to need one of these tools to create good 3D content. In addition, you should also learn to use a good image processor such as Photoshop. You ’ ll use it constantly in conjunction with your 3D creation tools. Photoshop and 3DSMax are the industry standards. But many other good options are available, some of which are free. Mipmapping When it comes to particle fields, mipmapping can save processor cycles, memory, and improve anti - aliasing by allowing your computer to start with pre - filtered, pre - scaled textures. Mipmapping was originally created to improve the quality of a scene by reducing anti - aliasing. Aliasing occurs from projecting a surface onto the viewport. Objects further away from the viewport are drawn with fewer pixels on the screen. As the object moves further and further away, its texture must be scaled down to match the fewer pixels drawn. But this scaling down process can introduce visual artifacts caused by aliasing.

Mipmapping solves this problem as it represents several different scaled versions of the same texture. Mipmaps are created by using textures that are powers of 2 such as 256x256, 128x512, or 32x64. Height and width don ’ t have to match but both must be a power of 2.

The factors of 2 principle works for Flash filters as well. They ’ re optimized by using filter numbers that are factors of 2 such as 2, 4, 8, 16, 32.

Plug-in Media Seb Lee - Delisle is the technical director at Plug - in media and is famous worldwide for his particle effects and entertaining presentations. During the Christmas season, Seb usually releases a snow particle system to the open source community. You can download Seb ’ s snow systems and more from his blog at http://www.sebleedelisle.com . In this section, you take his snow - storm particle effect and combine it with another great blog release found on John Lindquist ’ s site at http://www.pv3d.org . John also releases a ton of PV3D examples so you should take the time to visit his site as well.

244 Chapter 6: Working with Particle Systems

In this section, you combine Seb and John ’ s work to produce the reflective snowy Christmas scene that ’ s shown in Figure 6.11. This is a great example of hacking two different software apps to produce a superior effect.

Figure 6-11

Making it Snow for Christmas You want to start by either going to Seb and John ’ s blogs and downloading the corresponding snow storm and Flint Christmas scene or by downloading the “ Merry Christmas ” code from the book ’ s chapter code.

John uses this Christmas scene as an example of Flint particles. The problem is that his snow is just 2D and doesn ’ t interact with the reflective surface. Seb has 3D snow that does interact with the surface. Here are the steps:

❑ Delete the Flint Particle system from John ’ s code (you ’ ll learn about FLINT in an upcoming section) ❑ Import Seb ’ s snow storm package into John ’ s code and instantiate it using the following code:

snowStorm=new SnowStorm(100, .4,2000);

Also, make sure that you update the snow in your animation loop (enterFrameHandler) or it won ’ t show up.

snowStorm.update();

John uses planes for Christmas ornaments; you can spice this up a bit by replacing the planes with spheres and reducing the segments to 2x2, which makes your ornaments look like crystals.

245 Part II: Working with Models and Data

for ( var i:int = 0; i < total; i++) { var material:ColorMaterial; if((i & 1) == 0) material = new ColorMaterial(0xaa0000); else material = new ColorMaterial(0x00aa00); material.doubleSided = true ; //Add a sphere instead var sphere:Sphere = new Sphere(material, 10, 2,2); sphere.x = Math.cos(i) * (radius-i * 3); sphere.z = Math.sin(i) * (radius-i * 3); sphere.y = i / total * height;

myHolder.addChild(sphere); }

You now have 3D snow and cool looking ornaments, but the snow is out of wack and at this point you need to adjust a few parameters to make it look just right. In this case, we want the falling snow to disappear just as it touches the reflective surface. To accomplish this, adjust the following parameters:

snowStorm= new SnowStorm(100, .4,2000); myHolder.addChild(snowStorm); snowStorm.y=1000;

In addition to changing your particle parameters, you raise your snow ’ s y position so that its particles just touch the reflective surface before being recycled. Next you have to go into the snow class itself and adjust the particle ratio. In the SnowStorm class, in the update method make the following change

if(particles.length/10 < maxFlakes)

This causes your particles to be regenerated more rapidly and closes the gap that appears due to particle lag. No matter what you create, you always need to make adjustments. Get used to going down to sub classes and changing parameters. This is called emotional (or intuitive) programming. You may not know completely how the code works but by changing a few parameters you bring everything into agreement. This can be both dangerous and fun! So make sure that you keep backup copies in case you really mess something up.

Flex is a great tool for surfing logically to classes that you need to modify. Just roll over a method and hold down the command key (MAC) or alt key (PC). When you do so, the method will underscore and when you click on it you ’ ll automatically navigate to that method even if it ’ s in another class.

Finally you want to change the words from MERRY CHRISTMAS to IT ’ S A SNOWY CHRISTMAS. You need to play around with the spacing of the words a little to get it right (this is where a little knowledge of trig is helpful).

//IT’S A var itsaMaterial:Letter3DMaterial = new Letter3DMaterial(0x00cc00); var itsa:Text3D = new Text3D(“IT’S A” , rockwell, itsaMaterial); //SNOWY var snowyMaterial:Letter3DMaterial = new Letter3DMaterial(0xcc0000); var snowy:Text3D = new Text3D( “SNOWY” , rockwell, snowyMaterial); //CHRISTMAS var christmasMaterial:Letter3DMaterial = new Letter3DMaterial(0x00cc00); var christmas:Text3D = new Text3D( “CHRISTMAS” , rockwell, christmasMaterial);

246 Chapter 6: Working with Particle Systems

The final results appear in Figure 6.11 that appeared earlier.

You should note that John ’ s code illustrates a number of really cool effects such as reflection, use of the Letter3D Material, and the rendering of multiple primitives to create a Christmas tree.

Another cool Seb particle effect is scanning. Scanning In scanning, you start from the left and go to the right and whenever you find a color of interest you generate a particle (very similar to the previous discussion on interacting with video). So what you do is draw your stage into your bitmap, and on your “ update ” frame method you update your particles; with the addition of the scan code shown below. Check out the documented code below.

function enterFrame(e:Event):void { //Update your particles updateParticles();

//Scan X is the x position of your line that you’re tracking along the screen. scanX+=6; //6, 12, 18, 24 ... //If your x position is at the end of the frame go back to the beginning. if(scanX > screenW) scanX = 0;

//Go through all the y position values 2 pixels at a time for(var ypos:Number = 0; ypos < screenH; ypos+=2) { // -(ypos/4) gives you a diagonal on your sampling var xpos:Number = scanX-(ypos/4); if(xpos > 0) { //Get the color at the x,y position var col:Number = bmData.getPixel(xpos, ypos); //Make a particle out of if you hit a color greater than black. if(col > 0x000000) { //Two different types of particles addSparkParticle(xpos, ypos, 1); addSmokeParticle(xpos, ypos, 3); //Jump ahead so you don’t make a million particles. ypos+=10; } } }}

A more interesting effect would be to take random pixels across the surface, generating particles whenever there ’ s an intersection with a desired color (see Figure 6.12).

247 Part II: Working with Models and Data

Figure 6-12

The code for scanning random positions is given here:

function enterFrame(e:Event):void {

updateParticles();

for(var i:Number = 0; i < 50;i++) { //Scan random positions xpos = randRange(0, screenW); ypos = randRange(0, screenH);

col = bmData.getPixel(xpos, ypos) > > 16;

if(col > 0xee) {

addSparkParticle(xpos, ypos, 1);

} }

} function randRange(min:Number, max:Number) { var randomNum:Number = (Math.random() * (max-min )) + min; return randomNum; }

You may agree that particles are cool, but require much work to produce. And that ’ s where FLINT comes to the rescue.

248 Chapter 6: Working with Particle Systems Flint Particles After all this work you ’ re probably asking if there ’ s a better way. And one avenue to go, if you want to spare yourself a bit of programming, is to use Flint. Flint is an open source particle system created by Richard Lord (of Big Room) and it works with PV3D. Flint can be downloaded from http://flintparticles .org/ and handles both 2D and 3D particles. The Flint site contains great tutorials and tons of examples with source code provided, so you cover only a few examples in this section, designed to get you started. In the book ’ s code are two viewers, which allow you to see 2D and 3D Flint examples.

Viewing Particle Examples The particle viewers are very similar to the MD2 viewer presented in the last chapter; therefore, the details aren ’ t presented here. Such viewers can come in handy, giving you the ability to quickly sort through a number of particle effects (see Figure 6.13).

Figure 6-13

Go to the book ’ s code and test out the (Flex) 2D and 3D FLINT viewers. With so many great tutorials available on using FLINT on the FLINT website, the basics won ’ t be covered here. Instead, you focus on using FLINT in PV3D.

Using Flint in Papervision3D PV3D ’ s particle system is a little insufficient, whereas FLINT has a more robust particle system. However, initially it may not be apparent how to use Flint with PV3D. FLINT actually has three renderers that can be used in conjunction with PV3D:

❑ PV3DPixelRenderer — renders each particle as a single pixel, is the fastest of the three, and great for high particle density effects. ❑ PV3DParticleRenderer — renders particles according to a designated size and is ideal for less dense particle systems, and you can billboard the particles. ❑ PV3DRenderer — renders a PV3D display object, which means that you can render any 3D object as a particle, but is the slowest of the three as a result.

249 Part II: Working with Models and Data

At the time of this writing, both the PV3DParticleRenderer and PV3DRenderer have issues with some of the Flint color actions.

For this example, you examine the PV3DPixelRenderer as it gives the most brilliant results of the three renderers, as shown in Figure 6.14. John Lindquist has posted a number of FLINT particle examples on his pv3d.org site and this discussion follows his work closely.

Figure 6-14

In the preceding example, you have two spheres orbiting in space and leaving behind a rocket trail. To get FLINT to do this in PV3D, follow these steps:

1. Import the appropriate FLINT and PV3D packages. 2. Declare two FLINT 3D emitters (for your rocket trail) and two spheres (to lead the orbit) and add color materials to your spheres. 3. Declare the Flint Pixel renderer ( PV3DPixelRenderer ). 4. Initiate PV3D, the FLINT emitters, FLINT renderer, and createPixels method. 5. In your animation loop, set the spheres in motion and attach the emitters to your spheres by setting their x and y values equal.

override protected function onRenderTick(event:Event = null):void { //Iterate Oscillation parameter myOsc++; //Execute Sphere Flight Path emitter1.position.x=sphere1.x=200*Math.cos(myOsc/50)*Math.sin(myOsc/100); emitter2.position.x=sphere2.x=200*Math.sin(myOsc/50)*Math.sin(myOsc/100); emitter2.position.y=sphere2.y=200*Math.cos(myOsc/50)*Math.sin(myOsc/100); emitter1.position.z=sphere1.z=200*Math.cos(myOsc/100); emitter2.position.z=sphere2.z=200*Math.sin(myOsc/100); //Rotate holder to create a nonlinear effect on flight path myHolder.rotationY+=.1;

//Call the super.onRenderTick function super.onRenderTick(event); }

250 Chapter 6: Working with Particle Systems

One trick that ’ s used here is that the spheres and emitters are placed in a display object and that object is rotated to give the paths and rocket trails a non - linear look. To see the entire code, consult the book ’ s chapter code.

Getting More Bang Out of Flash The big news is that Flash can now import C/C++ libraries using Alchemy, significantly ramping up its speed and functionality. Alchemy is a research project that allows users to compile C and C++ code that ’s targeted to run on the open source ActionScript Virtual Machine (AVM2). With Alchemy, web application developers can reuse millions of lines of existing open source C and C++ code on the Flash Platform.

Alchemy is primarily intended to be used with C/C++ libraries that have few dependencies. Ideally suited for computation - intensive use cases, such as audio/video transcoding, data manipulation, XML parsing, cryptographic functions or physics simulation. Performance can be considerably faster than ActionScript 3.0, but it ’ s still a lot slower than native C++ because the AVM2 still doesn ’ t support multi - threading.

Combining Pixel Bender and Alchemy allows you to render large particle sets, up to 300,000 – a number unheard of in the Flash community before Alchemy. It ’ s pretty amazing! Download and install Alchemy (from http://labs.adobe.com/technologies/alchemy/). Check out the book ’ s website for more on Alchemy.

Summary This chapter gave a broad overview of particles in both PV3D and CS4. You started with the PV3D particle system and added some of your own particles to it and created a starry panorama. You built a 3D particle system from scratch and created a Flash CS4 glowworm. You learned how to slice and explode particle systems, and how to use them to interact with video. You took a look at the great work that Plug - in Media is doing and learned how to incorporate FLINT into PV3D.

251

Geocoding, XML, and Databases

Bringing external data into your 3D application significantly enhances user experience, increases application value, and reduces development time. Showing your client a nifty 3D animation may not sell your product, but telling him that he can change it himself and won ’ t need your help after the product is completed will seal the deal. But chances are he ’ ll need you again, for a follow - up project. And you ’ ll be able to develop software more rapidly using external data sources as opposed to hard - coding your applications.

PV3D wasn ’ t built to be data - driven; you ’ ve got to add the data piece in yourself. In this chapter you discover how to do this through a series of examples ranging from building an XML planetarium to a MySQL 3D Google Maps updater. Using the Flex data wizard, you find out how you can automatically generate PHP code, which you use to make server requests. Even though the emphasis in this chapter is on using PHP, the techniques that you learn can be easily extended to ASP.NET, Java, and ColdFusion.

You start this fascinating journey with Geocoding.

Geocoding Pointing to a realistic place on a map and navigating to it on the web is called “ Geocoding. ” Geocoding has gained momentum in the open source community, which for some is a counter - intuitive concept. The success of Google maps illustrates the point. After developing a robust interface, which parsed terabytes of satellite imagery and road data, they just gave it all away. This approach of “ giving it away for free ” has been met with great success, to the consternation of those who are scrambling to determine “ value ” on the web. Part II: Working with Models and Data

Google makes getting started with its Flash API really easy. On the API doc site there ’ s a step - by - step procedure for putting up your “ hello world ” map, tons of tutorials and helpful articles, and tons and tons of free examples with full source code. To get started visit their site at http://code.google .com/apis/maps/documentation/flash/ .

Once there, you want to follow the procedure for getting your Map key and setting up your first Google Flash Map:

1. Sign up for a Google Maps API Key. 2. Download the Google Maps API for Flash SDK. 3. Read the Developer ’ s Guide. 4. Read the API Reference. 5. Join the announcements group to receive important updates.

Once you ’ ve put up your first map in 2D, you ’ re ready to go to 3D and PV3D. So let ’ s get started.

Putting a Google Map on a PV3D Prim Putting a Google map on a PV3D primitive is extremely easy. All you need to do is to turn the map into a Movie (using the MovieMaterial class) and then place it on a primitive. It ’ s a simple two - step process! And you can do it in only six lines of code:

1. Turn Google Maps into a Movie:

movie.graphics.beginFill(0xFFFFFF); movie.graphics.drawRect(0,0,800,600) movie.addChild(Application.application.map); movie.graphics.endFill(); mat = new MovieMaterial(movie, false, true,true,new Rectangle(0,0,800,600));

2. Place that movie onto a primitive:

sphere = new Sphere(mat, 600, 32, 32);

In this case, you use a sphere, but you can use any primitive you want. The results are shown in Figure 7.1.

254 Chapter 7: Geocoding, XML, and Databases

Figure 7-1

The real key is just converting the Map into a movie using the MovieMaterial class and placing it on a primitive. Everything else is just simple Papervision3D and the Google Flash API.

You can run the API in either Flash or Flex. In the following set of examples, you use Flex, but the procedure is very similar for Flash and you should consult the Google Flash API docs for creating Flash apps.

Using Air (Flash Sandbox Security Issue) One of the first problems you run into when working with Google maps and PV3D is the Flash Sandbox issue. Whenever you use Google maps as a MovieMaterial (that is, BitmapData.draw ) in PV3D you get a Flash security sandbox violation. When processing a MovieMaterial , PV3D uses the BitmapData.draw method in MovieMaterial.bitmapDraw. The Flash sandbox error occurs because the method won ’ t yield a result if the origin of BitmapData is not in the same domain. Basically Flash doesn ’ t want to grab this information from another server without permission.

In your case, you don ’ t have access to the server that you ’ re loading the image data from, and therefore can ’ t create an appropriate cross - domain policy file (crossdomain.xml ) on that server. The easiest solution to this problem is to use an Air application, which will only reside on your desktop and not on the web. But don ’ t fret; in the next section, you fix this problem using the Flash 10 player – .

In Adobe Flex it ’ s easy to create an Adobe Air Application. Create a new Flex application and select Desktop application as shown in Figure 7.2.

255 Part II: Working with Models and Data

Figure 7-2

One big difference that you ’ ll notice immediately is that a Flex web application extends the Application class, whereas an Air application extends the WindowedApplication class.

When building a Flex PV3D Google maps application, you have three important things that you must do to get your application to run correctly.

❑ You must create an Air application (as discussed above). ❑ You must obtain a Google Key based on an application URL and put both in your application:

key=”ABQIAAAA4LpFEu2In0mhf90kMq6mnRSIIINVTCMn3- 6sCr8XLDIdy43daBStmo04li3ZOEGU0xaHRQBCkKZPxA” url=”http://www.professionalpapervision.com/demos/web/geocoding/”

You must place the Google Maps Flash API swc files in your Flex lib folder

Failure to perform any of these three steps will cause your application to fail upon testing or deployment. The complete documented code for the application that you saw previously in Figure 7.2 is provided next.

The code is very much like other applications presented in the book, so it isn ’ t covered here in detail. But if you ’ ve done your homework and worked through a few of the examples on the Google Flash docs website it ’ ll make total sense. Otherwise, check out the book ’ s video on the book ’ s website.

< ?xml version=”1.0” encoding=”utf-8”? > < mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml” xmlns:maps=”com.google.maps.*” layout=”absolute” width=”1200” height=”800” backgroundGradientAlphas=”[1.0, 1.0]” backgroundGradientColors=”[#9B9FAB, #05184C]”>

< maps:Map id=”map” //Google Map Key key=”ABQIAAAA4LpFEu2In0mhf90kMq6mnRSIIINVTCMn3- 6sCr8XLDIdy43daBStmo04li3ZOEGU0xaHRQBCkKZPxA”

256 Chapter 7: Geocoding, XML, and Databases

//URL where application will reside or is tied to (by Map Key) url=”http://www.professionalpapervision.com/demos/web/geocoding/” mapevent_mapready=”onMapReady(event)” width=”100%” height=”100%”/ >

< mx:Script > < ![CDATA[ //Google Map Imports import com.google.maps.MapEvent; import com.google.maps.Map; import com.google.maps.MapType; import com.google.maps.LatLng; import com.google.maps.MapMouseEvent; import com.google.maps.controls.*;

//Papervision Imports import org.papervision3d.view.Viewport3D; import org.papervision3d.scenes.Scene3D; import org.papervision3d.cameras.Camera3D; import org.papervision3d.objects.primitives.*; import org.papervision3d.render.BasicRenderEngine;

import org.papervision3d.materials.MovieMaterial; import org.papervision3d.events.InteractiveScene3DEvent; import org.papervision3d.materials.MovieMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.*;

//Flash imports import flash.display.Bitmap; import flash.display.MovieClip; import flash.display.Sprite; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Rectangle; //Application Import import mx.core.Application;

//Properties private var viewport: Viewport3D; private var scene: Scene3D; private var camera: Camera3D; private var renderer: BasicRenderEngine; private var sphere:Sphere;

// This is the movieclip that we’ll use as the texture. public var movie:MovieClip = new MovieClip(); public var movieParent:MovieClip = new MovieClip(); // Button that toggles overlayed interactive map private var showbutton:MovieClip = new MovieClip(); // A “MovieMaterial” will use a movieclip as a texture. private var mat:MovieMaterial; (continued)

257 Part II: Working with Models and Data

(continued) //Container for easier scene manipulation public var primitiveContainer:DisplayObject3D= new DisplayObject3D;

//Generate the Google Map private function onMapReady(event:Event):void { map.setCenter( new LatLng(37.4419, -122.1419), 13, MapType.NORMAL_MAP_TYPE); //Control position var bottomRight:ControlPosition = new ControlPosition(ControlPosition.ANCHOR_TOP_LEFT, 10, 10); var myMapTypeControl:MapTypeControl = new MapTypeControl(); myMapTypeControl.setControlPosition(bottomRight); map.addControl(myMapTypeControl);

//Initiate Papervision init(); } private function init(): void { viewport = new Viewport3D(800, 600, false, false ); pv3d.rawChildren.addChild(viewport); //Instantiates a Scene3D instance scene = new Scene3D(); //Instantiates a Camera3D instance camera = new Camera3D(); scene.addChild(primitiveContainer); //Renderer draws the scene to the stage renderer = new BasicRenderEngine(); movie.graphics.beginFill(0xFFFFFF); movie.graphics.drawRect(0,0,800,600) //movie.buttonMode=true movie.addChild(Application.application.map); movie.graphics.endFill(); mat = new MovieMaterial(movie, false , true, true , new Rectangle(0,0,800,600));

// mat.interactive = true; mat.smooth= true ; movieParent.addChild(movie); // Make it invisible. movieParent.alpha = 0; // Add the movieParent movieclip to the stage. pv3d.rawChildren.addChild(movieParent); //Add the movie to the sphere sphere = new Sphere(mat, 600, 32, 32);

//Add the primitive to the container primitiveContainer.addChild(sphere);

//Set the camera camera.rotationY=180; camera.z=1000;

//Set up enterFrame event addEventListener(Event.ENTER_FRAME, onEnterFrame);

} 258 Chapter 7: Geocoding, XML, and Databases

//Define enterFrame Method, render the PV3D Scene and animate the primitive private function onEnterFrame(e:Event): void { renderer.renderScene(scene, camera, viewport); }

]] > < /mx:Script >

< mx:Canvas x=”62” y=”44” width=”923” height=”778” id=”pv3d”> < /mx:Canvas > < /mx:WindowedApplication >

All right, this is well and good, but your client is screaming at you because he wants both a desktop application and a web application. To get this working on the web, you must use Flex 4 (or Gumbo).

Putting it on the Web with Flex 4 (Gumbo) The great thing about Flex 4 Gumbo (as with Flash CS4) is that it has a native z component and enables you to place 3D content on the web without the use of PV3D. You ’ re no longer restricted to using just an Air application as in the PV3D case above.

Placing a Google Map on a CS4 Plane is easier in Flex 4 (Gumbo) than it is in PV3D. All you do is throw your map into a movie clip using the code below, and then center it to get your map pivot correct. The following code snippet sets your map in a movie clip, centers its pivot, sets map controls, places your movie in a Flex canvas, and centers your movie on the stage:

//Place your map in a movie so you can position its pivot movie.addChild(map);

//Set your map pivot map.x=-map.width/2; map.y=-map.height/2;

//Add Map Controls map.addControl( new ZoomControl()); map.addControl( new PositionControl()); map.addControl( new MapTypeControl());

//Put your movie in a Flex Canvas mapHolder.rawChildren.addChild(movie);

//Center your Movie movie.z=0; movie.x=450; movie.y=300;

This is the great power of CS4 over PV3D 2.0. You can now bring native Flex components into your 3D environment in conjunction with your 3D objects. Bringing Flex components into PV3D is a difficult task and requires the use of the draw BitmapData feature. But with CS4 it occurs natively, and those components can be manipulated in 3D as well.

259 Part II: Working with Models and Data

The results are shown in Figure 7.3.

Figure 7-3

The code for the button components switches the label of the button upon transition to stop and start:

//Pause or rotate button for your map private function pauseRotate(event:MouseEvent):void {

if (mouseBtn.label== ”Stop” ){ mouseBtn.label= ”Start” ; stopRotation = true ;

} else { mouseBtn.label= ”Stop” ; stopRotation = false ; } }

The code for your slider uses the change method to dynamically change your map ’ s rotation speed and direction:

//Map Slider private function mySliderHere(event:SliderEvent):void {

rotationValue=-mySliderValue.value;

}

The rest of the code is typical to what has already been demonstrated in this book or to Flash Google Maps and can be found on the API docs site. The full document code is given here:

< ?xml version=”1.0” encoding=”utf-8”? > < maps:Map xmlns:maps=”com.google.maps.*” id=”map” mapevent_mapready=”onMapReady(event)” width=”800” height=”600” 260 Chapter 7: Geocoding, XML, and Databases

url=”http://www.professionalpapervision.com/demos/projects/tour/” key=”ABQIAAAA4LpFEu2In0mhf90kMq6mnRS- 6gvR7plocG5jEAw_XBK5krTJ_RRxjCMUXsM0nZ0Gam53OPR26zHU5w”/ >

< ![CDATA[ import mx.events.SliderEvent; import com.google.maps.*; import com.google.maps.controls.*; import mx.events.MoveEvent;

//Create map holder, oscillation angle, and rotation parameters private var myAngle:Number=0; private var movie:MovieClip = new MovieClip(); private var stopRotation:Boolean = false ; private var rotationValue:Number = -.05;

//When the map is ready run this function private function onMapReady(event:Event): void { this .map.setCenter( new LatLng(40.736072,-73.992062), 14, MapType.NORMAL_MAP_ TYPE); //Place your map in a movie so you can position its pivot movie.addChild(map); map.x=-map.width/2; map.y=-map.height/2; //Add Map Controls map.addControl( new ZoomControl()); map.addControl( new PositionControl()); map.addControl( new MapTypeControl()); //Put your movie in a Flex Canvas mapHolder.rawChildren.addChild(movie); //Center your Movie movie.z=0; movie.x=450; movie.y=300;

addEventListener(Event.ENTER_FRAME, frameLoop);

}

//Pause or rotate button for your map private function pauseRotate(event:MouseEvent): void {

if (mouseBtn.label==”Stop” ){ mouseBtn.label= ”Start” ; stopRotation = true ;

} else { mouseBtn.label= ”Stop” ; stopRotation = false ; } }

//Map Slider private function mySliderHere(event:SliderEvent): void { (continued) 261 Part II: Working with Models and Data

(continued) rotationValue=-mySliderValue.value;

}

//Map animation loop-enable rotation private function frameLoop(event:Event): void { //Iterate animation variable //Rotate map if stopRotation is equal to false if (!stopRotation){

movie.rotationX+=rotationValue;

} }

//Set up canvas components below

]] > < /mx:Script >

< mx:Canvas x=”0” y=”0” width=”60” height=”73” id=”mapHolder”> < mx:Canvas x=”87” y=”7” width=”267” height=”65” backgroundColor=”#DFAAF7”> < mx:Button x=”19.5” y=”10” label=”Stop” id=”mouseBtn” click=”pauseRotate(event)” /> < mx:HSlider x=”87.5” y=”15” minimum=”-4” maximum=”4” id=”mySliderValue” value=”.05” change=”mySliderHere(event)” / > < mx:Label x=”108.5” y=”33” text=”Speed and Direction”/> < /mx:Canvas > < /mx:Application >

This is just fantastic. You now have 3D access to an entire Google world (literally) of potential interaction. Use it to make a game, a worldwide presentation, or a virtual tour. The options are limitless. You now create a virtual tour using an XML backend.

XML Learning a bit of XML can really liven up your web application. XML is used in a vast majority of modern websites; it ’ s used for data - driven websites, RSS feeds, audio/video playlists, closed captioning, and configuration files, to name a few. But how to incorporate it into PV3D may seem a bit of a mystery.

The good news is that E4X (ECMAScript) makes everything much easier, and it treats XML as characters, integers, and Booleans (as opposed to being accessed at the object level). This translates into an easy - to - use dot syntax method, which lets you access XML nodes by name and throws out the difficult “ child - node ” language of past versions of Flash.

A complete treatment of XML is beyond the scope of this text. For more info on XML consult the book ’ s website and blog. In addition, a great book on XML for both Flex and Flash is “ XML and E4X ” by Sas Jacobs.

In the following section you jump right into XML.

262 Chapter 7: Geocoding, XML, and Databases

What Is XML Anyway? The first thing that you learn about XML is that it ’ s like HTML, but you can make your own tags. That may not make any sense to you, because it ’ s only half the story. After making your own tags, you must create a program that interprets (or parses) your tags. Parsing is like unwrapping a Christmas present which contains various items. Once the items are unwrapped, they ’ re identified and placed in their proper places (or your house will be a mess when your in - laws come over).

Below, we give a typical XML file where client contact and project info is stored. The first line of code is the XML processing instruction, which includes your version as well as encoding info. The first node that ’ s placed in the file is the root node. In this case, the name of the root node is clients . Below that tag you have logInfo and fileNum and then the XML document contains two client nodes (or elements) with additional child nodes nested in them.

Business Log 7 477751 [email protected] 123-45-6789 <![CDATA[Laughing <b>Spheres</b>!]]> Tomorrow <![CDATA[Happy <b>Spheres</b>]]> Next Week [email protected] 987-65-4321 <![CDATA[Standing <b>Cubes</b>!]]> Yesterday <![CDATA[Falling <b>Cubes</b>]]> Last Week

This XML document is referred to as being well formed , as each tag has a corresponding closing tag, and the order in which the closing tags appear is exactly the opposite of the order in which the opening tags appear within the file. For example, child elements must close before their parent elements, and nested elements close in the reverse order of their opening.

263 Part II: Working with Models and Data

What may look a little strange are the CDATA tags found between the title tags. The CDATA tags just tell your XML not to process the info contained therein as XML. This gives you the ability to use formatting within these tags: such as < b > < /b > to make text boldface. Flash only supports a limited number of HTML tags, but you also have robust CSS support.

Having well - formed XML is very important. If your XML is not well formed, it won ’ t work in Flash or Flex.

Each time you make an XML file you should validate its XML structure before trying to use it in Flash or Flex. This can save a lot of time. You can validate XML by opening the file in Dreamweaver and choosing File Validate As XML. If it validates, no error message is returned and your file is well formed. Otherwise, you get an error. When checking errors in XML, don ’ t just look at the error line, also look at the line (or lines) before the error, as error messages can cascade down and fool you.

XML elements are known as nodes; even the information held between opening and closing tags is called a text node. You retrieve data from two places in your XML files – from text nodes and attributes. Attributes are like “ name - value ” pairs that appear inside of an opening element such as

< Client id=”01” >

where id is the label (or attribute tag name), and 01 is its value. Attributes can be used for more than just extracting data. They can also be used to search XML files (or as data filters). Using E4X E4X stands for ECMAScript for XML. In AS3, Flash uses XML as a native data format, which means you can create XML in AS3 natively just the same way you work with any other data type; and in many cases it ’ s easier to do in AS3 than XML.

For the XML clients example given above, to retrieve info in between element tags (or get a text node) using e4x, you just use dot syntax:

dataXML.client[0].email //Retrieves the text node [email protected]

And to retrieve info from an attribute use the @ symbol as follows:

dataXML.client[1].@id //Retrieves the value 02

Or to drill down deeper into your XML structure you could use the following dot syntax:

dataXML.client[0].projects.project[1].endDate //Retrieves Next Week

264 Chapter 7: Geocoding, XML, and Databases

In all three cases, dataXML was assigned in Flex and represents your root document tag ( clients ). The generating code for the examples above can be found in the book ’ s Flash code in the ClientsLog folder.

The great thing is that you ’ re retrieving XML data based on actual tag names. Another cool mechanism is the descendants operator (.) which lets you skip all descending tags and go directly to the tag name of interest. When you deal with more advanced XML structures, you ’ ll find the descendants operator extremely useful.

As mentioned earlier, a full treatment of XML is beyond the scope of this book, and we assume that you ’ ve consulted the book ’ s website and blog for further info (or read Jacob ’ s book). You now proceed with the applications, and we point out the pertinent XML as it arises.

Taking a Historic Tour If you ’ re into historic preservation, you ’ ll find this section very useful. The project below is adapted from a prototype developed (by the author) for the City of Covington, Kentucky, and was used as a development tool for the city ’ s historic preservation activity. The prototype has the following features:

❑ 3D rotating Map (x and z rotation sliders) ❑ Combo and Button and map type selection (Map, Satellite, Hybrid, Terrain) ❑ Pan and Zoom controls both Google and Custom Buttons ❑ XML driven map markers with text, audio, and image popup Flex custom component ❑ Audio engine that plays an audio description when clicked ❑ List Box with clickable navigation ❑ Toggle driving directions with navigation icon (penguin) and text directions

The application is shown in Figure 7.4, and can be obtained from the book ’ s chapter code.

Figure 7-4

265 Part II: Working with Models and Data

At the heart of all these features is the XML file (named markersTour.xml ) shown here:

. . . (not all data is shown). . .

This particular XML file doesn ’ t use text nodes to hold data in between element tags; all of its data are attributes. Each marker contains eight attributes: name, address, lat (latitude), lng (longitude), type, audio, image, and descript (description). And they ’ re self - explanatory. But what may not be so self - explanatory is how you get this data into Flex and then into your Google popup information window. Importing XML into Flex and Flash Loading an external XML document can be accomplished by assessing a static XML document or by assessing one created from a database. Both Flex and Flash have several different ways of bringing in external XML data; both use the URLLoader class shown in the getXml() method below:

public function getXml():void { var urlRequest:URLRequest = new URLRequest(“data/markersTour.xml”); var xmlLoader:URLLoader = new URLLoader(urlRequest); xmlLoader.addEventListener(“complete”, readXml); xmlLoader.load(urlRequest);//For this case, this step is optional }

In the getXml() method above, the URLRequest is instantiated as xmlString , which represents the URL of the file you ’ re going to load. It ’ s then loaded using the URLLoader , which is instantiated as xmlLoader .

So in summary, the URLRequest represents the file you want to load and the URLLoader loads that file.

Then an event listener listens for the URLLoader complete event and then calls the method readXML , which then parses the XML data. It ’ s important to note that you can use URLLoader to load any type of plain text file, not just XML. The final line makes the xmlLoader load the URLRequest , which is optional in this case, because you ’ re transferring the event data to the readXml() method.

266 Chapter 7: Geocoding, XML, and Databases

Feeding XML Data into Your Popup The XML data is transferred to makersXML , an XML variable, which is used to create a markers XMLList . XML lists occur whenever there are repeating elements in a XML file. As shown in the code below, the markers XMLList variable is easily parsed using e4x. You should note the use of the modelLocator variables, which are used to transfer data to the Flex popup component. Model Locator is part of the Cairngorm architecture and is covered in the chapter on MVC.

What ’ s important here is that you get the flow of how the XML is being parsed in a more advanced application.

public function readXml(event:Event): void { var markersXML:XML = new XML(event.target.data); var markers:XMLList = markersXML.marker; var markersCount:int = markers.length(); var i:Number; for (i=0; i < markersCount; i++) { var markerXml:XML = markers[i]; //Cairngorm values modelLocator.myName=markerXml.@name; modelLocator.myAddress=markerXml.@address; modelLocator.myAudio=markerXml.@audio; modelLocator.myDescript = markerXml.@descript; modelLocator.myImage = markerXml.@image; modelLocator.myType=markerXml.@type; modelLocator.myLat=markerXml.@lat; modelLocator.myLgn=markerXml.@lng;

//Create the latlng variable var latlng:LatLng = new LatLng(markerXml.@lat, markerXml.@lng); acMarkers.addItem({label: markerXml.@name, marker: latlng, address: markerXml.@address}); //Create marker function var marker:Marker = createMarker(latlng, modelLocator.myName, markerXml.@address, markerXml.@type, modelLocator.myImage); map.addOverlay(marker); } }

Once the data is parsed using the @ symbol (as discussed earlier for attributes) it ’ s sent to the create marker method.

When it comes to working with markers, a lot of people want to know how to bring in media to their markers such as images, audio, video, 3D models, and so on. And here ’ s the coolest trick that enables you to get the job done easily.

The most significant trick you can use in Flex when working with markers, is to create a custom component for your marker popup. Using your custom popup component enables you to bring in any type of media that you want: audio, text, images, video, 3D models, and so on, as shown in Figure 7.5.

267 Part II: Working with Models and Data

Figure 7-5

What ’ s very handy is that the component has a remove from and add to stage method, which enables you to start or stop a process, such as turn off your audio when you hit the popup close button as shown in the method below:

private function leaveStage():void{ if(soundChannel){ //Stop audio when the component leaves the stage and set the label. soundChannel.stop(); SoundBtn.label=”Play Audio”; }}

In addition, you can fill the popup with as much code as you like to accomplish your purpose (such as an audio player in the case above). The custom component acts like a class with a “ visual view ” for development purposes; speeding development, and keeping everything modular. The entire code can be downloaded from the book ’ s website.

Next, you delve a little further into PV3D and CS4 and build a planetarium.

Building a Planetarium in PV3D Creating a planetarium, in principle, is easy.

1. You grab star data from a site like NOMAD ( http://www.nofs.navy.mil/nomad/ ). 2. Put it in an XML file. 3. Import that file into PV3D, and align your stars according to that data.

But you have two problems: PV3D ’ s Particle Material class doesn ’ t have a star image, and PV3D ’ s Particle Field class distributes particles randomly where you want to distribute them according to your XML star data (see Figure 7.6).

268 Chapter 7: Geocoding, XML, and Databases

Figure 7-6

Solution The solution to these two problems is to open up the classes in question and add the required functionality. Luckily, in Chapter 6 you did this for the ParticleMaterial class. So all you have to do is open up the Particle Field class and add the required functionality. In this case, it ’ s best just to make a copy of the file and rename it SkyMapArray . This keeps things working and gives you a reference to go back to in case you mess up the file with your modifications . . . and eventually you will!

After you ’ ve renamed the file, change the name of the public class and constructor function to SkyMapArray .

In the constructor method, create a variable that imports the starArray , which holds your parsed star data.

public function SkyMapArray(mat:ParticleMaterial, starArray:Array, particleSize: Number=4, skyRadius:Number = 2000)

With the starArray imported you can now create the method that distributes your data. The starArray is arranged as shown above which means that Abbreviation in the 0th position, Latitude in the 1st, Longitude in the 2nd, and Magnitude in the 3rd.

starArray(Abbreviation, Latitude, Longitude, Magnitude)

The code is essential to know when positioning your stars as shown below. In the code, the array iterates over only a quarter of the values as everything is parsed in terms of four values. The myX , myY , and myZ coordinates are generated using the spherical coordinates equations discussed in the Chapter 3 on Prims. The size of your star is changed based on the star magnitude. This simulates distance. And finally the stars are thrown into the addParticle method, which places them on the stage.

269 Part II: Working with Models and Data

//Iterate only over a quarter of the values var myPartNum:Number = starArray.length/4; for( var i:Number = 0; i < myPartNum; i++ ) { //Distribute your star based on Spherical Coordinates var myX:Number=skyRadius*(Math.cos(2*Math.PI*starArray[4*i+1]/24)*Math.sin((90- starArray[4*i+2])*Math.PI/180)); var myZ:Number=-skyRadius*(Math.sin(2*Math.PI*starArray[4*i+1]/24)*Math.sin((90- starArray[4*i+2])*Math.PI/180)); var myY:Number=skyRadius*Math.cos((90-starArray[4*i+2])*Math.PI/180) //Change size of your star based on brightness var mySize:Number=size*(3-starArray[(4*i+3)]);

addParticle(new Particle(material as ParticleMaterial, mySize, myX, myY, myZ)); }

Finally, you need to bring in your XML using the URLLoader class already discussed and use the split command to parse your CSV (comma - separate values) data and place that data into a starArray .

starArray=markersXML.myStars.split( “,” );

The XML file used for the star data, shown below, is just one huge CSV file. CSV stands for comma - separate values and is an industry standard text format for importing and exporting data between applications.

< myStarData > < myStars > AND, 2.065, 42.316, 0, AND, 1.161, 35.616, 1, AND, 0.655, 30.85, 1, AND, 0.139, 29.083, 1, . . . < myStars > < myStarData >

Flash handles CSV data very easily by using a split command to separate the data, and then by placing that data into an array. Once in an array the data can be easily handled using an index.

Once your starArray is created you just stuff it into the instance of your SkyMapArray and add it to your scene, and behold the heavens!

stars = new SkyMapArray(particlemat,starArray,10, 5000); scene.addChild(stars);

So in principle it’ s easy although there were a few things to do to get the job done. The entire documented code for the planetarium is given below:

< ?xml version=”1.0” encoding=”utf-8”? > < mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” creationComplete=”getXml()” backgroundGradientAlphas=”[1.0, 1.0]” backgroundGradientColors=”[#000001, #0D0D83]” > < mx:Script > < ![CDATA[

270 Chapter 7: Geocoding, XML, and Databases

//Flash imports import flash.display.Sprite; import flash.events.*; import mx.events.*; import flash.events.KeyboardEvent; import mx.collections.ArrayCollection; import mx.rpc.events.ResultEvent;

//Papervision imports import org.papervision3d.cameras.*; import org.papervision3d.events.InteractiveScene3DEvent; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.primitives.*; import org.papervision3d.render.BasicRenderEngine; import org.papervision3d.scenes.Scene3D; import org.papervision3d.view.Viewport3D;

//Lively Modified PV3D class import org.papervision3d.objects.special.SkyMapArray; import org.papervision3d.materials.special.ParticleMaterial;

//Declare basic PV3D variables private var viewport: Viewport3D; private var scene: Scene3D; private var camera: FreeCamera3D; private var renderer: BasicRenderEngine;

//Basic Planetarium variables private var particlemat:ParticleMaterial; private var stars:SkyMapArray; private var myMouseDown:Boolean = false ; private var starArray:Array= new Array();

//Bring in XML CSV (comma separate value) data public function getXml(): void { var urlRequest:URLRequest = new URLRequest(“data/starData.xml” ); var xmlLoader:URLLoader = new URLLoader(urlRequest); xmlLoader.addEventListener( “complete” , readXml); }

//Split data and place it in the star array and initiate PV3D public function readXml(event:Event): void { var markersXML:XML = new XML(event.target.data); starArray=markersXML.myStars.split( “,” ); initPV3D(); }

//Initiate PV3D private function initPV3D(): void { //viewport = new BasicRenderEngine(width, height, scaleToStage, interactive); viewport = new Viewport3D(550, 400, true , true ); pv3Canvas.rawChildren.addChild(viewport); //instantiates a Scene3D instance scene = new Scene3D(); (continued)

271 Part II: Working with Models and Data

(continued) //instantiates a Camera3D instance camera = new FreeCamera3D(); camera.zoom=100; //renderer draws the scene to the stage renderer = new BasicRenderEngine(); //Get star particle and throw it into your star array particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_STAR); stars = new SkyMapArray(particlemat,starArray,10, 5000);

//Add your stars to the stage and set your camera coordinates scene.addChild(stars); camera.x=camera.y=camera.z=0; camera.zoom = 6; camera.focus = 100;

//set up enterFrame event addEventListener(Event.ENTER_FRAME, onEnterFrame); //Create your mouse listeners so you can zoom in and out addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); addEventListener(MouseEvent.MOUSE_UP, onMouseUp); addEventListener(MouseEvent.ROLL_OVER, onMouseUp);

//Mouse down and up function function onMouseDown(): void { myMouseDown = true ; }

function onMouseUp(): void { myMouseDown = false ; }}

//Define enterFrame Method, render the PV3D Scene and animate private function onEnterFrame(e:Event):void { renderer.renderScene(scene, camera, viewport); //Zoom camera in and out depending on mouse state if (myMouseDown) { if (camera.zoom < 15){camera.zoom+=.1;} } else { if (camera.zoom > 6){camera.zoom-=.1;} } //rotate your camera based on mouse position camera.rotationY = camera.rotationY + (mouseX-stage.width / 2) / 500; camera.rotationX = camera.rotationX-(mouseY-stage.height / 2) / 500; if (camera.rotationX < = -90) { camera.rotationX = -90; } else if (camera.rotationX > = 90) { camera.rotationX = 90; } } ]] > < /mx:Script > 272 Chapter 7: Geocoding, XML, and Databases

< !-Set Canvas for PV3D- > < mx:Canvas x=”10” y=”10” width=”89” height=”94” id=”pv3Canvas”> < /mx:Canvas > < /mx:Application >

At this point, you’ re probably wondering how difficult it might be to construct a planetarium in Flash CS4.

Building a Flash CS4 Planetarium Creating a planetarium in Flash CS4 is a natural extension of the previous discussion. As CS4 has a native z component, you can create your planetarium with significant code savings – using only 49 lines. In the previous PV3D example, you were inside of your star sphere looking out. To make things more interesting, in this example you’ re on the outside looking in. This is of course a little nonphysical, as stars don’ t sit on a sphere but extend out light years. However, the effect is pretty cool as you can see in Figure 7.7.

Figure 7-7

The figure demonstrates being on the outside looking in on your planetarium. Creating this application requires two main steps:

1. Place a star object into your library (see Figure 7.8) so you can instantiate it onto the stage as you would your particles class. This step saves you tons of code: var myStar = new Star();

Figure 7-8 273 Part II: Working with Models and Data

2. Treat your stars like a particle system. This lets you stuff them into an updatable array so you can rotate them:

myParticles[j]

The rest of this stuff you ’ ve seen before. For a video explanation, you can check out the book ’ s website under this section. The full document code is given here:

import flash.events.*; import flash.display.*;

//Create star parameters var myStar:Star; var skyRadius:Number = 240; var myOsc:int=0; var starHolder:MovieClip= new MovieClip();

//Create a star array to hold CSV data and particle array var starArray:Array=new Array(); var myParticles = new Array();

//XML Statements used to bring in XML data var urlRequest:URLRequest = new URLRequest(“data/starData.xml”); var xmlLoader:URLLoader = new URLLoader(urlRequest); xmlLoader.addEventListener(“complete”, readXml);

//XML parser and CSV splitter function readXml(event:Event):void{ var markersXML:XML = new XML(event.target.data); starArray=markersXML.myStars.split(“,”); //Set the initial star system in place and add to a Movie holder for(var i:uint = 0; i < starArray.length/4; i++){

//Instantiate stars and place them in a particle array var myStar = new Star(); myParticles.push(myStar); starHolder.addChild(myStar);

//Position stars (x, y, z) on stage myStar.x=skyRadius*(Math.cos(2*Math.PI*starArray[4*i+1]/24)*Math.sin((90- starArray[4*i+2])*Math.PI/180)); myStar.z=-skyRadius*(Math.sin(2*Math.PI*starArray[4*i+1]/24)*Math.sin((90- starArray[4*i+2])*Math.PI/180)); myStar.y=skyRadius*Math.cos((90-starArray[4*i+2])*Math.PI/180);

//Scale stars according to magnitude and give a rand rotation myStar.scaleX=myStar.scaleY=(1/9)*(3-starArray[(4*i+3)]); myStar.rotation=180*Math.random(); }

//Add stars to star holder and position the holder addChild(starHolder); starHolder.x=400; starHolder.y=300;

274 Chapter 7: Geocoding, XML, and Databases

//Create your animation loop using an Enter Frame Listener this.addEventListener(Event.ENTER_FRAME, onEnterFrame); }

function onEnterFrame(event:Event):void{ //Increment oscillation parameter and iterate over stars myOsc++; for(var j:uint = 0; j < starArray.length/4; j++){ //Set new x, y, z for your stars as they rotate myParticles[j].x=skyRadius*(Math.cos(2*Math.PI*starArray[4*j+1]/24+myOsc/180) *Math.sin((90-starArray[4*j+2])*Math.PI/180)); myParticles[j].z=- skyRadius*(Math.sin(2*Math.PI*starArray[4*j+1]/24+myOsc/180)*Math.sin((90- starArray[4*j+2])*Math.PI/180)); myParticles[j].y=skyRadius*Math.cos((90-starArray[4*j+2])*Math.PI/180); }}

XML is great fun. It lets you store your data externally to your application. However, up to this point, you can ’ t change your data dynamically, and your users can ’ t permanently enter or change information. You need an intermediate language such as PHP, Coldfusion, or ASP to accomplish this. By using one of these intermediate languages, you can write your data dynamically to a flat - file, or MySQL database, for example

Eventually, you ’ re going to find yourself saying (or thinking), oh CRUD!

Working with MySQL and PHP If you ’ ve been working with databases any amount of time you ’ ve probably heard the acronym CRUD, which stands for C - create, R - read, U - update, and D - delete. Essentially, every database performs these tasks: create records, read records, delete records, and update records. Using databases allows you to take your website to the next level. You ’ re able to store, search, delete, and change data dynamically. And large numbers of users will be able to interact dynamically with your data as well.

As in the case for XML, you won ’ t get a blow - by - blow explanation of databases as there are many great books out there on how to use PHP and MySQL

What ’s a Database Anyway? Don ’ t get too frightened when you hear the word database – it ’ s just a table with rows and columns like an Excel spreadsheet. And in the case of a MySQL database, there ’ s a simple language called SQL that allows you to place info in your table and edit that info. The thing that differentiates a MySQL database from a spreadsheet, however, is that the MySQL table has a lock and key, which means that in order to access it you must have the right key (username and password). And depending on who you are, you ’ re allowed certain privileges. It ’ s that easy, right?

Not quite – there ’ s a catch. Your Flex application can ’ t talk (or transfer data) directly to your database. It needs an intermediate language, and in this case it ’ s PHP. You use PHP to turbo charge your Flex application, and if you ’ re good with AS3 already you ’ ll find PHP easy.

275 Part II: Working with Models and Data

Using Flex with a Database In Flex you can automatically generate the PHP code that you need to work with your MySQL database. This generated PHP code can be easily integrated into your Flex application and used to perform the CRUD operations described earlier.

Installing a Test Server Before you get started, you want to get a test server running on your local machine. This enables you easily to develop your applications locally and then upload them to the web when completed. The easiest way to install a test server is to use a server bundle, which contains (Apache, MySQL, and PHP). Three easy - to - install server packages are available for download depending on your type of machine: Linux, MAC, Windows. And the good news is that they ’ re all free!

❑ LAMP - Linux, Apache, MySQL, PHP ❑ MAMP - MAC (OS 10), Apache, MySQL, PHP ❑ WAMP - Windows, Apache, MySQL, PHP

For illustrative purposes, you step through the WAMP installation. The LAMP and MAMP installations have similar procedures, which can be found on their respective websites. To see a video on installing the MAMP server consult the book ’ s website. MAMP installation involves a little more work and requires setting ports.

Follow these steps for installing WAMP:

1. Download the WAMP Server from www.wampserver.com/en/ (this address may change, if so just search for WAMP server on the web). 2. Make sure that you don ’ t have any other web servers listening on port 80 (the Internet information service port). The Apache server listens on port 80 and conflicts with any servers listening on that port. 3. Now just click the WampServer.exe file and step through the information screens, accept the license, and install in the default folder c:\wamp. 4. Click through a few more options and complete the installation. Once the installation is complete you ’ ll be able to start the server from your system tray menu that appears in the lower right hand position of the Windows screen. 5. Once installed the WAMP installer looks for a – go ahead and accept the default browser. 6. Enter a SMTP server and email for using the PHP mail function (you can just click through this step if you ’ re not using this feature), click next and you ’ re finished.

Congratulations, you ’ ve done it! To start your WAMP server go to your start menu and choose WampServer start WampServer. When you start your WAMP server a gauge - like icon appears in your system tray that indicates when your server is ready to use. Clicking the gauge reveals the menu that ’ s shown in Figure 7.9.

276 Chapter 7: Geocoding, XML, and Databases

Figure 7-9

This menu gives you access to the administrative tools and lets you control the various services. For example, if you click “ Put Online ” button and select phpMyAdmin, the phpMyAdmin tool pops up. It ’ s a web interface that lets you manage your MySQL server – the greatest thing since sliced bread!

Creating a Map Maker Database In this section, you take the marker data found in the Covington Historic tour project, create a database, and load this data into that database using phpMyAdmin.

First you want to open up phpMyAdmin as described in the previous section and create a database named MarkerSubset using the “ Create new database ” option. Then create a table, giving it the name SubsetData with seven fields (id, name, address, lat, lng, type, and descript), as shown in Figure 7.10.

Figure 7-10

The table is a subset of the Covington project data, because image and audio data are left out. Using image and audio assets requires an upload component, which we don ’ t cover in this chapter.

To keep the storage requirements low, you can specify the lat and lng attribute. Set it to float of (10, 6). This allows four digits before the decimal and six after which is adequate for the zoom capabilities of

277 Part II: Working with Models and Data

Google maps. Also, saving lat/lng values, as opposed to addresses for decoding to lat/lng values, reduces your processor load, making things run faster. If you don ’ t have your data in lat/lng format, use a batch converter. You can find them referenced on the Flash API Google Maps docs page.

When you ’ ve created your database it ’ s time to generate the PHP required to talk to your database.

Automatically Generating PHP In this section, you create a Flex builder application that ’ s integrated automatically with the PHP. Here are the steps:

1. Identify the root document folder for your application server (for Windows it ’ s www and for Mac it ’ s applications/mamp/htdocs). Your output folder goes here. 2. Create a Flex project called MakerSubset, and from the Application server type drop down box choose PHP. 3. Click next and provide the webroot folder C:\wamp\www (for windows) and /Applications/ MAMP/htdocs (for Mac). 4. Then provide the RootURL: http//localhost and click the Validate Configuration button and you should get the message “ The web root and folder URL are valid ” . An error message could mean that you have another server running or you ’ re not on port 80. 5. Click Finish. You ’ re now able to run both the client and server code of your Flex application on your local machine.

Now it ’ s time to start talking to your MarkerSubset database. To do so, you need to have a PHP service. Using the Flex data wizard you automatically create the PHP required to make the connection to your database. Using the Flex Data Wizard The Flex data wizard introspects your MySQL database, and generates client and server side code required for CRUD operations. Here ’ s what you get when you run the data wizard on a database table:

Client Side Code: Simple data entry Server Side Code: A complete CRUD interface for a particular table

Here ’ s the trick: use the data wizard to generate the code for each of the database tables in your database and then write your own custom client code and use the generated server code to talk to your database.

Now you generate the PHP code:

1. With your Flex MarkerSubset project active, go to the Flex menu and choose the Data menu item from the top of the page and choose Create Application from Database. 2. In the popup, select a data source. 3. Click New and provide the name MarkerSubset, which was created in the previous section, and click Next. 4. In the New connection profile popup, provide the database name (MarkerSubset) and set the username and password (for Windows leave the password blank and for Mac use root), and 278 Chapter 7: Geocoding, XML, and Databases

then click the Test Connection button to make sure that you ’ re connecting. If you did everything correctly you should get a message back: The connection was successful. If not, go back and make sure that you used the correct name for your database and that it ’ s running. 5. Click Next, review the data, and click Finish. In the Choose data source window, you can choose the table you want to use for generating your PHP code. In this case, there ’ s only one table: SubsetData. 6. Click Next. Your PHP source folder should be in the directory labeled bin - debug. The PHP file name should be the same as your table name with the first Character in caps. Then click Next and in the “ Generate client - side code ” popup you can select the columns you want to appear in the client side data grid, and in the drop down menu named Enable filtering on column, you can select which column you want to filter on. Only one column can be filtered on. You can change the code once it ’ s generated. 7. Click Finish to generate the code. You get an editable data grid, which mirrors your database and lets you add and remove records.

Running the application produces the table that ’ s shown in Figure 7.11.

Figure 7-11

You can also insert records in your table by pressing the “ folder - plus ” icon that appears in the lower - left corner of Figure 7.ll. Pressing this icon produces the popup image that ’ s shown in Figure 7.12, which allows you to add a record to your marker database.

Figure 7-12

279 Part II: Working with Models and Data

In addition to adding and deleting entire records, you can click on the individual items of the data grid and change them. Next, you take a look at the server code. PHP Side The data wizard process generates quite a bit of code. The three most important generated files are:

❑ SubsetDataScript.as does the bulk of the ActionScript work and is included in the main application through a script tag. ❑ SubsetDataconfig.as points to the location of your service PHP page (SubsetData.php). ❑ SubsetData.php contains the heart of your PHP code and executes the CRUD code shown below.

In the following code, the switch statement has five cases: FindAll , Insert , Update , Delete , and Count . Using the method request parameter, the switch statement selects one of these five cases and executes an internal method for that case and returns the required data.

These five key words in the switch statement are important, as you use them to get marker data from your database using the method parameter:

switch (@$_REQUEST[“method”]) { case “FindAll”: //Read $ret = findAll(); break; case “Insert”: //Create $ret = insert(); break; case “Update”: //Update $ret = update(); break; case “Delete”: //Delete $ret = delete(); break; case “Count”: $ret = rowCount(); break; }

In the next section, you learn to access your database from the client side. You create a custom client side application from scratch that sends a request to the PHP service, gets the data, and displays this data in your custom application. Creating the Custom Client Side Application In this section, you examine the development of an application, which allows you to create, read, update, and delete marker data for a 3D spinning Google map. The program interface, shown in Figure 7.13, is actually a combination of the 3D spinning Google map that you created earlier in this chapter plus an updatable database component.

280 Chapter 7: Geocoding, XML, and Databases

Figure 7-13

Taking the Data Wizard results, you can now generate a custom client side application that makes a PHP service page request. You do this by using an HTTPService component:

< mx:HTTPService id=”myService” url=”SubsetData.php” showBusyCursor=”true” result=”resultHandler(event)”/ >

When you execute the myService.send() method, it makes a request to the SubsetData PHP file that was created by the data wizard. The HTTPService instance calls the resultHandler method. The resultHandler method contains your action token, which determines which CRUD element that you ’ re going to execute. You initially set the action token to FindAll , which brings in all your database information, and creates your map markers.

private function resultHandler(event:ResultEvent): void { //Create your action token which determines your CRUD action var token:AsyncToken = event.token; var markerObj:Object;

//The switch case executes the CRUD required based on your action token switch (token.action) { case “ FindAll”: //Read acMarker = event.result.response.data.row as ArrayCollection; //Create your map markers createMarkers(); break ;

(continued)

281 Part II: Working with Models and Data

(continued) case “ Update”: //Update markerObj = event.result.response.data.row; acMarker.setItemAt(markerObj, markerGrid.selectedIndex); currentState= ”” ; break ;

case “ Insert”: //Insert markerObj = event.result.response.data.row; acMarker.addItem(markerObj); markerGrid.selectedItem = markerObj; markerGrid.scrollToIndex(markerGrid. selectedIndex); currentState = “ “ ; break ;

case “ Delete”: //Delete acMarker.removeItemAt(markerGrid.selectedIndex); break ; }}

What ’s a Token? The AsyncToken class is a tracking mechanism. It tracks a request that ’ s sent to a service (using HTTPService when the response is received). In the custom application that you ’ re going to build, you make calls to many functions and you need a way to determine when a result event occurs, which operations are called, and the data associated with those operations. And that ’ s where the AsyncToken class comes into play.

But where does it come from? The send method of the HTTPService returns an instance AsyncToken when you make a request to the server. The AsyncToken is a dynamic object and remains in application memory. When the result event occurs, the event object (ResultEvent ) has a token property, which refers to the instance of the AsyncToken class that you created when you made the call. Inspecting the properties, from their names and values, determines when the event occurs, and the data associated with the executed operation.

So you can create an action token as follows:

var mapparams:Object = {method: ”FindAll” }; var token:AsyncToken=myService.send(mapparams); token.action = “ FindAll” ;

In this code snippet the token is received from the send method, and in the next line you create an action property for your token. In this case it ’ s assigned FindAll , and when the result event comes back from the server, the event property points to this object. It can then be examined by your code and appropriate actions taken. . The action token is used in the resultHandler switch case to call the appropriate state and action .

If you didn ’ t get all of this, check out the video tutorials on the book ’ s website or David Gassner on Lynda.com.

282 Chapter 7: Geocoding, XML, and Databases

David Gassner of Lynda.com does a super job of explaining this process for ColdFusion, PHP, and ASP.net in his Flex 3 video training series Beyond the Basics.

It ’ s important that you understand these concepts in order to build data - driven applications in Flex.

Creating Map Makers When the code executes, the action token is assigned the FindAll switch case variable. This is used in the resultHandler switch case and throws all the data in a data grid and then runs the createMarkers method.

Most importantly, you can actually refer to the elements in your database by using the same E4X approach addressed earlier in this chapter. When the data is brought in using the FindAll action token, it ’ s transferred to an acMarker array collection as follows:

acMarker = event.result.response.data.row as ArrayCollection;

This array collection can now be parsed just like any other E4X expression, by using the dot syntax notation to refer to the database table elements by name. Now isn ’ t that cool! This is demonstrated below as the for loop iterates over all the acMarker items in your library pulling out the lat , lng , and name data using dot syntax.

//Create your markers public function createMarkers(): void { var i:Number;

//Iterate over all the markers in your data base for (i=0; i < acMarker.length; i++) { var latlng:LatLng = new LatLng(acMarker[i].lat, acMarker[i].lng); map.addOverlay(createMarker(latlng,acMarker[i].name )); } }

The for loop the program calls a createMarker method, which refers to the marker data in your database using dot syntax and places your markers at the appropriate lat / lng and puts the marker name in a tool tip.

If you want to add more markers, change, or delete them, you can easily do so using a custom marker form component.

Changing Data Finally, to change the data you use a custom component named MarkerForm , as shown in Figure 7.14. When you double click on a row of your application, or click on the New button, your application switches to a new state and this component drops down.

283 Part II: Working with Models and Data

Figure 7-14

When you ’ ve filled in the new data, and clicked Save, the information is dispatched through a MarkerEvent class, which holds all the data in a single currentMarker object.

Summary In this chapter, you turned the corner from learning the inner workings of PV3D to using it to build data - driven web applications. You built a number of applications in Air, Flash CS4, and Flex, which illustrated the use of XML, PHP, and MySQL, and you learned how to use the Flex data wizard to automatically create PHP code, which was used to make server requests.

284 Gliding on AIR

You ’ re about to take another step deeper into the world of modeling: terrain mapping. But you ’ re probably wondering, “ Isn ’ t AIR a desktop application? Aren ’ t you trying to put things on the web instead? ” And the answer to both questions is, “ yes ” . Using AIR you ’ ll create a modeling program that allows you to save your work into an XML file. The file can then be uploaded into Papervision3D and run on the web.

It ’ s a simple equation: an AIR Modeling application outputs a data file that ’ s sucked up by PV3D and then used on the web.

None of this is currently in PV3D, and in this chapter you learn everything you need to know to build your own modeling program. You start by examining how vertices are placed, height maps made, and terrains are built. You use AIR to save your XML outputted terrain maps, and import those maps into PV3D for use. You then extend this application and make saveable terrain maps from your webcam. Finally, you build a simple lathe.

Building a Simple Modeling Program One of the coolest things you can do in PV3D is to build a simple modeling program. As there ’ s so much difficulty in bringing models in from other programs (such as 3DMax, SketchUp, Blender), why not build your models in PV3D natively? Models built natively in PV3D function more efficiently.

The geometry.vertices property is one of the keys that aid you in creating modeling programs in PV3D. This property let ’ s you rearrange vertices programmatically, and place particles in various geometric arrangements. You start by investigating the use of this property by creating the infamous Rubik ’ s Cube. Part II: Working with Models and Data

Placing Particles at Vertices You may not think of a Rubik ’ s cube as a particle system, but it is. You can think of it as 26 particles that are placed at various associative points in space. Placing your cubes at these points may seem like a mathematical mess, but there ’ s a trick that requires only eight lines of code. The end result is shown in Figure 8.1.

Figure 8 -1

To lay out your Rubik ’s cube, you use the geometry.vertices property to anchor your particles (which are cubes in this case) to the vertices of a larger cube, as shown in Figure 8.2.

Figure 8 -2

You can think of the process in terms of one large cube with many small cubes. Large Cube Segmenting a large cube by 2, 2, 2 creates the coordinates of the Rubik ’ s cube as shown here:

cubeGrid = new Cube(myMaterialsList1, mySize, mySize, mySize,2,2,2);

As previously stated, you place small cubes at each vertex of the large cube using the geometry. vertices property.

286 Chapter 8: Gliding on AIR

Small Cubes The geometry.vertices property gives you the position of each large cube vertex, which is stored in the vertices array and accessed using the geometry.vertices method as shown below:

for(var i:int = 0; i < cubeGrid.geometry.vertices.length; i++) { //Generate your small cubes var myPart:Cube = new Cube(materialArray[i], mySize/2.1,mySize/2.1,mySize/2.1,2,2,2); //x, y, x positions for your small cubes myPart.x=cubeGrid.geometry.vertices[i].x; myPart.y=cubeGrid.geometry.vertices[i].y; myPart.z=cubeGrid.geometry.vertices[i].z; //Stuff into a particle array particleArray.push(myPart); myGraphicsHolder.addChild(myPart); }

That ’ s really all there is to it, the only other thing you need to do is set the faces of the small cubes to the appropriate Rubik ’ s Cube colors using a materials list array. To check it out, consult the book ’ s chapter code under ActionScript projects.

Enough said about Rubik ’ s Cubes, you ’ ll find the completed cube on the book ’ s website. You now move on to create a simple terrain modeler.

Modeling Terrains Modeling terrains in PV3D can be accomplished using two different approaches:

❑ Duplicating the PV3D Plane class and renaming it Terrains (for example), and adding all the code required to accept a heightmap. ❑ Using OOP to add the needed modifications without changing the PV3D Plane class.

To this point in the book, you ’ ve used the first approach, but now, you discover how to get the same results using OOP without changing the Plane class, by using its public methods and properties. In addition, make sure you check out the links given in Appendix A for this chapter. They were used to help develop the material given below. What’s a Heightmap A heightmap is typically a grayscale image. It can be used as a displacement map to store surface elevation data. Displacing geometric positions over a textured surface can be used to convert a 3D mesh into a terrain. Typically, a height map contains one color channel, which is converted into distance or height above your planar mesh. In the case of grayscale, black is your minimum height and white is your maximum height and the remaining contrasting colors fall in between the two heights.

This is clearly illustrated in Figure 8.3, where the different grayscale values of the image on the right correspond to textured terrain elevation of the image on the left.

287 Part II: Working with Models and Data

Figure 8 -3

In Flash, it ’ s really easy to grab grayscale values from an image and convert them into a terrain map. Using the getPixel method you iterate over your grayscale map and send the resulting color value to a getHeight method to convert that pixel value to an elevation.

//Calculate gray scale elevation value public function getHeight(myColor:uint):Number { var red:Number=0; var green:Number=0; var blue:Number=0; var hexColor:String = myColor.toString(16); if (hexColor.length == 1) hexColor = “ 000000” ; //Calculate RGB values else if (hexColor.length == 4) hexColor= ”00” +hexColor; red = parseInt(hexColor.substr(0, 2), 16); green = parseInt(hexColor.substr(2, 2), 16); blue = parseInt(hexColor.substr(4, 2), 16);

return ((red/255) + (green/255) + (blue/255))/3; }

The code snippet above converts the pixel value to a hexadecimal, extracts the color components, and then averages them to get a grayscale elevation ranging from 0 to 255. This elevation number is then multiplied by a depth factor to give the desired elevation range. Displacing Vertices Now that you have your displacement data, it ’ s time to pull out the geometry.vertices property used to create your Rubik ’ s cube. But in this case you ’ re displacing vertices in a mesh, not placing cubes on a grid. You start the process by instantiating a Plane named myTerrain , shown here:

//Instantiate myTerrain myTerrain = new Plane(textureMaterial,512,512,20,20);

As you ’ ve previously imported the PV3D Plane class, you can use all of its public methods and properties. So without having to write another class specifically for a terrain, you add the necessary code below to augment the existing Plane class and turn it into a terrain.

This is simply done by accessing the instantiated myTerrain vertices, and displacing them over the plane ’ s grid components. The vertices are accessed using the geometry.vertices technique.

var vertices :Array = myTerrain.geometry.vertices;

288 Chapter 8: Gliding on AIR

The vertices can now be displaced by referencing their z - position properties, and calling the getHeight method discussed previously.

vertices[vertexIndex].z =- 1*Math.min(255,getHeight(Math.max(1,myBitmap.bitmapData.getPixel((ix*iW), myBitmap.height-((iy*iH))))))*depth; vertexIndex++;

The entire snippet is given here; this code can be adjusted to work with any primitive such as a sphere or cylinder to create a lathe or sculpt a prim:

//Create Terrain private function createTerrain(e:Event = null ): void { //Create texture using paletteMap

textureBitmap.paletteMap(myBitmap.bitmapData,heightMap.rect, new Point(), paletteArrayR,paletteArrayG,paletteArrayB); //Use public method of the Plane class var vertices :Array = myTerrain.geometry.vertices; var gridX :Number = myTerrain.segmentsW; var gridY :Number = myTerrain.segmentsH; var vertexIndex :Number = 0; var iW :Number = myBitmap.width / gridX; var iH :Number = myBitmap.height / gridY;

//Iterate over plane for ( var ix:int = 0; ix < gridX + 1; ix++ ) { for( var iy:int = 0; iy < gridY+1; iy++ ) { //Calculate elevation based on bitmap grayscale vertices[vertexIndex].z =- 1*Math.min(255,getHeight(Math.max(1,myBitmap.bitmapData.getPixel((ix*iW), myBitmap.height-((iy*iH))))))*depth; vertexIndex++; } }}

Livening up your terrain by adding color can be accomplished in several ways. For example, you could create a texture of a terrain and place it on your plane and then deform that plane to match your texture. That ’ s a lot of work though. An easier way is to create a palette map and color your pixels based upon elevation, which is what you do in the next section. Adding Color to Your Terrain Using a palette map to color your terrain elevations requires that you create an image gradient that corresponds to your terrain elevation as shown on the left side of Figure 8.4. Such a gradient palette can be created in Photoshop using the gradient tool (for more info on that check out the book ’ s website).

289 Part II: Working with Models and Data

Figure 8 -4

Once you ’ ve created the gradient image you need to import it into your program and sample it along the image ’ s height. You place this color sampling into a palette array to be used for coloring your terrain based on its elevation.

for ( var ra:uint = 0;ra < 256;ra++) { //Grab gradient value from photoshop color map var myPixelData:Number = grad.bitmapData.getPixel(10, 256-ra); var hexColor:String=myPixelData.toString(16);

if (hexColor.length == 1) {hexColor = “ 000000” ;} //Set R, G, B arrays for map texture creation paletteArrayR[ra] = “ 0x00”+hexColor.substr(0, 2)+ ”0000” ; paletteArrayG[ra] = “ 0x0000”+hexColor.substr(2, 2)+ ”00” ; paletteArrayB[ra] = “ 0x000000”+hexColor.substr(4, 2); }

In the code snippet, you sample your image gradient from top to bottom and store the red, green and blue values in a palette array. This palette array is then used in a palleteMap method, which replaces all red, green, and blue channels of your heightmap with the channels from your gradient map.

But how does this correspond to elevation? Well, there are actually two images imported: your gradient map which you get your new color channels from, and a grayscale bitmap (named myBitmap in this case), which represents the elevation of your terrain. The paletteMap replaces the color of your grayscale map according to its elevation values and sends that as a texture to your plane.

textureBitmap.paletteMap(myBitmap.bitmapData,heightMap.rect, new Point(), paletteArrayR,paletteArrayG,paletteArrayB);

The complete commented code given below can be used for any gradient or grayscale height map.

package { //Flash Imports import flash.display.*; import flash.events.*; import flash.geom.*; import mx.core.BitmapAsset; //Papervision Imports

290 Chapter 8: Gliding on AIR import org.papervision3d.materials.BitmapFileMaterial; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.primitives.*; import org.papervision3d.view.BasicView;

//Declare the Hello Plane class that extends BasicView public class TerrainBuilder5 extends BasicView { private var textureBitmap: BitmapData; private var heightMap: BitmapData; private var paletteArray: Array = new Array(); private var paletteArrayR: Array = new Array(); private var paletteArrayG: Array = new Array(); private var paletteArrayB: Array = new Array(); private var depth:Number=200; private var myTerrain: Plane;

//Declare the plane, bitmapdata, and material variables public var myBitmap:Bitmap; //Embed Grayscale image [Embed(source = “images/terragen.jpg”)] private var myImageIs:Class; //Embed gradient image [Embed(source=”images/image21.png”)] private var GradBmp:Class;

//Create the Hello Plane Constructor function public function TerrainBuilder5() { super(1, 1, true, false); initPV3D(); } //Set up your stage and plane private function initPV3D():void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; opaqueBackground = 0; //Create a Bitmap myBitmap = new myImageIs as Bitmap;

//Create heightMaps var heightMapWidth:Number = 512; heightMap = new BitmapData(heightMapWidth,heightMapWidth,false,0); textureBitmap = heightMap.clone(); var grad:BitmapAsset = BitmapAsset(new GradBmp()); for (var ra:uint = 0;ra < 256;ra++) { //Grab gradient values from Photoshop color map var myPixelData:Number = grad.bitmapData.getPixel(10, 256-ra); var hexColor:String=myPixelData.toString(16); (continued)

291 Part II: Working with Models and Data

(continued) if (hexColor.length == 1) {hexColor = “000000”;} //Set R, G, B arrays for map texture creation paletteArrayR[ra] = “0x00”+hexColor.substr(0, 2)+”0000”; paletteArrayG[ra] = “0x0000”+hexColor.substr(2, 2)+”00”; paletteArrayB[ra] = “0x000000”+hexColor.substr(4, 2); }

//Create texture for Plane var textureMaterial:BitmapMaterial = new BitmapMaterial(textureBitmap); textureMaterial.doubleSided = true;

//Instantiate myTerrain myTerrain = new Plane(textureMaterial,512,512,20,20); scene.addChild(myTerrain,”myTerrain”); myTerrain.rotationX = 90; createTerrain(); camera.lookAt(myTerrain); camera.focus=15; startRendering(); }

//Calculate grayscale elevation value public function getHeight(myColor:uint):Number { var red:Number=0; var green:Number=0; var blue:Number=0; var hexColor:String = myColor.toString(16); if (hexColor.length == 1) hexColor = “000000”; //Calculate RGB values else if (hexColor.length == 4) hexColor=”00”+hexColor; red = parseInt(hexColor.substr(0, 2), 16); green = parseInt(hexColor.substr(2, 2), 16); blue = parseInt(hexColor.substr(4, 2), 16); return ((red/255) + (green/255) + (blue/255))/3;

} //Create Terrain private function createTerrain(e:Event = null):void { //Create texture using paletteMap textureBitmap.paletteMap(myBitmap.bitmapData,heightMap.rect,new Point(), paletteArrayR,paletteArrayG,paletteArrayB); var vertices :Array = myTerrain.geometry.vertices; var gridX :Number = myTerrain.segmentsW; var gridY :Number = myTerrain.segmentsH; var vertexIndex :Number = 0; var iW :Number = myBitmap.width / gridX; var iH :Number = myBitmap.height / gridY; //Iterate over plane for( var ix:int = 0; ix < gridX + 1; ix++ ) { for( var iy:int = 0; iy < gridY+1; iy++ )

292 Chapter 8: Gliding on AIR

{ //Calculate elevation based on bitmap grayscale vertices[vertexIndex].z =- 1*Math.min(255,getHeight(Math.max(1,myBitmap.bitmapData.getPixel((ix*iW), myBitmap.height-((iy*iH))))))*depth; vertexIndex++; } } }

//Override protected function override protected function onRenderTick(event:Event=null):void { //Angle for stage manipulation var axeAngleX:Number = (stage.width/2-mouseX)/50; var axeAngleY:Number = -(mouseY)/200; //Orbiting Equation camera.x = (6600*Math.cos(axeAngleX)*Math.sin(axeAngleY))/10; camera.z = (6600*Math.sin(axeAngleX)*Math.sin(axeAngleY))/10; camera.y = (6600*Math.cos(axeAngleY))/10;

super.onRenderTick(event);

} } }

Before you leave this section, there ’ s one more thing you need to know, and that ’ s how to make a grayscale map for import into PV3D. A number of programs are available out there that can help you accomplish this. Bryce, Terrigen, and Photoshop are a few examples of programs that can help you with this. For the examples above, Photoshop and Terrigen were used. Terrigen is free and runs on both MAC and Windows. It has a terrain editor that let ’ s you capture grayscale maps. Photoshop, of course, is the industry standard.

However, you can create your own grayscale image editor quite easily using Adobe AIR.

Creating a Terrain Editor in AIR For building a terrain editor, Flex is your preferred tool. Flex was built to construct such applications easily and in this section you learn a number of different tricks for building what may seem to be a complex application in just a few lines of code. The techniques applied to build the terrain editor in figure 8.5 can be used to construct any number of editors for use with PV3D.

The terrain maker works similarly to a drawing package, but only draws in grayscale. You can use its various tools to create grayscale images. But it works a little differently than typical drawing packages. For example, when drawing a straight line, a typical Flash drawing package would use the lineTo method. But in this application, setPixels is used.

Using setPixels lets you create your height map from a black background and change its pixels to grayscale using the setPixel method. This bitmap can then be converted to a jpeg using Flex and saved to your hard drive using AIR ’ s file management system (see Figure 8.5).

293 Part II: Working with Models and Data

Figure 8 -5

Grasping how this application is created will aid you in making other applications like it. This application has the following features:

❑ Radial buttons for choosing between square and circular drawing tools ❑ Grayscale, gradient, size, and randomizing sliders ❑ Make line, make box, and add Perlin noise buttons ❑ A and D key listeners which when pressed change your grayscale

For an application like this you can just keep adding features, and refining the code using the same basic elements as in the list above. In this section, you go through each of these basic elements one - by - one. And in the next section, you extend this application to save your images to your local PC.

Drawing Code The drawing code is extremely simple; all you do is use the setPixel method to change the color of your pixel somewhere on your bitmap canvas. Here are the steps:

1. Create your Canvas:

private var myBitmapData:BitmapData = new BitmapData(512, 512, false , 0x00000000);

2. Change your canvas pixels using setPixel :

myBitmapData.setPixel(oldX +i , oldY+j, myColor);

To create a gradient effect, you need to create a mathematical distribution for your setPixel color parameter. The distribution chosen in this case is a parabolic function (or i*i):

myIntIs=Math.floor(myIntIs*(1- distrib.value*((i)*(i)/(sizeSlider.value*sizeSlider.value)))); var myColor:Number=pixelArray[myIntIs];

294 Chapter 8: Gliding on AIR

In the preceding code snippet, “ i ” is your iteration parameter and myIntIs is the integer for your grayscale distribution. Varying “ i ” parabolically changes your distribution, giving your drawn shape a gradient.

Radio buttons are great tools for selecting items in a list.

Using Radio Buttons Flex has a component panel and from that panel you can drag various items to the stage. In this case two radio buttons were selected and dropped onto the Flex stage (see Figure 8.6).

Figure 8 -6

Using the Flex properties panel the radio buttons were given the group name myDraw and the button for the square set to selected . Everything else works by using the id of the radio button.

< mx:RadioButton x=”27” y=”55” groupName=”myDraw” label=”Square” id=”mySquare” selected=”true”/ > < mx:RadioButton x=”106” y=”55” groupName=”myDraw” label=”Circle” id=”myCircle”/>

So in the following code snippet, if the parameter mySquare.selected is set to true, the square code is selected and a drawing square generated:

if (mySquare.selected){ for ( var i:int=0; i

The code above is filled with slider parameters, which let you change size, grayscale, gradient, and randomness of your drawing particles.

295 Part II: Working with Models and Data

Adding Sliders Sliders are really cool and easy - to - use components in Flex (see Figure 8.7). After you drag a few sliders to the stage all you have to do is refer to their idName.value to use them anywhere in your code. As long as you have liveDragging set to true, whenever you move your slider, the respective slider ’ s value is sent as a property of the slider ’ s change event when it ’ s broadcasted.

Figure 8 -7

As can be seen in the following code snippet, there are four sliders, each corresponding to the sliders in Figure 8.7:

< mx:HSlider x=”27” y=”113” maximum=”255” minimum=”0” value=”180” id=”colorScale” liveDragging=”true” snapInterval=”1”/ > < mx:HSlider id=”sizeSlider” x=”27” y=”205” minimum=”1” maximum=”200” value=”40” liveDragging=”true”/ > < mx:HSlider x=”27” y=”248” id=”randomSlide” minimum=”0” maximum=”10” liveDragging=”true” value=”1”/ > < mx:HSlider x=”27” y=”161” id=”distrib” minimum=”0” maximum=”1” liveDragging=”true” value=”.5”/ >

Buttons are also easy components to use in Flex.

Using Buttons After dragging a couple of buttons to the stage, all you have to do is give them a label and a click function. In Figure 8.8, a series of boxes were generated using the button labeled Make Box. Clicking this button causes its label to change to Don ’ t Make Box. The box is drawn by clicking the stage once and then clicking a second time. Your box is then drawn with its assigned color between the first and second clicks.

296 Chapter 8: Gliding on AIR

Figure 8 -8

The Make Box button has a click handler method called myMakeBox().

< mx:Button x=”37” y=”373” label=”Make Box” width=”126” click=”myMakeBox(event)” id=”myBoxBtn”/ >

When you click the Make Box button, the code below is executed, which allows you to draw a box on the stage between your first and second clicks.

private function myBox(): void { if (makeBox== true ){ if (clickCounter==2){ //Second click coordinates var newX:Number = pv3d.mouseX; var newY:Number = pv3d.mouseY;

var distIs1:Number=Math.floor(Math.abs(oldX-newX)); var distIs2:Number=Math.floor(Math.abs(oldY-newY)); //Draw box from first click to second click for ( var i:int=0; i < distIs1; i++){ for ( var j:int=0; j < distIs2; j++){

var myIntIs:int=Math.floor(colorScale.value); var myColor:Number=pixelArray[myIntIs]; //Four possibilities if (newX > oldX & & newY > oldY){ myBitmapData.setPixel(oldX +i , oldY+j, myColor); } if(newX > oldX & & newY < oldY){ myBitmapData.setPixel(oldX +i , oldY-j, myColor); } if(newX < oldX & & newY > oldY){ myBitmapData.setPixel(oldX -i , oldY+j , myColor); } if(newX < oldX & & newY < oldY){ myBitmapData.setPixel(oldX -i , oldY-j , myColor); } }}

The draw line method works similarly, but allows you to add multiple lines with each new click. Next you add Perlin noise, which is a must for terrain editing.

297 Part II: Working with Models and Data

Working with Perlin Noise Perlin noise can be used to simulate landscapes, wood grain, clouds, and mountain ranges. According to Adobe ’ s documentation:

The Perlin noise generation algorithm interpolates and combines individual random noise functions (called octaves) into a single function that generates more natural - seeming random noise. Like musical octaves, each octave function is twice the frequency of the one before it. Perlin noise has been described as a ‘ fractal sum of noise ’ because it combines multiple sets of noise data with different levels of detail.

Adobe Systems Incorporated

Tons of options come with Perlin noise. However, in the terrain editor, you only generate a basic Perlin background. Here ’ s an example:

perlinNoise(baseX:Number, baseY:Number, numOctaves:uint, randomSeed:int, stitch:Boolean, fractalNoise:Boolean, channelOptions:uint = 7, grayScale:Boolean = false, offsets:Array = null)

The Perlin noise method includes nine parameters:

❑ baseX:Number — Frequency to use in the x direction. ❑ baseY:Number — Frequency to use in the y direction. ❑ numOctaves — Number of octaves or individual noise functions to combine to create this noise. ❑ randomSeed — The random seed number to use. ❑ stitch — A Boolean value. If true, attempts to smooth the transition edges of the image to create seamless textures for tiling as a bitmap fill. ❑ fractalNoise — A Boolean value. If true, generates fractal noise; otherwise, it generates turbulence. ❑ channelOption s — A number that can be a combination of any of the four color channels ❑ grayscale — A Boolean value. If true, a grayscale image is created by setting each of the red, green, and blue color channels to identical values. ❑ offsets — An array of points that correspond to x and y offsets for each octave.

With all these options, you can expect many variations of Perlin noise. In this particular application only a very simple Perlin background is generated. But one easy upgrade to the terrain editor would be to add more sliders to enhance your Perlin noise background (see Figure 8.9).

298 Chapter 8: Gliding on AIR

Figure 8-9

Instituting Perlin noise is easy. Just drag a button on the Flex stage from the components menu and create a click function addPerlinNoise() , which fills your bitmapdata background with Perlin noise.

< mx:Button x=”37” y=”434” label=”Add Perlin Noise” click=”addPerlinNoise()”/>

When you click the button, the Perlin noise is applied to your bitmapdata canvas.

private function addPerlinNoise(): void { var seed:Number = Math.floor(Math.random()*10); var channels:uint = BitmapDataChannel.RED|BitmapDataChannel.GREEN|BitmapDataChannel.BLUE; myBitmapData.perlinNoise(512, 512, 6, seed, true , true, channels, true, null ); }

You must now add your key listener.

Adding Key listeners If you ’ ve done any game programming in Flash you ’ re intimately familiar with the use of key listeners. The concept is simple enough – just assign a key up and key down listener. And when a particular key is pressed it executes a function associated with that key. In the application, when the a - key is pressed your grayscale increases to white and when the d - key is pressed your grayscale decreases to black (see Figure 8.10).

Figure 8-10

299 Part II: Working with Models and Data

But the way, typically, that you execute the process in PV3D is a little different. You first assign event listeners to listen for key up and key down actions and assign a Boolean based on the key action.

//Assign your key up and key down listeners addEventListener(KeyboardEvent.KEY_UP, myKeyUp); addEventListener(Event.ENTER_FRAME, frameLoop); //Set focus on color scale to activate key listener colorScale.setFocus();

When the key down method is executed, for example, a Boolean value is assigned to true which is used in the iterative animation loop. As long as this Boolean value is true your grayscale is iterated respectively, increasing it or decreasing it depending on which key is pressed. When you release the key your Boolean value changes to false and your grayscale is no longer iterated. The main ingredient is that the Boolean values control if statements in the animation loop, determining if the key method is executed or not.

The following code sets your Boolean values based on the key press action:

//Keyboard actions //My key down method private function myKeyDown(event:KeyboardEvent): void{ if (isChar(event.charCode, ” a” )){ myAKey= true ; } else if (isChar(event.charCode, ” d” )){ myDKey= true ; } }

//My key up method private function myKeyUp(event:KeyboardEvent): void { if (isChar(event.charCode, ” a” )){ myAKey= false ; } else if (isChar(event.charCode, “ d” )){ myDKey= false ; } }

As can be seen from the code snippet below, if your myAKey parameter is true your colorScale is increased. But if myDKey Boolean is true , your colorScale decreases as your animation loop cycles.

//Frame Looper private function frameLoop(e:Event): void { //Increase (AKey) or decrease (DKey) grayscale if (myAKey== true) {colorScale.value++;};

if (myDKey== true) {colorScale.value-;}; }

The entire documented code is given here:

< ?xml version=”1.0” encoding=”utf-8”? > < mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml” creationComplete=”init()” layout=”absolute” width=”1000” height=”700”>

< mx:Script > 300 Chapter 8: Gliding on AIR

< ![CDATA[ //Flash Import Statements import flash.display.*; import flash.geom.Rectangle; import flash.events.*; import flash.events.KeyboardEvent; import flash.geom.Matrix; import flash.display.GradientType; import mx.core.BitmapAsset;

//Bitmap Variables private var myBitmapData:BitmapData = new BitmapData(512, 512, false, 0x00000000); private var bm:Bitmap = new Bitmap(myBitmapData);

//Embed grayscale and Editor parameters [ Embed (source = ” images/grayScale.png” )] private var myGrayScaleIs:Class; private var pixelArray:Array=[]; private var makeLine:Boolean= false ; private var makeBox:Boolean= false ; private var clickCounter:Number=0; private var myAKey:Boolean= false ; private var myDKey:Boolean= false ; //First click x and y coordinates private var oldX:Number; private var oldY:Number; //Initiate application and add Mouse Listeners private function init(): void { //Add bitmap to Flex Canvas PV3D using rawChildren pv3d.rawChildren.addChild(bm); //Generate grayscale Array generateGrayArray(); //Add Mouse up and down listeners pv3d.addEventListener(MouseEvent.MOUSE_DOWN, myMouseDownHandler); pv3d.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); addEventListener(KeyboardEvent.KEY_DOWN, myKeyDown); addEventListener(KeyboardEvent.KEY_UP, myKeyUp); addEventListener(Event.ENTER_FRAME, frameLoop); //Set focus on color scale to activate key listener colorScale.setFocus(); }

//Load grayscale into pixel Array private function generateGrayArray(): void { var grad:BitmapAsset = BitmapAsset( new myGrayScaleIs()); for ( var ra:uint = 0;ra < 256;ra++) { //Grab gradient value from photoshop color map var myPixelData:Number = grad.bitmapData.getPixel(10, 256-ra); pixelArray[ra]= ”0x” + myPixelData.toString(16); }}

//Code for the make line button private function myMakeLine(event:MouseEvent): void { if (myLineBtn.label==”Make Line” ){ makeLine= true ; (continued) 301 Part II: Working with Models and Data

(continued) myLineBtn.label= ”Don’t Make Line”; } else { makeLine= false ; myLineBtn.label= ”Make Line” ; clickCounter=0; }}

//Code for the make box button private function myMakeBox(event:MouseEvent): void { if (myBoxBtn.label==”Make Box” ){ makeBox= true ; myBoxBtn.label= ”Don’t Make Box”; } else { makeBox= false ; myBoxBtn.label= ”Make Box” ; clickCounter=0; }}

//Code to Generate Straight lines private function myStraightLine(): void { if (makeLine== true ){ if (clickCounter==2){ //Second mouse click coordinates var newX:Number = pv3d.mouseX; var newY:Number = pv3d.mouseY; //Draw straight line from first click to second click var distIs:Number=Math.floor(Math.abs(oldX-newX)); for ( var i:int=0; i < distIs; i++){ var myIntIs:int=Math.floor(colorScale.value); var myColor:Number=pixelArray[myIntIs]; if(newX > oldX){ myBitmapData.setPixel(oldX +i , oldY+((newY-oldY)/(newX-oldX))*i, myColor); } if(newX < oldX){ myBitmapData.setPixel(oldX -i , oldY+((newY-oldY)/(oldX-newX))*i , myColor); }} oldX=newX oldY=newY; clickCounter=1; }}}

//Code for creating your box private function myBox(): void { if (makeBox== true ){ if (clickCounter==2){ //Second click coordinates var newX:Number = pv3d.mouseX; var newY:Number = pv3d.mouseY;

var distIs1:Number=Math.floor(Math.abs(oldX-newX)); var distIs2:Number=Math.floor(Math.abs(oldY-newY)); //Draw box from first click to second click for ( var i:int=0; i < distIs1; i++){ 302 Chapter 8: Gliding on AIR

for ( var j:int=0; j < distIs2; j++){

var myIntIs:int=Math.floor(colorScale.value); var myColor:Number=pixelArray[myIntIs]; //Four possibilities if (newX > oldX & & newY > oldY){ myBitmapData.setPixel(oldX +i , oldY+j, myColor); } if(newX > oldX & & newY < oldY){ myBitmapData.setPixel(oldX +i , oldY-j, myColor); } if(newX < oldX & & newY > oldY){ myBitmapData.setPixel(oldX -i , oldY+j , myColor); } if(newX < oldX & & newY < oldY){ myBitmapData.setPixel(oldX -i , oldY-j , myColor); } }}

//Reset box initial parameters makeBox= false ; myBoxBtn.label= ”Make Box” ; clickCounter=0;

}}} //Clear your canvas for redrawing private function myClear(event:MouseEvent): void {

for ( var i:int=0; i < 512; i++){ for ( var j:int=0; j < 512; j++){

myBitmapData.setPixel(i , j, 0x000000);

}}}

//Keyboard actions //My key down method private function myKeyDown(event:KeyboardEvent): void{ if (isChar(event.charCode, ” a” )){ myAKey= true ; } else if (isChar(event.charCode, ” d” )){ myDKey= true ; } }

//My key up method private function myKeyUp(event:KeyboardEvent): void { if (isChar(event.charCode, ” a” )){ myAKey= false ; } else if (isChar(event.charCode, ” d” )){ myDKey= false ; } }

//Code for getting keyboard character numbers private function isChar(code:Number, str:String):Boolean{ (continued) 303 Part II: Working with Models and Data

(continued) if(code == str.charCodeAt()){ return true ; } else { return false ; }}

//Mouse Actions private function myMouseDownHandler(event:MouseEvent):void { //Add mouse move listener pv3d.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);

if(makeLine== true){ clickCounter++;

if (clickCounter==1){ oldX=pv3d.mouseX; oldY=pv3d.mouseY;} myStraightLine(); }

if(makeBox== true ){ clickCounter++;

if (clickCounter==1){ oldX=pv3d.mouseX; oldY=pv3d.mouseY;} myBox(); }}

//Mouse handler (Mouse Up) private function mouseUpHandler(event:MouseEvent): void{ //Remove mouse move listener pv3d.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);

}

//Mouse handler (Mouse Move) private function mouseMoveHandler(event:MouseEvent): void{

if(makeLine== false & & makeBox== false ){

if(mySquare.selected){ for( var i:int=0; i

304 Chapter 8: Gliding on AIR

myBitmapData.setPixel((pv3d.mouseX-sizeSlider.value/4)+(sizeSlider. value/8-i/4)*(1+randomSlide.value*Math.random()), (pv3d.mouseY)+(sizeSlider. value/8-j/4)*(1+randomSlide.value*Math.random()), myColor); }} } if (myCircle.selected){ for ( var i2:int=0; i2< sizeSlider.value; i2++){

var myPar:int = Math.floor(Math.sqrt(sizeSlider.value*sizeSlider .value-i2*i2));

for ( var j2:int=0; j2< myPar; j2++){ var myIntIs2:int=Math.floor(colorScale.value); //Gradient Code myIntIs2=Math.floor(myIntIs2*(1- distrib.value*((i2)*(i2)/(sizeSlider.value*sizeSlider.value)))); var myColor2:Number=pixelArray[myIntIs2]; //Set Pixels (not the most efficient way to do this) //Circle split in four segments myBitmapData.setPixel((pv3d.mouseX-sizeSlider.value/4)+ (sizeSlider.value/2+j2)*.25*(1+randomSlide.value*Math.random()), (pv3d.mouseY)+(sizeSlider.value/2-i2)*.25*(1+randomSlide.value*Math.random()), myColor2); myBitmapData.setPixel((pv3d.mouseX-sizeSlider.value/4)+ (sizeSlider.value/2-j2)*.25*(1+randomSlide.value*Math.random()), (pv3d.mouseY)+(sizeSlider.value/2-i2)*.25*(1+randomSlide.value*Math.random()), myColor2); myBitmapData.setPixel((pv3d.mouseX-sizeSlider.value/4)+ (sizeSlider.value/2-j2)*.25*(1+randomSlide.value*Math.random()), (pv3d.mouseY)+(sizeSlider.value/2+i2)*.25*(1+randomSlide.value*Math.random()), myColor2); myBitmapData.setPixel((pv3d.mouseX-sizeSlider.value/4)+ (sizeSlider.value/2+j2)*.25*(1+randomSlide.value*Math.random()), (pv3d.mouseY)+(sizeSlider.value/2+i2)*.25*(1+randomSlide.value*Math.random()), myColor2);

} }}}}

//Add Perlin noise background private function addPerlinNoise(): void { var seed:Number = Math.floor(Math.random()*10); var channels:uint = BitmapDataChannel.RED|BitmapDataChannel.GREEN| BitmapDataChannel.BLUE; myBitmapData.perlinNoise(512, 512, 6, seed, true , true , channels, true, null ); }

//Frame Looper private function frameLoop(e:Event): void { //Increase (AKey) or decrease (DKey) grayscale if (myAKey== true) {colorScale.value++;};

if (myDKey== true) {colorScale.value-;}; } (continued)

305 Part II: Working with Models and Data

(continued) ]]>

Incorporating Adobe AIR Air is definitely the tool of choice when it comes to building an editor. Eventually you want to save the editor output somewhere: in a web file, to a database, or to a file on your local PC. One of the problems with Flash (before Flash player 10), was that you couldn ’ t access the user ’ s local file system and it was difficult to save to the local hard drive (or desktop).With Flash 10 you can now save to the desktop, so why use AIR?

306 Chapter 8: Gliding on AIR

Air also provides a SQLite database, a higher level of security, a desktop application for distribution, and a number of other great features that we don ’ t cover here. As a number of great books are available out there by WROX on AIR, we won ’ t be covering AIR in detail here. However, you ’ ll learn what you need to get your application to save to your local PC or into a local SQLite database.

Accessing your Local File System Using AIR you can create, read, and write files and directories to your local hard drive. When working with the local file system, you need to determine if you ’ re going to work synchronously or asynchronously. Here ’ s what that means:

❑ Synchronous means that operations, while reading or writing data, are suspended during the process. So if you ’ re running animation or user interactions, the front - end thread is brought to a halt while Air does its thing (or completes its file operation). ❑ Asynchronous means that the front - end application thread still runs and your file operations are run in the background. If you have a large amount of data to work with, this is the way to go, or your application will become temporarily unavailable.

Coding for synchronous and asynchronous file operations is somewhat similar in both cases. Implementing asynchronous file operations requires that you listen for the complete event and react when the file operation is finished.

The two key classes that you ’ ll be working with are the File and FileStream classes. The File class represents a file or directory on your local PC. It supports moving, deleting, copying, browsing, and everything you want to do with a file or directory. The FileStream class reads and writes data to a local drive. You now use these classes to save your editor ’ s heightmaps to your local PC.

Saving BitmapData Most flashmaticians will agree that the BitmapData class is one of the most useful classes ever created in Flash. In this section, you use it to save your grayscale heightmap. Saving your heightmap is a simple process as Flex already has an image encoder built into its framework.

This is the process:

Modify your bitmap using the heightmap application built in the previous section, then encode it using the appropriate JPEG or PNG encoder (which is part of the mx.graphics.codec package). Then save the encoded file using the File and FileStream classes discussed in the previous section. The good news is that your bitmap is ready to go. It ’ s the canvas you ’ ve been drawing on (named myBitmapData ) and all you have to do is send it to your jpeg or png encoders.

//JPEG encoder var jpgenc:JPEGEncoder= new JPEGEncoder(80); imageByteArray=jpgenc.encode(myBitmapData);

//PNG encoder var pngenc:PNGEncoder= new PNGEncoder(); imageByteArray=pngenc.encode(myBitmapData);

Then you use your file stream class to write your encoded data to the correct file location. 307 Part II: Working with Models and Data

//Use a FileStream to save the bytearray as bytes to the new file var fStream:FileStream = new FileStream();

//open file in write mode fStream.open(myFile,FileMode.WRITE); //write bytes from the byte array fStream.writeBytes(imageByteArray);

The File class is used to create files and directories and resolve paths.

//resolvePath gets a file object that points to the correct file var myFile:File = File.desktopDirectory.resolvePath(imageFolder+fileName);

//Determine if the file exists if (myFile.exists){ //File doesn’t exist, create it return getNewImageFile(filext); }

return myFile; }

With this code you also need a simple user interface for saving your files and choosing your file type (.jpeg or .png) as shown in Figure 8.11.

Figure 8-11

All you do is type in the name you want to save your image as in the textbox, select the type of image (.jpeg or .png) and click the Save button.

When the save process is complete you get an Alert box indicating where the image file was saved to (see Figure 8.12). The location of the save file is resolved using the method: myFile.nativePath .

Alert.show(“Image is saved at “+myFile.nativePath,”Image Saved”);

Figure 8-12

308 Chapter 8: Gliding on AIR

So basically, after adding all the necessary import statements and properties, you just drop the code below into the heightmap editor program written in the previous section.

//Save And Load Files private function saveMyImage(): void {

var formatSwitch:int; if (myJpg.selected) formatSwitch=0; if (myPng.selected) formatSwitch=1;

//Choose encoding format jpeg or png switch (formatSwitch){

case 0: filext= ”.jpg” ; //JPEG encoder var jpgenc:JPEGEncoder= new JPEGEncoder(80); imageByteArray=jpgenc.encode(myBitmapData); break ;

case 1: filext= ”.png” ; //PNG encoder var pngenc:PNGEncoder= new PNGEncoder(); imageByteArray=pngenc.encode(myBitmapData); break ;

}

//Gets a reference to a new empty image file var myFile:File = getNewImageFile(filext);

//Use a FileStream to save the bytearray as bytes to the new file var fStream:FileStream = new FileStream(); try { //open file in write mode fStream.open(myFile,FileMode.WRITE); //write bytes from the byte array fStream.writeBytes(imageByteArray); Alert.show( “Image is saved at “ +myFile.nativePath,” Image Saved”); //close the file fStream.close(); } catch (e:Error){

Alert.show( “Image is not saved!” ,” Saving Error”); trace (e.message); }}

//Returns a unique new image file reference + specified extension private function getNewImageFile(filext:String):File{ //Create a new unique filename using time stamp var fileName:String = myImageName.text+getMyTimestamp()+filext; (continued)

309 Part II: Working with Models and Data

(continued) //Create a reference to a new file in the desktop folder

//Any directory that doesn’t exist in the path is created

//Resolvepath gets a file object that points to the correct file var myFile:File = File.desktopDirectory.resolvePath(imageFolder+fileName);

//Determine that the file exists if (myFile.exists){ //File doesn’t exist, create one return getNewImageFile(filext); }

return myFile; }

//Returns a timestamp private function getMyTimestamp():String{ var date:Date = new Date(); var timeStamp:String = date.getFullYear().toString()+date.getMonth()+date.getDate()+date.getHours()+ date.getMinutes()+date.getSeconds(); return timeStamp; }

//Method for the save button private function onClickSave(): void { saveMyImage(); }

As mentioned earlier, Flash 10 gives you the ability to save to your local PC. You now cover how to accomplish that.

Saving to Your Local PC Using Flash 10 Saving to the local PC is something that the Flash community has wanted to do for years – and it ’ s finally here! Hurray! Saving uses the FileReference class; all you have to do is add two lines of code.

var saveFileRef:FileReference = new FileReference(); saveFileRef.save(imageByteArray);

After adding this code to your editor program and deleting all the unneeded code, just run the program, make a terrain map, and hit Save. You then get a dialog box asking you where to save the file as shown in Figure 8.13. In this particular application of the FileReference class, you need to add the file extension to your file name even though you selected the encoding method from the radial buttons.

310 Chapter 8: Gliding on AIR

Figure 8-13

That ’ s all there is to it! Next, you ’ re going to turn a webcam image into a heightmap.

Turning Your Webcam into a PV3D Heightmap Flash has always been advertised as a tool that ’ s only limited by your imagination. Right now that statement has never been truer. As advanced features get added with each new Flash player, the possibilities for doing super - cool creative stuff grow exponentially. Flash now crosses all media boundaries and as a demonstration of the fun and power of Flash, you ’ ll turn your webcam into a heightmap.

This is much easier than you may think. You ’ ve already written most of the code in the previous sections, and with the addition of one line of code you can grab your webcam for a heightmap and then edit it.

myBitmapData.draw(camVideo);

This code draws your webcam image to your bitmap. The results are shown in Figure 8.14 and look like Han Solo frozen in carbonite. It ’ s one of those cool but useless effects that have the potential for leading to something useful.

Figure 8-14

311 Part II: Working with Models and Data

Even though your webcam image is in color, the terrain editor converts it to grayscale using the getHeight method discussed previously. The method averages your red, green, and blue colors to produce a grayscale height.

There ’ s one more important iteration – it ’ s integrating your editor with your viewer so that you can see a live preview of your work. All you have to do is combine the two programs, so that the bitmap being fed into your terrain viewer is coming from your grayscale editor. For now we ’ ll leave this as an exercise, but we address it again.

Using SQLite Saving data to a database (before AIR) usually meant talking to an external server using a language like PHP, ColdFusion, ASP.net. But AIR comes with an embedded SQLite database engine. AIR databases are stored on your local system with a .db extension, and must be stored in areas where AIR has the proper permission to read and write data. Here are the steps to using a SQLite database.

❑ Use the File class to point to the location of your database. ❑ Use the SQLConnection class to connect your database. ❑ Use the SQLStatement class to define your SQL statements. ❑ Connect your data. Similar to accessing file systems, your connection can also be synchronous or asynchronous.

When you ’ ve connected your database, you communicate with it using a slimmed down version of SQL. Essentially this amounts to support for standard CRUD operations, as mentioned in the previous chapter: Create, Read, Update, and Delete.

One of the cool things you can do with AIR is create an “ occasionally ” connected application; grabbing data from a server data base and storing it locally, then disconnecting for a while, and then reconnecting to synchronize data again.

Deploying AIR After you ’ ve built your Air application, you ’ ll want to distribute it using an installer file. Two types of installer files exist for distribution: a digital certificate, or self - signed. The problem with self - signed is that the Air app reports your identity as unknown when using the installer, which looks unprofessional and often causes the user to question the validity of the application being installed. Because Air can access your local machine files (unrestricted access), an unknown installation has the potential to do great damage to your machine, data, and even compromise your personal security by stealing information associated with your identity. In other words, the danger is real, so being marked as “ Unknown ” without a certificate isn ’ t unwarranted.

You can get a certificate from Thawte or VeriSign. Of course, the downside is that these certificates aren ’ t free. However, if you ’ re distributing an AIR application on the web, you should probably consider getting one. Creating installer files is covered on the book ’ s website and blog.

There is one last topic that you want to consider before concluding, and that ’ s sculpting.

312 Chapter 8: Gliding on AIR Sculpting a Prim Using face3d In order to build a modeling program in PV3D such as 3DSMax (or PaperMax in this case), you need to be able to sculpt prims. You can sculpt prims using the face3d method. This method was created by John Grden, part of the PV3D core team, and gives you the ability to transform triangle vertices when you roll over a triangle face. Figure 8.15 shows an example of a simple lathe program created using simple key listeners, InteractiveScene3DEvent listener, and the face3d method.

Figure 8-15

Here ’ s how it works. The lathe object (a sphere in this case) continually spins. When the w - key is pressed the active triangle faces (those being rolled over) are pushed out from the center and when the s - key is pressed the active triangle faces are pushed in towards the center. The main method that accomplishes this is the sphereMorphOver method that ’ s shown here:

//Lathe Method private function sphereMorphOver(event:InteractiveScene3DEvent):void { //Check your Boolean values if(booleanGoOut||booleanGoIn){

//Grab the three vertices of a triangle var vertexv0:Vertex3D = event.face3d.v0 as Vertex3D; var vertexv1:Vertex3D = event.face3d.v1 as Vertex3D; var vertexv2:Vertex3D = event.face3d.v2 as Vertex3D; //X and Z coordinates if(booleanGoOut){ //Push triangles out myZ=-5*Math.cos(sphere.rotationY*Math.PI/180); (continued)

313 Part II: Working with Models and Data

(continued) myX= 5*Math.sin(sphere.rotationY*Math.PI/180); }else{ //Push triangles in myZ= 5*Math.cos(sphere.rotationY*Math.PI/180); myX=-5*Math.sin(sphere.rotationY*Math.PI/180); } //Translate triangle faces (in or out) vertexv0.z+=myZ; vertexv1.z+=myZ; vertexv2.z+=myZ;

vertexv0.x+=myX; vertexv1.x+=myX; vertexv2.x+=myX;

}

}

The method checks your Boolean values (set by your key action) and then sets the value of myZ and myX (based on prim rotation) which determines which direction your triangle faces are pushed (in or out). Finally, your triangle vertices (obtained from the face3d method) are adjusted based upon these myZ and myX values.

The complete documented code is given here:

package { //Flash Imports import flash.events.Event; import flash.events.KeyboardEvent; //Papervision3D Imports import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.core.proto.MaterialObject3D; import org.papervision3d.events.InteractiveScene3DEvent; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView; //Set Stage Meta data [SWF(width=”640”, height=”480”, backgroundColor=”#222222”, frameRate=”24”)] public class SimpleLathe extends BasicView { //Declare Lathe properties private var sphere:Sphere; private var booleanGoOut:Boolean=false; private var booleanGoIn:Boolean=false; private var myZ:Number; private var myX:Number; public function SimpleLathe() { //Make Viewport interactive super(640, 480,false, true); //Instantiate an Interactive Sphere

314 Chapter 8: Gliding on AIR

var material:MaterialObject3D = new WireframeMaterial(0x00ff00); material.interactive = true; sphere = new Sphere(material, 300, 30, 30); //Over Sphere Method sphere.addEventListener(InteractiveScene3DEvent.OBJECT_MOVE, sphereMorphOver);

//Set Keyboard Listeners stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler ); stage.addEventListener( KeyboardEvent.KEY_UP, keyUpHandler ); //Add Sphere to your stage, set rotation, and start spinning scene.addChild(sphere); sphere.rotationY=0; startRendering(); }

//Lathe Method private function sphereMorphOver(event:InteractiveScene3DEvent):void { //Check your Boolean values if(booleanGoOut||booleanGoIn){

//Grab the three vertices of a triangle var vertexv0:Vertex3D = event.face3d.v0 as Vertex3D; var vertexv1:Vertex3D = event.face3d.v1 as Vertex3D; var vertexv2:Vertex3D = event.face3d.v2 as Vertex3D; //X and Z coordinates if(booleanGoOut){ //Push triangles out myZ=-5*Math.cos(sphere.rotationY*Math.PI/180); myX= 5*Math.sin(sphere.rotationY*Math.PI/180); }else{ //Push triangles in myZ= 5*Math.cos(sphere.rotationY*Math.PI/180); myX=-5*Math.sin(sphere.rotationY*Math.PI/180); } //Translate triangle faces (in or out) vertexv0.z+=myZ; vertexv1.z+=myZ; vertexv2.z+=myZ;

vertexv0.x+=myX; vertexv1.x+=myX; vertexv2.x+=myX;

}

}

//------// Key Listeners //------(continued)

315 Part II: Working with Models and Data

(continued) //Set Boolean Values //Key press method private function keyDownHandler( event:KeyboardEvent ):void { switch( event.keyCode ) { case “W”.charCodeAt(): booleanGoOut = true;//Start push out break;

case “S”.charCodeAt(): booleanGoIn = true;//Start push in break;

} } //Key up method private function keyUpHandler( event:KeyboardEvent ):void { switch( event.keyCode ) { case “W”.charCodeAt(): booleanGoOut = false;//Stop push out break;

case “S”.charCodeAt(): booleanGoIn = false;//Stop push in break;

} }

//Animation Loop override protected function onRenderTick(event:Event = null):void { //Continually Spin Sphere for Lathe sphere.rotationY+=1;

renderer.renderScene(scene, camera, viewport);

}

} }

As soon as you ’ ve sculpted the prim you need to save it using the techniques discussed previously. This program shows how easy it is to sculpt prims inside of PV3D. Using techniques like this you can create an entire modeling application, which would alleviate many of the problems with parsing externally generated models. But obviously there ’ s still much work to be done on this application to bring it up to that level. To see more on this topic check out the book ’ s website.

316 Chapter 8: Gliding on AIR Summary In this chapter, you built your first editor in Adobe AIR. During the development process you mastered a number of uses for basic Flex components, which you ’ ll use again in other applications. Additionally, by accessing your local PC file system using AIR and Flash10, you saved the results from the editor you built to your local hard drive. You also learned about creating grayscale heightmaps and using the geometry.vertices property to bring those maps into PV3D.

Using this, you then created a PV3D terrain viewer to view your heightmaps. Extending your editor, you captured your webcam and programmatically changed it into grayscale, and you brought it into your PV3D terrain viewer. Finally, you built a simple lathe using the face3d method created by John Grden.

317

Part III: Building Games and Websites

Chapter 9: Incorporating 3D Physics

Chapter 10: Building 3D Games for Wii

Chapter 11: Integrating the Flash Media Server

Chapter 12: Developing 3D Websites Part III: Building Games and Websites

In Part III, you explore how you bring physics into PV3D by creating custom physics classes. You modify the DisplayObject3D class, and you learn to use Jiglib. You build Wii controlled games, investigate creating multiplayer games using the Flash Media Server, and create a 3D website.

320 Incorporating 3D Physics

Physics is not yet well developed in PV3D, but having a good physics engine is a must for building dynamic websites and games.

Good physics engages users in kinesthetic modality, activating the “ buying ” part of their brains. Like particles, physics provides an important element of realism to your application. When a camera follows a racing car taking a sharp turn, the camera veers off a little to demonstrate centripetal force. This requires physics. In this chapter we demonstrate the method of adding physics to PV3D.

Also in this chapter you work through a number of examples, illustrating different approaches for incorporating physics into PV3D. You learn to make your objects interactive using the Interactive Scene Manager, and how to build large - scale applications using states, modules, and porting. You also add acceleration and gravity to your camera, as well as orbiting particles. You discover how to build encapsulated oscillation classes as well. Finally, you discuss 3rd party physics engines: WOW and Jiglibflash, and then build some Jiglibflash starter codes to be used in the next chapter.

This chapter is packed with examples, not all of which could be included in full in the text. In some cases, just highlights with code snippets are given, but for all examples, you can obtain the full code from the book ’ s website.

Understanding the Guts of Physics Believe it or not, the entirety of physics consists of only four concepts: position, time, mass, and perspective. Part III: Building Games and Websites

❑ From position and time you get: x, y, z, velocity, acceleration, and jerk. ❑ Adding mass to the mix produces: momentum, force, energy, and work. ❑ Adding perspective yields: frames of reference, macrophysics and microphysics, and the un - unified laws of physics (too numerous to name).

If you think this is an over simplification, think again . . . it took mankind thousands of years to figure it out. Real physics started with Isaac Newton and became elegantly and necessarily confused by Albert Einstein and Paul Dirac. Physicists are now trying to tie it all together with strings (String Theory), which really means that they ’ re starting back at square one, trying to understand the universe again. The core dilemma stems from perspective.

If your frame of reference is distant from something, you apply Newtonian Mechanics; if it is really close, then you apply Quantum Mechanics; and if you ’ re going really fast, you apply Relativistic Mechanics ( “ Special relativity ” ). Nothing is unified – you just change the laws of your environment based on your frame of reference (or where your camera is placed) and how fast you ’ re going. In this chapter, you stick to Newtonian physics.

Adding Physics to Your Camera PV3D gives you the ability to use multiple cameras and add physics to those cameras as well. The key to killer gaming is providing a realistic camera experience for your user. For example, in a racing game you don ’ t want your camera to follow your vehicle ’ s position exactly; this produces an unrealistic feel to your game. You need to add a little springiness!

One of the best improvements ever made to PV3D ’ s camera set was the Spring Powered Camera, created by Benoit Beausejour (see http://agit8.turbulent.ca/bwp/). Using spring physics, your camera is attached to a moving target using the PV3D SpringCamera3D class (found in org/papervision3d/ cameras). Here ’ s how it works:

Your camera is linked by a spring to your target like a car or a boat. When your target moves in 3D space, the spring extends and then pulls the camera along. This gives it a natural movement as opposed to a stiff following action. When your speed - racer makes a sharp turn your camera sways out a little. This is exactly what you want. But how is it coded?

Benoit added a number of physical parameters to the camera: mass, spring stiffness, damping, velocity, acceleration, and force. His equations follow from Hooke ’ s damping equation (and linear iterative approximations). Benoit gets a viscous or damping force by multiplying a camera velocity by damping. He gets spring force restoration (stretch) by multiplying the change in position by stiffness (Hooke ’ s law). Then he calculates the total force on the camera by subtracting the damping force from the restoration force. He calculates acceleration by dividing force by mass, and then steps it down to camera position.

This sounds like a lot, but it really isn ’ t that much code:

//Adding Stiffness to get Hooke’s restoration force _stretch.x = (x - _desiredPosition.x) * -stiffness; _stretch.y = (y - _desiredPosition.y) * -stiffness; _stretch.z = (z - _desiredPosition.z) * -stiffness;

322 Chapter 9: Incorporating 3D Physics

//Multiply velocity by damping to get damping force _dv.x = _velocity.x * damping; _dv.y = _velocity.y * damping; _dv.z = _velocity.z * damping; //Subtract damping force from restoration force to get the force on your camera _force.x = _stretch.x - _dv.x; _force.y = _stretch.y - _dv.y; _force.z = _stretch.z - _dv.z; //Divide force by mass to get acceleration _acceleration.x = _force.x * (1 / mass); _acceleration.y = _force.y * (1 / mass); _acceleration.z = _force.z * (1 / mass); //Calculate velocity _velocity.plusEq(_acceleration); //Step velocity down to position of your camera _xPosition.x = x + _velocity.x; _xPosition.y = y + _velocity.y; _xPosition.z = z + _velocity.z;

As you ponder the best way to add physics to PV3D, Benoit ’ s approach merits some consideration. As opposed to relying on a 3rd party engine, he incorporated the physics directly into PV3D. This obviously is the most efficient approach since you don ’ t double process your calculations to get your PV3D and 3rd party physics engine running at the same time. However, the physics engine in PV3D is not well developed and using a 3rd party engine really saves time (if you can tolerate the double processor hit).

Using Benoit ’ s spring camera you can also create different camera perspectives.

Creating Camera Perspectives In creating any game you need to determine your camera perspective early; changing game perspective mid - way can deal a death blow to your game ’ s development – scrapping months of work or causing you to eat precious hours already spent on a project. There are three perspectives that you need to consider in game development:

❑ 1st Person — a vantage point that attempts to simulate looking through a game character ’ s eyes. It is most commonly found in First - Person Shooters and Racing games. This is good for close - action. ❑ 3rd Person — seen at a distance from a number of different possible perspective angles. Your point of view is looking at the character you ’ re playing. This is good for seeing the global perspective in a story. ❑ 2nd Person — can be one of two things: a combination of both First and Third persons where you see a portion of your character, but not all, or your character is seen through the eyes of another player. In both cases, some portion of the character is shown. This creates a more complex psychology.

323 Part III: Building Games and Websites

When deciding which perspective to use, you should ask yourself the following questions:

❑ Are you inside or outside? If you ’ re inside a tight hallway or a jet cockpit use 1st person. Otherwise, your camera gets hung up on the walls or equipment. ❑ What do you want your player to see? If you ’ re blowing everything up and need to see all the action then use 3rd person. 3rd person will allow you to see the complete explosive effect, and let you remain oriented to your environment as the action happens .

❑ How do you want to tell your story? If the psychology of the game is complex (as opposed to blowing everything up) then 2nd person is the way to go. All the rules for making a good film and how you use your camera apply here!

The bottom line is that games are for exploring – your character, other characters, equipment, environment, your skills, and so on . . . check out Reid Kimball’s blog article on GAMASUTRA for more info on this topic at http://www.gamasutra.com/. Choose the camera perspective that most enhances your ability to explore. And you aren ’ t limited to just one camera. If you jump into a jet cockpit, change your perspective to 1st person. Then change it back to 3rd person when you eject from your jet and run across the desert to fight the enemy. And when you ’ re being interrogated go to 2nd person as the evil villain slashes away at you. Using a spring camera and a switch case makes it easy to do, as shown in the following code:

switch (cameraSwitch) { //Switch to First person case 0: renderer.renderScene(scene, firstPersonCam, viewport); break ; //Switch to Second person case 1: renderer.renderScene(scene, secondPersonCam, viewport); break ; //Switch to Third person case 2: renderer.renderScene(scene, thirdPersonCam, viewport); break ; //Switch to Top view case 3: renderer.renderScene(scene, topViewCam, viewport); break ; default: trace (“Not in my list!”) ; }

Using the switch case above lets you add as many cameras as you want to your 3D scene. We demonstrate this approach in the next section, where you build a star cruiser.

Cruising the Stars Using Cameras Using the Spring Camera class and camera switching you can build a dynamic navigation system using control points. Figure 9.1 shows an elongated - flattened cylinder, which cruises a double row of galaxy lights. The lights act as navigational control points that navigate through an XML - generated star system.

324 Chapter 9: Incorporating 3D Physics

Figure 9-1

Here are the key points of the program:

❑ The navigation markers are actually spheres brought down to 2x2 segments, which makes them look like diamonds. They ’ re distributed through an XML generated star system, and the navigation spheres are placed in this star system using a parametric equation (in this case a 3D figure eight). ❑ The two marker sets (top and bottom) are placed in two separate display containers, which are then added to your scene. ❑ The speed of the cruiser can be adjusted using up or down keyboard arrows. And the cameras can be switched from five different states: ❏ 1. 1st person ❏ 2. top close view ❏ 3. 3rd person ❏ 4. top far view ❏ 5. 2nd person, using the left or right keyboard arrows ❑ The ship follows the marker navigation trail by iterating to the next path position pointNav generated by your navPath method, as shown in the following code:

//Increase Nav Object Speed myObject.extra.angle += mySpeed ;

//Place the nav cylinder on the next position around the navPath var point:Number3D = navPath(myObject.extra.angle); var pointNav:Number3D = navPath(myObject.extra.angle+(0.4));

Using the extra object, included in the DisplayObject3D class, is essential to the navigation routine. The extra object is a public property of the DisplayObject3D class that lets you store data about your

325 Part III: Building Games and Websites

object ’ s state. So in the code snippet above the position of your object is taken from the marker ’ s navigation path method navPath (stored in the extra object) and then projected to an iteration of .4, giving the next point of navigation. Your craft is then made to move to this new position, giving it motion along the marker path created by the method navPath .

An important property of display objects is extra , and the code snippet that creates it in the DisplayObject3D class is given below:

//All properties of the extra field are copied into the instance. The properties specified with extra are publicly available. public var extra :Object;

Most click interactions use the “ extra ” property to determine what to do when an object ’ s state changes. For example, you could create an energy extra value for a particle, and over time, as your particle ’ s energy depletes, have your particle return home. An if statement is used in your animation loop and listens to your particle ’ s energy state and sends it home if its energy is lower than a certain value. Don ’ t be afraid to add additional parameters to your DisplayObject3D class as needed. You won ’ t break anything.

The entire code for the star cruiser is given in the Chapter 9 book code.

No doubt, the most important class in PV3D is the DisplayObject3D class. In this next section, you find out how to modify this class and add a little physics.

Adding Physics to the DisplayObject3D Class You can easily add physics to any PV3D object by modifying the DisplayObject3D class and adding the desired physical parameters, such as mass, velocity, and acceleration. As an example of this, you ’ ll add gravity to the DisplayObject3D class.

Adding gravity to a sphere allows you to create orbiting planets. But planets have mass, so the first order of business is to add mass to the PV3D DisplayObject3D class. So open up the DisplayObject3D class and first add the mass property.

private var _mass:Number

Next, add the mass getter and setter methods so you can get and set the mass property.

public function get mass():Number { return this._mass; }

public function set mass( value:Number ):void { this._mass = value; }

Now that you ’ ve added mass to your DisplayObject3D class, you need to add velocity and acceleration in the same way. Having these values available to you from the DisplayObject3D class lets you give an object acceleration, velocity and mass. The next step is to write a program that utilizes these

326 Chapter 9: Incorporating 3D Physics

physical properties, such as one that creates orbiting planets. The code snippet below determines the gravitational acceleration required to place your planets in orbit. The code can be divided into three parts. Add properties Add properties minMass , maxMass , and gravitationalConstant:

//Add itions for gravity private var minMass:Number=2; private var maxMass:Number=20; private var gravitationalConstant:Number=1; Iterate the routine Iterate the routine to determine the gravitational acceleration of each particle in the x, y, and z direction. You must iterate over i and j particles.

//Iterate through i and j particles for ( var j:int=i+1;j < numParticles;j++){ //Find delta x, y, z for i and j particles var ddx:Number =(particleArray[j].x-particleArray[i].x); var ddy:Number =(particleArray[j].y-particleArray[i].y); var ddz:Number =(particleArray[j].z-particleArray[i].z); //Calculate distance var d:Number =(Math.pow(ddx,2)+Math.pow(ddy,2)+Math.pow(ddz,2)); //Calculate force of gravity between particles var mainF:Number=(gravitationalConstant*particleArray[j].mass*particleArray[i]. mass)/d;

//Force times distance var xc:Number = mainF*ddx; var yc:Number = mainF*ddy; var zc:Number = mainF*ddz;

//Set acceleration for i and j particles particleArray[i].accelerationX+=xc; particleArray[i].accelerationY+=yc; particleArray[i].accelerationZ+=zc; particleArray[j].accelerationX+=-xc; particleArray[j].accelerationY+=-yc; particleArray[j].accelerationZ+=-zc;

} //Set acceleration of ith particle particleArray[i].accelerationX=particleArray[i].accelerationX/ particleArray[i].mass; particleArray[i].accelerationY=particleArray[i].accelerationY/ particleArray[i].mass; particleArray[i].accelerationZ=particleArray[i].accelerationZ/ particleArray[i].mass;

327 Part III: Building Games and Websites

Update the values Now update the calculated values in your DisplayObject3D class:

particleArray[i].updateAll();

Overall you ’ re just calculating the force on each particle due to every other particle in the system at 24 frames per second. Download the code from the book ’ s site and play around with it by changing particle number and mass values. If you ’ re the scientific type, creating your own virtual experiments in PV3D is pretty cool. This code should help you get started. Figure 9.2 illustrates what you should see for eight planets.

Figure 9-2

The orbiting planet 3D simulation slows down as you add more particles. One way of speeding it up is to fake the 3D appearance with a 2D image (or use billboarding, as discussed earlier in the book). With billboarding, you can get some fabulous results that look very 3D, and your client will never know the difference. In addition to adding physics to the DisplayObject3D class, you can also create your own classes.

Building Your Own Physics Classes To illustrate the process of creating your own classes, the book ’s code has four pendulum applications. These apps are for single, double, torsion, and Foucault pendulums, as shown in Figure 9.3. And as in the previous case for gravity, you only hit the highlights of the code development. The full code can be downloaded from Chapter 9 of the book ’ s website.

Figure 9-3 328 Chapter 9: Incorporating 3D Physics

Here ’ s how it works:

The overall code for each pendulum is separated into two classes. The main class runs BasicView (or any PV3D renderer) and is in charge of providing length, pinning, and animation information. The second class holds the elements to be animated. This is somewhat similar to when you built the Barney car using 3DSMax, where the exported collada file held the elements to be animated. In this case, the second class (sub - class) holds the elements to be animated. Referring back to the short discussion on design patterns earlier in this book, this is an example of object inheritance, as illustrated in Figure 9.4.

Main Class BasicView, Properties, and Animation

Sub Class Elements to be instantiated, animated and pinned Figure 9-4

Start by creating a folder to put your sub - classes into. In this case, create a folder called objects. Then create the pendulum elements for each of your four cases. Once your sub - classes are created, import them into the main class using

import objects.*;

Here the wildcard (*) is used on the objects folder, which imports all of your pendulum classes. Using the wildcard for small programs won ’ t slow your program and seldom gets you into any trouble. As your programs become large you need to be careful about wild card usage, which can create conflicting classes.

Once you ’ ve imported your pendulum classes then you can access all the public methods, which let you manipulate the instantiated objects (your pendulum elements) from this sub - class. Of course, the advantage here is that your pendulum elements can be substantiated and pinned as often as needed and used by any number of programs.

Simple Pendulums: Line, Ball, Animation In the simple pendulum case, you need to generate a line and ball, and create animation. The line (or string) for the pendulum is generated by using PV3D ’ s line3D method. Here ’ s what you need to know about creating lines:

❑ The Lines3D object is a DisplayObject3D that ’ s designed to contain and handle the rendering of Line3D objects. Note : Lines3D is not the same class as Line3D. They need each other to work. Line3D is used by Lines3D to store and render the lines. ❑ A Line (or Line3D ) is defined by two 3D vertices – one for each end. A line ’ s start and end vertices are converted into 2D space and rendered using the Flash drawing API ’ s lineTo method.

329 Part III: Building Games and Websites

❑ Line3D can also render curves by adding a control vertex using the Line3D .addControlVertex(...) method. The line ’ s control vertex is then converted into 2D space and rendered using the Flash drawing API ’ s curveTo method. Curves can also be generated adding many short straight lines together. ❑ The line ’ s appearance is defined by its LineMaterial .

The constructor function for the Line3D method is given here:

Line3D(instance:Lines3D, material:LineMaterial, size:Number, vertex0:Vertex3D, vertex1:Vertex3D)

Using this method, you create your pendulum strings. The pendulum balls are created using simple spheres. You can do many cool things with lines, but it ’ s important to remember that you can ’ t have too many line segments on the stage at once as this slows your processor. For continuous drawing, you have to destroy the old lines to make room for the new ones or your program eventually comes to a halt.

Double Pendulum: What ’s pinning? Pinning lets you attach one pendulum to another as shown in Figure 9.5. The program is written so you can pin as many pendulum balls as you want at various string lengths.

Figure 9-5

The two elements above were pinned together using the following code:

myPendulum1.rotationZ = Osc; myPendulum2.rotationZ = Osc*1.721; myPendulum2.x = myPendulum1.getPin(Osc).x; myPendulum2.y = myPendulum1.getPin(Osc).y;

Where the getPin(Osc) function gives the end point of the first segment (and starting point of the second) using a bit of trig.

330 Chapter 9: Incorporating 3D Physics

public function getPin(myRot:Number):Point { var angle:Number = myRot* Math.PI / 180; var xPos:Number = this.x + Math.sin(angle) * myLength; var yPos:Number = this.y - Math.cos(angle) * myLength; return new Point(xPos, yPos); }

This particular example demonstrates one of the normal modes of the double pendulum, with the second pendulum overextending to demonstrate the second degree of freedom. To properly handle this problem one needs to use numerical analysis (such as Runge - Kutta).

Runge - Kutta is a numerical technique for iteratively solving differential equations such as the double pendulum case above. It ’ s commonly used in academia and is the next step up from solving differential equations and is more accurate than the Euler method. It a multistep algorithm and provides greater stability, and higher accuracy with fewer steps. Further discussion of Runge - Kutta is beyond the scope of this book, but the numerical algorithms (and their discussion) can be obtained from Wikipedia at http://en.wikipedia.org/wiki/RungeKutta_methods.

Torsion Pendulum: Cylinder on a Rod In the torsion pendulum case, you use a slider to control your pendulum. As we discussed the use of sliders in the previous chapter, we won ’ t discuss it again here. The torsion pendulum is just a simple pendulum with a cylinder on the end of it that pivots, as opposed to swinging.

Foucault Pendulum: A Little More Realism In the case of the Foucault pendulum, it oscillates in 3D and in a different direction depending on which hemisphere you ’ re in. While it oscillates, it draws the path of oscillation as shown in Figure 9.6. This pendulum was made to look more realistic by placing an environment map on it.

Figure 9-6

331 Part III: Building Games and Websites

That ’ s pretty much it for oscillators. Check out the book ’ s website to see the full code for each one. Oscillators can be used for a number of things, like bird and butterfly wings; just change your sub - class from balls and strings to wings and you ’ ve got a butterfly that can be flown in 3D!

Next you learn to interact with the objects you ’ re creating.

Interacting with Objects in PV3D There ’ s a fundamental premise in physics: if you can ’ t interact with something it doesn ’ t exist. And it ’ s a statement that can never be proven wrong. If you can ’ t interact with an object – touch it, see it, smell it – as far as you ’ re concerned it ’ s not there.

Interacting with a 3D object typically means that you clicked something and something happened. And in PV3D there is more than one way to interact with an object using the Interactive Scene Manager (or ISM). The ISM handles all the traffic and sub - rendering of faces for mouse interactivity within an interactive scene.

Here ’ s how it works:

When you create an InteractiveScene3D object, it automatically creates the InteractiveSceneManager . Any 3D object using an interactive material is automatically given mouse events that are dispatched through the ISM or the DisplayObject3D. The event type is InteractiveScene3DEvent . ISM has 2 modes:

❑ Object level mode — Object level mode is a bit faster in that sprite containers for the objects are created and then all faces are drawn on that container. You ’ re able to receive mouse events based on the object, but not the contents of the materials used on the object. ❑ Face level mode — Face level mode creates sprite containers for each face of a DisplayObject3D that uses an interactive material, and gives you the ability to interact with objects.

The key is that in object level mode you interact with an object ’ s container, and in face level mode you interact with an object ’ s materials. In the next section, you interact with an object ’ s materials using the MouseEvent class.

Building an Interactive Cube: Face Level In this application, you ’ ll build an interactive cube that lets you click on its individual faces and call a particular URL depending on which side you click, as shown in Figure 9.7. You can interact directly with a cube ’ s faces using the Flash MouseEvent class as long as your materials are Flash movie clips. Otherwise, you need to use the ISM to interact with an object ’ s faces.

332 Chapter 9: Incorporating 3D Physics

Figure 9-7

Here are the steps required to build this application:

First, grab six images of your choosing; make sure that they ’ re mip - mapped (exponent of 2 in pixel size), and import them into the Flash library. Here ’ s the important step. You need to bring the images into your PV3D application. You can do this in several ways. Method #1 You could turn them into movie clips by dragging them to the stage and giving each one an instance name. You would then make them invisible by using blend mode (so no one sees them just sitting on the stage when you run your program). Then use the MovieMaterial in PV3D to put them into a MaterialsList and place them on a cube.

//Create your materials list var materialsList:MaterialsList = new MaterialsList(); materialsList.addMaterial( movieSide1, “front” ); materialsList.addMaterial( movieSide2, “back” ); materialsList.addMaterial( movieSide3, “left” ); materialsList.addMaterial( movieSide4, “right” ); materialsList.addMaterial( movieSide5, “top” ); materialsList.addMaterial( movieSide6, “bottom” );

//Add your materials to your cube and add your cube to the scene myCube = new Cube(materialsList, 256, 256, 256, 10, 10, 10); scene.addChild(myCube); Method #2 You could also turn each image into an exportable class and use the MovieAssetMaterial to put them into a MaterialsList for your cube (that ’ s almost it). But the best way to do this is to turn each image into an exportable class, and then to import them into your application as a class. This is the same method you used when you created the CS4 planetarium in a previous chapter. The star was an instantiated class that came from your Flash library.

333 Part III: Building Games and Websites

/*Grab the Image classes from the Flash Library and Instantiate the classes. The best way to do this.*/ var image1:Image1= new Image1(); var image2:Image2= new Image2(); var image3:Image3= new Image3(); var image4:Image4= new Image4(); var image5:Image5= new Image5(); var image6:Image6= new Image6();

Why is the latter the best approach? It gives you more programming control. In Flash, animation can be turned into pure ActionScript by right clicking on the Flash animation timeline and choosing Copy Motion as ActionScript 3.0 . When you import the image that you exported as a class, the animation script is imported along with the Image class.

After the materials are turned into classes, you then turn them into a movie material, which can be placed into a MaterialsList and then onto your cube.

//Grab Movie Clips from the Flash Stage //Each clip must have the referenced instance name (image1,...) var movieSide1 = new MovieMaterial(image1); var movieSide2 = new MovieMaterial(image2); var movieSide3 = new MovieMaterial(image3); var movieSide4 = new MovieMaterial(image4); var movieSide5 = new MovieMaterial(image5); var movieSide6 = new MovieMaterial(image6);

Now at this point there a number of important things you must do:

1. Set your viewport to button mode (viewport.buttonMode = true ). This shows the hand cursor when you rollover the cube faces. 2. Set the Viewport parameters to Viewport3D(0, 0, true, true) . The first Boolean scales your application to the stage, and the second Boolean makes your viewport interactive. 3. Set your materials to interactive:

//Make your material interactive movieSide1.interactive=true; movieSide2.interactive=true; movieSide3.interactive=true; movieSide4.interactive=true; movieSide5.interactive=true; movieSide6.interactive=true;

4. Add MouseEvent.CLICK listeners to each face image:

//Add your listeners image1.addEventListener(MouseEvent.CLICK, image1Click); image2.addEventListener(MouseEvent.CLICK, image2Click); image3.addEventListener(MouseEvent.CLICK, image3Click); image4.addEventListener(MouseEvent.CLICK, image4Click); image5.addEventListener(MouseEvent.CLICK, image5Click); image6.addEventListener(MouseEvent.CLICK, image6Click);

334 Chapter 9: Incorporating 3D Physics

5. Using the navigateToURL method to make each listener call a URL when a cube face is clicked:

function image1Click(e:MouseEvent):void { navigateToURL(new URLRequest(“http://professionalpapervision.wordpress.com/”)); }

The entire commented code is given here:

//Papervision Imports import org.papervision3d.scenes.*; import org.papervision3d.cameras.*; import org.papervision3d.objects.primitives.*; import org.papervision3d.materials.*; import org.papervision3d.materials.utils.*; import org.papervision3d.render.*; import org.papervision3d.view.*; import org.papervision3d.events.*;

//Render Variables var myCube:Cube; //Grab the Image classes from the Flash Library and //Instantiate the classes. The best way to do this. var image1:Image1= new Image1(); var image2:Image2= new Image2(); var image3:Image3= new Image3(); var image4:Image4= new Image4(); var image5:Image5= new Image5(); var image6:Image6= new Image6();

var viewport:Viewport3D = new Viewport3D(0, 0, true, true); addChild(viewport); viewport.buttonMode = true; var renderer:BasicRenderEngine = new BasicRenderEngine(); var scene:Scene3D = new Scene3D(); //Camera properties var camera:Camera3D = new Camera3D(); //Set camera to natural viewing size of the object camera.zoom = 11; camera.focus = 100;

//Grab Movie Clips from the Flash Stage //Each clip must have the referenced instance name (image1,...) var movieSide1 = new MovieMaterial(image1); var movieSide2 = new MovieMaterial(image2); var movieSide3 = new MovieMaterial(image3); var movieSide4 = new MovieMaterial(image4); var movieSide5 = new MovieMaterial(image5); var movieSide6 = new MovieMaterial(image6); //Make your material interactive movieSide1.interactive=true; movieSide2.interactive=true; (continued)

335 Part III: Building Games and Websites

(continued) movieSide3.interactive=true; movieSide4.interactive=true; movieSide5.interactive=true; movieSide6.interactive=true; //Increase quality, make it smooth movieSide1.smooth=true; movieSide2.smooth=true; movieSide3.smooth=true; movieSide4.smooth=true; movieSide5.smooth=true; movieSide6.smooth=true; //Not needed here but allows for animated clips movieSide1.animated=true; movieSide2.animated=true; movieSide3.animated=true; movieSide4.animated=true; movieSide5.animated=true; movieSide6.animated=true;

//Create your materials list var materialsList:MaterialsList = new MaterialsList(); materialsList.addMaterial( movieSide1, “front” ); materialsList.addMaterial( movieSide2, “back” ); materialsList.addMaterial( movieSide3, “left” ); materialsList.addMaterial( movieSide4, “right” ); materialsList.addMaterial( movieSide5, “top” ); materialsList.addMaterial( movieSide6, “bottom” ); //Add your materials to your cube and add your cube to the scene myCube = new Cube(materialsList, 256, 256, 256, 10, 10, 10); scene.addChild(myCube);

//Add your listeners image1.addEventListener(MouseEvent.CLICK, image1Click); image2.addEventListener(MouseEvent.CLICK, image2Click); image3.addEventListener(MouseEvent.CLICK, image3Click); image4.addEventListener(MouseEvent.CLICK, image4Click); image5.addEventListener(MouseEvent.CLICK, image5Click); image6.addEventListener(MouseEvent.CLICK, image6Click); //Link Functions function image1Click(e:MouseEvent):void { navigateToURL(new URLRequest(“http://professionalpapervision.wordpress .com/”)); }

function image2Click(e:MouseEvent):void { navigateToURL(new URLRequest(“http://www.professionalpapervision.com/”)); } function image3Click(e:MouseEvent):void { navigateToURL(new URLRequest(“http://code.google.com/p/flex3cookbook3/”)); }

336 Chapter 9: Incorporating 3D Physics

function image4Click(e:MouseEvent):void { navigateToURL(new URLRequest(“http://sketchup.google.com/”)); } function image5Click(e:MouseEvent):void { navigateToURL(new URLRequest(“http://www.youtube.com/mikenku”)); } function image6Click(e:MouseEvent):void { navigateToURL(new URLRequest(“http://www.bib-arch.org/”)); } //Add object to the stage with one render renderer.renderScene(scene, camera, viewport);

//Start your Animation Loop addEventListener(Event.ENTER_FRAME, myLoop); //Animation Loop function myLoop(e:Event):void { var myXDist:Number = mouseX - stage.stageWidth * 0.4; var myYDist:Number = mouseY - stage.stageHeight * 0.4;

myCube.rotationY += myXDist * 0.01; myCube.rotationX += -myYDist * 0.01;

renderer.renderScene(scene, camera, viewport);

}

Lee Brimelow on his popular “ Go To and Learn ” site at http://www.gotoandlearn.com/ does a similar cube example (but doesn ’ t create image classes as discussed above). Lee ’ s site covers a number of cutting edge topics.

Browser Double -Click Problems One of the hassles that arises when working with Flash is browser control. A Flash program needs to be clicked on to transfer focus to it from the browser. Many flashmaticians get around this by putting a start button at the beginning of their programs. The user clicks it, and transfers focus to the Flash program. In the case of the interactive cube, you end up having to double click each cube face to get it to navigate to the next site. That ’ s because the first click transfers focus from the browser to the Flash program and the second click executes the navigation function.

However, from an instructional design point of view, it ’ s undesirable to navigate in - and - out of a site repeatedly. In this section you used the MouseEvent class, but if your object ’ s materials aren ’ t movie clips you need to use the ISM to interact with polygon faces. You do this by changing your listener from MouseEvent to the ISM. In this next section, you interact with a particle system on the object level mode using the ISM.

337 Part III: Building Games and Websites

Building Interactive Particles: Object Level Interacting with particles on the object level mode requires that you add a listener to an object ’ s container (as opposed to its material). In the example that ’ s shown in Figure 9.8, you interact on the object level in three ways:

❑ Rotating the small sphere at the upper right hand side (Figure 9.8) controls rotation of the large wireframe sphere. ❑ Clicking on the particles dispersed on the large wireframe sphere ’ s nodes moves an x, y, and z coordinate gizmo to the location of where the particle was clicked.

❑ Rolling over the x, y, or z - axis of your coordinate gizmo enlarges that axis, demonstrating that it ’ s active.

Figure 9-8

Here are the code snippets for each event:

Rotating Small Sphere Rotating the small navigation sphere with your mouse rotates the large sphere as well. You accomplish this by adding an ISM listener to your smallSphere . A Boolean parameter doRotate is set to true when your mouse is held down on the small sphere. Its motion is picked up in the animation loop (which follows your mouse movement). When you let up on the small nav sphere, the Boolean value is set to false and your animation loop can no longer follow your mouse movements and change the large sphere position.

338 Chapter 9: Incorporating 3D Physics

smallSphere.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS,function(e : InteractiveScene3DEvent):void { doRatate=true; oldMouseX=mouseX; oldMouseY=mouseY; });

smallSphere.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE,function(e : InteractiveScene3DEvent):void { doRatate=false;

oldRotX=smallSphere.rotationX; oldRotY=smallSphere.rotationY;

myXKey=false; myYKey=false;

});

Using Booleans to change motion in animation loops is a common practice in PV3D. In addition to the mouse press on the large sphere you can also hold down the x - key or the y - key on your keyboard, which constrains the motion to those respective axes. Use of the Boolean myXKey and myYKey is shown in the code snippet below.

if(doRatate){

if(!myXKey){ particleContainer.rotationY=smallSphere.rotationY = (oldMouseX*4-mouseX*4+oldRotY); }

if(!myYKey){ particleContainer.rotationX=smallSphere.rotationX = (oldMouseY*4-mouseY*4+oldRotX); }}

Next, you make your particles interactive.

Clicking on Particles You can make your particles interactive by assigning an ISM listener to each particle container as you generate it. In the code snippet below, you place a particle on each node of your large sphere. Each iteration over the large sphere ’ s nodes grabs a billboard movie clip from the Flash library and places it in a particle container, which is assigned an ISM click listener. When the particles are clicked on they move the coordinate gizmo to the particle position.

339 Part III: Building Games and Websites

//Disperse the particles over the large sphere for(var i:int = 0; i < sphere.geometry.vertices.length; i++) { //Grab the billboard particle from the Flash library var billboard:MovieAssetParticleMaterial = new MovieAssetParticleMaterial(“mySphere”,true); //Make it interactive and smooth billboard.interactive = true; billboard.smooth = true; //Create a particle container and add your billboard var particles3D:Particles = new Particles(“ParticleContainer”+i); var p:Particle = new Particle(billboard,1,0,0,0); particles3D.addParticle(p);

//Add a Click listener to your particle. particles3D.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, particleClick); //Add state and index to the particle’s object extra particles3D.extra = {myMove: false, myIndex: i}; /*Add your particle to the particle container and push it into a particles array*/ particleContainer.addChild(particles3D); particles.push(particles3D); //Place your particle on the large sphere node particles3D.x=sphere.geometry.vertices[i].x; particles3D.y=sphere.geometry.vertices[i].y; particles3D.z=sphere.geometry.vertices[i].z; }

In addition, the extra property of the particle is set with a Boolean move and Integer index parameter. Then it lets you know if any particle is moving and what its index is.

The particleClick snippet below moves your coordinate Gizmo to the particle position you clicked on.

//Particle click function private function particleClick(e : InteractiveScene3DEvent) : void { /*Move the coordinate axis gizmo to the particle position that was clicked*/ coordContainer.x=e.displayObject3D.x; coordContainer.y=e.displayObject3D.y; coordContainer.z=e.displayObject3D.z; }

Finally, you create an interactive x, y, z gizmo.

Rolling Over the x, y, z Gizmo The code snippet below creates an x, y, and z - axis gizmo out of cylinders and assigns ISM over and out listeners. As your mouse rolls over a particular axis it enlarges, and as it rolls off, it returns to its regular size.

340 Chapter 9: Incorporating 3D Physics

//Create a Coordinates axis private function Coords():void{

/*Create axis Gizmo - in this case cylinders are used but default line material Lines3D object can be used as well*/ colormaterialR = new ColorMaterial(0xFF0000, .8, true); colormaterialG = new ColorMaterial(0x00FF00, .8, true); colormaterialB = new ColorMaterial(0x0000FF, .8, true); //Set x-axis axisX = new Cylinder(colormaterialR, 5, 120, 4, 4); axisX.rotationZ=90; //Set y-axis axisY = new Cylinder(colormaterialG, 5, 120, 4, 4); //Set z-axis axisZ = new Cylinder(colormaterialB, 5, 120, 4, 4); axisZ.rotationX=90;

//Put axes x,y,and z in a coordinate container coordContainer.addChild(axisX); coordContainer.addChild(axisY); coordContainer.addChild(axisZ);

//Add ISM listeners to the x, y, z cylinder axis axisX.addEventListener(InteractiveScene3DEvent.OBJECT_OVER,function(e : InteractiveScene3DEvent):void {axisX.scale=1.5;});

axisX.addEventListener(InteractiveScene3DEvent.OBJECT_OUT,function(e : InteractiveScene3DEvent):void {axisX.scale=1;});

axisY.addEventListener(InteractiveScene3DEvent.OBJECT_OVER,function(e : InteractiveScene3DEvent):void {axisY.scale=1.5;});

axisY.addEventListener(InteractiveScene3DEvent.OBJECT_OUT,function(e : InteractiveScene3DEvent):void {axisY.scale=1;}); axisZ.addEventListener(InteractiveScene3DEvent.OBJECT_OVER,function(e : InteractiveScene3DEvent):void {axisZ.scale=1.5;});

axisZ.addEventListener(InteractiveScene3DEvent.OBJECT_OUT,function(e : InteractiveScene3DEvent):void {axisZ.scale=1;});

particleContainer.addChild(coordContainer); }

There are six listeners applied – two for each of the gizmo axes (x, y, z). You ’ re now going to take this concept of ISM click interaction and build a 3D wall course.

Taking it to the Next Level: 3D Wall Course Carousels are some of the most interesting interactive objects, and with just a little work you can turn an interactive carousel into a complete learning management system that could be used for an online course. Figure 9.9 shows a 3D wall carousel that was created by the author and used for a faculty learning community course at Northern Kentucky University.

341 Part III: Building Games and Websites

Figure 9-9

The carousel course was built in Adobe Flex and represents how to put a scaleable application together. Here are the main features of the 3D Wall Carousel Course:

❑ Simple Password System (not to be used for real applications). ❑ Home, Introduce Yourself, Announcements, Syllabus, and Discussion Link Panel. ❑ Two - level interactive carousel wall with reflection, slider/button navigation, and easing. ❑ Video play component with list selection box. ❑ Weekly Assignment component. ❑ Cairngorm MVC states controlled architecture.

At the heart of the program is the interactive 3D Wall Carousel. This is how it was built:

❑ Three sets of 15 images were pulled from the images library using BitmapFileMaterial from bottom to top. ❑ For the bottom image row ( bam1 set) material interactivity was set to false since it serves as the reflection. And the next two rows ( bam2 set and bam3 set) interactivity was set to true so the ISM listeners could be created for them. ❑ Three sets of planes were created (named p1 , p2 , and p3 ) for holding the image sets (bam1 , bam2 , and bam3 ). The planes were positioned around a circle using simple sine and cosine functions. The planes were oriented to the correct angle using the appropriate rotationY based on the ith iteration. ❑ Two extra objects were used in the DisplayObject3D class. A second extra object called extra2 was created by opening up the DisplayObject3D class and creating it. ❑ For the final two rows ( p2 and p3 ) set extra equal to in . This indicates that the initial state of each interactive panel in the carousel is “ in ” .

342 Chapter 9: Incorporating 3D Physics

❑ Next the extra2 property for both sets of planes (p2 and p3 ) was set to the index number of the plane. This lets you programmatically place the panel back where it needs to go. ❑ Finally, an ISM listener object was set for both sets of planes, which executes a toggle function when clicked: //Create Carousel private function createObjects(): void { for( var i:uint=0; i

var bam3:BitmapFileMaterial = new BitmapFileMaterial( “images/”+allMyData [14-i].myLec); bam3.oneSide = false ; bam3.smooth = true ; bam3.interactive = true ;

/* Three planes created p1, p2, and p3 for holding the images bam1, bam2, and bam3. The planes are positioned around a circle using simple sine and cosine functions. The planes are oriented to the correct angle using the appropriate rotationY based on the iteration i. */ p1 = new Plane(bam1, 128, 128, 2, 2); p1.x = Math.cos(i*anglePer) * radius; p1.z = Math.sin(i*anglePer) * radius; p1.y = -100; p1.rotationY = (-i*anglePer) * (180/Math.PI) + 90; p1.scaleY=.7;

scene.addChild(p1);

p2 = new Plane(bam2, 128, 128, 2, 2); p2.x = Math.cos(i*anglePer) * radius; p2.z = Math.sin(i*anglePer) * radius; p2.y = 0; p2.rotationY = (-i*anglePer) * (180/Math.PI) + 90; p2.scaleY=.7; scene.addChild(p2);

p3 = new Plane(bam3, 128, 128, 2, 2); p3.x = Math.cos(i*anglePer) * radius; (continued)

343 Part III: Building Games and Websites

(continued) p3.z = Math.sin(i*anglePer) * radius; p3.y = 100; p3.rotationY = (-i*anglePer) * (180/Math.PI) + 90;

p3.scaleY=.7; scene.addChild(p3); //Set the extra property to in, and extra2 to plane index number p2.extra={pIdent: ”in” }; p2.extra2={myIdent:3*(allMyData.length-i)-2}; p2.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, toggler);

p3.extra={pIdent: ”in” }; p3.extra2={myIdent:3*(allMyData.length-i)-3}; p3.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, toggler); }}

Clicking on an interactive carousel image causes the toggler function to be executed. The first thing the toggler function does is check to see if the plane has been clicked on, by checking its material ’ s “ extra ” property. If not, it runs the moveOut() methods which moves your plane out. If so, it runs the moveBack function, which moves your plane back.

private function toggler(event:InteractiveScene3DEvent): void { // If the cube’s position is “in”, move it out or else move it back if (event.target.extra.pIdent == “ in” ) { if (firstClick== true ){ //If plane has been clicked on, move it back. moveBack(youClickedMe); } //If the plane has not been clicked on execute moveOut moveOut(event.target); } else { moveBack(event.target); }firstClick= true ; }

The entire code can be obtained from the book ’ s Chapter 9 code. However, before you move on to the next topic there ’ s a problem that needs to be addressed with running standard Flash and Flex components in PV3D 2.0.

The Big Problem There ’ s a big problem when it comes to using Flex or Flash components (such as sliders, buttons, data grids . . . ) directly in PV3D: they don ’ t work well! Importing these components and getting them to work well in PV3D is difficult. OK, there are a few examples out there where Flex and Flash components

344 Chapter 9: Incorporating 3D Physics

have been brought into PV3D ’ s 3D environment, and in some cases they look pretty good. But scratch a little below the surface and you ’ ll go looking for another approach.

What ’ s the problem? Simply put, PV3D uses the BitmapData object to take snap shots of components. It then brings those snapshots into your PV3D app. As a result you ’ re never on frame. You ’ re always a frame or two off the action.

This may be OK for playing video on a primitive, but if you ’ ve ever worked with Google maps you find that the Google Flash API buttons in PV3D are just a little displaced from their correct positions. This makes for a bad user experience.

So what ’ s the solution? There are two:

❑ Overlay your components on your 3D animation. Many successful sites do this, but it ’ s only a trick. When you get to a point where you need a component such as video or a text box, you drop that component in from Flex or Flash and layer it on top of your 3D animation. The carousel course shown above does this very effectively; all the time you think you ’ re still in the PV3D app. But in reality you ’ ve frozen it and have layered a 2D component on top of it. When you ’ re done with the 2D component, you simply hit the appropriate button, and your 3D app comes back to life. ❑ You can upgrade PV3D 2.0 to use the Flash 10 player and take advantage of the native z component. In Flash 10, all Flash and Flex (Gumbo) components are natively 3D. So you can put a video into a 3D app and rotate it and stay on frame – with no lag as in the preceding case.

So in this book, you’ll use the second option. Flash 10 makes it really easy to work with components in 3D. If you need a component in PV3D use the Flash 10 player addition . . . choosing the right tool is 80 per cent of the job.

If you work in a fast - paced environment, you ’ re definitely interested in building large - scale deployments fast. Choosing the right tool is essential in creating a streamlined production process.

Rapid Large -Scale Development For building large - scale data -driven applications, Flex is the tool of choice. You have a number of options available to you for the building process: states, view stacks, custom components, and modules.

The carousel course was built using states and custom components. Each one of the items in the states panel shown in Figure 9.10 was built as a custom component and then woven together into a navigation system using states.

345 Part III: Building Games and Websites

Figure 9-10

The important thing to remember about states is that you don ’ t want to fill the base state with anything. You can always choose another state for your starting state. Put all your states below the base state. Thus, if you ever need to get under your states (and you will) the base state will be available for you to do that. In addition, initially center your states on the screen by placing them in a screen - centered canvas component. Reworking states is difficult; make sure that you do a little up - front planning before you code. Another option for creating large - scale applications is using modules; you cover their use in an upcoming section. With interactivity under your belt, it ’ s time to get a little more physical with physics.

Using 3rd Party Physics Engines There ’ s an upside and a downside to using 3rd party physics engines. The upside is that it ’ s already built and you don ’ t have to spend all the time it takes to develop one. The downside is that most 3rd party physics engines for PV3D require that you double process your elements.

What does it mean to double process? When an object is created in PV3D (like a sphere for example), then a corresponding physics object needs to be created in your physics engine. You then attach this physics object to your PV3D object and it now becomes physical. In many cases, this means collision - enabled. The two objects follow each other.

One of the first 3rd party physics engines for PV3D was WOW followed by JiglibFlash. The big difference between WOW and Jiglib is that WOW is a particle - based engine; every vertex has its own physics. Jiglib is a Rigid Body - based engine, using primitive types to create a rough shape around your PV3D object, which reduces the amount of processing needed to run your physics. Jiglib is easier to use and can be run using an XML backend to build game levels.

Both WOW and Jiglib have some similarities, and are executed in a similar manner.

346 Chapter 9: Incorporating 3D Physics

WOW Physics For a time WOW physics was the mainstream of PV3D physics. It was created by Seraf (Jerome Birembaut) and can be downloaded from http://seraf.dediabox.fr/wow-engine/. It’ s based on Sandy3D and APE (ActionScript Physics Engine). Basically, both WOW and Jiglib are executed by pairing a PV3D object with a WOW or Jiglib interactive object. In PV3D, if two objects overlap in 3D space there’ ll be no effect. They pass through each other since PV3D objects don ’t interact with each other.

But by attaching a physics object that follows the position of the PV3D object, a collision can be made to occur. Of course the downside is that you ’ re now double processing, creating both a physics and PV3D object as shown in the following code:

//Enter Frame Function (24 frames per second) private function update(e:Event): void {

//Execute WOW physics rendering updatePhy();

//Execute PV3D rendering updatePV3D();

//Pair objects together for ( var i:uint = 0; i < pv3DObjects.length; i++) { apply3DToPhy(pv3DObjects[i], wowObjects[i]); }}

In the preceding code, the updatePhy() function runs the physics engine, and the updatePV3D() function runs the PV3D renderer. The for statement loops through every object in both PV3D and WOW and pairs them together.

For small experiments the results are very good. In Figure 9.11 a simulation shows bouncing planets. Click the screen and the force vector is randomized, and the planets are set into motion. You can adjust a number of parameters as well: gravity, zoom, rotation, and gravitational direction.

Figure 9-11 347 Part III: Building Games and Websites

The implementation of WOW is not difficult for small experiments. As mentioned above, you just pair a PV3D object with a WOW object. But applying WOW to large- scale projects with complicated geometries can be challenging. Therefore, we won ’ t be covering WOW any further in this book. You can download the full code for the simulation in Figure 9.11 and find out more about WOW physics from the book ’ s website.

Jiglibflash Jiglibflash is becoming the tool of choice for Flash 3D physics, supporting PV3D, Away3D, and Sandy3D. You can obtain Jiglibflash from Google code at http://code.google.com/jiglib/flash/ . Just grab the SVN link the same way you did for PV3D in Chapter 2. Jiglibflash is a port from the Jiglib C++ library and is based on nonconvex rigid body physics.

What on earth is nonconvex rigid body physics?

Unlike particles, which only translate, rigid bodies occupy space and have geometrical properties such as center of mass, and moment of inertia. Rigid bodies have six degrees of freedom (translation and rotation in three directions). They require a full application of Newton ’ s laws of motion. Nonconvex means not smooth, which if you ’ re a mathematician or physicist means a breaking of symmetry. At the moment all particles in Jiglibflash are smooth, and that ’ s an easier solution.

In simple terms, you ’ re working with the physics of everyday objects. In Jiglib, you ’ re limited to a small set of object types (sphere, plane, capsule, and box). But if you strike a ball with a pole (or capsule), you ’ ve got a baseball game, or short, fat poles with a ball – you ’ ve got bowling. So even though the system is limited in its object types, with a little imaginative texturing there ’ s a ton you can do with it.

Implementing Jiglibflash In this section, you start by building your first Jiglibflash HelloWorld program. You first need to create a 3D scene. You ’ ve got two options for doing this: you can use BasicView or you can instantiate your individual elements such as cameras, viewport, renderer, and scene. Both approaches were demonstrated in Chapter 2. For this example, you use BasicView , but later on, as your scenes grow in complexity, you ’ ll need to instantiate the individual elements. BasicView Starter Code The code below demonstrates how to institute Jiglib with PV3D using BasicView . Essential to getting Jiglib to work in PV3D is importing the PV3D plug - in written by Bartek Drozdz of Everyday Flash. The plug - in lets you access PV3D/Jiglib interactive plane, cube, and sphere primitives simultaneously. In addition, you must declare a physics parameter data - typed as Papervision3DPhysics . This parameter allows you to run the Jiglib physics system.

Immediately you should recognize that, as in the WOW case, you ’ re double processing both PV3D and Jiglib in the onRenderTick animation loop using super.onRenderTick(event) and physics.step() methods:

348 Chapter 9: Incorporating 3D Physics

package { //Flash, Jiglib, PV3D imports import flash.events.Event; import jiglib.plugin.papervision3d.Papervision3DPhysics; import org.papervision3d.view.BasicView; //Jiglib Starter Code public class StarterCode extends BasicView { //Instantiate Papervision3DPhysics parameter private var physics:Papervision3DPhysics;

//Starter Code Constructor public function StarterCode() { super(0, 0, true); //Start Physics and PV3D Engine initPhysics(); startRendering(); } //Start Up Physics Engine private function initPhysics():void { //Set scene and physics engine speed physics = new Papervision3DPhysics(scene, 7); } //BasicView Animation Loop override protected function onRenderTick(event:Event = null):void { //update the engine in each frame physics.step(); super.onRenderTick(event); } }}

When you run this code nothing happens. You ’ ve got to add some objects. Here ’ s how to do that using Jiglib. Adding Objects By nature, all PV3D objects are non - interactive. Just like with the WOW engine a Jiglib object needs to be paired with a PV3D object. You have two ways by which to add physical objects (objects that interact with other objects).

The first way to add objects is to create a PV3D object and insert it into a Jiglib object as follows:

var myMaterial:BitmapMaterial = new BitmapMaterial(Bitmap(new myTexture()). bitmapData, true); myMaterial.tiled = true; myMaterial.smooth = true; spherePV3DObject = new Sphere(myMaterial, 64, 16, 8); scene.addChild(spherePV3DObject);

349 Part III: Building Games and Websites

Insert the spherePV3DObject into the JSphere using Pv3dMesh :

physicsJibObject = new JSphere(new Pv3dMesh(spherePV3DObject), 100); physicsJibObject.y = 200; physicsJibObject.restitution = 3; physicsJibObject.mass = 1; physics.addBody(physicsJibObject);

But there ’ s an easier way to do this; there ’ s an all - in - one that comes from the PV3D plug - in:

private function createSphere():void { var mySphere:RigidBody = _physics.createSphere(myMaterial , 64, 16, 8); }

The code from Bartek ’ s shortcut plug - in is given here. It ’ s a great example of encapsulation. Why write so much code when you can keep it below the surface?

public function createSphere(material:MaterialObject3D, radius:Number=100, segmentsW:int=8, segmentsH:int = 6):RigidBody { var sphere:Sphere = new Sphere(material, radius, segmentsW, segmentsH); scene.addChild(sphere); var jsphere:JSphere = new JSphere(new Pv3dMesh(sphere), radius); addBody(jsphere); return jsphere;}

Currently there aren ’ t that many Jiglib objects: plane (called ground), cube, and sphere. The simplified method for adding them is given below:

Plane Shortcut method for a creating a physical plane:

private function createGround():void { var myMaterial:WireframeMaterial = new WireframeMaterial(0xCCCCCC); var floor:RigidBody = _physics.createGround(myMaterial, 1024, 0); }

Cube Shortcut method for a creating a physical cube:

private function createBox():void { var myMaterials:MaterialsList = new MaterialsList(); myMaterials.addMaterial(new WireframeMaterial(0xFF4444), “all”); var myBox:RigidBody = _physics.createCube(myMaterials, 64, 64, 64, 4, 4, 4); }

350 Chapter 9: Incorporating 3D Physics

Sphere Shortcut method for a creating a physical sphere:

private function createSphere():void { var mySphere:RigidBody = _physics.createSphere(myMaterial , 64, 16, 8); }

Well that ’ s not much to work with, but the good news is that if you love to develop, Jiglib physics is wide open for doing some creative work. You ’ re now going to build a simple Hello World. Jiglib Hello World The Hello World example shown in Figure 9.12 is a texture PV3D sphere followed by a spring camera and controlled by keyboard commands.

Figure 9-12

The main elements that you must instantiate are a sphere, a plane (or ground), and a spring camera. To accomplish this you run the following three methods in the HelloWorldJiglib constructor method.

createFloor(); setSpringCamera(); createHelloSphere();

These methods have already been demonstrated earlier in this chapter. Once these objects are generated the key listeners are put in place and use Booleans to control the sphere ’ s force vectors as shown below:

if (keyLeft){ helloSphere.addWorldForce(new JNumber3D(-moveForce, 0, 0), } if (keyRight){ helloSphere.addWorldForce(new JNumber3D(moveForce, 0, 0), helloSphere.currentState.position); } if (keyForward){ helloSphere.addWorldForce(new JNumber3D(0, 0, moveForce), helloSphere.currentState.position); } (continued)

351 Part III: Building Games and Websites

(continued) if (keyReverse){ helloSphere.addWorldForce(new JNumber3D(0, 0, -moveForce), helloSphere.currentState.position); } if (keyUp){ helloSphere.addWorldForce(new JNumber3D(0, 2*moveForce, 0), helloSphere.currentState.position); }

So as different keys are pressed the force vector for the sphere is applied, and it rolls around on the screen based upon the force vector applied. The entire code is given here and can be downloaded from the book ’ s Chapter 9 code:

package{ //Flash Imports import flash.display.Bitmap; import flash.events.Event; import flash.events.KeyboardEvent; import flash.ui.Keyboard; //Jiglib Flash import jiglib.geometry.JSphere; import jiglib.math.JNumber3D; import jiglib.physics.RigidBody; import jiglib.plugin.papervision3d.Papervision3DPhysics; import jiglib.plugin.papervision3d.Pv3dMesh; //Papervision3D Imports import org.papervision3d.cameras.CameraType; import org.papervision3d.cameras.SpringCamera3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView; import org.papervision3d.view.layer.util.ViewportLayerSortMode;

[SWF(width=”1000”, height=”700”, backgroundColor=”#000000”, frameRate=”30”)]

//Hello World Constructor function public class HelloWorldJiglib extends BasicView { [Embed(source=”assets/helloearth.jpg”)] public var EarthTexture:Class;

//Variable Declaration private var physics:Papervision3DPhysics; private var sphereObject:Sphere; private var helloSphere:RigidBody; private var moveForce:Number = 12;

352 Chapter 9: Incorporating 3D Physics private var springCamera:SpringCamera3D; private var cameraTarget:DisplayObject3D; private var sceneLight:PointLight3D;

//Key Listeners private var keyRight:Boolean = false; private var keyLeft:Boolean = false; private var keyForward:Boolean = false; private var keyReverse:Boolean = false; private var keyUp:Boolean = false;

//Initiate Listeners public function HelloWorldJiglib() {

// Initialize the Papervision3D BasicView super(stage.stageWidth, stage.stageHeight, true, false, CameraType.TARGET); //Keyboard Listener stage.addEventListener( KeyboardEvent.KEY_DOWN, myKeyDown ); stage.addEventListener( KeyboardEvent.KEY_UP, myKeyUp ); //Set Light sceneLight = new PointLight3D(true, true); sceneLight.x = 10; sceneLight.y = 300; sceneLight.z = -400; // Initialize the Papervision3D physics plugin physics = new Papervision3DPhysics(scene, 7); //Initiate Listeners createFloor(); setSpringCamera(); createHelloSphere(); startRendering(); }

//Use Spring Camera private function setSpringCamera():void { //Instantiate Spring Camera and set constants springCamera = new SpringCamera3D(); springCamera.mass = 8; springCamera.damping = 8; springCamera.stiffness = 1; springCamera.focus = 80; springCamera.zoom = 8; //Set Offsets springCamera.lookOffset = new Number3D(0, 30, 20); springCamera.positionOffset = new Number3D(0, 100, -1200); } //Create Hello World Sphere private function createHelloSphere():void { //Define Material for Hello World Sphere (continued)

353 Part III: Building Games and Websites

(continued) var helloWorld:BitmapMaterial = new BitmapMaterial(Bitmap(new EarthTexture()). bitmapData, true); helloWorld.smooth = true; sphereObject = new Sphere(helloWorld, 120, 12, 12); scene.addChild(sphereObject); //Instantiate Jiglib sphere and attach PV3D Sphere helloSphere = new JSphere(new Pv3dMesh(sphereObject), 100); helloSphere.y = 180; helloSphere.rotationY+=60; helloSphere.restitution = 3; helloSphere.mass = 1 physics.addBody(helloSphere);

//Define DisplayObject3D cameraTarget cameraTarget = new DisplayObject3D(); cameraTarget.copyPosition(sphereObject); scene.addChild(cameraTarget); //Spring camera follows target. springCamera.target = cameraTarget; }

//Create your floor private function createFloor():void { var myFloor:Plane = new Plane(new WireframeMaterial(0xFFFFFF), 10000, 10000, 10000*0.001, 10000*0.001); myFloor.rotationX = 90; myFloor.y = -150; scene.addChild(myFloor); //Generate ground from PV3D Plugin physics.createGround(new WireframeMaterial(0xFFFFFF, 0), 1800, 0); } //Key Handler Methods //myKeyUp Method private function myKeyUp(event:KeyboardEvent):void { switch(event.keyCode) { case Keyboard.UP: keyForward = false; break;

case Keyboard.DOWN: keyReverse = false; break;

case Keyboard.LEFT: keyLeft = false; break;

case Keyboard.RIGHT: keyRight = false; break;

354 Chapter 9: Incorporating 3D Physics

case Keyboard.SPACE: keyUp=false; } }

//myKeyDown Method private function myKeyDown(event:KeyboardEvent):void { switch(event.keyCode) { case Keyboard.UP: keyForward = true; keyReverse = false; break;

case Keyboard.DOWN: keyReverse = true; keyForward = false; break;

case Keyboard.LEFT: keyLeft = true; keyRight = false; break;

case Keyboard.RIGHT: keyRight = true; keyLeft = false; break; case Keyboard.SPACE: keyUp = true; break; } }

//BasicView Animation Loop override protected function onRenderTick(event:Event = null):void { if(keyLeft) { helloSphere.addWorldForce(new JNumber3D(-moveForce, 0, 0), helloSphere.currentState.position); } if(keyRight) { helloSphere.addWorldForce(new JNumber3D(moveForce, 0, 0), helloSphere.currentState.position); } if(keyForward) { helloSphere.addWorldForce(new JNumber3D(0, 0, moveForce), helloSphere.currentState.position); (continued)

355 Part III: Building Games and Websites

(continued) } if(keyReverse) { helloSphere.addWorldForce(new JNumber3D(0, 0, -moveForce), helloSphere.currentState.position); } if(keyUp) { helloSphere.addWorldForce(new JNumber3D(0, 2*moveForce, 0), helloSphere.currentState.position); }

//Execute rendering process physics.step(); cameraTarget.copyPosition(sphereObject); renderer.renderScene(scene, springCamera, viewport); } }}

Jiglib comes with a number of examples that are designed to help you get started. In the next section, you learn how to build an example viewer that allows you to view all the Jiglib examples and their code.

Building an Example Viewer Building large - scale applications in Flex can easily be accomplished using modules and when dealing with PV3D, it ’ s the optimal way to present varying 3D content. To illustrate the modules ’ use, you ’ re going to build the example viewer that ’ s shown later in Figure 9.15. The viewer shows the various Jiglibflash examples that come with the Jiglibflash download source files examples. Using Modules Modules can be loaded dynamically at runtime. They ’ re swf files generated in Flex ’ s module wizard. The module architecture lets you separate your views into multiple swf files. This enables you to keep your main application small for fast viewing on the web, and adds needed functionality, which requires longer download time, based on encapsulated modules.

Modules are similar to components, derived from the VBox container, but have an additional layout property that lets you lay out your module similarly to a Flex application: horizontal, vertical, or absolute.

To create a module, right click in the Flex navigator window on the JiglibLoaderApp modules folder (ctrl - click for Mac), select New, then MXML Module, as shown in Figure 9.13.

356 Chapter 9: Incorporating 3D Physics

Figure 9-13

When you ’ ve created the module, you want to pass the application code for the different Jiglib examples into the module.

< ?xml version=”1.0” encoding=”utf-8”? > < mx:Module xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”100%” height=”100%” > < mx:Script > < ![CDATA[

//Put the Application Here!!!

]] > < /mx:Script > < /mx:Module >

Figure 9.14 shows how the Flex folder structure looks. The modules are represented by squares in the upper - right corner of the module icons.

Figure 9-14

357 Part III: Building Games and Websites

The final application that ’ s shown in Figure 9.15 lets you select the different Jiglib examples from an XML - driven data grid. So if you come up with an additional example, just create another module, add it to your module folder and to your XML list. Finally, there ’ s a check box at the upper left corner of the application that allows you to hide the code from view when unchecked.

Figure 9-15

Surprisingly, the entire application is only 76 lines of code – the modules do most of the work. Modules are great for building robust applications and integrating different types of media. The documented code is given here and the complete application with modules can be obtained from the book’ s Chapter 9 code:

< ?xml version=”1.0” encoding=”utf-8”? > < mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” creationComplete=”loadExample()” layout=”absolute” color=”#FEFFFF” borderColor=”#FDFEFE” backgroundGradientAlphas=”[1.0, 1.0]” backgroundGradientColors=” [#FFFFFF, #000000]”>

< mx:Script > < ![CDATA[ //Import Modules import modules.*; //Flash Imports import mx.collections.ArrayCollection; import flash.net.navigateToURL; import flash.net.URLRequest; import flash.net.URLVariables; import flash.net.URLRequest; //Array collection for Datagrid [ Bindable ] private var acMarkers:ArrayCollection = new ArrayCollection();

//Initiation Function, Loads and Initiates Example and XML private function loadExample(): void { //Load initial example loader.url= ”MYRagDollNow.swf” ; loader.loadModule(); //Load XML for Datagrid getXml(); }

358 Chapter 9: Incorporating 3D Physics

//Load XML using URLLoader private function getXml(): void { var xmlString:URLRequest = new URLRequest( “data/markers.xml”); var xmlLoader:URLLoader = new URLLoader(xmlString); xmlLoader.addEventListener( “complete” , readXml); }

//Parse XML using e4x tag names private function readXml(event:Event): void {

var markersXML:XML = new XML(event.target.data); var markers:XMLList = markersXML..marker; var markersCount:int = markers.length();

for ( var i:uint=0; i < markersCount; i++) { var markerXml:XML = markers[i]; //Load XML into the Collections Array for datagrid dataProvider acMarkers.addItem({label: markerXml.@name, address: markerXml.@address, directions: markerXml.@directions}); } }

//Click Handler for the datagrid private function showInfo(event:Event): void { //Load the individual examples loader.url=myDataGrid.selectedItem.address; loader.loadModule(); }

//Load Handler private function readHandler(event:Event): void { trace ( “module loaded” ); }

]] > < /mx:Script >

< !-- Module loader and load handler -- > < mx:ModuleLoader id=”loader” width=”100%” height=”100%” ready=”readHandler(event)” x=”0” y=”0”/ > < !-- Data Grid using acMarkers as your dataProvider --> < mx:DataGrid id=”myDataGrid” dataProvider=”{this .acMarkers}” itemClick=”showInfo (event)” height=”284.65” y=”61” x=”10” width=”162.55” alternatingItemColors=”[#F7F7 F7, #EFE4E4]” sortableColumns=”false” color=”#000101”> < mx:columns > < mx:DataGridColumn headerText=”JigLib Examples” dataField=”label” /> < /mx:columns > < /mx:DataGrid > < mx:Label x=”18” y=”23” text=”Jiglib Examples” fontSize=”14” fontWeight=”bold” color=”#000000”/ > < mx:Label x=”19” y=”21” text=”Jiglib Examples” fontSize=”14” fontWeight=”bold”/> < /mx:Application >

There ’ s one more trick you need to learn before you move on from this section, and that ’ s adding a preloader. 359 Part III: Building Games and Websites

Adding a Simple Preloader One of the problems with modules is that when you select one, the screen is blank until it loads and this makes for a poor user experience. An easy work around is to use the ready handler method of the module loader:

< mx:ModuleLoader id=”loader” width=”100%” height=”100%” ready=”readHandler(event)” x=”0” y=”0”/ >

The ready method listens for when a module has completed loading, and when it does it executes a method (in this case readHandler ). So here ’ s the trick. Create a simple animated swf loader in Flash. Use the swf component in Flex to place this simple animation on the screen. Give it the instance name myLoader . When you click on the XML - driven data grid to select a module you run the showInfo() method, which starts loading your module and makes your swf loading animation visible:

//Click Handler for the datagrid private function showInfo(event:Event): void { //Load the individual examples opaqueBackground=0; loader.url=myDataGrid.selectedItem.address; loader.loadModule(); myLoader.visible= true ;}

When the module has completed loading the ready readHandler is executed which makes your animated swf invisible:

//Load Handler private function readHandler(event:Event): void { myLoader.visible= false ;}

You obviously can do much more with this, but this shows you the basics of making a simple module loader. Later in this book, you learn how to work more dynamically with Flash in Flex.

To learn more about working with modules check out the book ’ s website. There ’ s an entire video tutorial set on how this application was built.

AS3Dmod Early in the development of PV3D 2.0 Bartek Drozdz of Everyday Flash built a bend modifier, which later blossomed in to AS3Dmod. AS3Dmod gives you the ability to add seven modifiers to PV3D primitives: bend, noise, skew, taper, bloat, perlin, and twist. And it features an abstract layer and simple plug - in architecture that allows it to work with most Flash 3D engines. You can obtain AS3Dmod from Google Code at http://code.google.com/p/as3dmod/. When you ’ re there just click on the source tab and download the code from SVN just as you did with PV3D in Chapter 2.

360 Chapter 9: Incorporating 3D Physics

As Bartek already has a full tutorial on how to use his modifiers you won ’ t cover any basic example here but you take a look at how to port a more advanced example. To get started, first check out his tutorial at http://code.google.com/p/as3dmod/wiki/AS3Dmod_Tutorial .

Understanding how the modifier stack works is the most important element of AS3Dmod.

The Heart of AS3Dmod At the heart of AS3Dmod is the modifier stack. It ’ s the first thing you import and instantiate. According to Barteck ’ s tutorial:

The modifier stack is a link between the object that we want to modify and the modifiers themselves. The modifier stack is a central class of the whole AS3Dmod library. It ’ s important to note that modifiers can ’ t be applied directly to objects; everything is handled by the stack. For example, consider creating a modifier stack for a plane where mstack is the Modifier Stack instance name:

mstack = new ModifierStack(new LibraryPv3d(), plane);

This important line creates the stack by providing it with two arguments. The first one indicates the 3D library you ’ re using. The second argument is the 3D object that the modifiers will be applied to. In the case above you pass the plane created before. AS3Dmod works with any 3D object, including all the primitives as well as with different imported types like a non - nested Collada DAE file.

When you ’ ve created the modifier stack, you can add effects to your primitive. Noise is a good example. After importing the Noise AS3Dmod class, make an instance of it, and add it to your plane in the following way:

var noise:Noise = new Noise(20); mstack.addModifier(noise); mstack.apply();

In the first line you create a modifier: noise in this case. The first argument indicates the force of the effect: 0 means no deformation at all, while a value somewhere around a 200 (in this case) creates a very strong deformation.

When you ’ ve created the modifier you need add it to the stack. That ’ s the only way it can be applied to the 3D object. The last line in the code above is of key importance. It tells the stack to apply ALL the modifiers to the 3D object:

mstack.apply()

That ’ s the heart of AS3Dmod, and it ’ s beautifully simple. To learn more about the basics check out Bartek ’ s tutorial at the link given above. Now, you build National Treasure .

Making National Treasure: Porting You ’ ll recall in the movie, National Treasure, stealing the Declaration of Independence was at the core of the film ’ s action. Creating a realistic looking Declaration of Independence requires that you apply a bend modifier to the plane that holds its image, as shown in Figure 9.16. But before you start coding it ’ s

361 Part III: Building Games and Websites

always a good idea to see if anyone else has done a similar program, and if he or she is willing to help you out. Who knows, someone may have already released the code on the web, saving you hours of work – it pays to do your homework before you code.

Figure 9-16

Luckily, Bartek has already created the solution in Away3D, so porting it to PV3D shouldn ’ t be too difficult, right? Porting means to take code from one code set, such as Away3D, and merge it with a different code set, such as PV3D. You ’ re moving it to a different software environment.

This may sound much easier than it really is; both Away3D and PV3D are very similar, but there ’ s enough difference between the two that you might find yourself doing some development work to get a successful port. For example, Bartek ’ s Away 3D version uses a double - sided plane, but PV3D doesn ’ t have a double - sided plane. So to do a successful port you would have to develop a double - sided plane. Luckily, we developed one in Chapter 3 of this book.

Another issue is that Away3D ’ s lookAt method uses a 3DNumber where PV3D ’ s lookAt method uses a DisplayObject . So you have to place a dummy display object at the Away3D number to get PV3D to look at it. How do you find all this out? Well the best place to start is Adobe Flex. Flex has superior error checking and let ’ s you roll over code names, with a ctrl (PC) or command (Mac), and navigate to those elements to check errors, code, and connections. As errors pop up in your port you can quickly navigate to them and fix them. Using Flex you find yourself porting code more rapidly.

There ’ s one more important thing about porting. There ’ s a trade off of time versus perfection. If an element doesn ’ t exist from one program to the next you ’ re going to have to create it and at this point it really depends on how much time that ’ s going to take you. You might find it easier to get a little creative and make an enhancement instead. Think about whether or not the issue can be used to your advantage, and how your particular software engine measures up compared to the one from which you ’ re porting .

The PV3D Declaration of Independence port is in the book ’ s chapter code and has two solutions: the solution for a one - sided plane with perlin wind effects, and a double - sided bending plane. If you didn ’ t get all of this don ’ t worry, it ’ s handled on the book ’ s website.

362 Chapter 9: Incorporating 3D Physics Summary In this chapter, you examined a number of approaches to bring physics into PV3D. You started by creating a spring camera, and beefing up the DisplayObject3D class to add gravity effects to create orbiting planets. You created custom classes for oscillation and learned how to make particles interactive using the interactive scene manager (or ISM). You discovered how to build large - scale applications using states, modules, and porting. You examined both WOW and Jiglibflash physics engines. Finally, you built a Jiglib Hello World example and a Jiglib example viewer.

363

Building 3D Games for Wii

It ’ s no coincidence that the chapter on physics precedes the chapter on gaming. Characters and objects obeying the laws of physics is a must for creating realistic games. Having a good physics engine can save you tons of coding. In this chapter you expand your knowledge of Jiglibflash even further. But no physics engine is perfect, so in order to build the games presented in this chapter you need to hack the PV3D and the Jiglibflash engines.

You ’ re going to build a 3D pool “ shooter ” and Pong: In the pool game the table is a 3D bounding box and your stick is a ray gun. You navigate around the 3D bounding box using your arrow keys while shooting the balls into the corner pockets with your ray gun. If you shoot a ball three times without getting it into a pocket it explodes.

Pong is a great example of a multilevel Wii game that uses artificial intelligence to compete with a single player. With each new level the computer competitor gets a little smarter.

Most Flash player games are built in Flash, but in this chapter you build the pool game in Flex and Pong as an ActionScript package. You discover just how powerful Flex is in building games and how to bring Flash content into Flex and control movie clip animation.

Finally, you go below the surface of many of theses classes and hack them to make needed improvements to build your game. Chances are that the developers of PV3D and Jiglib didn ’ t have your particular game in mind when they created their 3D engines. To custom design your game, you ’ ve got to hack. There are a number of techniques demonstrated in this chapter that aren ’ t found anywhere else.

So put on your hacking shoes, you ’ re going to need them in this chapter. Part III: Building Games and Websites Shooting Pool in Space You ’ ve probably been amazed by images from space, showing astronauts floating in space performing gravity - free tasks. One such task would be playing 3D pool where the balls aren ’ t confined to a table, but bound inside a rectangular box. You don ’ t have to be an astronaut to play this game – you can play it in virtual space using PV3D and Jiglibflash. Like most Flash games, this one starts with a splash screen where you click on a button to start the game and set the focus from the browser to the Flash application as shown in Figure 10.1.

Figure 10 -1

This game sounds easy enough, shooting balls into corner pockets in 3D space, it shouldn ’ t be too hard to create! But even this simple game requires a number of parts:

❑ Five Game States — Start, Play, Win, Lose, Cancel ❑ Skybox Custom Class ❑ Jiglib Bounding Box ❑ Timer Function ❑ Keyboard Control and Navigation around Bounding Box ❑ Mouse Control ❑ Weapon (model, sight, Flash animation, and holder) ❑ Camera Set Up ❑ Audio States Engine ❑ Ball Creator and 3D Rack ❑ Stats for Optimization ❑ Hit Method with Ball Tracking

366 Chapter 10: Building 3D Games for Wii

❑ Scoring System and Score Board ❑ Animation Loop ❑ Imprint on Balls

In the following sections, you first proceed through the individual parts, building up the game, and afterwards learn to optimize its performance for the web.

It should be noted that Andy Zupko created a shooting experiment at http://blog.zupko.info and in the development of the game in this chapter we use a number of his ideas to create the weapon container, bullet hole, tracer, and animated muzzle flash. Andy is part of the PV3D core team and his site features the more complex technical concepts of PV3D.

Five Game States: Start, Play, Win, Lose, Cancel Your game has five possible states: start, play, win, lose, and cancel. Generating these states in Flex can be accomplished in three ways:

❑ Popup Manager ❑ Custom Components ❑ States

Both the Popup Manager and Custom Components instantiate custom components (just as you would instantiate elements from your Flash Library). As custom MXML components are classes, they ’ re treated as classes. For example, if you generate a StartWindow component that extends the Panel container, you can bring it to your stage using the following code snippet:

import panel.StartWindow; //StartWindow is in the panel folder private startPanel:StartWindow; //Data type startPanel as StartWindow startPanel=new StartWindow(); //Instantiate startPanel scene.addChild.startPanel; //add to your PV3D scene or pv3dCanvas.rawChildren.addChild(startPanel); //add to MXML canvas

In the last line of code you have the option of adding your panel to your PV3D scene or to your pv3dCanvas using rawChildren . Using this technique you can add as many panels as you want, and using the PopManager you can manage their display with the addition of modal control. You use the same approach with Custom Components; the only difference is that you remove the PopUpManager and you lose modal control.

What ’s Modal Control? Modal control blocks user interaction with the rest of your application and indicates this by blurring the application background. But if you ’ re used to JavaScript modal there ’ s an important distinction; the Flash player runs on a single application thread and doesn ’ t pause! Code blocks – once started – always

367 Part III: Building Games and Websites

execute to their end. So even though modal control blocks the user ’ s interaction, it doesn ’ t stop ActionScript from executing in the background. To do that, you need to write a little extra code to control what ’ s happening behind your modal blur.

Once the popup has been closed, the modal blur goes away and you can again interact with the main application. But there ’ s another problem that arises with custom components. They don ’ t naturally transfer data to your main MXML application. You need to use metadata and a custom event that holds a generically typed Object variable to pass data from your custom component to your main MXML application. Another option is to use a central event dispatcher, a centralized class that extends the Flex EventDispatcher class, then have the various elements of the application listen for events on the central dispatcher.

For a small application such as this, use a stateful design pattern. Programming states is very easy. All the code can be placed in just one single MXML file and all the components talk to each other without the use of metadata or events.

Adding a Background Image One easy trick to creating a splash screen background is to add an image to your mx application tag using the backgroundImage property. The added background image then forms the background for all your states.

< mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” backgroundImage=”assets/image/side08.jpg” horizontalScrollPolicy=”off” verticalScrollPolicy=”off” >

In addition, you want to set the layout to vertical so your stage is centered, and set the verticalScrollPolicy and horizontalScrollPolicy values to off . Flex automatically spawns scroll bars; you should turn them off when not needed to avoid extra drain on your CPU resources.

Using View States View States are created in Design View and give you the ability to:

❑ Make Incremental Changes ❑ Add and Remove Components ❑ Change Properties and Styles ❑ Change Event Handling

Most flashmaticians use View States to make minor changes to applications, such as tweened drop down boxes. But View States can be used to create robust application navigation. They ’ re ideal for game programming since games have different states and each game state can be placed into a different View State. To create the states for your game, first create your Flex MXML application and open it up in design view. Then open up the View State panel, and add a View State for each one of your game states as shown in Figure 10.2.

368 Chapter 10: Building 3D Games for Wii

Figure 10 -2

The base state is generated automatically and many Flash applications use this as their starting state – don ’ t do that! Use the base state to carry common graphical elements that occur in most of your states and then tailor each state as needed. This lets you get under all your states and add and subtract global assets as needed. Creating a starting state is easy. Just create a new state, introState in this case, right click on it and choose start state .

In Chapter 16, you rebuild this game in Flash Catalyst using the new Flex 4 ViewStates architecture. Besides that the rest of the coding and approach shown below is solid.

As you create your view states, the code for your states generates automatically on the source side of your MXML application:

< ?xml version=”1.0” encoding=”utf-8”? > < mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” currentState=”introState” > < mx:states > < /mx:states >

< mx:Canvas x=”0” y=”0” width=”550” height=”505”> < mx:Label x=”10” y=”10” text=”Space Ray Pool Game” id=”label1”/> < /mx:Canvas >

< /mx:Application >

An important tip is to place a canvas component on your stage, then size it to 900x700, give it the id “ stateCanvas ” , and then vertically center it in the main application tag: doing this ensures that your states are centered on the Flex stage. Trying to center states after you ’ ve created them is difficult – center first!

Another important tip is to make sure that you ’ re editing the right state. It ’ s a frustrating mistake to make changes to the wrong state. In design view make sure that you ’ ve clicked on the state you ’ re editing before you make changes. This sounds really simple, but it ’ s a common mistake.

If you make this mistake, don ’ t try to fix it in code view. Go to design view and use control - z or manually add or delete graphical elements. States can be a little tricky, but they ’ re easy to create and work with and just require a little experience in figuring out where the pitfalls are.

369 Part III: Building Games and Websites

Filling Your States Now that you ’ ve created your states, all you need to do is to start dragging the appropriate graphical elements into those states. Here ’ s what each state requires:

❑ base state — add pv3dCanvas, add score board ❑ introState — delete pv3dCanvas, add a panel container ❑ playState — everything comes from the base state ❑ winState — delete pv3dCanvas, add a panel container, from the score board drag the score and timer textboxes into your panel container and delete the rest of the score board ❑ loseState — delete pv3dCanvas, add a panel container, from the score board drag the score and timer textboxes into your panel container and delete the rest of the score board ❑ cancelState — delete pv3dCanvas, delete score board, add textbox

Once your graphical elements are set on the screen it ’ s time to go to code view and start programming them. Programming Panel Containers Programming the panel containers is just like programming any application in Flex, with the exception that the panel is contained in a state. So you need to first open up code view and navigate to the state where the panel is. In this section, you create the intro and win state panels shown in Figure 10.3. In Chapter 16, when you rebuild this game using Flash Catalyst you ’ ll create these same panel states from Photoshop graphics and the Flash Catalyst HUD (Heads Up Display).

Figure 10 -3

In this chapter, you use the standard Flex panel containers, but in Chapter 15 you learn how to skin containers and make them look more dynamic.

Intro State Panel After navigating to the introState panel make the following changes:

1. Add a background image, title, and creationComplete method ( initP1 ) to your mx panel tag and remove the vertical and horizontal scroll policy.

370 Chapter 10: Building 3D Games for Wii

2. Center your panel using the following code snippet of the initP1 method:

startPanel.x=stateCanvas.width/2-startPanel.width/2; startPanel.y=stateCanvas.height/2-startPanel.height;

3. Drag a single button onto your panel container, give it the label Start Game, and add the following click handler. When the code is executed it sends you to the playState using the currentState method, initiates PV3D, and changes the music state using the myGameState method.

//Move to Play State, Initiate PV3D, and Start Music private function cancelClickHandler(event:MouseEvent): void { //Go to playState currentState= ”playState” ; initPV3D(); myGameState(1); }

4. Finally, add a text and text area box to the stage and place the game play instructions in those boxes. The complete code for the startGame panel is given here:

< mx:State name=”introState” > < mx:AddChild relativeTo=”{stateCanvas}” position=”lastChild”> < mx:Panel creationComplete=”initP1()” x=”314” y=”159” title=”Shooting Pool! by Mike Lively” width=”400” height=”300” backgroundImage=”assets/image/imageGame .jpg” verticalScrollPolicy=”off” horizontalScrollPolicy=”off” color=”#FEFFFF” layout=”absolute” id=”startPanel” >

< mx:Script > < ![CDATA[

private function initP1(): void { //Center Your Panel startPanel.x=stateCanvas.width/2-startPanel.width/2; startPanel.y=stateCanvas.height/2-startPanel.height; }

//Move to Play State, Initiate PV3D, and Start Music private function cancelClickHandler(event:MouseEvent): void { currentState= ”playState” ; initPV3D(); myGameState(1);

} ]] > < /mx:Script >

< mx:Canvas width=”380” height=”262” > < mx:Button x=”133” y=”189” label=”Start Game” width=”114” height=”46” click=”cancelClickHandler(event)”/ > (continued)

371 Part III: Building Games and Websites

(continued) < mx:Label text=”Arrow Keys Control Gun Orientation Around Box” width=”269” color=”#FFFFFF” x=”10” y=”13”/ > < mx:TextArea x=”10” y=”29” width=”113” height=”168” alpha=”0.0”> < mx:text> < ![CDATA[Aim and click the mouse to shoot.

Shoot a ball into a pocket and you win a point. Shoot a ball more than 3 times you lose a point. You have 1 minute to play.]] > < /mx:text> < /mx:TextArea >

< /mx:Canvas > < /mx:Panel > < /mx:AddChild > < mx:RemoveChild target=”{scoreboard}”/ > < mx:RemoveChild target=”{pv3dCanvas}”/ > < /mx:State >

Next, you create the win panel, which is similar to the lose container, the only difference being that one says you win and the other says you lose.

Win and Lose Panels Navigate to the win panel and make the following changes:

1. As you did with the start game panel add a background image, centering method (initP2 ), and title. 2. Drag two buttons to the stage and give one the label Play Again and the other Quit . 3. Create a click handler for your Play Again button named cancelClickHandler2 which runs the restartParams() method. This method contains all the parameters required to restart your game, which includes a rack method that re - racks your balls in 3D. 4. Finally, create a quit function method which sends you to the cancel state. This is the simplest state and requires that you set currentState = cancelState .

That ’ s all there is to it. The entire code snippet for the win panel is provided here:

< mx:State name=”winState” > < mx:RemoveChild target=”{pv3dCanvas}”/ > < mx:AddChild relativeTo=”{stateCanvas}” position=”lastChild”> < mx:Panel creationComplete=”initP2()” x=”314” y=”159” title=”You Win State!” width=”400” height=”300” backgroundImage=”assets/image/imageGame.jpg” verticalScrol lPolicy=”off” horizontalScrollPolicy=”off” color=”#FEFFFF” layout=”absolute” id=”startPanel2” >

< mx:Script > < ![CDATA[ //Center Panel private function initP2(): void { startPanel2.x=stateCanvas.width/2-startPanel.width/2; startPanel2.y=stateCanvas.height/2-startPanel.height;

372 Chapter 10: Building 3D Games for Wii

} //Restart function private function cancelClickHandler2(event:MouseEvent): void { restartParams();

} //Quit function private function quitFunction(): void { currentState = “ cancelState” ; }

]] >

< /mx:Script >

< mx:Canvas width=”380” height=”262” id=”canvas2”> < mx:Button x=”47” y=”189” label=”Play Again!” width=”114” height=”46” click=”cancelClickHandler2(event)”/ > < mx:Label x=”31” y=”77” text=”You” fontSize=”30”/> < mx:Label x=”27” y=”123” text=”Win!” fontSize=”30”/> < mx:Button x=”202” y=”189” label=”Quit” height=”46” width=”114” click=”quitFunction()”/ > < /mx:Canvas > < /mx:Panel > < /mx:AddChild > < mx:RemoveChild target=”{gameTimeField}”/> < mx:AddChild relativeTo=”{canvas2}” position=”lastChild” target=”{gameTimeField}”/> < mx:SetProperty target=”{gameTimeField}” name=”x” value=”10”/> < mx:SetProperty target=”{gameTimeField}” name=”y” value=”10”/> < mx:RemoveChild target=”{myText}”/ > < mx:AddChild relativeTo=”{canvas2}” position=”lastChild” target=”{myText}”/> < mx:SetProperty target=”{myText}” name=”y” value=”10”/> < mx:SetProperty target=”{myText}” name=”x” value=”164”/> < mx:RemoveChild target=”{scoreboard}”/ > < /mx:State >

Now that you ’ ve got your states and panels completed, it ’ s time to build a custom skybox.

Building a Custom Skybox Skyboxes are really useful for creating cool - looking enclosures for your games. But unlike Away3D, PV3D doesn ’ t have a native skybox in its primitive set. A skybox is easy to make: it ’ s just a large double - sided cube that you sit inside of. So instead of creating a new skybox each time you need one, it ’ s better to create a skybox class, which extends the standard cube primitive. So whenever you need a skybox you just instantiate it using the following code:

//Skybox import Statement import org.lively3d.jiglib.Skybox; //Data type your skybox private var mySkyBox :Skybox; //Instantiate your SkyBox mySkyBox=new Skybox(); scene.addChild(mySkyBox);

373 Part III: Building Games and Websites

You ’ ve already seen a number of custom classes and have dealt with building a skybox before. We therefore only give the document code for the skybox below:

package org.lively3d.jiglib { //PV3D import statements import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.primitives.Cube;

//Public class extends the cube public class Skybox extends Cube { //Embed your image assets [Embed (source=”/./assets/skybox/top01.jpg”)] private var BMTop: Class; [Embed (source=”/./assets/skybox/side10.jpg”)] private var BMBottom: Class; [Embed (source=”/./assets/skybox/side08.jpg”)] private var BMFront: Class; [Embed (source=”/./assets/skybox/bottom02.jpg”)] private var BMBack: Class; [Embed (source=”/./assets/skybox/side09.jpg”)] private var BMRight: Class; [Embed (source=”/./assets/skybox/top02.jpg”)] private var BMLeft: Class;

//Skybox Constructor public function Skybox() {var bmTop: BitmapMaterial = new BitmapMaterial(new BMTop().bitmapData); var bmBottom: BitmapMaterial = new BitmapMaterial(new BMBottom().bitmapData); var bmFront: BitmapMaterial = new BitmapMaterial(new BMFront().bitmapData); var bmBack: BitmapMaterial = new BitmapMaterial(new BMBack().bitmapData); var bmRight: BitmapMaterial = new BitmapMaterial(new BMRight().bitmapData); var bmLeft: BitmapMaterial = new BitmapMaterial(new BMLeft().bitmapData);

//Set your material to double side so you’ll see the inside of your skybox

bmTop.doubleSided = true; bmBottom.doubleSided = true; bmFront.doubleSided = true; bmBack.doubleSided = true; bmRight.doubleSided = true; bmLeft.doubleSided = true;

//Create your material list

374 Chapter 10: Building 3D Games for Wii

var materials: MaterialsList = new MaterialsList();

//Add Your Materials to your Materials list materials.addMaterial(bmTop, “top”); materials.addMaterial(bmBottom, “bottom”); materials.addMaterial(bmFront, “front”); materials.addMaterial(bmBack, “back”); materials.addMaterial(bmRight, “right”); materials.addMaterial(bmLeft, “left”);

// Add your materials, large sides, and low segments to your super class super(materials,10000,10000,10000,8,8,8); }}}

Using this code you can quickly instantiate a skybox for any game. You ’ re now going to build a Jiglib bounding box for your 3D balls. Note: Seb Lee-Delisle of Plugin Media has a similar skybox class.

Building a Jiglib Bounding Box In order to “ shoot ” pool in space, you ’ ve got to have a rectangular bounding box (shown in Figure 10.4) that keeps your balls from bouncing away. Creating such a bounding box in Jiglib requires that you hack the PV3D Jiglib plugin.

Figure 10-4

Start by opening up the Papervision3DPhysics class found in the jiglib/plugin/papervision3d folder. Next, make a copy of the createGround method and rename it createPlane . Then add the ability to rotate and reposition the plane in x, y, and z as shown in the code here:

public function createPlane(material:MaterialObject3D, width:Number, height:Number, position:Number3D, rotation:Number3D):RigidBody { var ground:Plane = new Plane(material, width, height); scene.addChild(ground); var jGround:JPlane = new JPlane(new Pv3dMesh(ground)); jGround.movable = false; (continued)

375 Part III: Building Games and Websites

(continued) jGround.setOrientation(JMatrix3D.rotationX(rotation.x*convPI+Math.PI/2)); jGround.rotationX = rotation.x; jGround.rotationY = rotation.y; jGround.rotationZ = rotation.z; jGround.x = position.x; jGround.y = position.y; jGround.z = position.z; jGround.friction = .2; jGround.restitution = .8; jGround.movable = false; addBody(jGround); return jGround; }

Using this modification you can now write a createBoundingBox method, which receives material, dimension, and position as shown in the code here:

public function createBoundingBox(material:MaterialObject3D, myWidth:Number, myDepth:Number, myHeight:Number, myX:Number=0, myY:Number=0, myZ:Number=0):void{

//Top Bottom createPlane(material, myWidth, myDepth, new Number3D(myX,myHeight/2+myY,myZ), new Number3D(-90,0,0)); createPlane(material, myWidth, myDepth, new Number3D(myX,-myHeight/2+myY,myZ), new Number3D(90,0,0)); //Left Right createPlane(material, myHeight, myWidth, new Number3D(myX,myY,-myDepth/2+myZ), new Number3D(0,180,90)); createPlane(material, myHeight, myWidth, new Number3D(myX,myY,myDepth/2+myZ), new Number3D(180,180,270)); //Front Back createPlane(material, myDepth, myHeight, new Number3D(myWidth/2+myX,myY,myZ), new Number3D(180,270,0)); createPlane(material, myDepth, myHeight, new Number3D(-myWidth/2+myX,myY,myZ), new Number3D(180,90,0)); }

Using this method makes it pretty easy to create a bounding box of any size and material in your application just by executing a single line of code:

physics.createBoundingBox(materialBox, myWidth, myDepth, myHeight);

Finally, open up Photoshop and make a transparent glass - like material for your bounding box and darken the corners for pockets. The resulting bounding box (or 3D pool table) is shown in figure 10 - 4.

376 Chapter 10: Building 3D Games for Wii

Creating Your Game Elements You need a large skill set in order to build 3D games in PV3D. On the one side you have to program in AS3 and on the other you have to 3D model. Building game elements is what makes working with PV3D really sing. That means becoming a good 3D artist, an essential skill in PV3D game creation. You have to create five game elements for this game: gun, muzzle flash, tracer, bullet imprint, and balls.

You now walk through the creation of each one step by step along with some of the programming required to make them do their stuff! Building a Ray Gun Modeling a simple ray gun can be done in a variety of modeling packages. This particular gun was modeled in 3DSMax and converted into an MD2 model using Milkshape (as described in Chapter 5 on modeling). Once you ’ ve completed modeling your gun in 3DSMax you must add a texture to it by adding an unwrap UV modifier, selecting all faces, editing, and rendering the UVW template in the Edit UVWs panel. The flattened map is shown in Figure 10.5.

Figure 10-5

Taking this map into Photoshop and applying a texture to it brings your gun to life. Once completed, export your model as a 3D studio file and then convert it to a MD2 model using Milkshape for import into PV3D.

Converting to MD2 wouldn ’ t be necessary if the Collada format was working in 3DSMax, but in a few versions of 3DSMax it doesn ’ t work with PV3D. So in this case MD2 was chosen. Once your model is imported into PV3D it doesn ’ t matter what format you brought it in as vertices are vertices!

In addition to using Milkshape you could have used one of the plugins developed by the author for 3DSMax or Blender (see the book website for more details).

Once your model is imported into PV3D, it ’ s pretty easy to instantiate it. As shown in the code below, the MD2 model along with its skin is added to a display container named weaponContainer . This container lets you position your weapon and rotate it toward your target using the lookAt method. The

377 Part III: Building Games and Websites

weaponContainer is placed into a viewport layer and given the highest layer number (100). This places the weapon container on the top layer. And within this layer you can create child layers to sort all the elements that exist inside the weapon container, as shown here.

var myCarSkin:BitmapFileMaterial = new BitmapFileMaterial(‘assets/model3d/ rayGun.jpg’, true); weapon.load(‘assets/model3d/rayGun4.md2’, myCarSkin, 10, 3); //Create a weapon container weaponContainer = new DisplayObject3D(); //Scale weapon weapon.scale = 0.3; //Add weapon to a container and orient it weaponContainer.addChild(weapon); weapon.rotationX=180; weapon.rotationY=90; weapon.y=-30; weapon.x=50; //Weapon container to scene scene.addChild(weaponContainer);

var weaponVPL:ViewportLayer = new ViewportLayer(null, weaponContainer); weaponVPL.layerIndex = 100; weaponVPL.sortMode = ViewportLayerSortMode.INDEX_SORT; weaponVPL.getChildLayer(weaponContainer, true, true).layerIndex = 8; viewport.containerSprite.addLayer(weaponVPL);

So what ’ s a viewport layer?

Early on in PV3D, one of the issues people had was of conflicting display objects due to sorting issues. For example, if you tried to put a house on a ground plain, the ground plain would lose half its triangles or disappear altogether. The solution to this was to create two viewports and put your ground layer in one and your house in another. But creating a new viewport each time you needed to layer objects became tedious and thus viewport layer was born. Viewport layer is a convenient mechanism that lets you layer sets of objects easily, thus avoiding triangle - sorting problems.

You can also use the quadrant render engine, which was ported over to PV3D from Away 3D. But you take a huge performance hit with it. So for now, stick with the viewport layer approach. To see more examples of how the viewport layer mechanism is used, open up the Jiglibflash modules in the examples viewer created in the previous chapter (or consult the book ’ s website).

Now you add some animation to your muzzle. Animating Your Muzzle Animating swf skins gives you the ability to create a muzzle flash and exploding balls. First open up Flash and create a timeline animation of your muzzle flash as shown in Figure 10.6.

378 Chapter 10: Building 3D Games for Wii

Figure 10-6

Once you ’ ve created your timeline animation, place it on one movie frame in the Flash Library. Using the properties panel (Export for ActionScript), name the class LaserFlash3 and export it. Now generate your swf and place it in your assets/swf folder. You can now embed this swf in Flex using the following code snippet:

[Embed(source=”assets/swf/laserFlash3.swf”, symbol=”laserFlash3”)] public var muzzleFlashAsset:Class;

You can use this swf as an animated skin on a PV3D plane, which you can translate in 3D space.

muzzleFlash = new Plane( new MovieMaterial( new muzzleFlashAsset(), false , true ), 50, 50, 1, 1); weaponContainer.addChild(muzzleFlash); muzzleFlash.x = 40; muzzleFlash.y = 12; muzzleFlash.z = 30;

There ’ s a subtle trick here; you add your muzzle flash to your weapon container and position it in front of your gun. Then you put it on a lower child layer than your gun so it looks like it ’ s in front of your gun.

weaponVPL.getChildLayer(muzzleFlash).layerIndex = 7;

When you rotate the weapon container using the lookAt method, the gun and the muzzle flash rotate together, because they ’ re children of the weapon container. And as your muzzle flash is on a lower child layer than the weapon, it always looks as if it ’ s in front of your weapon. Making a Tracer Creating a bullet tracer is a nice effect that you accomplish by using the LineOut class created by Andy Zupko (and distributed freely on his website). The LineOut method uses PV3D ’ s Line3D class to draw a line from your muzzle flash to your target crosshairs each time a bullet (or ray) is fired. The line is then

379 Part III: Building Games and Websites

faded and removed from the scene when alpha equals zero. In the main program, the tracer (its viewport layer) is given a blur using a blur filter to give it an atmospheric effect.

lineLayer.filters = [new BlurFilter(8, 8, 1)];

It ’ s important to note that Flash filters also obey the optimization rules of mipmapping – that is – that their filter parameters are optimized for exponential powers of 2. Creating Pool Balls with Bullet Imprint Creating a pool ball is simple enough – you just create a PV3D sphere and put a material on it – right? But there ’ s a twist; you want your pool balls to show your bullet ’ s imprint after it ’ s been shot. This means drawing the imprint of the bullet on the skin of your pool balls dynamically. But there ’ s nothing to draw to. So you need to pair your ball skin with a drawing skin. Here ’ s how it is accomplished:

Add your pool ball material to a sprite, and add the sprite to a MovieMaterial with a drawing rectangle. You then make your material interactive and add a hit listener to it.

var sprite:Sprite = new Sprite(); sprite.addChild(myBallArray[i]); var mat:MaterialObject3D = new MovieMaterial(sprite, false, false , false , rect); mat.interactive = true ; var pv3dBox:Sphere = new Sphere(mat, 20, 8, 6); pv3dBox.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, hitCube); scene.addChild(pv3dBox);

Each time your pool ball ’ s material is hit, a hole is drawn on it using the drawBitmap method shown in the following code snippet:

//draw the bullet hole var mat:MovieMaterial = event.renderHitData.material as MovieMaterial; var hole: Bitmap = Sprite(mat.movie).addChild( new bulletAsset()) as Bitmap; hole.blendMode = “ overlay” ; hole.x = event.renderHitData.u-hole.width*0.5; hole.y = event.renderHitData.v-hole.height*0.5; //Use drawBitmap to draw the hole mat.drawBitmap();

The material that you draw to your pool ball is shown in Figure 10.7 and was generated using Flash spray paint.

Figure 10-7

And by using the “ overlay ” blend mode your holes are made to blend into your pool balls naturally.

380 Chapter 10: Building 3D Games for Wii

Finally, you create the pool balls themselves and place them in a jBall array (for control as a particle system). The full code snippet is given here:

// SETUP BOXES jBall=new Array();

//materialBox.addMaterial(mat,”all”); var rect:Rectangle = new Rectangle(0, 0, 128, 128);

//RackSphere var sphereMaterial:WireframeMaterial = new WireframeMaterial(0xFFFFFF); rackSphere = new Sphere(sphereMaterial, mySize, 4, 3); myNum=rackSphere.geometry.vertices.length; //Create your Pool Balls for (var i:int = 0; i < myNum; i++) { var sprite:Sprite = new Sprite(); sprite.addChild(myBallArray[i]); var mat:MaterialObject3D = new MovieMaterial(sprite, false, false, false, rect); mat.interactive = true; var pv3dBox:Sphere = new Sphere(mat, 20, 8, 6); pv3dBox.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, hitCube); scene.addChild(pv3dBox);

//Set Ballphysics jBall[i] = new JSphere(new Pv3dMesh(pv3dBox), 20); jBall[i].moveTo(new JNumber3D(rackSphere.geometry.vertices[i].x, rackSphere.geometry.vertices[i].y, rackSphere.geometry.vertices[i].z)); //Start position RigidBody(jBall[i]).myName=jBall[i]; PhysicsSystem.getInstance().addBody(jBall[i]); PhysicsSystem.getInstance().setGravity(new JNumber3D(0,0,0)); RigidBody(jBall[i]).mass=.2; //link the skin with the box. You can use jiglib to retrieve this, but this way is faster. skins[pv3dBox] = jBall[i]; }

In addition to creating the pool balls, you also give them mass and set their gravity to zero so they can be racked and shot. Racking Pool Balls Racking balls is accomplished by pinning them to a wireframe structure (just as you did in the previous chapter). But you don ’ t want your wireframe to show, so you don ’ t add your rack structure to your scene.

//RackSphere var sphereMaterial:WireframeMaterial = new WireframeMaterial(0xFFFFFF); rackSphere = new Sphere(sphereMaterial, mySize, 4, 3); myNum=rackSphere.geometry.vertices.length;

381 Part III: Building Games and Websites

When your wire frame is in place you then run the rackBalls method below to pin your pool balls to the nodes of your rack using the geometry.vertices method:

private function rackBalls():void{ //Iterate over your racking sphere for (var i:int = 0; i < myNum; i++) { jBall[i].moveTo(new JNumber3D(rackSphere.geometry.vertices[i].x, rackSphere.geometry.vertices[i].y, rackSphere.geometry.vertices[i].z)); // start position jBall[i].hitNumber=0; jBall[i].movable=true; }}

But there ’ s an important point to make here – even though you couldn ’ t see your wireframe rack you still were able to use it. Which means that even though objects are invisible your processor still sees them and processes them.

Your processor actually expends one third of its resources on processing “ invisible ” objects (or vertices), and the rendering process, which makes them visible, takes up the other two thirds. Just because an object is invisible, doesn ’ t mean it ’ s not burning precious CPU cycles. If you don ’ t need it, remove it from your display list!!! This includes removing listeners as well.

Finally, notice that during the racking process you set the hit number of each ball to zero (jBall[i]. hitNumber=0). This variable keeps track of how many times each ball has been hit. You use this for making your pool balls explode after a certain number of hits. Exploding Balls In the same way as you got your muzzle flash to animate, you can make your pool balls explode. Just create an explosion animation as shown in Figure 10.8, apply it to a plane, add that plane to your scene, and run your animation every time you hit a ball more than three times.

Figure 10-8

382 Chapter 10: Building 3D Games for Wii

In the code snippet below rb (your rigid body pool ball) represents the rigid body that has been hit. If it ’ s been hit three times then your explosion plane is displaced to where you hit the ball and the lookAt method points toward your gun container. This ensures that you always see the explosion, as it ’ s on a plane. Your animation and sound are then run, your score decremented, total hit number incremented, and your ball placed out of view and made immovable, as shown in the following code:

if (rb.hitNumber==3){ //Move explosion to ball position and lookAt weapon Container ballFlash.x=rb.x; ballFlash.y=rb.y; ballFlash.z=rb.z; ballFlash.visible=true; ballFlash.lookAt(weaponContainer); ballFlash.visible= true ; ballFlash.lookAt(event.displayObject3D); clearTimeout(flashTimer2); flashTimer2 = setTimeout(killballFlash, 300); //Play Explosion Animation ((ballFlash.material as MovieMaterial).movie as MovieClip).gotoAndPlay(1); //Play explosion sound ( new popSound() as Sound).play(); //Decrement Score myScore — ; //Increment total hit number numHitSank++; //Up date Score board myText.text= ”Score: “ +String(myScore); //Place exploded ball far out of view and make it immovable rb.x=5000; rb.movable= false ; }

In the code snippet above you updated your scoreboard. The scoreboard in this game is very simple. Scoring Balls It ’ s not really a game if you don ’ t keep score. Though some may argue this, it turns out that losing is more important than winning (well at least some of the time). The psychological impact of losing carries two thirds more weight than winning. In game design there ’ s a fine balance here. If your game is too difficult, your user will get frustrated and stop playing, and if it ’ s too easy a similar result occurs. It ’ s got to be challenging, and death (or failure) must be an option.

The scoreboard for this game consists of only three components: game time and game score, and show gun as shown in figure 10.9.

Figure 10-9

383 Part III: Building Games and Websites

Game Time To make the game more exciting, institute a timer and limit game play using the getTimer method. There ’ s another important psychological impact here. It turns out that people learn timed tasks more efficiently and keep them in memory longer. So if you want your technicians to remember how to repair a circuit, play a time game – they ’ ll never forget it.

private function gameTimer():void{ getStartTime=getTimer(); gameTime=0; }

The getTimer method gives you the number of milliseconds since your Flash application began running. By subtracting the getStartTime parameter (milliseconds from when you started playing your game level) from your getTimer (milliseconds from when your Flash app started), you always have your game time.

gameTime=getTimer() -getStartTime;

The code snippet that stops your game after a minute and checks your score is given here:

private function showTime():void{ gameTime=getTimer()-getStartTime; if(gameTime > 60000||numHitSank==10){ //Remove processes tickTimer=false; removeSight(); yesShoot=false; numHitSank=0; if(myScore < =0){ currentState=”loseState”; myGameState(3); }else{ currentState=”winState”; myGameState(2); }} gameTimeField.text = “Time “+ clockTime(gameTime); }

If your score is greater than zero the code above sends you to a win state, if zero or less it sends you to lose state. In either state you have the option to play again or quit. The clockTime method below turns your milliseconds into seconds and minutes.

//Convert milliseconds to seconds and minutes public function clockTime(ms:int):String{ var seconds:int = Math.floor(ms/1000); var minutes:int = Math.floor(seconds/60); seconds-=minutes*60; var timeString:String = minutes+”:”+String(seconds+100).substr(1,2); return timeString; }

You now learn how to score your balls when they make a corner pocket.

384 Chapter 10: Building 3D Games for Wii

Game Score Earlier, you learned how score was reduced after a ball was hit three times and exploded. In this section your score is increased whenever a ball enters a pocket. You do this geometrically by checking the location of the balls, and if they ’ ve entered a pocket or not. If they have, the ball is removed from view, a blooping sound played, and the score board incremented.

//Scoring System //Look at each pocket on every iteration. for (var i:int = 0; i < myNum; i++) { if(jBall[i].x > myWidth/2-60 & & jBall[i].y > myHeight/2-60& & jBall[i].z >myDepth/ 2-60 & & jBall[i].x < 2000){ madeScore(i); } if(jBall[i].x < -myWidth/2+60 & & jBall[i].y > myHeight/2-60& & jBall[i].z >myDepth/ 2-60 & & jBall[i].x < 2000){ madeScore(i); } if(jBall[i].x > myWidth/2-60 & & jBall[i].y < -myHeight/2+60& & jBall[i].z >myDepth/ 2-60 & & jBall[i].x < 2000){ madeScore(i); } if(jBall[i].x > myWidth/2-60 & & jBall[i].y > myHeight/2-60& & jBall[i].z <-myDepth/ 2+60 & & jBall[i].x < 2000){ madeScore(i); } if(jBall[i].x < -myWidth/2+60 & & jBall[i].y < -myHeight/2+60& & jBall[i].z >myDepth/ 2-60 & & jBall[i].x < 2000){ madeScore(i); } if(jBall[i].x < -myWidth/2+60 & & jBall[i].y > myHeight/2-60& & jBall[i].z <-myDepth/ 2+60 & & jBall[i].x < 2000){ madeScore(i); } if(jBall[i].x > myWidth/2-60 & & jBall[i].y < -myHeight/2+60& & jBall[i].z <-myDepth/ 2+60 & & jBall[i].x < 2000){ madeScore(i); } if(jBall[i].x < -myWidth/2+60 & & jBall[i].y < -myHeight/2+60& & jBall[i].z <-myDepth/ 2+60 & & jBall[i].x < 2000){ madeScore(i); }}

As there are eight corner pockets there are eight if statements that check to see if a ball has entered its corner (or area of success). You can do this in other ways, such as using hit - test objects – but using geometrical check is just as valid.

Show Gun and Keyboard Focus The Show Gun check box uses the change method to execute the selectedVisGun method shown below. This method changes the visibility of your gun based upon if your check box is selected or not. That shouldn ’ t cause you a problem, should it? But it does – it turns off your keyboard navigation by changing focus from the keyboard “ textbox ” to the Show Gun checkbox.

385 Part III: Building Games and Websites

private function selectedVisGun():void{

if(myCheck.selected){ weapon.visible=true; myTester.setFocus(); }else{ weapon.visible=false; myTester.setFocus(); }}

So after you ’ ve changed the visibility of your weapon, you need to reset your keyboard focus. You do this by transferring focus back to a tiny 1x1 textbox on your stage (named myTester ), used by your keyboard listener. Otherwise, your keyboard navigation will no longer work.

This hack arises in Flex as your applications become more complex and you begin transferring focus among different text - based components. Game Sight The sight is an important member of your game cast. It ’ s a simple crosshair bitmap that you add to your viewport and give a blend mode of add. The mouse hide method is used to hide the mouse pointer and the sight is made to follow your mouse position using the following code snippet in your animation loop.

sight.x = viewport.mouseX-sight.width*0.5; sight.y = viewport.mouseY-sight.height*0.5;

The snippet above accounts for the size of your sight graphic by subtracting half its size in order to center it to where your mouse is. Below is the code needed to initiate your sight:

private function setSight():void{ sight = new mySight() as Bitmap; viewport.addChild(sight); sight.blendMode = “add”; sight.alpha = 0.8; Mouse.hide(); }

After you ’ ve initially created your sight, this method removes it from the stage:

private function removeSight():void{ sight.visible = false; Mouse.show(); }

If you remove your sight from the stage, this method adds it back:

private function addSight():void{ sight.visible = true; Mouse.hide(); }

Now that you ’ ve got all the major players created you go through the game flow.

386 Chapter 10: Building 3D Games for Wii

Following Game Flow You now walk through the game execution step by step to see how all the cast members discussed above interact to create the game. The first thing that you do is run the code. The first command to be executed in your code is the init() method found in the creation complete method located in your mx application tag:

private function init():void{ initLayout(); myGameState(0); }

The init method initiates the program layout and starts up the music. The initiation of the layout (initLayout ) just centers the game play canvas and scoreboard and sets a few drop shadows. The myGameState method sets which song should be playing using the following switch case:

private function myGameState(gameState:int):void{ switch(gameState) { case 0: //Start State soundControl1=(new startSound() as Sound).play(0,20); break; case 1: //Play State soundControl1.stop(); soundControl2=(new gameSound() as Sound).play(0,100); break; case 2: //Win State soundControl2.stop(); (new wonSound() as Sound).play(); break; case 3: //Lose State soundControl2.stop(); (new lostSound() as Sound).play(); break; case 4: //Restart State break; default: trace(“Not in data”); break; }}

The songs and background music are selected by changing the argument of your switch case above.

So you ’ re probably wondering, how does the game get started? The magic happens in the states. Your initial state is set to introState , which brings a state panel to the stage. The start method is executed when you click on the Start Game button shown in Figure 10 - 1. Clicking on the Start Game button in the start panel executes three methods:

currentState= ”playState” ; initPV3D(); myGameState(1);

The currentState method takes you to the play state of the game, initPV3D () initiates PV3D and creates your 3D scene, and myGameState(1) starts the music for your game play. At this point of the game flow a number of functions are brought into the mix. 387 Part III: Building Games and Websites

initPV3D() The initPV3D() method initiates a PV3D viewport, pv3dCanvas , scene and render engine and then executes the init3D method, which creates a number of your 3D objects.

//Initialize Papervision private function initPV3D():void { // Create the viewport viewport = new Viewport3D(pv3dCanvas.width, pv3dCanvas.height, false, true); pv3dCanvas.rawChildren.addChild(viewport); // Create the scene scene = new Scene3D(); // Create the renderer renderer = new BasicRenderEngine(); //Initiate 3D Game Pieces init3D(); }

The last line of the code snippet above executes the init3D method. init3D() The init3D() method creates your skybox, your bounding box, and your pool balls (adding a hit listener to each one of them) and then racks your balls. It then calls the createGun() method. createGun() The createGun() method creates your gun, muzzle flash, bullet explosion and display target and runs five important methods: setSight(), init2D(), gameTimer(), setUpCamera(), and setUpListeners() . init2D() This method sets your stats: frames - per - second and memory usage. It ’ s an important mechanism for optimizing your application. setUpCamera() This method sets up your spring camera:

private function setUpCamera():void{ //Spring Camera camera = new SpringCamera3D(); //Set Spring camera Values camera.damping = 10; camera.stiffness = 1; camera.mass = 10; camera.lookOffset= new Number3D(-50,-30,80); camera.positionOffset= new Number3D(-50,10,-20); camera.target=weaponContainer; camera.zoom=50;}

The most important line of code above is where you set your camera target to the weapon container. This causes the camera target to become your weapon container (as it orbits around your bounding box). The orbital equations for your gun and camera can be found in the animation loop and are given here: 388 Chapter 10: Building 3D Games for Wii

//Weapon Orbit weaponContainer.x=(gunRadius)*Math.cos(math360Angle*convFac)*Math.cos(math180Angle* convFac)/1.2; weaponContainer.z=gunRadius*Math.sin(math360Angle*convFac)*Math.cos(math180Angle* convFac)/1.2; weaponContainer.y=gunRadius*Math.sin(math180Angle*convFac)/1.2;

//Camera Orbit camera.x=gunRadius*Math.cos(math360Angle*convFac)*Math.cos(math180Angle*convFac); camera.z=gunRadius*Math.sin(math360Angle*convFac)*Math.cos(math180Angle*convFac); camera.y=gunRadius*Math.sin(math180Angle*convFac);

The orbital path of the camera and gun are given by simple sine and cosine functions. The orbital radius of the weapon container is less than that of the camera orbital radius, setting it closer to your bounding box. This puts the gun on the inside of the camera so you can see it and so the camera can follow it. There are other ways to do this, using PV3D built - in methods. And in the Pong game (discussed later in this chapter), we demonstrate another approach. setUpListeners() This method sets up the listeners for your mouse, animation loop, and keyboard. Your keyboard listener is attached to a 1x1 textbox named myTester . As mentioned earlier, this is a hack that lets you transfer focus to other text elements and get it back so you don ’ t lose keyboard navigation control. This may seem odd to you, but it ’ s an easy trick when dealing with multiple text items and keyboard navigation.

private function setUpListeners():void{

//Add Mouse Listener this.addEventListener(MouseEvent.CLICK, handleClick);

//Add Key Listeners myTester.addEventListener( KeyboardEvent.KEY_DOWN, mykeyDownHandler); myTester.addEventListener( KeyboardEvent.KEY_UP, mykeyUpHandler); myTester.setFocus(); //Animation Loop pv3dCanvas.addEventListener(Event.ENTER_FRAME, myLoop);

}

The animation loop is the heart of your program. It listens to your keyboard Booleans and allows for rotation around your bounding box and scores a hole when your ball enters the corner pockets.

So now that you have all your functions initiated you can continue with the program flow by taking a look at game play. Game Play As you move from the start state to the play state your timer starts ticking. You ’ ve only got a minute of play and if you go over that, your timer halts the game and determines if you ’ ve won or lost and sends you to the appropriate panel where you ’ re asked if you want to play again or quit. All this occurs in the showTime method discussed earlier.

389 Part III: Building Games and Websites

To shoot a ball, the first thing you do is aim your gun at a ball and fire by clicking your mouse. If you miss, the gun fires and nothing happens – but if you hit the ball, the hitBall method is executed. This method transfers force to the ball in the direction of your tracer using the Jiglib addWorldForce method.

var hitPoint: Number3D = new Number3D(event.renderHitData.x, event.renderHitData.y, event.renderHitData.z);

var force:Number3D = new Number3D(hitPoint.x-weaponContainer.x, hitPoint.y- weaponContainer.y, hitPoint.z-weaponContainer.z);

var rb:RigidBody = PhysicsSystem.getInstance().bodys[findSkinBody(event.displayObject3D)]; rb. addWorldForce(new JNumber3D(force.x, force.y, force.z), new JNumber3D(hitPoint.x, hitPoint.y, hitPoint.z));

In the code above, the hit point is first determined and then the force based upon its position is transferred to your ball using addWorldForce . If you hit your ball three times, it blows up and is sent off the screen and made immovable, and your score decremented. If you make a corner pocket your score is incremented and the ball sent off the viewable screen and made immovable, as discussed earlier.

At the end of one minute, or when you ’ ve shot all your pool balls up, you ’ re taken to a win or lose state as described above. Once in a win or lose state you can choose to play again, which racks your balls and sends you back to the play state. Or you can cancel, which sends you to the cancel state as shown in Figure 10.10.

Figure 10-10

The entire documented code can be obtained from the book ’ s website under Chapter 10 code . But if you want to put this game on the web you need to optimize it.

Optimizing Your Game for the Web Optimizing PV3D applications is an important topic, especially when it comes to running games on the web. The present game runs at about 15 frames per second, but slows as game play continues. You now examine a number of methods designed to speed up your game for the web.

390 Chapter 10: Building 3D Games for Wii

It ’ s important to realize that optimization is a counting game. You ’ re counting resources, such as vertices, listeners, textures, transparencies, loops, and so on. Reducing any of these reduces load on your CPU and increases the efficiency of your program. Here are a number of things you can look at when optimizing your pool game (or any PV3D website):

❑ Viewport size — as mentioned earlier, rendering your 3D objects to the stage is about two thirds of the CPU work. Whenever you can reduce viewport size, this lessens the amount that needs to be rendered. So bring your viewport down to the point that you don ’ t diminish game play. In the shooting game this has already been done. But when you design your next game keep this in mind. If you need to fill the entire screen do it with some static graphics. ❑ Transparency — transparency is a real CPU killer in PV3D. So in the pool game, get rid of your transparent bounding box material and add a line3D outline instead. It ’ s not as cool but it ’ s faster. You ’ ll have to do a little reprogramming of the PV3D plugin and create a Line3D bounding box class to accomplish this. ❑ Movie Clip Animation — Movie clips are expensive items on your CPU — they carry a lot of overhead. You can use a frame ripper instead, which cycles individual bitmaps, or just use a single graphic. ❑ Mipmap all your Graphics and Filters — you ’ ve already done this for the pool game, but keep this in mind as you create other games in the future. ❑ Use Solid Colors When Possible — whenever possible, exchange your texture colors with a solid color. In the case of the pool game, you can dump all your ball textures for solid colors. However, this may save some resources, but it ’ s guaranteed to make your game look ugly. Use this option sparingly. ❑ Reduce Number of Vertices — reducing vertices can make a big difference. For example, if your balls are 8x8 and you reduce them to 8x6 you ’ ve saved 16 vertices per ball or a total of 160 vertices for 10 balls. It adds up, so whenever possible bring your vertex count down. ❑ Drop the Bullet Imprint — adding more bitmap material slows your processor; dropping the bullet imprint will save some CPU resources. ❑ Drop the Spring Camera — the spring camera requires a few extra iterations and substituting in a Camera3D in this case makes little difference to how your game looks and performs. ❑ Remove tracer filters, line blur, and panel drop shadows — removing drop shadow from your panel won ’ t change much, but removing the line trace blur filter will, as you ’ re generating it every time you shoot. ❑ Remove the tracer line or replace it with a recycling particle or bitmap. The problem with line material in PV3D is that it accumulates in memory and even if you remove it properly, it still waits around for garbage collection. If you really need a tracer, use a recycling particle that doesn ’ t need to be removed from memory. ❑ Drop the Skybox and add a simple background image. Skyboxes are beautiful but can add lug to your game — especially if they ’ re being moved often. ❑ Turn off scroll policies — in Flex, make sure that all scroll policies are turned off. Scroll bars require resources and turning them off saves on CPU resources.

391 Part III: Building Games and Websites

❑ Keep stage quality low (stage.quality= “ low ” ) — setting stage quality to a higher level will cause anti - aliasing to kick in, requiring a x2, or x4 iteration of every frame which is a significant processor hit. In most cases, keeping stage quality low doesn ’ t make a difference in appearance. ❑ Watch out for hidden background objects. Sometimes in game development, objects are left invisible, layered in other objects, or left in the background. You may not see them, but your CPU does. So make sure that you remove all unnecessary objects from your game.

After you ’ ve made all these optimizations, you ’ ll end up with a less appealing but faster game (x2) as shown in Figure 10.11.

Figure 10-11

The game you want to put up on the web is somewhere between your un - optimized and optimized versions. There ’ s a trade - off between beauty and speed, assuming that you ’ ve mipmapped and your stage quality is set to low. The four most significant optimization steps you can make for this game are:

❑ Viewport size reduction ❑ Transparency removal ❑ Vertex reduction ❑ Tracer optimization

It ’ s a good practice to totally optimize your game to the point that it ’ s ugly. It gives you a feel for where your resources are being drained and where you need to put most of your time. You can also present your client with various versions of your game – from beautiful and slow, to ugly and fast.

Ultimately, you need to remember that optimization is an art and it takes some experimentation to discover all the tricks!

392 Chapter 10: Building 3D Games for Wii Using Your Wii Take cover as your kids scramble in front of your computer frantically fighting over the Wii controller to try out your newest and greatest Wii creation. With the Wii, you add a new dimension to your 3D experience. Here are the steps to get you started (along with helpful starter code):

❑ Buy a Wii controller (you need only the controller). You can get a new one off of Amazon for $35 ( £ 27) (buy it new — an old one may be broken) ❑ Check out the great WiiFlash video tutorial by Thibault Imberton at http://wiiflash .bytearray.org/ (or check out the book ’ s website for similar tutorials). ❑ Download the WiiFlash classes at http://wiiflash.bytearray.org/?page_id=50 . ❑ Insert the classes into your Flex or Flash project (you can use classes or the SWC provided). ❑ Try out the WiiFlash examples given in the WiiFlash Download. ❑ If you don ’ t have bluetooth on your machine purchase an external bluetooth USB connector (AZiO BTD603 — 132 USB 2.0 Bluetooth Adaptermine from NewEgg.com for $14.99). Note — you must install the driver that comes with your external bluetooth for it to detect your Wii properly. ❑ Place a Wii server shortcut on your desktop — you ’ ll be clicking on it a bunch. ❑ Download the starter code (from the book ’ s website) and run it. Examine the trace statements as you press the different buttons on your Wii. ❑ Finally, add the PV3D classes to your Flex or Flash project files.

With the Wii starter code and the Papervision classes you ’ re now ready to start programming your own Wii games.

Wii Starter Code The Wii starter code (in the book ’ s download code) is amazingly simple. It ’ s adapted from the Wiimote Demo provided in the WiiFlash examples folder. And it ’ s designed to make it easy for you to put anywhere. Here ’ s how it works:

1. After importing the appropriate classes, you must instantiate the Wiimote and then connect to your Wii server as follows:

MyWiiIs = new Wiimote(); MyWiiIs.connect();

2. Next declare a button listener on your Wii controller (both press and release are detectable)

MyWiiIs.addEventListener( ButtonEvent.A_PRESS, onAPressed ); MyWiiIs.addEventListener( ButtonEvent.A_RELEASE, onAReleased);

3. Finally, create functions for your listeners that are executed when you click on the designated button;

393 Part III: Building Games and Websites

private function onAPressed ( pEvt:ButtonEvent ):void { trace(“pressed A”); }

private function onAReleased ( pEvt:ButtonEvent ):void { trace(“released A”); }

4. The only other thing to watch out for is reacting to pitch, yaw, roll, sensorX, sensorY, sensorZ, and batteryLevel. This can be handled in PV3D ’ s render loop (or WiimoteEvent.UPDATE) using your instantiated name and dot syntax such as:

MyWiiIs.pitch

5. Also, depending on the application you may need a smoothing function, as the Wii numbers are very accurate and a little jumpy. In the example below, you’ re controlling the rotation of a sphere using Wii pitch, yaw, and roll. To get two decimal places of accuracy (and throw away what you don ’ t need), multiply by 100 and then divide by 100 after taking the integer value as shown below:

sphere.rotationX+=-4*(int(MyWiiIs.pitch*100)/100); sphere.rotationY+=4*(int(MyWiiIs.yaw*100)/100); sphere.rotationZ+=4*(int(MyWiiIs.roll*400)/100);

That ’ s all there is to it. As powerful as WiiFlash is, it ’ s amazing that it ’ s so simple to use. The entire Wii starter code is listed here:

package { import flash.display.Sprite; import org.wiiflash.Wiimote; import org.wiiflash.events.ButtonEvent; import org.wiiflash.events.WiimoteEvent; public class MyWiiBase extends Sprite { private var MyWiiIs:Wiimote;

public function MyWiiBase() { //Instantiate your Wii controller MyWiiIs = new Wiimote(); MyWiiIs.connect(); WiiListeners(); } //Add your Wii listeners private function WiiListeners():void{

//Pitch, Yaw, Roll MyWiiIs.addEventListener( WiimoteEvent.UPDATE, onUpdated ); //Buttons MyWiiIs.addEventListener( ButtonEvent.A_PRESS, onAPressed ); MyWiiIs.addEventListener( ButtonEvent.A_RELEASE, onAReleased);

394 Chapter 10: Building 3D Games for Wii

MyWiiIs.addEventListener( ButtonEvent.LEFT_PRESS, onLeftPressed ); MyWiiIs.addEventListener( ButtonEvent.LEFT_RELEASE, onLeftReleased); MyWiiIs.addEventListener( ButtonEvent.RIGHT_PRESS, onRightPressed ); MyWiiIs.addEventListener( ButtonEvent.RIGHT_RELEASE, onRightReleased); MyWiiIs.addEventListener( ButtonEvent.UP_PRESS, onUpPressed ); MyWiiIs.addEventListener( ButtonEvent.UP_RELEASE, onUpReleased); MyWiiIs.addEventListener( ButtonEvent.DOWN_PRESS, onDownPressed ); MyWiiIs.addEventListener( ButtonEvent.DOWN_RELEASE, onDownReleased); MyWiiIs.addEventListener( ButtonEvent.B_PRESS, onBPressed ); MyWiiIs.addEventListener( ButtonEvent.B_RELEASE, onBReleased); MyWiiIs.addEventListener( ButtonEvent.MINUS_PRESS, onMinusPressed); MyWiiIs.addEventListener( ButtonEvent.MINUS_RELEASE, onMinusReleased); MyWiiIs.addEventListener( ButtonEvent.PLUS_PRESS, onPlusPressed); MyWiiIs.addEventListener( ButtonEvent.PLUS_RELEASE, onPlusReleased); MyWiiIs.addEventListener( ButtonEvent.HOME_PRESS, onHomePressed); MyWiiIs.addEventListener( ButtonEvent.HOME_RELEASE, onHomeReleased); MyWiiIs.addEventListener( ButtonEvent.ONE_PRESS, onOnePressed); MyWiiIs.addEventListener( ButtonEvent.ONE_RELEASE, onOneReleased); MyWiiIs.addEventListener( ButtonEvent.TWO_PRESS, onTwoPressed); MyWiiIs.addEventListener( ButtonEvent.TWO_RELEASE, onTwoReleased); } //Add your accelerometer and battery methods private function onUpdated ( pEvt:WiimoteEvent ):void {

//trace(pEvt.target.pitch +” pitch”); //trace(pEvt.target.roll +” roll”); //trace( pEvt.target.yaw +” yaw”); //trace(pEvt.target.sensorX +” x”); //trace(pEvt.target.sensorY +” y”); //trace(pEvt.target.batteryLevel +” battery level”); } //Add your button press methods private function onAPressed ( pEvt:ButtonEvent ):void { trace(“pressed A”); } private function onAReleased ( pEvt:ButtonEvent ):void { trace(“released A”); } private function onMinusPressed ( pEvt:ButtonEvent ):void { trace(“onMinusPressed”); } private function onMinusReleased ( pEvt:ButtonEvent ):void { trace(“onMinusReleased”); } private function onPlusPressed ( pEvt:ButtonEvent ):void { trace(“onPlusPressed”); } (continued)

395 Part III: Building Games and Websites

(continued) private function onPlusReleased ( pEvt:ButtonEvent ):void { trace(“onPlusReleased”); } private function onHomePressed ( pEvt:ButtonEvent ):void { trace(“Home Pressed”); } private function onHomeReleased ( pEvt:ButtonEvent ):void { trace(“Home Released”); } private function onOnePressed ( pEvt:ButtonEvent ):void { trace(“onOnePressed”); } private function onOneReleased ( pEvt:ButtonEvent ):void { trace(“onOneReleased”); } private function onTwoPressed ( pEvt:ButtonEvent ):void { trace(“onTwoPressed”); } private function onTwoReleased ( pEvt:ButtonEvent ):void { trace(“onTwoReleased”); } private function onBPressed ( pEvt:ButtonEvent ):void { trace(“onBPressed”); } private function onBReleased ( pEvt:ButtonEvent ):void { trace(“onBReleased”); } private function onUpPressed ( pEvt:ButtonEvent ):void { trace(“onUpPressed”); } private function onUpReleased ( pEvt:ButtonEvent ):void { trace(“onUpReleased”); } private function onLeftPressed ( pEvt:ButtonEvent ):void { trace(“onLeftPressed”); } private function onLeftReleased ( pEvt:ButtonEvent ):void { trace(“onLeftReleased”); }

396 Chapter 10: Building 3D Games for Wii

private function onRightPressed ( pEvt:ButtonEvent ):void { trace(“onRightPressed”); } private function onRightReleased ( pEvt:ButtonEvent ):void { trace(“onRightReleased”); } private function onDownPressed ( pEvt:ButtonEvent ):void { trace(“onDownPressed”); } private function onDownReleased ( pEvt:ButtonEvent ):void { trace(“onDownReleased”); }}}

Using the Wii Starter code above, all you have to do is tie your key commands in with your Wii controller, eliminate the listeners that you don ’ t need, and place the appropriate Booleans in your animation loop. You now apply this starter code by creating a silly string drawing program.

Drawing with Silly String Drawing silly string is a fun application of the Wii and it gives you the ability to see how the Wii is integrated with PV3D. In essence the program draws three different color strings on the stage (red, green, and blue). A wireframe sphere leads the three - string ensemble, as it jettisons through space. Figure 10.12 demonstrates two different drawings made using the Wii Silly String program.

Figure 10-12

The strings that are drawn can vary in thickness. Many options are available using the different keys on your Wiimote:

❑ A PRESS — increases the size of the leading sphere and string rotational radius. ❑ A RELEASE — returns the leading sphere (and string rotational radius) back to normal. ❑ UP PRESS — zooms in.

397 Part III: Building Games and Websites

❑ UP RELEASE — stops zooming in. ❑ DOWN PRESS — zooms out. ❑ DOWN RELEASE — stops zooming out. ❑ B PRESS — makes individual strings grow thicker. ❑ B RELEASE — lets the string return to original thickness. ❑ MINUS PRESS — slows the string (and sphere) longitudinal velocity down to zero. ❑ MINUS RELEASE — stops decrease of velocity. ❑ PLUS PRESS — increases the string (and sphere) longitudinal velocity. ❑ PLUS RELEASE — stops increase of velocity. ❑ HOME PRESS — blows up the scene. Continually pressing eventually wipes the scene clear. ❑ PITCH, YAW, ROLL — changes the angle of your string as it shoots through space — pitch, yaw, and roll (as you change your Wiimote angle). Note : yaw requires a sensor bar.

It ’ s easy to incorporate the Wii into PV3D. First drop the Wii class files in with your PV3D class files (or throw the SWC folder into your library folder). Then import your Wiimote files, instantiate the Wiimote, and connect it to your server.

import org.wiiflash.Wiimote; private var MyWiiIs:Wiimote; //Instantiate your Wiimote and Connect it MyWiiIs = new Wiimote(); MyWiiIs.connect(); //Listen to when the Wiimote connects and then runs initiateDrawing MyWiiIs.addEventListener(Event.CONNECT, initiateDrawing)

When your event listener signals the Wii connect event, the initiateDrawing method is executed, which adds your Wii listeners (from your starter code) and initiates your PV3D objects.

//Add your WiiListeners and initiate PV3D function initiateDrawing():void { WiiListeners(); initPV3D(); }

The heart of making the Wii work with PV3D is to let the Wii set Boolean variables to true or false as the Wii keys are pressed and released. Then by using a series of if statements in your animation loop that responds to those Boolean variables, you can turn processes on and off based on which Wii key is pressed.

For example, consider how the Wii changes the string thickness of your silly string. When pressing the B key your string grows in thickness and by releasing the B key it shrinks back to its normal size. This is first accomplished by setting listeners for your Wii B key.

398 Chapter 10: Building 3D Games for Wii

MyWiiIs.addEventListener( ButtonEvent.B_PRESS, onBPressed ); MyWiiIs.addEventListener( ButtonEvent.B_RELEASE, onBReleased);

When a B key event fires (or the B key is pressed) the listeners execute a function that sets a Boolean variable (mylinesize ) to true or false .

private function onBPressed ( pEvt:ButtonEvent ):void { mylinesize=true; } private function onBReleased ( pEvt:ButtonEvent ):void { mylinesize=false; }

Within the animation loop an if statement is created that increases line size if the mylinesize Boolean is true , and decreases it down to a fixed value (of 2) if the Boolean value is false (or the B key is released).

if(mylinesize){lineSize++; } else{lineSize — ; if(lineSize < =2){lineSize=2; }

Incorporating the Wiimote into PV3D is a natural process, especially since you ’ ve already got an animation loop in PV3D. Got the Jitters In addition to button control, the Wii outputs accelerometer data, based upon its orientation in space (as mentioned earlier). Using an accelerometer, the Wiimote can sense acceleration along three axes. The output of the accelerometer is related to the forces that the Wiimote experiences (not absolute data). The accelerometer output data comes from sensor X, Y & Z (X being left/right, Y being up/down, Z being front/back). The Wiimote is capable of extracting pitch and roll data from the X, Y, & Z accelerometers, but needs a sensor bar (discussed in the next section) to detect yaw.

To best view the results, open up the Wii examples that come with your Wii downloads and run the Wiimote demo. What you find is that the data being outputted has an annoying 18 decimals of accuracy that you don ’ t need for any Wii game. However, the data itself is very easy to extract from the Wii using the following code:

First, create a Wiimote update listener that runs the method onUpdated . This provides the sensor data.

myWiimote.addEventListener( WiimoteEvent.UPDATE, onUpdated );

The sensor data is easily obtained just by referencing its name through a target event:

pEvt.target.(sensorX, sensorY, pitch, roll, yaw, and batteryLevel);

In addition to the accelerometer data, you also get a battery level sensor that can be used to alert your user when they about to run out of Wii - juice.

399 Part III: Building Games and Websites

function onUpdated ( pEvt:WiimoteEvent ):void { sensor_txt.htmlText = “Wiimote Infos: < br > < br> ”; //Sensor X and Y data sensor_txt.htmlText += “Sensor X: “ + String ( pEvt.target.sensorX ) + “< br> Sensor Y: “ + String ( pEvt.target.sensorY ); //Pitch and Roll sensor_txt.htmlText += “Pitch: “ + String ( pEvt.target.pitch ) + “< br> Roll: “ + String ( pEvt.target.roll ); //Yaw sensor_txt.htmlText += “Yaw: “ + String ( pEvt.target.yaw ); //Battery Life sensor_txt.htmlText += “Battery level: “ + String ( pEvt.target.batteryLevel ); }

But there ’ s a problem – the Wiimote data is very jittery. So if you hook your avatar ’ s roll and pitch (or x, y position) up to your Wii, what you get is an avatar with the jitters. There are two ways of handling this problem:

❑ You can average five (or so) values of your accelerometer and then attach each averaged value to a tween (or easing) engine. This really smoothes things out but makes your response a little slower. Averaging is demonstrated in the Pong game developed in the next section. ❑ You can truncate the data and throw out erratic terms. This is the poor man ’ s way of doing it and in some instances works well.

Truncation is how the Silly String program handles its accelerometer data.

sphere.rotationX+=-(int(MyWiiIs.pitch*100)/100)*4; sphere.rotationY+=(int(MyWiiIs.yaw*100)/100)*4; sphere.rotationZ+=(int(MyWiiIs.roll*100)/100)*4;

The sphere rotation accepts the pitch, yaw and roll data by truncating it using the int object.

The rest of the Silly String application code can be downloaded from the book ’ s website. Before you leave accelerometers, there ’ s an important example of its use: using sensor X to control vehicle steering. Virtual Wii Driving One of the coolest examples that comes with the Wii downloads is a PV3D car shown in Figure 10.13. You can steer this car ’ s wheels using yaw, and drive it all around your screen using your Wiimote. The code is simple enough:

private function updateCar( car:DisplayObject3D ):void { // Steer front wheels var steerFR:DisplayObject3D = car.getChildByName( “Steer_FR” ); var steerFL:DisplayObject3D = car.getChildByName( “Steer_FL” );

steerFR.rotationY = steer; steerFL.rotationY = steer;

// Rotate wheels

400 Chapter 10: Building 3D Games for Wii

var wheelFR:DisplayObject3D = steerFR.getChildByName( “Wheel_FR” ); var wheelFL:DisplayObject3D = steerFL.getChildByName( “Wheel_FL” ); var wheelRR:DisplayObject3D = car.getChildByName( “Wheel_RR” ); var wheelRL:DisplayObject3D = car.getChildByName( “Wheel_RL” );

var roll:Number = speed/2 wheelFR.roll( roll ); wheelRR.roll( roll ); wheelFL.roll( -roll ); wheelRL.roll( -roll );

// Steer car car.yaw( speed * steer / 500 );

// Move car car.moveForward( speed ); }

The two most important parameters in the code above are steer and speed . Steer controls the turning angle of your wheels that comes from the sensor X data of your Wiimote. It ’ s limited to 45 degree angles, keeping your wheels realistic in their rotation. You obtain Speed by pushing the B key on your Wiimote: it moves your car forward and rolls your wheels, giving your car a very realistic look. You do this by telling your wheels (in your Collada file covered previously in this book) to roll, based on a multiple of your speed (half). Finally, your entire car is turned based on a multiple of your speed and steer parameters.

Figure 10-13

Make sure that you check out this great example in the Wiimote sample code.

In the next chapter, you attach a Spring Camera to this car example and start the creation of a driving game that you can play with a friend online.

401 Part III: Building Games and Websites

Adding Wii to Your Game At this point, you may think that you ’ re ready to add the Wii to your pool shooting game that you developed earlier. But to do so, you have three fundamental hurdles that you must overcome:

❑ Using a Sensor Bar ❑ Adding a Virtual Mouse ❑ Integrating Environments

In the previous section, you learned to draw using the Wii controller button clicks and accelerometer. But getting your shooting game to work with the Wii requires that you add two more important items: a Sensor Bar, and a Virtual Mouse. Using a Sensor Bar There ’ s no special sensor bar for Wiiflash. Any sensor bar that works with a regular Wii will work just fine. Here you use the Nyko wireless bar, shown in Figure 10.14, which can be purchased from Amazon for about $15.99 ( £ 7.99). But you can make your own IR sensors (and many people do). To see some really creative work with IR sensors make sure you check out Johnny Chung Lee ’ s site at http://johnnylee.net/projects/wii/.

Figure 10-14

The bar allows you to easily triangulate the position of your x,y screen coordinates and use your Wiimote as a virtual mouse. You may need to build a calibration routine for Wiimote, since placement and sensor bar/type will influence pairing of your virtual mouse with your Wiimote. Grabbing sensor data requires that you use the IR class. The code for grabbing the x,y positions of your Wiimote is given here:

private function getIRValues(even:WiimoteEvent):void{

if(!isNaN(_wiimote.ir.point1.x)){ dataIRX=(stage.width-_wiimote.ir.point1.x*stage.width);}

if(!isNaN(_wiimote.ir.point1.y)){ dataIRY=(-_wiimote.ir.point1.y*stage.height); } //Calibration provided if needed. _virtualMouse.x = dataIRX+calibration.x; _virtualMouse.y = dataIRY+calibration.y;

}

402 Chapter 10: Building 3D Games for Wii

In the last two lines of code you use the calibration parameter to calibrate your IR data and position your virtual mouse if needed (for the Nyco bar calibration used in this chapter calibration was not needed beyond 2 ft).

So why do you need a virtual mouse anyway, isn ’ t one mouse enough?

Here ’ s the problem.

When you use your mouse, you ’ re scrolling on the screen. But with the Wii you ’ re not scrolling on the screen. You ’ re standing 5 to 16 feet away and you ’ re not clicking with your mouse. So you need to create a virtual mouse that can be moved with your Wiimote and fire mouse events as if you were clicking with your real mouse. Adding a Virtual Mouse The VirtualMouse class is used to create a programmatic version of the your mouse that can be moved about the Flash player stage firing off mouse events of the display objects it interacts with. This can allow you to simulate interaction with buttons and movie clips through ActionScript. One of the first virtual mouse systems was created by Senocular at www.senocular.com/flash/tutorials . Senocular has a number of great tutorials on his site and has been a big contributor to the open source community for years.

Senocular ’ s original demo didn ’ t use the sensor bar (but roll and pitch), and a rewritten version of his application, which incorporates the sensor bar with calibration sliders, can be downloaded from the book ’ s chapter code.

private function onUpdated ( pEvt:WiimoteEvent ): void { //Get X IR Value if (!isNaN(_wiimote.ir.point1.x)){ oldIRX=(stage.width-_wiimote.ir.point1.x*stage.width); } //Get Y IR Value if (!isNaN(_wiimote.ir.point1.y)){ oldIRY=(-_wiimote.ir.point1.y*stage.height); } //Set Position of Virtual Mouse _virtualMouse.x = int(oldIRX*1000)/1000+xSlider.value; _virtualMouse.y = int(oldIRY*1000)/1000+ySlider.value; //Print out values to textbox myText.text= _virtualMouse.x + ”, “ +_virtualMouse.y + ”\n”; } //Move your mouse icon to the position of your virtual mouse private function OnMouse_MOVE(evt:MouseEvent): void{ imgCursor.x = _virtualMouse.x; imgCursor.y = _virtualMouse.y; }

Note that, even with truncating the IR x and y data, your cursor is still jittery. This is where averaging data values can be helpful. In addition, there ’ s still code to be written for the boundaries. When your cursor hits the end of its boundary it flips over to the other side of the screen.

403 Part III: Building Games and Websites

The virtual mouse handles the following events:

❑ Event.MOUSE_LEAVE, MouseEvent.MOUSE_MOVE ❑ MouseEvent.MOUSE_OUT, Mouse ❑ Event.ROLL_OUT, MouseEvent.MOUSE_OVER ❑ MouseEvent.ROLL_OVER, MouseEvent.MOUSE_DOWN ❑ MouseEvent.MOUSE_UP. MouseEvent.CLICK ❑ MouseEvent.DOUBLE_CLICK

However, the VirtualMouse mouse can ’ t do the following: activate states of SimpleButton instances; change object focus; handle mouseWheel related events; change the system ’ s cursor location, or spoof the location of the mouseX and mouseY properties (which some components rely on).

The PV3D team has incorporated Senocular ’ s virtual mouse classes in PV3D and the classes are located at org.papervision3d.core.utils.virtualmouse . Integrating Environments As mentioned in the previous chapter, one of the biggest problems when working with PV3D is integrating environments. You really see this when trying to convert the pool shooting game with the Wiimote. The sights (which become the graphic for your virtual mouse) are in the viewport, but the controls are in the screen canvas. So when you try to navigate the controls with the virtual mouse your sight graphic can ’ t go there, because it ’ s in a different container.

You have two ways to solve this problem: create a PV3D version of your controls or migrate PV3D and Jiglib into the Flex 4 SDK and use the native z component. Regardless of how you solve this problem, integrating environments and getting your virtual mouse to interact with your 3D components is a non - trivial task. And in any Wii - PV3D game design these issues need to be addressed upfront – not at the end of your Wii - game development when you ’ re trying to convert your game to the Wiiflash platform.

Waiting until the end process (converting your game to Wii at the end) can be painful and require many hours of reprogramming and hacking, whereas treating these Wiimote integration problems upfront can lead to different approaches to handling problems (or avoid them altogether).

You leave the pool shooting game for now and build the Pong game from scratch, integrating the Wiimote up front.

Playing Wii Jiglib Pong Creating Pong using Jiglib is a breeze. It ’ s a ball, two paddles, and a Pong bounding box as shown in Figure 10.15. And as Jiglib handles all the physics for you, there ’ s no boundary adhoc angle programming that needs to be done – it ’ s real physics!

404 Chapter 10: Building 3D Games for Wii

Using the Wiimote you ’ re going to rotate your camera around your playing arena using the Wiimote arrow keys. You use the sensor bar to guide one Pong paddle and artificial intelligence to guide the other Pong paddle.

Figure 10-15

It should be pointed out that Seb Lee - Delisle of Plug - in Media has developed a similar project in PV3D, which doesn ’ t use Jiglib, the Wii, or have game levels. But Seb ’ s Pong simulation is a great example of how to take a 2D game and make it 3D in PV3D. Seb has released his code under the Creative Commons 2.0 license and this game uses modifications of his line3D arena and orbiting camera concept (which in essence is just a yaw and pitch).

This is a true game with six levels, each one increasing in difficulty as the computers AI tightens and the ball speeds up on each level. You can lose, and losing is important when creating a game. It carries two thirds of the psychological impact of learning.

Here are the features of the game:

❑ Pong Bounding Box ❑ True Jiglib Physics engine ❑ Constant Velocity Ball ❑ Artificial Intelligence ❑ 6 Levels of Game Play each Increasing in Difficulty ❑ Scoring System ❑ Reflective Surface & Skybox ❑ Wii controlled Navigation ❑ Wii controlled Paddle using the IR Sensor Bar

405 Part III: Building Games and Websites

Building the Bounding Box As in the case of the 3D pool game you need a bounding box. You do this by modifying the 3D pool game bounding box as follows: remove the right, left, and top panels. Then bring the bottom panel up to the middle as shown in the following code:

public function createPongBoundingBox(material:MaterialObject3D, myWidth:Number, myDepth:Number, myHeight:Number, myX:Number=0, myY:Number=0, myZ:Number=0):void {

//Bottom createPhongPlane(material, myWidth, myDepth, new Number3D(myX,0+myY,myZ), new Number3D(90,0,0)); //Sides createPhongPlane(material, myHeight, myWidth, new Number3D(myX,myY,-myDepth/2+myZ), new Number3D(0,180,90)); createPhongPlane(material, myHeight, myWidth, new Number3D(myX,myY,myDepth/2+myZ), new Number3D(180,180,270)); }

Next you add the Jiglib physics engine.

Adding Jiglib Adding the Jiglib physics engine is where all the magic happens. You no longer have to fake your physics, and as so many Flash games on the web require just a little bit of physics, Jiglib really opens up a world of opportunity.

In this game, it ’ s easy to create the game pieces – create two paddles and a ball, make the paddles immovable, and change their positions using typical paddle code.

So what does it mean to make an object immovable?

It means that when another Jiglib object hits the immovable object that it doesn ’ t react physically (it stays in its position). You really don ’ t want your paddles moving all over the place when your play ball hits them. But it doesn ’ t mean that you can ’ t move the paddles by changing their x and y coordinates. But your immovable object still has restitution – thus you get recoil of the ball, but not of the paddle.

Hey wait, what ’ s restitution? You commonly set three physical parameters for an object: mass, friction, and restitution. If an object doesn ’ t have mass it floats away. If the ball and its surface don ’ t have friction it won ’ t roll. And restitution is what happens at impact. It ’ s how much recoil you get, and if the number is greater than one you get more than you give. The code for creating all the physical objects of the game is given here:

private function initObject():void { //Instantiate the PV3D plugin physics = new Papervision3DPhysics(scene, 8); //Create a shade material shadeMateria = new FlatShadeMaterial(mylight, 0x77ee77); //Create a wire frame for your bounding box, //but don’t add it in the plane creation stage

406 Chapter 10: Building 3D Games for Wii

var myWire:WireframeMaterial=new WireframeMaterial(0x000000); myWire.doubleSided=true; //Create your bounding box physics.createPongBoundingBox(myWire, myWidth, myDepth, myHeight,0,-10,0); //Sort your objects vplObjects = new ViewportLayer(viewport,null); vplObjects.layerIndex = 2; vplObjects.sortMode = ViewportLayerSortMode.Z_SORT; viewport.containerSprite.addLayer(vplObjects); shadeMateria = new FlatShadeMaterial(mylight, 0xff6666); //Add your Jiglib ball shadeMateria.interactive = true; ballBody = physics.createSphere(shadeMateria, 16, 12, 12); ballBody.mass = 1; ballBody.friction=.2; ballBody.restitution=.8; ballBody.moveTo(new JNumber3D( -100, 0, -100)); vplObjects.addDisplayObject3D(physics.getMesh(ballBody)); //Add Your Jiglib paddles (called bar 1 and bar 2) shadeMateria = new FlatShadeMaterial(mylight, 0xffff44);

materiaList = new MaterialsList(); materiaList.addMaterial(shadeMateria,”all”); //Give them both friction and restitution bar1 = physics.createCube(materiaList, 16, 86, 32); bar1.moveTo(new JNumber3D(-280, 0, 0)); bar1.movable=false; bar1.restitution=.8; bar1.friction=.2; vplObjects.addDisplayObject3D(physics.getMesh(bar1)); //Create your AI second paddle bar2 = physics.createCube(materiaList, 16, 86, 32); bar2.moveTo(new JNumber3D(280, 0, 0)); bar2.movable=false; bar2.restitution=.8; bar2.friction=.2; vplObjects.addDisplayObject3D(physics.getMesh(bar2)); //Add a random force when the ball enters the stage ballBody.addWorldForce(new JNumber3D(300-600*Math.random(),0,100- 200*Math.random()),ballBody.currentState.position); }

In the code above, you create all your physical objects: your Pong bounding box, play ball, player paddle and AI paddle. This would have been really cumbersome before Jiglib, but now it only takes a few lines of code.

But now you have a problem; in Pong you want your ball to move with constant velocity. But with friction and restitution values of less than 1, the ball loses energy. If you try to balance friction and restitution your system can go crazy - just like an Air hockey game.

407 Part III: Building Games and Websites

Moving at Constant Velocity So, to create a constant velocity ball you ’ ve got to calculate the direction your ball is moving, and its velocity components. Then constantly add its velocity components according to their magnitudes and directions, which are changing on each bounce. This may sound complicated but the code is very simple:

//Calculate the arctangent of your ball Var myTang:Number=Math.atan2(ballBody.currentState.linVelocity.z,ballBody .currentState.linVelocity.x);

//Increase x direction of your ball proportionately ballBody.currentState.linVelocity.x=Math.cos(myTang)*ballSpeedIs; //Increase y direction of your ball proportionately ballBody.currentState.linVelocity.z=Math.sin(myTang)*ballSpeedIs;

In the code you just calculate the tangent of your ball and then use cosine and sine to ramp your ball up to the game ballSpeedIs parameter. This parameter is useful, as you can use it to increase your ball speed on each new game level.

Now that you ’ ve got your ball moving at constant velocity, you add a little AI to your computer ’ s paddle.

Adding AI When it comes to AI (Artificial Intelligence) your ball paddle is as simple as it gets. Basically, you create the paddle to follow your ball ’ s z coordinate using easing. With a large easing number your paddle misses the ball a lot, but as you tighten up the easing, your paddle misses less and less. So as you increase your game level your easing number gets smaller (or your paddle gets smarter).

private function moveAiPaddle():void { // AI based on easing targetY=(ballBody.z+43); //Split the difference bar2.z +=(targetY-bar2.z)/easing;

//Freeze bar x position and rotation bar2.x=280; bar2.rotationY=0; }

Easing works on Xeno ’ s hypothesis and cuts the difference in half on each iteration. You never actually get there, but you get close enough. The smaller the easing number, the more the distance gets cut.

With easing and constant ball speed taken into account it ’ s easy to make game levels.

Making Game Levels and a Scoring System The process of keeping score and game levels all happens in the program ’ s animation loop. In the animation loop, the program checks to see if the ball has left the computer ’ s side or the player ’ s side. If it

408 Chapter 10: Building 3D Games for Wii has, it iterates the player ’ s or computer ’ s score respectively and plays a sound indicating that the ball has left the table. Immediately, a new ball is put into play with a new random force vector at the center of the table so no two plays are the same. Also friction and restitution change ball bounce angle.

When the player ’ s score reaches 21, the player receives a winning message (and sound) and is then pushed up to the next level with a greater ball speed and a smarter paddle (less easing). If the player makes it to the sixth level, he receives a message that he ’ s beaten the computer. If at any time the computer wins, then the player is pushed all the way back down to the first level (with a losing sound) with a losing score message. The documented code snippet is given here:

private function gameLoop(event:Event):void { //Update Camera moveCamera(); //Update AI Paddle Motion moveAiPaddle(); //Update Collisions checkCollisions(); //Reflective View Renders singleRender(); //Iterate Physics engine physics.engine.integrate(0.2); //Score Player if ball goes out on Computer side if(ballBody.x > myWidth/2){ score1++; myTextField.text=”My Score: “+String(score1); } //Score Computer if ball goes out on player side if(ballBody.x < -myWidth/2){ score2++; hisTextField.text=”Computer Score: “+String(score2); } //Check to see if ball leaves the court if(ballBody.x > myWidth/2||ballBody.x < -myWidth/2){ //If ball leaves the court add a new ball with random force ballBody.addWorldForce(new JNumber3D(-600*Math.random(),0,200-400*Math .random()),ballBody.currentState.position); ballBody.x=ballBody.y=ballBody.z=0; //add ball out sound (new scoreSound() as Sound).play(); } //If player’s score is 21 go to the next level if(score1==21){ //Iterate level and play sound levelInt++; (new wonSound() as Sound).play(); //Output winning score message and reinitialize score myMessage.text=”Up to Level “+String(levelInt)+” You Won: “+String(score1)+”/”+String(score2); score1=score2=0; //Increase ball speed ballSpeedIs+=10; (continued)

409 Part III: Building Games and Websites

(continued) //Decrease easing easing-=1; if(levelInt==7){ //If you have beat 6 levels send beat Computer message and start again score1=score2=0; myTextField.text=”My Score: “+String(score1); hisTextField.text=”Computer Score: “+String(score2); ballSpeedIs=60; easing=7; (new wonSound() as Sound).play(); myMessage.text=”Level “+String(levelInt)+” You Beat the Computer: “+String(score1)+”/”+String(score2); levelInt=1; }}

//If the computer wins go back to level 1 and start all over again if(score2==21){ levelInt=1; (new lostSound() as Sound).play();

myMessage.text=”Back to Level “+String(levelInt)+” You Lost: “+String(score1)+”/”+String(score2); score1=score2=0; myTextField.text=”My Score: “+String(score1); hisTextField.text=”Computer Score: “+String(score2); ballSpeedIs=60; easing=7; }}

In order to see the messages and scores you must instantiate textboxes.

Creating Textboxes One of the great powers of OOP is your ability to grab reusable code out of thin air. Just as you ’ ve instantiated any class before, you can also instantiate a textbox and drop it onto your stage. But you can do this with any component, not just textboxes. The fully documented code for putting two scoreboards and a game level message is given here:

private function addTextFields():void{ //Instantiate your score and message textboxes myTextField=new TextField(); hisTextField = new TextField(); myMessage = new TextField(); //Message Textbox properties myMessage.y=20; myMessage.x=400; myMessage.scaleX=1.5; myMessage.scaleY=1.5; myMessage.textColor=0xffffff; myMessage.text=”Level “+String(levelInt)+” Game Goes to 21”; myMessage.maxChars=400;

410 Chapter 10: Building 3D Games for Wii

myMessage.width=400; //Score textbox properties myTextField.y=hisTextField.y=50; myTextField.x=400; hisTextField.x=520; myTextField.textColor=0xffffff; hisTextField.scaleX=1.5; hisTextField.scaleY=1.5; myTextField.scaleX=1.5; myTextField.scaleY=1.5; myTextField.textColor=0xffffff; hisTextField.textColor=0xffffff; //Score Messages myTextField.text=”My Score: “+String(score1); hisTextField.text=”Computer Score: “+String(score2); //Add Boxes to stage addChild(myTextField); addChild(hisTextField); addChild(myMessage); }

In the last three lines of code you add your textboxes to the stage using the addChild method. You ’ re most likely used to adjusting your text properties in a Flash or Flex panel. The only difference here is that you ’ re doing the same thing with code. Eventually, it all has to become code anyway.

Adding Reflectivity and a SkyBox You ’ ve already seen how to add a skybox in the previous section, so you won ’ t go through it here. But there ’ s one difference – you want to take the bottom out of your skybox and then drop it a little on the y axis.

// Add your materials, large sides, and low segments to your super super(materials,8000,8000,8000,8,8,8, Cube.NONE, Cube.BOTTOM); y = 4000-100;

So why do you get rid of the bottom? You ’ re going to make your system reflective in y so without a bottom everything up top will reflect down. Also, you ’ re going to drop the box a little to create a discontinuity in your skybox (and reflected skybox) that will give the illusion of a surface.

You create reflectivity by switching your BasicView class to the ReflectionView class (which inherits from the BasicView class). The ReflectionView class basically takes a camera shot of your top view and flips (rotates) it to the bottom – it ’ s pretty cool! Its main method is the singleRender() and must be called from the PV3D animation loop as shown above.

You ’ re now ready to add Wiimote.

Building a Wii Navigation System Navigating around your 3D court using your arrow keys requires that you set the appropriate Wii key listeners. But first you must add the Wii to your system. Adding the Wii to your program is easy. You just

411 Part III: Building Games and Websites

instantiate your Wii and run the connect method with a listener that adds your Wii key listeners and virtual mouse after connection.

//Instantiate your Wii and run the connect method _wiimote = new Wiimote(); _wiimote.connect(); _wiimote.addEventListener(Event.CONNECT, initiateDrawing);

//After Wii connection add listeners and virtual mouse function initiateDrawing():void{ //Wii Listeners WiiListeners(); //Virtual Mouse _virtualMouse = new VirtualMouse(stage, 0,0); _virtualMouse.addEventListener(MouseEvent.MOUSE_MOVE, OnMouse_MOVE); _wiimote.addEventListener( WiimoteEvent.UPDATE, onUpdated ); }

The navigation system listens for when you press the up/down and right/left keys and sets the appropriate Booleans.

//Wii Key Listeners _wiimote.addEventListener( ButtonEvent.LEFT_PRESS, onLeftPressed ); _wiimote.addEventListener( ButtonEvent.LEFT_RELEASE, onLeftReleased); _wiimote.addEventListener( ButtonEvent.RIGHT_PRESS, onRightPressed ); _wiimote.addEventListener( ButtonEvent.RIGHT_RELEASE, onRightReleased); _wiimote.addEventListener( ButtonEvent.UP_PRESS, onUpPressed ); _wiimote.addEventListener( ButtonEvent.UP_RELEASE, onUpReleased); _wiimote.addEventListener( ButtonEvent.DOWN_PRESS, onDownPressed ); _wiimote.addEventListener( ButtonEvent.DOWN_RELEASE, onDownReleased); } //Set Booleans upon key press and release //Wii Up private function onUpPressed ( pEvt:ButtonEvent ):void { keyForward = true ; } private function onUpReleased ( pEvt:ButtonEvent ):void { keyForward = false ; } //Wii Left private function onLeftPressed ( pEvt:ButtonEvent ):void { keyLeft = true ; } private function onLeftReleased ( pEvt:ButtonEvent ):void { keyLeft = false ; } //Wii Right private function onRightPressed ( pEvt:ButtonEvent ):void { keyRight = true ;

412 Chapter 10: Building 3D Games for Wii

} private function onRightReleased ( pEvt:ButtonEvent ):void { keyRight = false ; } //Wii Down private function onDownPressed ( pEvt:ButtonEvent ):void { keyReverse = true ; } private function onDownReleased ( pEvt:ButtonEvent ):void { keyReverse = false ; }

Once the Booleans are set (based upon key press or release) your camera responds to them by iterating your pitch or yaw. The camera is in the animation loop so it ’ s always scanning your Boolean key press values.

public function moveCamera():void { // Move Camera to center of Court camera.x = 0; camera.y = 0; camera.z = 0; //Wii controlled Boolean functions //Move Up if(keyForward){ camera.rotationX += myPitch; } //Move Down if(keyReverse){ camera.rotationX -= myPitch; } //Move Right if(keyRight){ camera.rotationY += myYaw; } //Move Left if(keyLeft){ camera.rotationY -= myYaw; }

// Move camera back from stage camera.moveBackward(700); }

Having an animation loop is key to using key press Boolean values from the Wii. And as PV3D automatically has such a loop, creating a Wii key press navigation system is an easy process.

But going beyond key press action to mouse pointing takes a little more effort. It requires both a virtual mouse and a sensor bar.

413 Part III: Building Games and Websites

Adding Mouse -Controlled Paddle Motion In the previous section, when you instantiated your Wiimote into your program you also added the virtual mouse. As mentioned earlier, the virtual mouse gives you the ability to use your Wii controller as a mouse. In this case, you only use it to move your player paddle. Getting Your IR Data As soon as your sensor bar is turned on and positioned, the first thing you want to do is grab the IR data from your Wiimote. The Wiimote outputs NaN data so you want to create a conditional statement that throws out this NaN data. The Wiimote x and y data is grabbed using the _wiimote.ir.point1.x or _wiimote.ir.point1.y commands. You could have also used _wiimote.ir.x1 or _wiimote.ir.y1 . The point method is just the encapsulated form. When you ’ ve got your IR data you set the position of your virtual mouse:

private function onUpdated ( pEvt:WiimoteEvent ):void { //Throw out NaN data if(!isNaN(_wiimote.ir.point1.y)){ oldIRY=-_wiimote.ir.point1.y*stage.height; } /*In this game you’re interested in y data, set the position of your virtual mouse*/ _virtualMouse.y = oldIRY; }

In this game you ’ re only moving your paddle in the y direction, so you ’ re only interested in the y - data. When you ’ ve got your y IR data and have applied it to your virtual mouse, you average 5 values to reduce the paddle jitter. This slows the response time of your paddle (a little), but it reduces the jitter as well. After you ’ ve done your averaging you apply this value to an easing equation that smoothes your paddle motion.

private function OnMouse_MOVE(evt:MouseEvent):void{

//Average is taken to reduce jitter but slows reaction time var myYAverageIS:Number=0; for(var i:int=0; i < 5; i++){

myYAverageIS+=int(_virtualMouse.y*1000)/1000; } targetX=myDepth/2-myYAverageIS/5+43;

//Use easing bar1.z +=(targetX-bar1.z)/easing;

//Restrict your bar position in x, and restrict rotation bar1.x =-280; bar1.rotationY=0; }

414 Chapter 10: Building 3D Games for Wii

Once you ’ ve got your game working, call in the kids! They ’ ll have a great time playing your Wii game on their computers and you ’ ll be a great hit at home. For more information on building games and the Wii, check out the book ’ s website.

The entire Pong code can be downloaded from the book ’ s website. Included in the download are both Wii and non - Wii versions of the game.

Summary Building games is great fun and in this chapter you learned the basics of building game systems, which include game states and multi - levels. This chapter explored two different games: pool “ shooting ” and Pong. In the pool game you learned to build an entire game system based on Flex View States. And in the Pong game you learned to build a multi - level Wii controlled game. Both games were built in Flex, but the Pong game could have easily been built in Flash, as it ’ s an ActionScript package. You built bounding boxes for your games by hacking the Jiglib PV3D plugin class and creating a skybox.

The emphasis of this chapter was on learning how to modify and create classes so you can customize your games and go beyond the present limitations of PV3D and Jiglib.

415

Integrating the Flash Media Server

Engaging your client with rich media experiences using shared objects, webcam chat, and streaming media is the purpose behind the Flash Media Server. It all started with the Flash 6 player and had the code name “ tin can ” . It was called the Flash Communication Server and could stream media and shared objects. Since then, the Flash Communication Server has evolved into two separate versions – one for just streaming media (Media Streaming Server) and the other for streaming media plus interactivity (Media Interactive Server). For 3D you ’ re most interested in using the Media Interactive Server, which lets you create interactive applications such as video recorders and multiplayer games.

So what ’ s a Media Server? The Flash Media Server (both versions) is an open socket server – which means that the connection to the server stays open until you quit the connection (most likely by exiting the application). As a result you can stream media, audio, video, text, and shared objects. Regular web servers don ’ t do that – they close connection after info has been transmitted. The Flash Media Server uses a different protocol than your typical web server. It ’ s called RTMP, which stands for Real - Time Messaging Protocol (or the newer RTMPE where E stands for encryption).

When running an FMS app you first connect to a Web server using HTTP and then to the Flash Media Server using RTMP (or RTMPE). Essentially, the FMS rides (or piggy backs) on your Web server. In this chapter, you build a Hello World example and Track Racing Car using the Flash Media Server 3.5.

Getting Started The easiest way to get started with the Flash Media Server is to get an Influxis account (at http://www.influxis.com/ ) and try out some of their pre - built applications. A basic account costs around $10 a month, and for development purposes, it ’ s perfect. Influxis runs the most up - to - date version of the media server and they ’ re continually updating their pre - built Part III: Building Games and Websites

applications. Also, they provide application source codes so you can extend these applications and create your own.

Some of the pre - built applications that you can find on Influxis are:

❑ Massiva3D — a 3D avatar text chat environment that ’ s highly customizable. ❑ VideoBlogger — a simple blogging system that allows users to post blog entries with the option of video. The client side is full open - source and is written in AS3 and FLEX. ❑ TVStation — a media scheduling application designed to let you create and broadcast your own Internet TV and/or radio station. ❑ LiveSupport — a sales and support application that gives you the ability to assist your customers from your website using live chat and a/v. Presenter Chat ❑ Presenter Chat — a chat environment in which an instructor or presenter can have a two - way a/v chat with a group and choose which person from the audience will have the user podium (room mike).

Running these applications is easy. You just download the client application from Influxis and the server code is automatically placed in your RTMP File Manager Panel shown in Figure 11.1 (the manager panel is where your server side scripts live). Then you can tailor the downloaded application to meet your needs, and then place the app on the web. Distribute the app address and everyone ’ ll think you ’ re an FMS genius.

Figure 11-1

Keep in mind that when creating an FMS application you actually have to create two applications: a client side and a server side app. Your client side app is what your client sees and it talks to your Flash Media Server using the RTMP protocol. The server side application reacts to your client side app and performs tasks such as saving video streams and tracking shared objects.

Another way to get started is to download the FMS developer edition from Adobe, which you can run from your local machine. This edition doesn ’ t run on a Mac (so use Influxis or your own server instead). The Adobe download is a standard executable – just accept the defaults and make sure that you write down your username and password, so you don ’ t forget them.

Once the FMS is installed go to Start All Programs Adobe Flash Media Server 3.5. Here you can read the documentation, look at samples, view the Flash Media Admin Console, start and stop the media

418 Chapter 11: Integrating the Flash Media Server

and admin servers, and uninstall the program. If you started with the Flash Communications Server 1.0 (like the author did), you ’ ll be impressed with how far the FMS has come. In the following discussion, you use this local PC installation (discussed previously) to develop applications.

Configuring Your Development Environment If you ’ re using the Adobe Developer ’ s edition, the server should start automatically when you start your computer. But you need to set up your work environment so it ’ s easy to test your applications. When you installed the FMS, your server - side files were installed in the following path: C:\Program Files\ Adobe\Flash Media Server < version # > \applications .

The applications folder is where all your server - side application folders go (these folders can be empty or contain server - side ActionScript files), and your server - side folder must have the same name as your application. For example, if you create a client side application named chatWithMe your server - side folder must be named chatWithMe . Go ahead and create a desktop shortcut to this folder; you ’ ll be using it a lot. If you ’ re using Influxis, you place your server - side file in the File Manager and link to it using the Influxis RTMP server address.

Unlike your server - side files described above, your client - side files can go anywhere on your computer (it ’ s your main FLA or Flex program) and when you run them they should pick up your FMS automatically. And when connecting, here ’ s an important tip:

If you ’ re running your FMS server on your local PC, the path to your server uses a single “ / ” slash, but if you ’ re talking to a remote server such as Influxis, use a double slash “ // ” . Or to completely avoid confusion over when you use single or double slashes you can use the localhost designator, which uses the double slash and accesses your local FMS as shown here:

rtmp://localhost/myappname

You now get connected to the FMS.

Connecting to the FMS Connecting to the FMS is essentially a three - line process:

netConnect= new NetConnection(); netConnect.addEventListener(NetStatusEvent.NET_STATUS, connectStatus); netConnect.connect(“rtmp://localhost/ConnectingFMS”);

You instantiate your net connection, connect to your server using your rtmp address (local or external), and listen for your connection. When you ’ re connected, you run a connected method. If you successfully connect, the event.info.code should return the “ NetConnection.Connect.Success ” message that can be used in a switch case statement. The event.info.code outputs a number of messages indicating the state of your connection. It can be used to start up your program or provide connection info to your user.

In the following case, a simple “ You Are Connected! ” message is printed to the screen if you ’ re connected to the FMS. The entire net connection snippet is listed below. You can run it in Flash or as an ActionScript package in Flex.

419 Part III: Building Games and Websites

package { //Flash Imports import flash.display.Sprite; import flash.events.NetStatusEvent; import flash.net.NetConnection; import flash.text.TextField;

//Connection Class public class ConnectingFMS extends Sprite { //Communication Properties private var netConnect:NetConnection; private var textField:TextField;

//Connection Constructor public function ConnectingFMS() { //Instantiate net connection and connect to rtmp netConnect= new NetConnection(); netConnect.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); netConnect.connect(“rtmp://localhost/ConnectingFMS”); }

//Check connection Status public function netStatusHandler(event:NetStatusEvent):void { textField = new TextField(); addChild(textField); //Trace connection status trace(“connected is: “ + netConnect.connected ); trace(“event.info.level: “ + event.info.level); trace(“event.info.code: “ + event.info.code);

//Use switch case to tab through different info messages switch (event.info.code) { case “NetConnection.Connect.Success”: textField.text=”You Are Connected!”; break;

case “NetConnection.Connect.Rejected”: textField.text=”You Connection is Rejected!”; break;

case “NetConnection.Connect.Failed”: textField.text=”Connection Failed!”; break; } } }}

But now you ’ ve got a really big problem.

420 Chapter 11: Integrating the Flash Media Server

The Big Mistake Stop – you ’ re about to make a really big mistake!

Your rtmp address is embedded in your application code – and that ’ s really bad!!! Malicious hackers can use an swf decompiler (such as the Sothink SWF Decompiler at www.sothink.com) to decompile the SWF file and grab the rtmp address and run their own FMS applications: for free – on your server! So put your rtmp address in another file, which is not accessible to the public. Influxis uses an XML file, such as the one given here for the Chat Presenter:

< ?xml version=”1.0”? > < settings > < !-This is the rtmp connection to your influxis account-> < rtmp path=”rtmp://youraddress.rtmphost.com/PresenterChatV2” /> < /settings >

The code snippet that Influxis uses to bring the XML rtmp address above into its Chat Presenter is given below. The snippet was obtained by decompiling the Chat Presenter swffile using the SoThink SWF Decompiler. This demonstrates how easy it is to decompile an swf file that ’ s obtained from the web using Google ’ s about: cache method:

var appDirectory; if (appDirectory == undefined) { var xmlSettings = new XML(); xmlSettings.ignoreWhite = true; xmlSettings.onLoad = function (success) { if (success) { var _loc5 = this.firstChild.childNodes; var _loc6 = _loc5.length; for (var _loc2 = 0; _loc2 < _loc6; ++_loc2) { var _loc3 = _loc5[_loc2]; var _loc4 = _loc3.attributes; if (_loc3.nodeName == “rtmp”) { appDirectory = _loc4.path; play (); } // end if } // end of for } // end if }; xmlSettings.load(“settings.xml”); } else { play (); } // end else if

But you don ’ t need to use a decompiler to extract the code from an Influxis application. Just sign up for an account, and Influxis provides you with their source code.

421 Part III: Building Games and Websites

On occasion the author has lost his own FLA files (it happens), and has used Sothink to decompile his own apps and recreate them. It can really get you out of a bind.

Importantly, realize that the swf format is an open source format and is unsecure. Decompiling an swf file is the virtual equivalent to “ view source ” for html pages. Use SWF Encrypt or AIR if you want to protect your applications.

Don ’ t put passwords, rtmp addresses, or database access info into swf files!!! They aren ’ t secure.

The preceding Influxis code works fine, but doesn ’ t use the new e4x demonstrated in Chapter 7. The e4x code snippet required to load and parse your XML is given here:

//Communication Properties private var netConnect:NetConnection; private var textField:TextField; //Connection Constructor public function ConnectingFMSXML() { getXml(); } //Load your XML public function getXml():void{

var urlRequest:URLRequest = new URLRequest(“assets/data/rtmpAddress.xml”); var xmlLoader:URLLoader = new URLLoader(urlRequest); xmlLoader.addEventListener(Event.COMPLETE, readXml); } //Parse your XML public function readXml(event:Event):void{

var readRtmp:XML=new XML(event.target.data);

//Instantiate net connection and connect to rtmp netConnect= new NetConnection(); netConnect.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); netConnect.connect(readRtmp.rtmp.@path); }

The great advantage of e4x (as discussed in Chapter 7) is that you can reference your data using dot syntax as opposed to child node notation. After the rtmp XML address is parsed, the connect method is called. The complete XML connection code can be found in the book ’ s Chapter 11 code.

Once you ’ re connected to the FMS you can check on your program ’ s performance by using the FMS Administration Console.

Checking on Your Program ’s Performance The FMS Administration Console can be very useful when debugging your FMS applications. Using the FMS Admin Console, you can discover how your program is performing and the number of connected clients. To open it, navigate to: Start All Programs Adobe Flash Media Server Flash Media Administration Console as shown in Figure 11.2. Go ahead and create a desktop shortcut to it – you ’ ll be using it a lot.

422 Chapter 11: Integrating the Flash Media Server

Figure 11-2

The panel has three views:

❑ View Applications ❑ Manage Users ❑ Manage Servers

Each panel has a number of different options that lets you check your application log, shared objects, streams, performance, users, connections, and more.

You now apply all of this to build a Shared Object Hello World program using Remote Shared Objects (RSO).

Running a RSO Hello World In this section you create a 3D Hello World FMS program. The application uses a shared object to move a JigLib - PV3D sphere around your screen (and the screens of whoever is connected). As shown in Figure 11.3, you can also pass the control of that object to any user who’ s connected. They can then move the “ Hello World ” sphere around their synchronized screen. You accomplish this synchronization by using a remote shared object (RSO).

Figure 11-3

An RSO lets you move an object on your screen and have that movement appear on another screen somewhere else on the web. Shared objects are powerful and you can use them to create a PV3D version of Second Life. As you move your avatar around, everyone logged into your app sees your avatar in

423 Part III: Building Games and Websites

motion, and can interact with you, as well as using their avatars. The FMS polls RSOs and each time a movement or change is made, that change transmits to everyone connected.

This is the good and bad of the FMS: connected users exponentiate the needed CPU resources required to keep your FMS communicating with everyone. As users increase in number, eventually everything slows down (your resources get drained exponentially) and to keep things running optimally, you need to upgrade your services. If you ’ re running high usage applications, it ’ s more efficient to buy your own FMS server. Given that you have the staff to maintain them.

You now go through the flow of instantiating an RSO, and make changes to it. Instantiating Your RSO To create your FMS Hello World program you ’ ve got to instantiate an RSO and sync its changes to all connected users. RSOs come in two flavors: persistent and nonpersistent. Persistent objects hold their values after the application is exited (like a data base), while nonpersistent RMO values disappear (like variables) when you disconnect from an application. The first step in getting your RSO working is to instantiate it.

Using getRemote Instantiating an RSO uses a different syntax to most instantiated objects in Flash. Instead of using the new statement you use the getRemote() method. In the case of your Hello World example, the RMO is given the name world_so . The method for instantiating an RSO (as world_so ) is shown below.

world_so=SharedObject.getRemote(“world”, nc.uri, false);

In the getRemote method, the first parameter is the name (which appears in the Admin Console), the url, and the persistence of the RSO. If persistence is set to false, the shared object behaves as a variable (as opposed to a database for persistence).

Using setProperty For the RSO to be loaded with a data value, the FMS uses the SharedObject.setProperty() method to assign values to a shared object, which lets you create slots for your data. This is a change from the previous Media Servers. In the code snippet below the RSO ( world_so ) property pos is loaded with the helloSphere position every time the syncPosition method is executed.

private function syncPosition(e:TimerEvent):void{ //Update your event e.updateAfterEvent(); world_so.setProperty(“pos”, helloSphere.currentState.position); }

424 Chapter 11: Integrating the Flash Media Server

The setProperty method creates a slot for your data to be held in.

So what ’ s a slot? It ’ s a place where data can be stored and shared on the FMS. Slots are polled and connected users are updated according to changes made to a slot ’ s data. Each time a slot ’ s data is changed a SyncEvent is fired that synchronizes your browser events with other browsers using your shared object.

Using Sync Event You accomplish this by assigning a Sync event listener to your shared object. Every time your RSO changes its syncHandler method is executed.

world_so.addEventListener(SyncEvent.SYNC, syncHandler);

Now there ’ s one more property you need to know about, and that ’ s the SyncEvent.changeList property.

Using changeList The changeList is an Array. It stores the shared object properties. The code property of the changeList array is used to determine the type of event that has fired. This is very important, as it can be used to determine who is making the change to the RSO as shown in the syncHandler method. The syncHandler method updates six shared object properties; the hello sphere ’ s position (x and z) and the Boolean keyboard clicks.

The most important line of code in the syncHandler method is the “ code ” equal “ change ” line.

if(event.changeList[cList].code==”change”)

Why is this so important? In old days of the Flash Media servers it was hard to tell who made the change to the shared object. There needed to be a way to tell who was making the change, because the change made was transmitted to all users connected. So if you made a change to an RSO that change would bounce back to your screen as well and create an undesirable feedback loop.

To get around this, Boolean flags had to be set on the side of the user making the change so it wouldn ’ t accept an incoming transmitted change (as all changes were transmitted out to all connected users – even the one making the change). It was somewhat cumbersome, confusing, and sometimes just didn ’ t work!

With the changeList code property, two code events are generated when a change is made: success and change . A change value indicates that another user made the change, not you. So if your change list code value equals change (as in the code line above) then update the RSO on your side. Otherwise, you made the change and don ’ t have to update this change on your side.

The rest of the following code snippet cycles through all the RSO properties using the switch statement to make the changes to your hello sphere using the RMO data property.

425 Part III: Building Games and Websites

// update clients when the shared object changes private function syncHandler(event:SyncEvent):void { //When a sync event is fired //Update the position of the ball

for (var cList:uint; cList< event.changeList.length; cList++) { if(event.changeList[cList].code==”change”) { switch (event.changeList[cList].name) { //Grab position case “pos”: helloSphere.currentState.position.x = world_so.data.pos.x; helloSphere.currentState.position.z = world_so.data.pos.z; break;

//Grab Key Press State case “keyForward”: keyForward = world_so.data.keyForward; break;

case “keyReverse”: keyReverse = world_so.data.keyReverse; break;

case “keyLeft”: keyLeft = world_so.data.keyLeft; break;

case “keyRight”: keyRight = world_so.data.keyRight; break;

case “keyUp”: keyUp = world_so.data.keyUp; break; }}}}

Another important change to the FMS 3.5 is that you don ’ t have to change the NetConnection default object encoding to AMF0. Of course, this requires that you use the Flash Player 10.

Now that you ’ ve got the basics of instantiating an RSO and transmitting its changes to your users (or them to you), you go through the flow of creating your FMS Hello World program.

To create the FMS “ Hello World ” application, follow these steps:

1. Start by downloading the Jiglib Hello World code from Chapter 9. This is the base code that you add the FMS to. Using this code, create a new Flex ActionScript project named HelloWorldJiglibShared . 2. Use Flex 4 to upgrade your project to a Flash 10 player application as shown in Figure 11.4. Make sure that, in setting your SDK to Flex 4, you change your Flash Player to version 10.

426 Chapter 11: Integrating the Flash Media Server

Figure 11-4

3. Open up the FMS applications folder and create a server - side HelloWorldJiglibShared folder as shown in Figure 11.5. In this case, no server - side scripting is required. But if the folder isn ’ t present the application won ’ t work.

Figure 11-5

4. Add the required media server imports to your program.

import flash.events.NetStatusEvent; import flash.events.SyncEvent; import flash.net.NetConnection; import flash.net.SharedObject; import flash.ui.Keyboard;

5. Add the shared object, net connection and synch timer properties.

//Flash Comm Server private var world_so:SharedObject; private var nc:NetConnection;

//Flash Synch Timer private var syncTimer:Timer;

427 Part III: Building Games and Websites

6. Add the connection method, instantiate your shared object, add your synch event listener, and synch handler method as described earlier. 7. Add your synch position timer method as described previously. This method synchs the positions of your balls and is key to making shared object ball games work. The Jiglib physics engine is run on each client side, but due to synching lag, the balls eventually get out of synch so it ’ s important to periodically reposition the balls to their correct position. In this case a timer event is run four times a second, which positions your balls in the correct x and z positions using the syncPosition method.

//Synch Timer-for matching positions syncTimer = new Timer(250); syncTimer.addEventListener(TimerEvent.TIMER, syncPosition);

This is a hack and you can do this in other ways, but you can ’ t beat the simplicity. The syncPosition handler isn ’ t run in the animation loop since a higher data rate bogs the system down, but you can limit its transmission with a modulo counter/if statement combo. Doing so would let you eliminate the timer and its listener. Note that, in this example, ball rotation wasn ’ t synched and would require an additional RSO to do so. 8. Finally, add your Boolean setProperty methods for your arrow key up and key down methods. When you release the arrow key your RSO Boolean is set to false:

case Keyboard.UP: keyForward = false; //Set key press Shared Object world_so.setProperty(“keyForward”, false); break;

When you press the arrow key your RSO Boolean is set to true :

case Keyboard.UP: keyForward = true; //Set key press Shared Object world_so.setProperty(“keyForward”, true); keyReverse = false; break;

The key - pressed Boolean is then transferred to your clients and picked up in their animation loops and the ball physics of your Hello World Jiglib application is set into play.

The complete documented code is listed here:

package { //Flash Imports import flash.display.Bitmap; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.TimerEvent; import flash.ui.Keyboard;

428 Chapter 11: Integrating the Flash Media Server import flash.utils.Timer; import flash.events.TimerEvent;

//Media Server Imports import flash.events.NetStatusEvent; import flash.events.SyncEvent; import flash.net.NetConnection; import flash.net.SharedObject;

//Jiglib imports import jiglib.geometry.JSphere; import jiglib.math.JNumber3D; import jiglib.physics.RigidBody; import jiglib.plugin.papervision3d.Papervision3DPhysics; import jiglib.plugin.papervision3d.Pv3dMesh; import org.papervision3d.cameras.CameraType; import org.papervision3d.cameras.SpringCamera3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.objects.primitives.Sphere; import org.papervision3d.view.BasicView;

//Set SWF Size and frame rate [SWF(width=”1000”, height=”700”, backgroundColor=”#000000”, frameRate=”60”)]

//Hello World Constructor function public class HelloWorldJiglibShared extends BasicView { //Embed Earth Graphic [Embed(source=”assets/helloearth.jpg”)] public var EarthTexture:Class;

//Variable Declaration private var physics:Papervision3DPhysics; private var sphereObject:Sphere; private var helloSphere:RigidBody; private var moveForce:Number = 12; private var springCamera:SpringCamera3D; private var cameraTarget:DisplayObject3D; private var sceneLight:PointLight3D;

//Key Listeners private var keyRight:Boolean = false; private var keyLeft:Boolean = false; private var keyForward:Boolean = false; private var keyReverse:Boolean = false; private var keyUp:Boolean = false;

(continued)

429 Part III: Building Games and Websites

(continued) //Flash Comm Server private var world_so:SharedObject; private var nc:NetConnection;

//Flash Synch Timer private var syncTimer:Timer;

//Initiate Listeners public function HelloWorldJiglibShared() {

// Initialize the Papervision3D BasicView super(stage.stageWidth, stage.stageHeight, true, false, CameraType.TARGET); //Keyboard Listener stage.addEventListener( KeyboardEvent.KEY_DOWN, myKeyDown ); stage.addEventListener( KeyboardEvent.KEY_UP, myKeyUp ); //Set Light sceneLight = new PointLight3D(true, true); sceneLight.x = 10; sceneLight.y = 300; sceneLight.z = -400;

// Initialize the Papervision3D physics plug-in physics = new Papervision3DPhysics(scene, 7); //Initiate Listeners createFloor(); setSpringCamera(); createHelloSphere(); startRendering(); //Create a net connection nc=new NetConnection; //Don’t Embed your rtmp address in your application!!! nc.connect(“rtmp://localhost/HelloWorldJiglibShared”); nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); //Synch Timer-for matching positions syncTimer = new Timer(250); syncTimer.addEventListener(TimerEvent.TIMER, syncPosition); }

private function syncPosition(e:TimerEvent):void{ //Update your Sphere’s position e.updateAfterEvent(); world_so.setProperty(“pos”, helloSphere.currentState.position); }

// Handle connection events public function netStatusHandler(event:NetStatusEvent):void { trace(“connected is: “ + nc.connected ); trace(“event.info.level: “ + event.info.level); trace(“event.info.code: “ + event.info.code);

430 Chapter 11: Integrating the Flash Media Server

//Use Switch Case to tab through different connection messages switch (event.info.code) { case “NetConnection.Connect.Success”: trace(“Congratulations! you’re connected”);

//Shared Object world_so=SharedObject.getRemote(“world”, nc.uri, false); world_so.connect(nc); world_so.addEventListener(SyncEvent.SYNC, syncHandler); //Start position synching timer syncTimer.start(); break; case “NetConnection.Connect.Rejected”: case “NetConnection.Connect.Failed”: trace (“Oops! you weren’t able to connect”); break; }}

// Update clients when the shared object changes private function syncHandler(event:SyncEvent):void { //When a synch event is fired //Update the position of the ball to all clients for (var cList:uint; cList< event.changeList.length; cList++) { if(event.changeList[cList].code==”change”) { switch (event.changeList[cList].name) { //Grab position case “pos”: helloSphere.currentState.position.x = world_so.data.pos.x; helloSphere.currentState.position.z = world_so.data.pos.z; break;

//Grab Keys case “keyForward”: keyForward = world_so.data.keyForward; break; case “keyReverse”: keyReverse = world_so.data.keyReverse; break; case “keyLeft”: keyLeft = world_so.data.keyLeft; break; (continued)

431 Part III: Building Games and Websites

(continued) case “keyRight”: keyRight = world_so.data.keyRight; break;

case “keyUp”: keyUp = world_so.data.keyUp; break; }}}}

//Use Spring Camera private function setSpringCamera():void { //Instantiate Spring Camera and set constants springCamera = new SpringCamera3D(); springCamera.mass = 8; springCamera.damping = 8; springCamera.stiffness = 1; springCamera.focus = 80; springCamera.zoom = 8; //Set Offsets springCamera.lookOffset = new Number3D(0, 30, 20); springCamera.positionOffset = new Number3D(0, 100, -1200); }

//Create Hello World Sphere private function createHelloSphere():void { //Define Material for Hello World Sphere var helloWorld:BitmapMaterial = new BitmapMaterial(Bitmap(new EarthTexture()). bitmapData, true); helloWorld.smooth = true;

sphereObject = new Sphere(helloWorld, 120, 12, 12); scene.addChild(sphereObject); //Instantiate Jiglib sphere and attach PV3D Sphere helloSphere = new JSphere(new Pv3dMesh(sphereObject), 100); helloSphere.y = 180; helloSphere.rotationY+=60; helloSphere.restitution = 3; helloSphere.mass = 1;

physics.addBody(helloSphere);

//Define DisplayObject3D cameraTarget cameraTarget = new DisplayObject3D(); cameraTarget.copyPosition(sphereObject); scene.addChild(cameraTarget); //Spring camera follows target. springCamera.target = cameraTarget; }

//Create your floor private function createFloor():void

432 Chapter 11: Integrating the Flash Media Server

{ var myFloor:Plane = new Plane(new WireframeMaterial(0xFFFFFF), 10000, 10000, 10000*0.001, 10000*0.001); myFloor.rotationX = 90; myFloor.y = -150; scene.addChild(myFloor); //Generate ground from PV3D Plugin physics.createGround(new WireframeMaterial(0xFFFFFF, 0), 1800, 0); }

//Key Handler Methods //myKeyUp Method private function myKeyUp(event:KeyboardEvent):void { event.updateAfterEvent(); switch(event.keyCode) { case Keyboard.UP: keyForward = false; //Set key press Shared Object world_so.setProperty(“keyForward”, false); break; case Keyboard.DOWN: keyReverse = false; //Set key press Shared Object world_so.setProperty(“keyReverse”, false); break; case Keyboard.LEFT: keyLeft = false; //Set key press Shared Object world_so.setProperty(“keyLeft”, false); break; case Keyboard.RIGHT: keyRight = false; //Set key press Shared Object world_so.setProperty(“keyRight”, false); break; case Keyboard.SPACE: //Set key press Shared Object world_so.setProperty(“keyUp”, false); keyUp=false; } }

//myKeyDown Method private function myKeyDown(event:KeyboardEvent):void { //Update after event event.updateAfterEvent(); (continued)

433 Part III: Building Games and Websites

(continued) //Key switch case switch(event.keyCode) { case Keyboard.UP: keyForward = true; //Set key press Shared Object world_so.setProperty(“keyForward”, true); keyReverse = false; break;

case Keyboard.DOWN: keyReverse = true; //Set key press Shared Object world_so.setProperty(“keyReverse”, true); keyForward = false; break;

case Keyboard.LEFT: keyLeft = true; //Set key press Shared Object world_so.setProperty(“keyLeft”, true); keyRight = false; break;

case Keyboard.RIGHT: keyRight = true; //Set key press Shared Object world_so.setProperty(“keyRight”, true); keyLeft = false; break; case Keyboard.SPACE: keyUp = true; //Set key press Shared Object world_so.setProperty(“keyUp”, true); break; }}

//BasicView Animation Loop override protected function onRenderTick(event:Event = null):void {

//Add force vector to sphere depending on Key press if(keyLeft) { helloSphere.addWorldForce(new JNumber3D(-moveForce, 0, 0), helloSphere.currentState.position); }

434 Chapter 11: Integrating the Flash Media Server

if(keyRight) {helloSphere.addWorldForce(new JNumber3D(moveForce, 0, 0), helloSphere.currentState.position); } if(keyForward) { helloSphere.addWorldForce(new JNumber3D(0, 0, moveForce), helloSphere.currentState.position); } if(keyReverse) { helloSphere.addWorldForce(new JNumber3D(0, 0, -moveForce), helloSphere.currentState.position); } if(keyUp) { helloSphere.addWorldForce(new JNumber3D(0, 2*moveForce, 0), helloSphere.currentState.position); }

//Execute rendering process physics.step();

//Target Spring Camera cameraTarget.copyPosition(sphereObject); renderer.renderScene(scene, springCamera, viewport); }}}

We ’ ve covered a great deal in this section, and if you want to learn more about FMS 3, here ’ s a tip.

A great book on the FMS has been written by William Sanders. This is Bill ’ s second book on Media servers, and compared to previous books on this subject, his is the most up to date (on the FMS 3) and is fully OOP. Currently, it ’ s the most clearly written book on this subject.

Now if you want to run this program in Flash CS4, you ’ ve got a problem. Using the Flex Embed Tag in Flash If you ’ re a Flash programmer and you want to run this program in Flash (as opposed to running it as a Flex ActionScript project), you may be thinking that all you have to do is call it up using your document class, right? But you ’ ve got a problem. The ActionScript package uses the Embed statement to embed the skin of the earth. But Embed is a Flex element and doesn ’ t exist in Flash.

What do you do? Embed the Flex SWC file into your Flash application. This gives your Flash program access to the Flex Embed Tag. Here ’ s how you do it:

Go to Flash Publish settings, click on the Flash tab, click on Settings, click on the External library path, click on the Browse to SWC file and embed the Flex SWC into your Flash application, as shown in Figure 11.6.

435 Part III: Building Games and Websites

Figure 11-6

The Flash version of the Hello World application can be obtained from the book ’ s Chapter 11 download code.

With the eight simple steps (described in this section) it ’ s easy to convert any Jiglib application into a FMS application. But there ’ s one more issue that must be discussed, especially if you ’ re using your mouse to move 3D objects around the stage. That issue is unprojecting coordinates.

Unprojecting Coordinates As complicated as CGI coordinate systems can get, the good news is that PV3D coordinate systems are not. There are actually only two: Flash screen (x, y) coordinates, and x, y, z perspective projection (or world) coordinates. Your mouse is in screen coordinates and your objects are in world coordinates. So if you want to drag an object in 3D space using your mouse you ’ ve got to unproject your mouse ’ s coordinates.

Dragging Objects So what does unprojecting mean anyway? Unprojecting takes 2D screen coordinates, and returns back a vector in world coordinates. This is accomplished by shooting a ray from your camera to your mouse (x, y) coordinates. As you know the ray must be a straight line, you can invert the equation of a straight line in 3D space and calculate your unknown z - value for the mouse. But in the case of your mouse, you don ’ t have an x, y, z coordinate system that you ’ re referencing. So your returned x, y, z values are the difference between your camera and unprojected mouse coordinates. So you ’ re returning the difference (delta) between your mouse and camera, not your actual mouse coordinates.

//Find the x, y, z deltas for your unprojected mouse var ray:Number3D = camera.unproject(viewport.containerSprite.mouseX, viewport. containerSprite.mouseY); /*Add unprojected coordinates to your camera position to get world coordinates*/ ray = Number3D.add(ray, new Number3D(camera.x, camera.y, camera.z));

To get the actual mouse coordinate values in 3D space, just add your unprojected (delta) values to your camera position. Since these two points define a straight line (and you ’ ve already projected a ray into your world coordinate system) you now only need an intersection plane to drag an object in 3D space. PV3D has a special Plane3D class that allows you to find the intersection of a line with a plane. In the

436 Chapter 11: Integrating the Flash Media Server

code snippet below, you define your Plane3D (instance name planeToDragOn ) by using a normal vector to the plane and a point on the plane. Other ways exist in which to define a plane, such as using three points.

//Define dragging plane using Plane3D planeToDragOn = new Plane3D(new Number3D(0, 0, -1), new Number3D(0, 0, - startMousePos.z));

Once you have your Plane3D defined you can calculate the intersection point of your projected ray with your plane. Having this intersection point you can move your object to that position.

//Get camera 3D vertex var cameraVertex3D:Vertex3D = new Vertex3D(camera.x, camera.y, camera.z); //Get ray 3D vertex var rayVertex3D:Vertex3D = new Vertex3D(ray.x, ray.y, ray.z);

var intersectPoint:Vertex3D = planeToDragOn.getIntersectionLine(cameraVertex3D, rayVertex3D);

dragConstraint.worldPosition = new JNumber3D(intersectPoint.x, intersectPoint.y, intersectPoint.z);

The unproject method was written Andy Zupko (at http://blog.zupko.info/ ), and is used extensively in PV3D - Jiglib applications.

Building a Race Track Racing cars on a track was once a very popular hobby. You can still buy slot car racing games for your kids, but in comparison to the old 10 - inch cars of the 1970s the new 2 - inch cars of today can be disappointing. But don ’ t fret; you can relive the experience virtually! So how does a slot car racing game work?

You place two cars on a track and control their speeds with an electronic plunger. The car that gets to the finish line first (after a designated number of laps) wins. If your car goes too fast in a turn it can fly or spin off the track. It then needs to be reset to continue the race, as shown in Figure 7.11.

Figure 11-7

437 Part III: Building Games and Websites

In the rest of this chapter, you develop the base FMS code required to create such a game with a discussion on how to turn this base code into a multiplayer racing game. The multiplayer game is fully developed on the book ’ s website.

Creating a Track Creating even a simple slot car racing game takes some thought. In two dimensions, it ’ s not that hard, but as you move into higher dimensions, drawing paths in space becomes more difficult. Not only do you need to be able to draw a path in 3D, you need to be able to navigate it. That means putting together parameterized splines.

A great place to learn more about parameterized splines is from The Algorithmist (at http:// algorithmist.wordpress.com/). The blog demonstrates how to build tracks (or parameterized polynomial fitted splines) using open source Degrapha for both cubic and Bezier splines fits. The Algorithmist uses knot theory to create fitted curves in 2D and Flex, which can be easily extended to multi - dimensions.

In this case, you ’ re actually creating two tracks: a graphical representation for your user to see and a mathematical path that exactly matches the graphical one that your car will follow. Just think of the Pong game from the previous chapter. Your court (what your user saw) was drawn using the Arena class and the ball was kept in bounds using an invisible bounding box. In the same way, you create a Track class for your user to see, and a virtual track for your car to follow. Constructing a Visible Track Class Building a successful track requires that you parameterize it. And what does that mean? Basically, you have one variable and as it changes you ’ re pushed along your curve (no matter how complex it may be). Complex curves can be segmented in space using knots (or points). The curves are fitted between these knots using a curve fitting scheme such as cubic or Bezier fitting.

In Figure 11.8, your race track is a simple pill shape. But even a simple pill shape has four knots with four separate curves to be fitted: two straight - line fits between knots 1 & 2 and knots 3 & 4 and two semi - circle fits between knots 2 & 3 and knots 4 & 1.

knot 4 knot 3

knot 1 knot 2 Figure 11-8

438 Chapter 11: Integrating the Flash Media Server

The first order of business is to create a Track class that draws a graphical interface that your user will see. In PV3D this is best done using the Lines3D class. As shown in Figure 11 - 8, the interface is just a pill - shaped dashed - line figure. You draw these lines using the addNewSegmentedLine method found the Lines3D class.

addNewSegmentedLine(size, segments, x0, y0, z0, x1, y1, z1)

The addNewSegmentedLine method has eight parameters: the first two are size and segments and the next six are the starting and ending 3D coordinates. The secret to drawing the dashed lines is to use the modulo method (one of the most useful methods in AS3 programming).

Making a Dash The secret to creating a segmented line is to use the modulo function. For example, consider the top semi - circle of your pill - shaped figure. To draw it use the following code snippet:

//Draw upper half Circle var prevKnotX1Cir:Number=knotRadius; var prevKnotY1Cir:Number=knot2.y; //Iterate over half circle for(var knotPos1:uint=0;knotPos1 < 40;knotPos1++){ var knotX1Cir:Number= knotRadius*Math.cos(Math.PI*knotPos1/40); var knotY1Cir:Number= knotRadius*Math.sin(Math.PI*knotPos1/40)+knot2.y; //Use modulo trick to skip lines and make strips if(knotPos1%2==1){ addNewSegmentedLine(4,1,prevKnotX1Cir, -2, prevKnotY1Cir, knotX1Cir, -2, knotY1Cir); } //Store previous value for next iteration starting point prevKnotX1Cir=knotX1Cir; prevKnotY1Cir=knotY1Cir;

}

After defining the semi - circle radius and y - position, you tick through 40 line segments, drawing them end - to - end using a previous variable to pick up the beginning coordinates of your next segment. But this draws a continual line. To draw a dashed line you need to skip every other line. Using the following modulo snippet (knotPos1%2 ) you can determine if your segment is on an odd or even count. So in the code snippet above you only draw on the odd count, giving you a dashed line.

The modulo is an easy concept. Essentially it returns the remainder of a divisor. So whenever you need to cycle over any base number system you just take the base modulo of the parameter to be cycled over. Whenever you reach the base you get zero and the cycle starts all over again.

439 Part III: Building Games and Websites

The remainder of the Track class draws the lower semi - circle and two straight line segments using the same technique discussed above. The entire documented class is given here:

package org.lively3d.gameclasses { //Line imports import org.papervision3d.core.geom.Lines3D; import org.papervision3d.core.math.Number2D; import org.papervision3d.materials.special.LineMaterial; //Application uses addNewSegmentLine and knots to draw track public class Track extends Lines3D { public function Track(knot1:Number2D, knot2:Number2D, knot3:Number2D, knot4: Number2D, knotRadius:Number) { //Set line color and Thickness super(new LineMaterial(0x998800,1)); //Draw track from four knots and knot radius drawTrack(knot1,knot2,knot3,knot4, knotRadius); } //Draw Track constructor function public function drawTrack(knot1:Number2D, knot2:Number2D, knot3:Number2D, knot4: Number2D, knotRadius:Number): void { //Draw upper half circle var prevKnotX1Cir:Number=knotRadius; var prevKnotY1Cir:Number=knot2.y; //Iterate over half circle for(var knotPos1:uint=0;knotPos1 < 40;knotPos1++){ var knotX1Cir:Number= knotRadius*Math.cos(Math.PI*knotPos1/40); var knotY1Cir:Number= knotRadius*Math.sin(Math.PI*knotPos1/40)+knot2.y; //Use modulo trick to skip lines and make strips if(knotPos1%2==1){ addNewSegmentedLine(4,1,prevKnotX1Cir, -2, prevKnotY1Cir, knotX1Cir, -2, knotY1Cir); } //Store previous value for next iteration starting point prevKnotX1Cir=knotX1Cir; prevKnotY1Cir=knotY1Cir;

} //Draw lower half circle var prevKnotX3Cir:Number=-knotRadius; var prevKnotY3Cir:Number = knot4.y; //Iterate over half circle for(var knotPos2:uint=0;knotPos2 < 40;knotPos2++){ var knotX3Cir:Number= knotRadius*Math.cos(Math.PI*knotPos2/40+Math.PI); var knotY3Cir:Number= knotRadius*Math.sin(Math.PI*knotPos2/40+Math.PI)+knot4.y; //Use modulo trick to skip lines and make strips

440 Chapter 11: Integrating the Flash Media Server

if(knotPos2%2==1){ addNewSegmentedLine(4,1,prevKnotX3Cir, -2, prevKnotY3Cir, knotX3Cir, -2, knotY3Cir); } //Store previous value for next iteration starting point prevKnotX3Cir = knotX3Cir; prevKnotY3Cir = knotY3Cir; }

//Draw Straight Line Segments //var prevKnotX1Lin:Number = knot1.x; var prevKnotY1Lin:Number = knot1.y; //var prevKnotX2Lin:Number = knot3.x; var prevKnotY2Lin:Number = knot3.y; //Iterate over segments for(var knotPos3:uint=0;knotPos3 < 100;knotPos3++){

var delta1:Number=(knot2.y-knot1.y)/100; var delta2:Number=(knot4.y-knot3.y)/100;

var goHereY1:Number=delta1*knotPos3+knot1.y; var goHereY2:Number=delta2*knotPos3+knot3.y; //Use modulo trick to skip lines and make strips if(knotPos3%2==1){ addNewSegmentedLine(4,1,knot1.x, -2, prevKnotY1Lin, knot1.x, -2, goHereY1); addNewSegmentedLine(4,1,knot3.x, -2, prevKnotY2Lin, knot3.x, -2, goHereY2); } //Store previous value for next iteration starting point prevKnotY1Lin=goHereY1; prevKnotY2Lin=goHereY2; }}}}

Once your track class is completed all you need to do is instantiate it, feed in the appropriately sized knots and knotRadius , and use addChild to have it create your graphical track interface.

//Add Track myTrack=new Track(knot1, knot2, knot3, knot4, knotRadius); scene.addChild(myTrack);

After drawing your track, your next order of business is to create a virtual parameterized path that your car will follow. Along this parameterized path different things can happen to your car, such as wrecking in a turn. Constructing a Virtual Highway Being parameterized and pieced together means that your curve begins and ends at the same point as your parameter ticks through its entire value. Then use the modulo method to start your curve cycle all over again.

441 Part III: Building Games and Websites

You create the virtual path in the trackUpdate method, which is composed of a switch statement consisting of four cases:

❑ Case 0 — first line segment (from knot 1 to knot 2) created using a straight line function. ❑ Case 1 — first semi - circle (from knot 2 to knot 3) created using the circular sine and cosine functions. ❑ Case 2 — second line segment (from knot 3 to knot 4) created using a straight line function. ❑ Case 3 — second semi - circle (from knot 4 to knot 1) created using the circular sine and cosine functions.

The trackUpdate method takes one value -param . The param variable is the single parameter that iterates you through your curve. From this parameter you can determine the appropriate case that you should be on and where on the particular curve for that case you should be. The knotSeg variable gives the case (using the floor method) and the knotPos variable gives the curve position (using the modulo).

var knotSeg:int=Math.floor(param/paraLength); var knotPos:Number=param%paraLength;

Using these two variables takes your car to the appropriate spline and to the correct position on that spline. In addition to the correct position, your car is also given an orientation on the curve using the myCarAngle parameter.

The rest of the trackUpdate method centers on having a wreck.

Having a Wreck To have a wreck in this game you ’ ve got to remember all the way back to the 1970s when track racing was really popular. Your car could fly off the track, but that seldom happened. Typically, your car would spin out in a curve and slow down (and at worst need to be reset on the track). In comparison to today ’ s games, it may not sound very exciting, but back in the 70s it was the only game in town – literally; this was before arcade games (and pool was considered immoral!).

To recreate this virtually, you ’ ve got to do three things: spin your car, stop acceleration, and slow down to a minimum restart speed. Spinning out is easy enough and can only occur in a curve that ’ s created in case 1 or 3 of the switch statement.

If your car goes beyond a certain speed in a curve (7.8) a wrecking sound is played and the doFast Boolean is set to false . This Boolean value disables your up arrow key and lets your car slow to a certain value before being reset to true (allowing you to accelerate again).

if(changeParam > 7.8){ //Play wreck sound and set wreck Boolean (new skidSound() as Sound).play(); doFast=false; }

In addition, when doFast is set to false, your car angle is set to - .01 , causing it to spin out as it slows. The experience looks pretty cool with the sound effect and spring camera in play. Almost topping the experience of the 70s!

442 Chapter 11: Integrating the Flash Media Server

if(doFast){ myCarAngle=-Math.PI*knotPos/paraLength; }else{myCarAngle=-.01;}

The slowing during the spinout, besides looking cool, serves the purpose of allowing your competitor to gain the lead. Therefore in a 25 - lap race for example, the winner is the car who goes the fastest and spins out the least. The winning strategy was to speed up on the straights and slow in the curves. The entire trackUpdate method is listed below:

public function trackUpdate(param:Number):void { //Set Knot and curve parameter var knotSeg:int=Math.floor(param/paraLength); var knotPos:Number=param%paraLength;

switch(knotSeg) { //Create Segment 1 (straight line) case 0: //Knot x and y var knotX0Lin:Number= knotPos*(knotArray[1].x-knotArray[0].x)/ paraLength+knotArray[0].x; var knotY0Lin:Number= knotPos*(knotArray[1].y-knotArray[0].y)/ paraLength+knotArray[0].y; myKnotPt = new Point(knotX0Lin,knotY0Lin); //if no wreck orient car to curve angle if(doFast){ myCarAngle=0; }else{myCarAngle=-.01;} if(changeParam > 6){ //Play Doppler racing sound if speed is greater than 6 (new passSound() as Sound).play(); } //Orient car to track steerFR.rotationY = 0; steerFL.rotationY = 0; car.rotationZ=0;

break;

//Create Segment 2 (180 degree curve) case 1: //Knot x and y var knotX1Cir:Number= knotRadius*Math.cos(Math.PI*knotPos/paraLength); var knotY1Cir:Number= knotRadius*Math.sin(Math.PI*knotPos/paraLength)+knotArray[1].y; myKnotPt = new Point(knotX1Cir,knotY1Cir); //if no wreck turn car to curve angle if(doFast){ myCarAngle=-Math.PI*knotPos/paraLength; }else{myCarAngle=-.01;} steerFR.rotationY = 25; steerFL.rotationY = 25; (continued)

443 Part III: Building Games and Websites

(continued) if(changeParam > 7.8){ //Play wreck sound and set wreck Boolean (new skidSound() as Sound).play(); doFast=false; } break;

//Create Segment 3 (Straight Line) case 2: //Knot x and y var knotX2Lin:Number= knotPos*(knotArray[3].x-knotArray[2].x)/ paraLength+knotArray[2].x; var knotY2Lin:Number= knotPos*(knotArray[3].y-knotArray[2].y)/ paraLength+knotArray[2].y;

myKnotPt = new Point(knotX2Lin,knotY2Lin); //if no wreck orient car to curve angle if(doFast){ myCarAngle=-Math.PI; }else{myCarAngle=-.01;} steerFR.rotationY = 0; steerFL.rotationY = 0; break;

//Create Segment 4 (180 degree curve) case 3: //Knot x and y var knotX3Cir:Number= knotRadius*Math.cos(Math.PI*knotPos/paraLength+Math.PI); var knotY3Cir:Number= knotRadius*Math.sin(Math.PI*knotPos/paraLength+Math. PI)+knotArray[3].y; myKnotPt = new Point(knotX3Cir,knotY3Cir); //if no wreck turn car to curve angle if(doFast){ myCarAngle=-Math.PI*knotPos/paraLength+Math.PI; }else{myCarAngle=-.01;} steerFR.rotationY = 25; steerFL.rotationY = 25; car.rotationZ=0;

//Create wreck if velocity is greater than 7.8 if(changeParam > 7.8){ //Play wreck sound and set wreck Boolean (new skidSound() as Sound).play(); //There has been a wreck, don’t accelerate doFast=false; } break;

default: trace(“Not in data”); break; } }

444 Chapter 11: Integrating the Flash Media Server

Two important resources used in development of this game were the Wii car discussed in the previous chapter, and royalty - free sounds obtained from Flash Kit.

In the Wii Server examples bundle, there ’ s a working car example (the car was originally created by Carlos Ulloa, the originator of PV3D). Flash Kit (at www.flashkit.com ) has a number of royalty - free sounds and loops available.

To transmit your racing game to a connected user you use an RSO.

Adding the FMS to Your Slot Car To run this game you only have to press one key – the up arrow – for acceleration. It ’ s a little anticlimactic after the previous chapter on the Wii controller, which offered a multitude of options. But that ’ s all you could do in track games of the 70s – accelerate – and when you let up on the spring control your car slowed down to a stop. So to implement the FMS all you need to communicate is this key press info through your remote shared object.

Figure 11-9

Figure 11.9 shows two synchronized cars using a single shared object property ( keyForward ). Whenever the upkey is pressed or released the world_so setProperty sets the keyForward Boolean to true or false as shown in the following code snippet:

//Key down acceleration private function keyDownHandler( event:KeyboardEvent ):void { switch( event.keyCode ) { case “W”.charCodeAt(): case Keyboard.UP: keyForward = true; event.updateAfterEvent(); world_so.setProperty(“keyForward”, true); break;

} } (continued)

445 Part III: Building Games and Websites

(continued) //Key up de-acceleration private function keyUpHandler( event:KeyboardEvent ):void { switch( event.keyCode ) { case “W”.charCodeAt(): case Keyboard.UP: keyForward = false; event.updateAfterEvent(); world_so.setProperty(“keyForward”, false); break; }}

Whenever the keyForward slot is altered the synch event is fired, which transmits the change to all connected users.

// update clients when the shared object changes private function syncHandler(event:SyncEvent):void { // when a synch event is fired // update the position of the ball for (var cList:uint; cList< event.changeList.length; cList++) { if(event.changeList[cList].code==”change”) { switch (event.changeList[cList].name) { //Grab Keys case “keyForward”: keyForward = world_so.data.keyForward; break; }}}}

Using the changeList code property, the change is only accepted by the connected user that didn ’ t make the change. The entire documented code is listed here:

//------// Flash Imports //------import flash.display.Bitmap; import flash.display.StageQuality; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.geom.Point; import flash.text.TextField; import flash.ui.Keyboard; import flash.utils.ByteArray; import flash.utils.Timer; import flash.media.*; import flash.ui.*; import flash.utils.*;

446 Chapter 11: Integrating the Flash Media Server

//------// Flash Media Imports //------import flash.events.NetStatusEvent; import flash.events.SyncEvent; import flash.net.NetConnection; import flash.net.SharedObject; import flash.ui.Keyboard;

//------// Papervision3D and Lively Imports //------import org.lively3d.gameclasses.Track; import org.lively3d.jiglib.Skybox; import org.papervision3d.cameras.SpringCamera3D; import org.papervision3d.core.clipping.FrustumClipping; import org.papervision3d.core.geom.Lines3D; import org.papervision3d.core.geom.renderables.*; import org.papervision3d.core.math.Number2D; import org.papervision3d.core.math.Number3D; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.special.LineMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.parsers.DAE; import org.papervision3d.view.BasicView; import org.papervision3d.view.layer.ViewportLayer; import org.papervision3d.view.layer.util.ViewportLayerSortMode;

[SWF(width=”800”, height=”600”, backgroundColor=”#ffffff”, frameRate=”30”)] public class DrivingACar3 extends BasicView {

//------// Properties //------

//Spring Camera private var springCamera:SpringCamera3D; //Key Forward Boolean private var keyForward:Boolean = false;

//Track private var myTrack:Track;

//Track Knots private var knotArray:Array; private var knot1:Number2D; private var knot2:Number2D; (continued)

447 Part III: Building Games and Websites

(continued) private var knot3:Number2D; private var knot4:Number2D;

//Track Parameters private var myKnotAngle:Number; private var knotRadius:Number private var sizeWidth:Number=8; private var sizeDepth:Number=10; private var paraLength:Number=100; private var newSize:Number=1; private var myKnotPt:Point; private var myCarAngle:Number;

//Track Travel Parameters private var paramOsc:Number=0; private var changeParam:Number=0;

//Skybox private var mySkybox:Skybox = new Skybox();

//Wreck parameter-turns of acceleration private var doFast:Boolean=true;

//Flash Comm Server private var world_so:SharedObject; private var nc:NetConnection;

//Flash Synch Timer private var syncTimer:Timer;

//------// Model Asset Embed //------[Embed(source=”assets/dae/Focus.dae”, mimeType = “application/octet-stream”)] private var carAsset:Class;

[Embed(source=”/assets/wheel.jpg”)] private var wheelBitmapAsset:Class;

[Embed(source=”/assets/body1.jpg”)] private var bodyBitmapAsset:Class;

[Embed(source=”/assets/grassTexture.jpg”)] private var grassTextureBitmapAsset:Class;

//Model private var car:DAE; //Materials private var carMaterials:MaterialsList;

//Car Display Objects private var steerFR:DisplayObject3D; private var steerFL:DisplayObject3D;

448 Chapter 11: Integrating the Flash Media Server

// Rotate wheels private var wheelFR:DisplayObject3D; private var wheelFL:DisplayObject3D; private var wheelRR:DisplayObject3D; private var wheelRL:DisplayObject3D;

//------// Sound Embed //------//Sound Assets [Embed(source=”assets/sound/GameIntro.mp3”)] public var introSound:Class; [Embed(source=”assets/sound/Passinglow.mp3”)] public var passSound:Class; [Embed(source=”assets/sound/Racingbackgnd2.mp3”)] public var raceSound:Class; [Embed(source=”assets/sound/Skidstop.mp3”)] public var skidSound:Class; [Embed(source=”assets/sound/StartCar.mp3”)] public var startSound:Class; [Embed(source=”assets/sound/win.mp3”)] public var winSound:Class;

//Sound Channel private var sound:Sound= new Sound(); private var soundControl1:SoundChannel=new SoundChannel(); private var soundControl2:SoundChannel=new SoundChannel(); private var soundControl3:SoundChannel=new SoundChannel();

//------// Game Constructor Function //------public function DrivingACar3() { super(); //Set up car scene, stage quality, listeners, and track setupScene(); stage.quality = StageQuality.LOW; initListeners(); initTrack(); //Start Game Sound myGameState(1);

//Flash Media Server Code //Create a net connection nc=new NetConnection; nc.connect(“rtmp://localhost/DrivingACar3”); nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); }

// Handle connection events public function netStatusHandler(event:NetStatusEvent):void (continued)

449 Part III: Building Games and Websites

(continued) { trace(“connected is: “ + nc.connected ); trace(“event.info.level: “ + event.info.level); trace(“event.info.code: “ + event.info.code);

switch (event.info.code) { case “NetConnection.Connect.Success”: trace(“Congratulations! you’re connected”);

//Shared Object world_so=SharedObject.getRemote(“world”, nc.uri, false); world_so.connect(nc); world_so.addEventListener(SyncEvent.SYNC, syncHandler); syncTimer.start(); break; case “NetConnection.Connect.Rejected”: case “NetConnection.Connect.Failed”: trace (“Oops! you weren’t able to connect”); break; } }

// Update clients when the shared object changes private function syncHandler(event:SyncEvent):void { // When a synch event is fired // Update the position of the ball for (var cList:uint; cList< event.changeList.length; cList++) { if(event.changeList[cList].code==”change”) { switch (event.changeList[cList].name) { //Grab Key case “keyForward”: keyForward = world_so.data.keyForward; break; }}}}

//------// Sound Switch case for Game States //------

private function myGameState(gameState:int):void{ switch(gameState) { case 0: //Start State soundControl1=(new introSound() as Sound).play(0,1); break; case 1: //Play State soundControl1.stop();

450 Chapter 11: Integrating the Flash Media Server soundControl2=(new raceSound() as Sound).play(0,100); break; case 2: //Win State soundControl2.stop(); (new winSound() as Sound).play(); break; case 3: //Start Car soundControl2.stop(); (new startSound() as Sound).play(); break; case 4: //Restart State break; default: trace(“Not in data”); break; }}

//------// Initiate Track //------private function initTrack():void{

//Track Trial Values knot1=new Number2D( sizeWidth*40,-sizeDepth*60); knot2=new Number2D( sizeWidth*40, sizeDepth*60); knot3=new Number2D(-sizeWidth*40, sizeDepth*60); knot4=new Number2D(-sizeWidth*40,-sizeDepth*60); //param goes from zero to 100 each knot param over 25 knotArray=[knot1, knot2, knot3, knot4] myKnotAngle = Math.atan2(knotArray[1].y, knotArray[1].x); for(var i:int=0; i < 4;i++){ knotArray[i].x=knotArray[i].x*newSize*Math.cos(myKnotAngle); knotArray[i].y=knotArray[i].y*newSize*Math.sin(myKnotAngle); } //Track radius knotRadius= Math.abs(knotArray[1].x);

//Add Track myTrack=new Track(knot1, knot2, knot3, knot4, knotRadius); scene.addChild(myTrack); //Add Skybox scene.addChild(mySkybox); }

//------// Set Up Car Scene //------private function setupScene():void { (continued)

451 Part III: Building Games and Websites

(continued) //Spring Camera springCamera = new SpringCamera3D(); springCamera.mass = 10; springCamera.damping = 10; springCamera.stiffness = 2; springCamera.lookOffset = new Number3D(0, 0, 10); springCamera.positionOffset = new Number3D(0, 5, -15); springCamera.near = -1000;

//Setup materials for the object var wheelBitmap:Bitmap = new wheelBitmapAsset() as Bitmap; var bodyBitmap:Bitmap = new bodyBitmapAsset() as Bitmap; var bodyMaterial:BitmapMaterial = new BitmapMaterial(bodyBitmap.bitmapData); bodyMaterial.smooth = true; var wheelMaterial:BitmapMaterial = new BitmapMaterial(wheelBitmap.bitmapData); wheelMaterial.smooth = true;

//Wheel and Body Materials List carMaterials = new MaterialsList(); carMaterials.addMaterial(bodyMaterial, “materialBody”); carMaterials.addMaterial(wheelMaterial, “materialWheel”);

// Load Collada var byteArray:ByteArray = new carAsset() as ByteArray; car = new DAE(true); car.load(byteArray, carMaterials); car.scale = 10; car.useClipping = false;

// Steer front wheels steerFR = car.getChildByName( “Steer_FR”, true ); steerFL = car.getChildByName( “Steer_FL”, true );

// Rotate wheels wheelFR = steerFR.getChildByName( “Wheel_FR”, true ); wheelFL = steerFL.getChildByName( “Wheel_FL”, true ); wheelRR = car.getChildByName( “Wheel_RR”, true ); wheelRL = car.getChildByName( “Wheel_RL”, true ); //Set Spring camera car target springCamera.target = car; //Add Car to your scene scene.addChild(car); }

//------// Game Animation Loop //------private function gameLoop(event:Event):void { //Execute if you haven’t wrecked if(doFast){ //Increase car velocity if(keyForward){changeParam+=.1;

452 Chapter 11: Integrating the Flash Media Server if(changeParam > 8){changeParam=8;}

}else{ //Decrease Car velocity changeParam-=.05; if(changeParam < 0){changeParam=0;} } //If wrecked decrease car velocity }else{changeParam-=.1; //Once velocity is below 1 allow acceleration again if(changeParam < 1){doFast=true;} } //Track parameter paramOsc+=changeParam; paramOsc=paramOsc%400; //Update Track based on parameter value trackUpdate(paramOsc); //Place car on x, y knot position car.x= (myKnotPt.x); car.z= (myKnotPt.y); //Rotate car from update knot position car.rotationY=myCarAngle*180/Math.PI;

//Add Velocity to wheels-hack wheelFR.roll(-40*Math.PI*100/400 ); wheelRR.roll(-40*Math.PI*100/400 ); wheelFL.roll( 40*Math.PI*100/400 ); wheelRL.roll( 40*Math.PI*100/400 );

//Render Animation renderer.renderScene(scene, springCamera, viewport); }

//------// Key Listeners //------//Key down acceleration private function keyDownHandler( event:KeyboardEvent ):void { switch( event.keyCode ) { case “W”.charCodeAt(): case Keyboard.UP: keyForward = true; event.updateAfterEvent(); world_so.setProperty(“keyForward”, true); break; } }

//Key up de-acceleration private function keyUpHandler( event:KeyboardEvent ):void {switch( event.keyCode ) (continued)

453 Part III: Building Games and Websites

(continued) { case “W”.charCodeAt(): case Keyboard.UP: keyForward = false; event.updateAfterEvent(); world_so.setProperty(“keyForward”, false); break; } }

//------// Track Updater //------

public function trackUpdate(param:Number):void { //Set Knot and curve parameter var knotSeg:int=Math.floor(param/paraLength); var knotPos:Number=param%paraLength;

switch(knotSeg) { //Create Segment 1 (straight line) case 0: //Knot x and y var knotX0Lin:Number= knotPos*(knotArray[1].x-knotArray[0].x)/ paraLength+knotArray[0].x; var knotY0Lin:Number= knotPos*(knotArray[1].y-knotArray[0].y)/ paraLength+knotArray[0].y; myKnotPt = new Point(knotX0Lin,knotY0Lin); //If no wreck orient car to curve angle if(doFast){ myCarAngle=0; }else{myCarAngle=-.01;} if(changeParam > 6){ //Play Doppler racing sound if speed is greater than 6 (new passSound() as Sound).play(); } //Orient car to track steerFR.rotationY = 0; steerFL.rotationY = 0; car.rotationZ=0; break;

//Create Segment 2 (180 degree curve) case 1: //Knot x and y var knotX1Cir:Number= knotRadius*Math.cos(Math.PI*knotPos/paraLength); var knotY1Cir:Number= knotRadius*Math.sin(Math.PI*knotPos/paraLength)+knotArray[1].y; myKnotPt = new Point(knotX1Cir,knotY1Cir); //If no wreck turn car to curve angle if(doFast){

454 Chapter 11: Integrating the Flash Media Server myCarAngle=-Math.PI*knotPos/paraLength; }else{myCarAngle=-.01;} steerFR.rotationY = 25; steerFL.rotationY = 25;

//Create a wreck if velocity is greater than 7.8 if(changeParam > 7.8){ //Play wreck sound and set wreck Boolean (new skidSound() as Sound).play(); doFast=false; } break;

//Create Segment 3 (Straight Line) case 2: //Knot x and y var knotX2Lin:Number= knotPos*(knotArray[3].x-knotArray[2].x)/ paraLength+knotArray[2].x; var knotY2Lin:Number= knotPos*(knotArray[3].y-knotArray[2].y)/ paraLength+knotArray[2].y; myKnotPt = new Point(knotX2Lin,knotY2Lin); //if no wreck orient car to curve angle if(doFast){ myCarAngle=-Math.PI; }else{myCarAngle=-.01;} steerFR.rotationY = 0; steerFL.rotationY = 0; break;

//Create Segment 4 (180 degree curve) case 3: //Knot x and y var knotX3Cir:Number= knotRadius*Math.cos(Math.PI*knotPos/paraLength+Math.PI); var knotY3Cir:Number= knotRadius*Math.sin(Math.PI*knotPos/paraLength+Math. PI)+knotArray[3].y; myKnotPt = new Point(knotX3Cir,knotY3Cir); //If no wreck turn car to curve angle if(doFast){ myCarAngle=-Math.PI*knotPos/paraLength+Math.PI; }else{myCarAngle=-.01;} steerFR.rotationY = 25; steerFL.rotationY = 25; car.rotationZ=0;

//Create wreck if velocity is greater than 7.8 if(changeParam > 7.8){ //Play wreck sound and set wreck Boolean (new skidSound() as Sound).play(); //There has been a wreck don’t accelerate doFast=false; }

(continued)

455 Part III: Building Games and Websites

(continued) break; default: trace(“Not in data”); break; } }

//------// Listeners //------public function initListeners():void { //Start Game Animation Loop addEventListener(Event.ENTER_FRAME, gameLoop); //Set Keyboard Listeners stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler ); stage.addEventListener( KeyboardEvent.KEY_UP, keyUpHandler ); }}}

The key strategy in building FMS - PV3D games is to let the client - side application handle as much of the physics and motion as possible. Keep the data rate down and only transmit necessary information. In the case above, the synching worked really well with just the transmission of a single Boolean value. And control between users can be easily passed back and forth. But every once in a while other data needs to be transmitted to ensure that everything is in synch – that ’ s where programming becomes an art.

Turning Your Code into a Game Turning the application above into a two slot racing game requires that you double the application up. Create two cars, two tracks, and two shared objects, a car selection system, and a scoring system. When the user logs in he selects the car he will race. That sends out a shared object that keeps the other connected user from selecting the first user ’ s car. The other user selects an unselected car and the race begins. Whoever goes the fastest and crashes the least for a prescribed number of laps – wins the game!

Also try adding a couple of webcams so you can web chat with your opponent as you race. Check out an update on this game on the author ’ s book site.

FMS Alternatives If you have a small budget, don ’ t despair, there are alternatives to purchasing an expensive server account (or server software). The web is full of open source solutions. Two great places to look for them are Google Code (at http://code.google.com/ ) and Open Source Flash (at http://osflash.org/ ). And yes, there ’ s a completely free version of the Flash Media Server called Red 5. Red 5 was created in response to Macromedia ’ s pricing model and what seemed like a broken promise of unlimited bandwidth by Adobe (with the release of the Flash Communication Server 2).

456 Chapter 11: Integrating the Flash Media Server

Currently, the FMS 3 offers unlimited bandwidth and most likely the inability of Adobe to deliver unlimited bandwidth (as it transitioned the server platform from Macromedia) was one of a technology barrier rather than a broken promise. But out of the community ’ s frustration arose Red 5 (at http:// osflash.org/red5 ). Red 5 is the Java version of the Flash Media Server.

Red 5 Having a completely free media server tailored for the Flash player is a dream come true for the Flash development community. Leveraging powerful open source technology like Red 5 can really give you an advantage on the web. But how do you learn how to use it?

In addition to visiting the Red 5 site and poring through all the documentation, John Grden (one of the PV3D core team members at http://rockonflash.wordpress.com/) has written an excellent applications chapter on the Red 5 in the book Open Source Flash Development. It ’ s a great place to start. And in the book, John demonstrates how to get Red 5 running in Eclipse. If you ’ re a Linux or Java programmer, you ’ ll love Red 5.

From Red 5 arose Wowza (at http://www.wowzamedia.com/ ), a very affordable version of the Flash Media Server with a totally awesome support team that helps you get up and running (something missing from both Red 5 and Adobe ’ s FMS).

Wowza Wowza, like the FMS, offers a 10 - user- free development license. It costs about 75 per cent less than the FMS and has a great support team that helps you get up and going. If you ’ re not ready for Red 5, start with Wowza. The Wowza server offers:

❑ Live and on - demand streaming, interactive audio/video/text chat, and remote recording. ❑ Plug - in Java architecture for extensibility and easy development of custom applications ❑ Scaleable, high-performance operation with load balancing and edge/origin support ❑ Robust anti- ripping and encryption for security and content protection

It ’ s more than just a Red 5 clone, and once you get rolling with Wowza you may never make the switch back to Red 5 or FMS.

Finally there ’ s a new kid on the block, Adobe Flash Collaboration Service (formerly known as Cocomo).

Adobe Flash Collaboration Service Adobe Flash Collaboration Service (formerly Cocomo) allows Flex developers to easily add real - time social capabilities into their RIA (Rich Internet Applications). It ’ s much like the FMS, but without the hassle of programming it. Many pre - built RIA ’ s are designed to get you into social network programming, without the complex programming typically required.

457 Part III: Building Games and Websites

The platform features:

❑ VoIP audio, webcam video, and chat ❑ Multi - user whiteboards and real - time file sharing ❑ User management, roles and permissions, and robust data messaging

All of this – without the hassle of trying to program it from scratch using the FMS. In addition, Acrobat .com hosts the service, eliminating issues like deployment, maintenance, and scaleability. Check it out at Adobe labs (at http://labs.adobe.com/technologies/afcs/ ). In addition, there ’ s a ton of sample code released with the product to help you get started.

Summary Creating Rich Internet Applications (RIAs) has long been the goal of Macromedia (now Adobe). An integral part of that has been using the Flash Media Server to create interactive web experiences. In this chapter, you learned how to get your users interacting with 3D objects using remote shared objects. You also created the starter code for a slot car racing game. Finally, you examined alternatives to the FMS such as Red 5, Wowza, and the Flash Collaboration Service.

As technology evolves, the use of open socket servers (such as the FMS) will become more widely used.

458 Developing 3D Websites

The coolest thing you can do in Flash is build 3D websites. But 3D is more than about being cool. It hooks into a cognitive process that accelerates learning, and makes possible the exploration of items typically limited to 2D. If you ’ re selling a car on the web with 3D, you can spin around it, and even get inside it and take it for a virtual test drive. And if your site has the virtual test drive and your competitor doesn ’ t, it ’ s not hard to guess who ’ ll most likely make the sale. Or if you ’ re teaching radiation safety, and you want to avoid watching toxic waste burn off your gloves in the real world, you can do it in the virtual world.

In this chapter, you examine a few helpful tips for creating 3D websites, convert an existing 2D website to 3D, and learn how to optimize your site for web search engines.

What You Need to Know In this section, you examine the why of 3D and a few reality checks that apply to working with 3D in Flash. Every true “ flashmatician ” gets excited about the potential of creating ubiquitous 3D content for the web. But once the enthusiasm dies down, there are a number of issues that you need to examine in order to save time and frustration when developing in 3D.

Why Use 3D Why use 3D? Because it ’ s all about learning!

Many educators regard using 3D in the academic arena as overkill. They ’ d rather plug their students into lifeless 2D learning management systems, whose learning content rests solely on endless threaded discussion boards. Such educators have missed a fundamental aspect of the human brain – the visual cortex. It ’ s the most efficient information processor in your entire body. And it ’ s fueled by depth perception – or 3D! Part III: Building Games and Websites

If you want to inject information into your client rapidly, do it visually! But it ’ s not just visual stimulation that creates a good learning environment – you ’ ve got to tap into your client ’ s emotions, and key in feedback as needed. This is the essence of brain - based learning; an emotionally driven 3D visual experience. It has the highest learning impact on the brain. That ’ s why 3D RPGs (role playing games), like Second Life, have so much popularity on the web. They ’ ve tapped into their client ’ s emotionally driven visual cortex.

Building it Right in Flash OK – it ’ s time for a reality check. How much 3D can you really do in Flash? You can render about 2,000 vertices at a time! Sites that run sluggishly, take a long time to load (more than five seconds), or drive the processor too hard, run the risk of being avoided by impatient clients. When your vertex count gets above 2,000, things really start to slow down; especially when you ’ re animating objects. So what ’ s the right approach in Flash? A great example of how to do it right is Big and Small created by Plug - in Media. Big and Small is a comedy TV show for kids (adults will like it too). It ’ s about a character called Big, a character called Small, and despite their differences (being Big and Small) they ’ re best friends. So here ’ s the problem: if you try to create everything in 3D – animated characters, props, and rooms – you hit the animation wall and everything comes to a screeching halt. Even if you ’ ve got the coolest 3D graphics in the world it won ’ t make a difference, as it won ’ t run on the web.

So here ’ s the solution. Build your rooms (or environments) in 3D and turn the items that you put into those rooms into billboards (or point sprites).

What ’ s a point sprite? A point sprite in essence looks the same no matter what direction you observe it from. Take for example Small (a point sprite) jumping on his 3D bed in Figure 12.1. The bed ’ s vertices spring up and down giving it 3D realism while Small is a multiframe sprite that looks 3D, but isn ’ t. Using point sprites like Small is a huge economy saver on your CPU usage, and gives you a full 3D look. Here ’ s an important point. Small (though a 2D point sprite) is still in 3D. He has an x, y and z position. And your visual cortex loves that!

Figure 12-1

460 Chapter 12: Developing 3D Websites

You can read an entire write up on how Big and Small was created, on Seb Lee - Delisle ’ s blog (at http:// www.sebleedelisle.com/?p=455). This idea of using 3D graphics with a 2D sprite is one that you use for the remainder of this book.

Of course another approach is to do the opposite . . . create a few 3D characters and place 2D backgrounds behind them. You find this done a lot with animated cartoon shows. But as characters typically cost so much in vertex count, Seb ’ s approach is the more efficient of the two for PV3D.

Learning Photoshop It ’ s time for another reality check. If your clients like what they see, the graphics often carry more weight than program functionality when it comes to winning an initial bid. ActionScript OOP has created an ever - growing chasm between designer (the guy that does the graphics) and developer (the guy that does the coding).

If you ’ re a designer, you can skip to the next section, but if you ’ re a developer, listen up! Learn Photoshop; it takes you 90 per cent of the way when it comes to building dazzling sites, and saves you tons of money in the process. The fastest way to come up to speed in Photoshop is to use the Lynda.com tutorial video service, which costs about US$25 a month. The service offers an extensive video library of Photoshop tutorials that ’ ll have you up to speed in no time. It ’ s so easy to create incredible looking graphics in just a few clicks: don ’ t pay for them on the web or hire a designer when you can do it yourself.

And if you ’ re a designer (and didn ’ t skip to the next section), learn to code! The best place to go for that is the book ’ s website which hosts tons of tutorials on coding Flash and Flex in 3D (at www. professionalpapervision.com ). With your design talents and new - found coding skills the sky ’ s the limit. Most employers are looking for designers who are skilled in ActionScript as well.

While at Northern Kentucky University (NKU) the author has trained numerous graphic designers in Flash and Flex. The coding skills that they gained (in conjunction with their design skills) landed them great positions when they graduated. The key to surviving in this ever - changing technology quagmire is continual training. You should spend two hours a day minimum training and upgrading your skill sets . . . get in the habit of becoming a lifelong learner. You must actively keep pace or you ’ ll be left behind!

If you ’ re new to web programming, here ’ s an important tip:

Creating a Site Map It ’ s time for one more reality check! Create a design document that your clients sign off on. And don ’ t over extend yourself.

Once you ’ ve decided what your goals are and how you want your user to interact with your site, you can proceed to creating your site, right? Wrong! You ’ ve got to map it out. You ’ ll save yourself and your client hours of frustration, confusion, and possibly an angry parting by doing this first. You should spend at least 20 per cent of your website development in planning and layout. In the long run, doing so saves you hours and hours of work.

461 Part III: Building Games and Websites

Creating a site map can be an elaborate process. Tons of design engines are available out there that can assist you in this process. But what is a site map anyway? A site map details data requirements, page layout, and navigation control. The simplest way to get started is to take a look at your content first and match this content with your goals and expected user experience. Also make sure that your site meets the technical requirements of your user. For example, if you ’ re creating a site that will be viewed only on dial up (if there still is such a thing), you don ’ t want to be running video on it.

Here are a few tips:

❑ Make your initial page small – around 400k (this number gets larger every year). For 3D work this is your steepest requirement and not always obtainable. So you may need to add a storefront (or great preloader) before starting your full 3D app. ❑ Make sure that your targeted client is seeing something on his screen within the first five seconds that he logs into your site. Then kick in your preloaders for higher - end content. ❑ Make sure that your website fits the standard monitor 1,024x768 (this number gets larger every year as well), which means about 1,000x700, taking browser chrome into account. ❑ Avoid scrolling. Stats show that people don ’ t like to scroll. ❑ Make sure that the hierarchy of your site is clear and easy to navigate: branding, navigation, and content should all flow together. ❑ Keep your text legible and font consistent. Keep the body copy short for people who scan pages. You ’ ve got their attention for 30 seconds. ❑ Differentiate your buttons and hyperlinks. Also, indicate what clicking a link will do. ❑ Kick your site up a notch by making it webbot (SEO) friendly!

Finally, have some fun, surf the web a little and see what other people have done, and how they ’ ve treated similar content. It ’ s important to look at good examples. For PV3D a great place to go is Daily Papervision at http://dailypv3d.wordpress.com/ .

With a few reality checks under your belt, you ’ re now ready to build your first 3D website. You start with a real world example; converting a 2D website to a 3D website.

Converting CSIS to 3D In this section, you convert the CSIS (Center for Strategic and International Studies) 2D website into 3D. Specifically, you ’ ll be working with the Seven Revolutions content. You won ’ t build out the complete site in this chapter (that would actually take an entire book), but instead, you create a starter code that you can use to convert any site from 2D to 3D. Your conversion project centers around the Seven Revolutions initiative of the Global Strategy Institute found at http://gsi.csis.org/ .

462 Chapter 12: Developing 3D Websites

Seven Revolutions is a project led by the Global Strategy Institute at the Center for Strategic and International Studies (CSIS) to identify and analyze the key policy challenges that policymakers, business figures, and other leaders face, out to the year 2025. It ’ s an effort to promote strategic thinking on the long - term trends that too few leaders take the time to consider.

In exploring the world of 2025, CSIS have identified seven areas of change we expect to be most “ revolutionary ” :

❑ Population ❑ Resource management and environmental stewardship ❑ Technological innovation and diffusion ❑ The development and dissemination of information and knowledge ❑ Economic integration ❑ The nature and mode of conflict ❑ The challenge of governance

Each of these seven forces embodies both opportunity and risk in the years ahead. Together, they ’ ll transform the way you live and interact.

By far the most powerful tool at your disposal for rapidly creating stunning 3D websites is Flash CS4. With its native 3D tools and ability to integrate with 3D Photoshop – it can ’ t be beat! However, in this section, you rely completely on PV3D and AS3, as PV3D has the classes needed to do the job. But later in the book you use Flash CS4.

Making it 3D Converting sites to 3D often involves creating some type of 3D environment. You should start the process by carefully studying the site you ’ re going to convert, talking to your clients and doing a little story boarding (creating a design doc), and then by surfing the web to get some ideas. As mentioned earlier Daily Papervision is a great place to start when looking for ideas. And for this conversion the site LEDJAM was chosen as a model (at www.ledjam.com ) that ’ s shown in Figure 12.2.

Figure 12-2

463 Part III: Building Games and Websites

LEDJAM has a number of great characteristics that matched up with the goals of the CSIS conversation:

❑ 360 Cylindrical Collage of the World that goes well with the CSIS global strategy theme. ❑ Simple 2D graphics on planes distributed throughout a 3D environment that runs lighter on the CPU process. ❑ Simple Navigation Scheme that ’ s easy to make in PV3D using the “ extra ” property. ❑ Reflective Surface giving the site a great look and 3D feel. ❑ Cloud coverage and artistic plants that can be built into separate classes allowing for modulo distribution. ❑ Sliding environment that produces a thrilling ride for your visual cortex.

If you decompile the site (using SoThink) and look at how it was made, you ’ re going to find out that it ’ s easier just to build it yourself. Sites like LEDJAM are perfect for getting good ideas, but 100 per cent of the time it ’ s just easier to start from scratch, create your own graphics and sounds, and code framework. Most likely you ’ ll create something even better, but don ’ t expect it to be the same.

One of the easiest ways to make a site 3D is to use ReflectionView .

Using Reflection This is a trick that you ’ ve already used. Just by replacing BasicView with ReflectionView (and doing a little reordering) you get a reflective surface.

public class CSIS extends ReflectionView

We covered this procedure in the chapter on gaming when you created the Pong game. Reflection isn ’ t the only way to go; you can open up 3DSMax and build an environment such as a building, or mathematically lay primitives together for an interactive system. There are many options, but for now you stick with reflection to create that 3D look as shown in Figure 12.3.

Figure 12-3

Now that you have a reflective surface, you need to put something in it to reflect. Start by building a collage of the world.

464 Chapter 12: Developing 3D Websites

Making your Collage Making a collage in Photoshop can be easily done using masking and photo adjustments. The collage created for this website, shown in Figure 12.4, was built using masked image snippets from the web and Google warehouse models.

Figure 12-4

Once you ’ ve created your 360 - degree collage in Photoshop, you export it as a mip - mapped jpeg. In this case the dimensions were 4,096x256 pixels and optimized image size was 128k. That ’ s great compression for an image. Take your exported collage and put it in your assets/image folder.

You then build a cylinder class to hold your collage and bring it into your scene. Place your collage cylinder just above your reflective surface in order to produce a good reflection. Building your collage cylinder is very similar to building the skybox that you created in Chapter 10. But instead of extending your class with a Cube, you extend it using a Cylinder. Then embed the collage image and use the same technique as you did for the cube, to create a bitmap material to place on your cylinder.

In addition, you create a radius property that you can use to change the size of your cylinder.

public function SlidingCylinder(radius:Number)

A number of items depend on your radius, so it ’ s best to create a parameter that can be changed as opposed to hard coding its size in the class (as was done in the skybox class). The entire SlidingCylinder class is given here:

package org.lively3d.jiglib { //by Lively import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.primitives.Cylinder;

public class SlidingCylinder extends Cylinder { //Embed your image assets [Embed (source=”/./assets/image/backGround.jpg”)] private var BMFront: Class;

public function SlidingCylinder(radius:Number) { var bmFront: BitmapMaterial = new BitmapMaterial(new BMFront().bitmapData);

//Set your material to double side so you’ll see the inside of your skybox (continued)

465 Part III: Building Games and Websites

(continued) bmFront.doubleSided = true;

//Add your material, large sides, and low segments to your super method

var myHeight:Number= 2*Math.PI*radius*256/4096; super(bmFront,radius,myHeight,20,4,radius, false, false); y = myHeight/2-8;

}}}

Instantiating your class in your main program is easy. Just import it, instantiate it, and add it to your scene. Of course one line doesn ’ t follow after another as shown in the code below. You must place them in the appropriate places in your code.

import org.lively3d.jiglib.SlidingCylinder; private var myCylinder:SlidingCylinder = new SlidingCylinder(myRadius); scene.addChild(myCylinder);

You should notice that the SlideCylinder constructor class takes the myRadius parameter, which is set at the beginning of the code:

private var myRadius:Number=1200;

The radius is set only once at the beginning of the code and is used throughout by many different methods.

Making Clouds Using the Filter/Render/Difference Clouds option in Photoshop, you can create clouds as shown in Figure 12.5. You can also create some really cool effects using Perlin noise and the PS liquify tool. Once you ’ ve got your clouds looking the way you want, you can put them in your program.

Figure 12-5

466 Chapter 12: Developing 3D Websites

Writing the Cloud class is very similar to writing the SlidingCylinder class above. However in this case you extend the Cloud class with the DisplayObject3D class. This lets you stack several clouds in a display object and then add that display object to your scene (actually in this case to a sliding display object). After embedding, you load the cloud images into planes, which are distributed randomly across your display object. You also vary the y (height) a little, so if two planes overlap they show a little depth.

And here ’ s the cool part . . . you can add as many cloud - planes as you want. Just stick the desired number in your constructor function. The unlimited number of planes is obtained using the modulo trick.

Here ’ s how the trick works. Put all your cloud images in an array as follows:

var myCloud:Array=[cloud0, cloud1,cloud2, cloud3,cloud4, cloud5];

You can now reference your cloud images using an index of your array. But you only have six clouds (zero through five). So if you want nine clouds, just use the modulo method. When you get to six, it recycles back to zero and you start pulling your cloud images from the front of the list.

cloudPlane = new Plane(myCloud[i%6], 256, 256, 1, 1);

This way you can have as many clouds as you want randomly distributed across your scene. Remember that for each new cloud you create, your CPU has to work just a little harder though. The entire Cloud class is given here:

package org.lively3d.jiglib { //by Lively import org.papervision3d.materials.BitmapFileMaterial; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Plane;

public class Clouds extends DisplayObject3D { [Embed (source=”/./assets/cloud/cloud0.png”)] private var Cloud0: Class; [Embed (source=”/./assets/cloud/cloud1.png”)] private var Cloud1: Class; [Embed (source=”/./assets/cloud/cloud2.png”)] private var Cloud2: Class; [Embed (source=”/./assets/cloud/cloud3.png”)] private var Cloud3: Class; [Embed (source=”/./assets/cloud/cloud4.png”)] private var Cloud4: Class; [Embed (source=”/./assets/cloud/cloud5.png”)] private var Cloud5: Class;

private var cloudPlane:Plane; //Cloud constructor accepts radius and cloud Number public function Clouds(radius:Number, cloudNum:int) { (continued)

467 Part III: Building Games and Websites

(continued) var cloud0: BitmapMaterial = new BitmapMaterial(new Cloud0().bitmapData); cloud0.doubleSided=true; var cloud1: BitmapMaterial = new BitmapMaterial(new Cloud1().bitmapData); cloud1.doubleSided=true;

var cloud2: BitmapMaterial = new BitmapMaterial(new Cloud2().bitmapData); cloud2.doubleSided=true; var cloud3: BitmapMaterial = new BitmapMaterial(new Cloud3().bitmapData); cloud3.doubleSided=true; var cloud4: BitmapMaterial = new BitmapMaterial(new Cloud4().bitmapData); cloud4.doubleSided=true; var cloud5: BitmapMaterial = new BitmapMaterial(new Cloud5().bitmapData); cloud5.doubleSided=true; //Add your clouds to an array var myCould:Array=[cloud0, cloud1,cloud2, cloud3,cloud4, cloud5];

//Randomly distribute your clouds using the modulo method for(var i:int=0; i < cloudNum; i++) { cloudPlane = new Plane(myCloud[i%6], 256, 256, 1, 1); cloudPlane.x = radius-Math.random()*2*radius; cloudPlane.z = radius-Math.random()*2*radius; cloudPlane.y=64-Math.random()*4; cloudPlane.rotationX=90; this.addChild(cloudPlane);

}}}}

Now all you have to do is import your class, instantiate your clouds and add them to a slider layer display object named mySlideLayer . And then you add your slide layer to your scene.

import org.lively3d.jiglib.Clouds; private var myClouds:Clouds= new Clouds(myRadius*.9, 6); mySlideLayer.addChild(myClouds); scene.addChild(mySlideLayer);

Why use a slide layer? You ’ re actually incorporating two motion types: translation of the slide layer and rotation of your collage cylinder. The purpose of the slide layer is to separate the translational motion from the rotational motion. You produce cloud translation by translating your slide layer, which contains your clouds. So you ’ re not really translating the clouds, you ’ re translating their container.

Next, you create trees in the same way you created clouds.

Making Trees The Trees class is almost an exact copy of the Clouds class, except that you ’ re using different images – trees instead of clouds – and the planes are oriented vertically, not horizontally. The trees were drawn in Flash (by Jonathan Lively) and a gradient was applied to them in Photoshop as shown in Figure 12.6.

468 Chapter 12: Developing 3D Websites

Figure 12-6

The entire Trees class is given here:

package org.lively3d.jiglib { //by Lively import org.papervision3d.materials.BitmapFileMaterial; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Plane;

public class Trees extends DisplayObject3D { [Embed (source=”/./assets/tree/tree1.png”)] private var Tree1: Class; [Embed (source=”/./assets/tree/tree2.png”)] private var Tree2: Class; [Embed (source=”/./assets/tree/tree3.png”)] private var Tree3: Class; [Embed (source=”/./assets/tree/tree4.png”)] private var Tree4: Class; [Embed (source=”/./assets/tree/tree5.png”)] private var Tree5: Class;

//Create a plane for your trees private var treePlane:Plane;

public function Trees(radius:Number, treeNum:int) {

var tree1: BitmapMaterial = new BitmapMaterial(new Tree1().bitmapData); tree1.doubleSided=true; var tree2: BitmapMaterial = new BitmapMaterial(new Tree2().bitmapData); tree2.doubleSided=true; var tree3: BitmapMaterial = new BitmapMaterial(new Tree3().bitmapData); tree3.doubleSided=true; var tree4: BitmapMaterial = new BitmapMaterial(new Tree4().bitmapData); tree4.doubleSided=true; var tree5: BitmapMaterial = new BitmapMaterial(new Tree5().bitmapData); tree5.doubleSided=true; (continued)

469 Part III: Building Games and Websites

(continued) //Create a Tree Array var myTree:Array=[tree1, tree2, tree3, tree4, tree5];

for(var i:int=0; i < treeNum; i++) {

treePlane = new Plane(myTree[i%5], 64, 64, 1, 1); treePlane.x = radius*.4-Math.random()*2*radius*.4; treePlane.z = radius-Math.random()*2*radius; treePlane.y=24; //Add your Tree this.addChild(treePlane);

} }}}

As the Trees class is so similar to the Clouds class (already discussed), no further discussion is needed. You proceed by placing panels on the screen using XML.

Placing your Panels with XML Positioning panels on the stage is most efficiently done using XML. The XML below shows the data needed to place the first two panels (check out the book ’ s code for the full XML). Here ’ s how the XML is broken down:

❑ revs — data opening and closing tags for all XML panel data ❑ revData — opening and closing tags for each panel ❑ revNum — revolution number ❑ title — revolution title ❑ planeX — panel x - position as a fraction of the radius ❑ planeZ — panel z - position as a fraction of the radius ❑ planeYRot — plane y rotation

The panels are placed on the stage as a fraction of the collage radius. For example, if your panel ’ s x - position fraction is .75 and your collage radius is 10,000, then your panel ’ s x - position is 7,500. This works great, as it lets you change your radius without having to worry about changing every value in your XML, as your panels are relatively placed by fraction of radius.

< ?xml version=”1.0” encoding=”utf-8”? > < revs > < revData > < revNum > Seven Revolutions < /revNum> < title >About Seven Revolutions < /title > < planeX > .75 < /planeX > < planeZ > 0 < /planeZ > < planeYRot > 20 < /planeYRot >

470 Chapter 12: Developing 3D Websites

< /revData >

< revData > < revNum > REVOLUTION 1 < /revNum > < title > POPULATION < /title > < planeX > -.75 < /planeX > < planeZ > 0 < /planeZ > < planeYRot > 45 < /planeYRot > < /revData > . . . < revs >

The great thing about XML is that you can easily add as many tags as you want, such as a description and an introductory video. And this info can be updated without recreating the main swf. So if you want to change the rotation of a panel, you just have to change the planeYRot text node. Figure 12.7 shows the panels being positioned on the stage using XML.

Figure 12-7

The XML code used to bring in your panels and place them on the stage is given in the commented code below. You ’ ve already performed many of these same code tasks earlier in the book, so not much discussion is needed here. As mentioned earlier, your x and z fractional positions are multiplied by the collage radius (myRadius ) to set them in place.

myPanel.x = myData.revData[i].planeX*myRadius; myPanel.z = myData.revData[i].planeZ*myRadius;

In the final line of code, you initialize your system using the method toggle(0) . This method brings your first image panel to the front and sets your CSIS button to its highlight. This is your starting state.

471 Part III: Building Games and Websites

//Simple XML used to bring in revs data private function getXml():void{ var urlRequest:URLRequest = new URLRequest(“assets/data/revsData.xml”); var xmlLoader:URLLoader = new URLLoader(urlRequest); xmlLoader.addEventListener(Event.COMPLETE, readXml); } //E4X parsing statements private function readXml(event:Event):void{ //load your XML data into myData myData = new XML(event.target.data); createPanels(); //Create Your Panels function createPanels():void{ for(var i:uint=0; i < 8; i++) { //Using an image array to load embedded images myPanelArray[i].smooth=true; myPanel = new Plane(myPanelArray[i], 180, 100, 4, 4); myPanel.x = myData.revData[i].planeX*myRadius; myPanel.z = myData.revData[i].planeZ*myRadius; myPanel.y=44; //Loading the panels into an Array (particle trick) panelArray[i]=myPanel; //Adding your panels to your 3D Display Object mySlideLayer.addChild(myPanel); //Setting the XML rotation of each panel myPanel.rotationY=myData.revData[i].planeYRot; }} //Setting your initial state togglerNumber(0); }

In addition to placing the panels on the stage, you need to consider how to bring them into your program. There ’ s a trade - off between user experience and program initiation time. In the example code found in the book ’ s download, the panel images are embedded into the code, but this does bloat the size of your program, causing it to load more slowly.

But if you don ’ t embed your panel images, the program waits for them to be brought in externally and this can create a jerky effect. It ’ s really a trade off – multiple preloaders can really be annoying, stalled programs unprofessional, and bloated programs ignored. Take your pick – it ’ s always a trade off. Toggling Panels In the last line of code above, you toggled your panel. Not only does this bring your panel into place but it also does a coordinate transformation that rotates your panel into the correct orientation.

So here ’ s the problem. Each panel has a different rotational orientation brought in from your XML data file. But you want each panel that you click on to reorient to the same place (see Figure 12.10). The best way to think about this transformation is to view your panel system as a pendulum, where the pivot

472 Chapter 12: Developing 3D Websites point is your panel and the pendulum point is the “ center - coordinate ” of your sliding display object. So here are the transformation steps:

❑ Move your sliding display object to the x and z - positions from your XML coordinates. This positions your panel into the correct x and z position. ❑ Calculate the distance of your panel from the sliding display object origin (this is your pendulum string length). ❑ Calculate the angle of your pendulum pivot before rotation. ❑ Grab the panel rotational orientation from your XML and convert it to radians. ❑ Calculate your total rotation by adding the two angles together (pivot pendulum before rotation and panel rotational orientation). ❑ Calculate your new x and y coordinate based upon your total rotation and trig relationships. ❑ Use TweenLite to get a smooth transition to your new rotational values.

The complete documented code is shown here:

//Panel Rotation private function togglerNumber(hitNumber:int):void { //This positions your panel into the correct x and z position var myZ:Number=panelArray[hitNumber].z; var myX:Number=panelArray[hitNumber].x; //Calculate the Distance of your panel from the origin var myDist:Number=Math.sqrt(myX*myX+myZ*myZ); //Calculate the angle pendulum pivot point before rotation var myAngle:Number = Math.atan2(-panelArray[hitNumber].z,-panelArray[hitNumber].x); //Grab the panel rotational orientation from the XML and convert it to radians var rotNeeded:Number = (panelArray[hitNumber].rotationY+30)*Math.PI/180; var rotNeeded2:Number = (panelArray[hitNumber].rotationY+30); //Calculate your total rotation by adding the two rotations together var totRot:Number=myAngle+rotNeeded; //Calculate your new x and y coordinates based upon your total rotation and trig var newMyX:Number = myDist*Math.cos(totRot); var newMyZ:Number = myDist*Math.sin(totRot); //Use the TweenLite engine to get a smooth transition to your new rotational values TweenLite.to(mySlideLayer, 1.5, {x:newMyX-100, z:-myRadius/2+newMyZ,rotationY:- rotNeeded2}); TweenLite.to(myCylinder,2,{rotationY:myAngle*180/Math.PI}); //Set the material for your button state myGameStateIs(hitNumber); }

The code brings each panel into the same position and orientation (as later shown in Figure 12.10), regardless of original position and rotational orientation! Mathematical relationships like this one aren ’ t written down anywhere. You just need to work with the math a little, make a few bad tries, and fix your mistakes. In this case, it really helps to think of your system as an oscillating pendulum that you translate in x and z on each state change (or button click of your navigation panel).

You now build your navigation system.

473 Part III: Building Games and Websites

Building a Nav System Building a navigation system in PV3D requires that you construct your buttons using PV3D components. In Figure 12.8, a clickable carousel (created by the author) is used as the starting point for your nav panel.

Figure 12-8

In the carousel example the images are placed in a circular pattern using trig functions. But for your nav system you just place them end to end as shown in Figure 12.9.

Figure 12-9

You put the nav buttons into position by incrementing each panel ’ s x - position with each panel iteration as shown in the following code snippet:

p.x = 12*i-40;

This small snippet is part of the createObjects method that ’ s shown in the final part of this discussion.

When your panels are in place you need to make them interactive by doing the following two things:

1. First, set your viewport interactivity to true . Do this in your super statement below, where the first true sets the scale to that of the stage, and the second true sets your interactivity.

474 Chapter 12: Developing 3D Websites

super(0,0,true, true, CameraType.FREE);

2. Next, set your button material ’ s interactive property to true as shown in the following code snippet:

//Instantiate Plane material and make it interactive var bam:BitmapFileMaterial = new BitmapFileMaterial(“assets/button/rev” +i+ ”webBW .jpg” ); bam.interactive = true ;

Finally, you want your button to lift slightly vertically when you roll over it, and then go back into position when you roll out. You do so by placing listeners on the rollOver and rollOut mouse events, and using two event handler methods to specify the actions or processing that should occur when those events are fired. The rollOver event causes your button plane to be lifted a few pixels and rollOut puts the button back into place.

private function createObjects():void{ for(var i:uint=0; i < 8; i++) { //Instantiate Plane material and make it interactive var bam:BitmapFileMaterial = new BitmapFileMaterial(“assets/button/rev”+i+”webBW .jpg”); bam.interactive = true; //Create, Position, and Scale Plane (Button Panel) p = new Plane(bam, 16, 16, 2, 2); p.x = 12*i-40; p.z = -800; p.scale=.6; p.y=-2; //Add Plane to Scene scene.addChild(p); //Set your extra object p.extra={pIndex:i}; //Add Plane Listeners p.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, toggler); p.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, rollOver); p.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, rollOut); planeArray[i] = p; //Add Plane to Array }

//Switch Button Material planeArray[0].material= new BitmapFileMaterial(“assets/button/rev0web.jpg”); } //RollOver and RollOut Methods private function rollOver(event:InteractiveScene3DEvent):void{ event.target.y=0; (new overSound() as Sound).play(); } private function rollOut(event:InteractiveScene3DEvent):void{ event.target.y=-2; }

475 Part III: Building Games and Websites

One key element in the entire navigation system is the extra property, which is set to the index of your button as you create your panels.

p.extra={pIndex:i};

Whenever you click on a button (plane), your index is found using event.target.extra.pIndex . This passes the proper index number to bring the appropriate panel forward.

Figure 12.10 shows the panel and button navigation system together in the same scene.

Figure 12-10

Hey! Where’s the Preloader? You ’ ll notice when you run the code that there ’ s no preloader. In the next chapter you add the preloader when you bump everything up a notch using Flash Builder.

The complete commented code is given here:

package { //Flash Imports import flash.display.*; import flash.events.*; import flash.media.*; import flash.net.URLLoader; import flash.net.URLRequest; import flash.text.TextField; import flash.ui.*; import flash.utils.Timer; //TweenLite import gs.TweenLite; //Lively Imports import org.lively3d.jiglib.Clouds; import org.lively3d.jiglib.SlidingCylinder;

476 Chapter 12: Developing 3D Websites import org.lively3d.jiglib.Trees; //Papervision Imports import org.papervision3d.cameras.CameraType; import org.papervision3d.core.effects.view.ReflectionView; import org.papervision3d.events.InteractiveScene3DEvent; import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.BitmapFileMaterial; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.shadematerials.*; import org.papervision3d.objects.*; import org.papervision3d.objects.primitives.*; import org.papervision3d.view.stats.StatsView;

//Background color and framerate [SWF (backgroundColor=”0x000000”, frameRate=”24”)]

//------// Reflective View CSIS Website //------public class CSISEmbed2 extends ReflectionView { //Radius of cylinder collage private var myRadius:Number=1200; //Instantiate sliding Display object and panel elements private var planeArray:Array = new Array(); private var panelArray:Array = new Array(); private var mySlideLayer:DisplayObject3D = new DisplayObject3D(); private var arrayPlane:Object; private var p:Plane;

//Instantiate collage cylinder, trees, clouds, and text private var myCylinder:SlidingCylinder = new SlidingCylinder(myRadius); private var myClouds:Clouds= new Clouds(myRadius*.9, 6); private var cloudPlane:Plane; private var myTrees:Trees = new Trees(myRadius*.9, 6); private var myTextField:TextField;

//Sound Channel private var sound:Sound= new Sound(); private var soundControl1:SoundChannel=new SoundChannel(); private var soundControl2:SoundChannel=new SoundChannel(); private var soundControl3:SoundChannel=new SoundChannel(); private var oldIndex:uint=1; private var myPanel:Plane; private var myData:XML;

//Embed Sounds [Embed(source=”assets/sound/over.mp3”)] public var overSound:Class; [Embed(source=”assets/sound/ambient2.mp3”)] public var ambientSound:Class; (continued)

477 Part III: Building Games and Websites

(continued) //Embed Panel Images [Embed(source=”assets/panel/plane0web256.jpg”)] public var Panel0:Class; private var myPanel0: BitmapMaterial = new BitmapMaterial(new Panel0().bitmapData); [Embed(source=”assets/panel/plane1web256.jpg”)] public var Panel1:Class; private var myPanel1: BitmapMaterial = new BitmapMaterial(new Panel1().bitmapData); [Embed(source=”assets/panel/plane2web256.jpg”)] public var Panel2:Class; private var myPanel2: BitmapMaterial = new BitmapMaterial(new Panel2().bitmapData); [Embed(source=”assets/panel/plane3web256.jpg”)] public var Panel3:Class; private var myPanel3: BitmapMaterial = new BitmapMaterial(new Panel3().bitmapData); [Embed(source=”assets/panel/plane4web256.jpg”)] public var Panel4:Class; private var myPanel4: BitmapMaterial = new BitmapMaterial(new Panel4().bitmapData); [Embed(source=”assets/panel/plane5web256.jpg”)] public var Panel5:Class; private var myPanel5: BitmapMaterial = new BitmapMaterial(new Panel5().bitmapData); [Embed(source=”assets/panel/plane6web256.jpg”)] public var Panel6:Class; private var myPanel6: BitmapMaterial = new BitmapMaterial(new Panel6().bitmapData); [Embed(source=”assets/panel/plane7web256.jpg”)] public var Panel7:Class; private var myPanel7: BitmapMaterial = new BitmapMaterial(new Panel7().bitmapData); //Array for Panels public var my PanelArray:Array=[myPanel0,myPanel1,myPanel2,myPanel3,myPanel4,myPanel5,myPanel6,my Panel7];

//Constructor initializes key components public function CSISEmbed2() { //Set up Stage and full screen super(0,0,true, true, CameraType.FREE); stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; //Create Panels createObjects(); surfaceHeight = -16; init3D(); createSlider(); //Add Stats, Sound Engine, Text Field and Initial Listeners var stats:StatsView = new StatsView(renderer); addChild(stats); mySoundState(0); initListeners(); addTextFields(); //Grab XML getXml(); }

478 Chapter 12: Developing 3D Websites

//------// Grab XML and create your panels //------private function getXml():void{ var urlRequest:URLRequest = new URLRequest(“assets/data/revsData.xml”); var xmlLoader:URLLoader = new URLLoader(urlRequest); xmlLoader.addEventListener(Event.COMPLETE, readXml); } //E4X parsing statements private function readXml(event:Event):void{ myData = new XML(event.target.data); createPanels(); //Create Your Panels function createPanels():void{ for(var i:uint=0; i < 8; i++) { //Using an image array to load embedded images myPanelArray[i].smooth=true; myPanel = new Plane(myPanelArray[i], 180, 100, 4, 4); myPanel.x = myData.revData[i].planeX*myRadius; myPanel.z = myData.revData[i].planeZ*myRadius; myPanel.y=44; //Loading the panels into an Array (particle trick) panelArray[i]=myPanel; //Adding your panels to your 3D Display Object mySlideLayer.addChild(myPanel); //Setting the XML rotation of each panel myPanel.rotationY=myData.revData[i].planeYRot; }} //Setting your initial state togglerNumber(0); }

//------// Sound Switch case for Game States //------private function mySoundState(soundState:int):void{ switch(soundState) { case 0: //Start State soundControl1=(new ambientSound() as Sound).play(0,100); break; case 1: //Play State

break; default: trace(“Not in data”); break; }}

//------// Add Text Field(s) //------(continued)

479 Part III: Building Games and Websites

(continued) private function addTextFields():void{ myTextField=new TextField(); myTextField.y==40; myTextField.scaleX=myTextField.scaleY=2; myTextField.x=stage.width/2-myTextField.width/2; myTextField.textColor=0xffffff; myTextField.text=”CSIS from 2D to 3D”; addChild(myTextField); myTextField.maxChars=400; myTextField.width=400; }

//------// Game Loop (Animation Loop) //------

private function gameLoop(event:Event):void { //Adjust camera and render frame moveCamera(); singleRender(); }

//------// Create Panels //------private function createObjects():void{ for(var i:uint=0; i < 8; i++) { //Instantiate Plane material and make it interactive var bam:BitmapFileMaterial = new BitmapFileMaterial(“assets/button/rev”+i+”webBW .jpg”); bam.interactive = true; //Create, Position, and Scale Plane (Button Panel) p = new Plane(bam, 16, 16, 2, 2); p.x = 12*i-40; p.z = -800; p.scale=.6; p.y=-2; //Add Plane to Scene scene.addChild(p); p.extra={pIndex:i}; //Add Plane Listeners p.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, toggler); p.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, rollOver); p.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, rollOut); planeArray[i] = p; //Add Plane to Array }

//Switch Button Material planeArray[0].material= new BitmapFileMaterial(“assets/button/rev0web.jpg”); }

480 Chapter 12: Developing 3D Websites

//RollOver and RollOut Methods private function rollOver(event:InteractiveScene3DEvent):void{ event.target.y=0; (new overSound() as Sound).play(); } private function rollOut(event:InteractiveScene3DEvent):void{ event.target.y=-2; } //Add Clouds and Trees to slider display object and add to scene private function createSlider():void{ mySlideLayer.addChild(myClouds); mySlideLayer.addChild(myTrees); scene.addChild(mySlideLayer); }

//------// Panel Toggle Method //------//Panel Change private function toggler(event:InteractiveScene3DEvent):void { //This positions your panel into the correct x and z position var myZ:Number=panelArray[event.target.extra.pIndex].z; var myX:Number=panelArray[event.target.extra.pIndex].x; //Calculate the Distance of your panel from the origin var myDist:Number=Math.sqrt(myX*myX+myZ*myZ); //Calculate the angle of your panel var myAngle:Number = Math.atan2(-panelArray[event.target.extra.pIndex] .z,-panelArray[event.target.extra.pIndex].x); //Calculate your total rotation (radian and angle number-comes from your XML) var rotNeeded:Number = (panelArray[event.target.extra.pIndex].rotationY+30)*Math. PI/180; var rotNeeded2:Number = (panelArray[event.target.extra.pIndex].rotationY+30); var totRot:Number=myAngle+rotNeeded; //Calculate your new x and y coordinates based upon your total rotation and trig var newMyX:Number = myDist*Math.cos(totRot); var newMyZ:Number = myDist*Math.sin(totRot); //Use the TweenLite engine to get a smooth transition TweenLite.to(mySlideLayer, 1.2, {x:newMyX-100, z:-myRadius/2+newMyZ,rotationY:- rotNeeded2}); TweenLite.to(myCylinder,2,{rotationY:myAngle*180/Math.PI}); //Set the material for your button state myGameStateIs(event.target.extra.pIndex); }

//Panel Rotation private function togglerNumber(hitNumber:int):void { //This positions your panel into the correct x and z position var myZ:Number=panelArray[hitNumber].z; var myX:Number=panelArray[hitNumber].x; //Calculate the Distance of your panel from the origin var myDist:Number=Math.sqrt(myX*myX+myZ*myZ); (continued)

481 Part III: Building Games and Websites

(continued) //Calculate the angle of your panel var myAngle:Number = Math.atan2(-panelArray[hitNumber].z,-panelArray[hitNumber].x); //Calculate your total rotation (radian and angle number-comes from your XML) var rotNeeded:Number = (panelArray[hitNumber].rotationY+30)*Math.PI/180; var rotNeeded2:Number = (panelArray[hitNumber].rotationY+30); var totRot:Number=myAngle+rotNeeded; //Calculate your new x and y coordinates based upon your total rotation and trig var newMyX:Number = myDist*Math.cos(totRot); var newMyZ:Number = myDist*Math.sin(totRot); //Use the TweenLite engine to get a smooth transition TweenLite.to(mySlideLayer, 1.5, {x:newMyX-100, z:-myRadius/2+newMyZ,rotationY:- rotNeeded2}); TweenLite.to(myCylinder,2,{rotationY:myAngle*180/Math.PI}); //Set the material for your button state myGameStateIs(hitNumber); }

//------// Game State (Web Site State) Switch Case //------

private function myGameStateIs(gameState:int):void{ //Set Button Material to active (light) var bam:BitmapFileMaterial = new BitmapFileMaterial(“assets/button/rev”+gameState+”web.jpg”); bam.interactive = true; planeArray[gameState].material=bam;

//Set Button Material to inactive (dark) var bam2:BitmapFileMaterial = new BitmapFileMaterial(“assets/button/ rev”+oldIndex+”webBW.jpg”); bam2.interactive = true; planeArray[oldIndex].material=bam2; //Set previous panel index oldIndex=gameState;

}

//------// Initiate camera and collage into scene //------

public function init3D():void { //Set up camera and collage cylinder camera.zoom = 8; camera.focus = 100;

camera.x = 0; camera.y = 0; camera.z = 0;

482 Chapter 12: Developing 3D Websites

//Add collage cylinder scene.addChild(myCylinder); myCylinder.z=0; }

//------// Add Listeners //------public function initListeners():void { //Start up your animation loop addEventListener(Event.ENTER_FRAME, gameLoop); }

//------// Camera View //------

public function moveCamera():void { // move the camera to the center point camera.x = 0; camera.y = 0; camera.z = 0;

camera.rotationY = 0; camera.rotationX = 2;

//Then move backwards depending on the new angle we’re facing camera.moveBackward(1000); }}}

Now that you ’ ve built your site, you ’ ll want to put it on the web: export it using release build and upload it to your server using Filezilla. Check out the book ’ s website for a complete video tutorial on this topic (exporting and uploading). But you aren ’ t done. Putting a site on the web is only half the battle. You ’ ve got to bring it up on the search engines. When people search on a particle title – do you want your site to be number 20? Absolutely not! You ’ re trying for the 1st or 2nd spot. Here ’ s how you improve your chances of being there.

Flirting with Web Spiders The idea of a web - spider may conjure in your mind a number of images ranging from bad to good. Some web - spiders (or web crawlers) are used maliciously to search for email addresses, blog form scripts, and so on. But not all web - spiders come from malicious hackers . . . some come from friendly search engines. Companies like Google send out spiders to search the web and scan web pages for content. As they crawl through the web, they index information and store it in a massive data base. And then as you search the web, they return this information to you, based on your query.

Your goal is to get your pages higher in search engine results by optimizing your pages for the web. In doing so, you want to portray your pages accurately and ethically while pushing them higher in the SERPs (search engine result pages). By all means, avoid what ’ s called “ black hat ” (deceptive)

483 Part III: Building Games and Websites

optimization techniques, which can have your website excluded entirely from a search engine ’ s result pages. Black hat optimization includes such tactics as hiding text that ’ s visible to search engines, but not to people, by making the text the same color as the background, for example.

All too often Search Engine Optimization (SEO) is an afterthought. Developers often wait until a website is completed before thinking about getting high rankings by search engines. This can be an expensive mistake. You must intertwine the web development process with SEO. It pays to do some upfront work. And to find out more about SEO (or Search Engine Optimization), do a web search on SEO. You can find tons of info on the web and check out such websites as www.seomoz.org , www.webmasters.live .com , and www.google.com/webmasters.

Using HTML In the past, one of the big downsides of using Flash was that it wasn ’ t accessible to search engine spiders for scanning and page ranking. But recently Adobe, Yahoo and Google have teamed up to make Flash sites searchable. The details of this effort remain unknown at the time of this writing, and it ’ s still unclear to what extent your swf files will be indexed or how. Most SEO experts still stress the importance of having alternate content. That means that SEO is still very much about contextual content and getting the right text into the hands of your friendly web spiders.

So here ’ s the big problem: when you open up a Flash html wrapper, it has 300+ lines of code designed primarily to detect the installed version of the Flash player or go get the right version if necessary. At a minimum, you need to open this html wrapper up in a program like Dreamweaver and add a bit of html that enables your web crawlers to read and index your page. Here are a few tips:

❑ Optimize Your Titles — the title of your page has the biggest impact on search engine optimization. The html created by Flash uses your FLA name – that ’ s bad. It needs to be changed. Underscores and hyphens used in file names are not good for page titles. Make it relevant to the page and appealing for keyword searches. ❑ Add Descriptions to Your Meta Tags — meta data tags used to hold much weight in getting your page ranked, not so much now. But in Flash it can be used to provide extra info to the searcher. You should definitely add a site description here especially if your page is just an swf. ❑ Put Key Words in Your Headings Tags — where your content appears in your heading tags can carry weight. For example, text in an “ h1 ” tag will carry more weight than text in an “ h2 ” tag and text in an “ h2 ” tag will carry more weight than text in a paragraph tag. ❑ Associate Key Words with Links — key words on links tells search engines where you ’ re going and takes precedence over plain url addresses. ❑ Use XML/PHP Alternate Content — feed the same XML content into both your Flash site and to a PHP output page that search engines can read. Thus, the content is identical and your web crawlers are indexing your php output page accurately to your Flash site.

But in addition to opening up your swf - html wrapper page and making the preceding changes, you have another powerful tool at your disposal –SWFObject .

484 Chapter 12: Developing 3D Websites

Using SWFObject SWFObject is a powerful search engine optimizer created by Geoff Stearns. It ’ s a JavaScript library that provides an easy method for providing alternate content for your Flash site. It detects the Flash Player version and determines if Flash content or alternate content should be shown. It provides alternate content for search engines (and Adobe express install of the latest Flash Player). To get going on the SWFObject, follow these steps:

1. Download the latest version from Google code at http://code.google.com/p/swfobject. 2. Download the Air code generator. 3. Unzip both files and place the .js file in your Flash project folder or your Flex bin - release folder. 4. Install the Adobe code generator and generate the appropriate html wrapper code for your project. Choose the following parameters that are shown in Figure 12.11: ❏ dynamic ❏ myAlternativeContent ❏ CSIS.swf ❏ 100x100 per cent

Figure 12-11

5. Once you ’ ve generated the swf - html wrapper code, open it up in Dreamweaver and validate it. If there are any errors correct them (there shouldn ’ t be) but validating is always a good idea. Then add your alternate content in between your div tags (then make sure you validate again). The SWFObject generated the following code:

< !DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd” > < html xmlns=”http://www.w3.org/1999/xhtml” lang=”en” xml:lang=”en”> < head > CSIS Seven Revolutions by Mike Lively< /title > < meta name=”description” content=”SEVEN REVOLUTIONS is a project led by the Global Strategy Institute at the Center for Strategic and International Studies (CSIS).” /> (continued)</p><p>485 Part III: Building Games and Websites</p><p>(continued) < meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1” /> < script type=”text/javascript” src=”swfobject.js”> < /script > < script type=”text/javascript” > var flashvars = {}; var params = {}; var attributes = {}; swfobject.embedSWF(“CSISEmbed2.swf”, “myAlternativeContent”, “100%”, “100%”, “9.0.124”, false, flashvars, params, attributes); < /script > < style type=”text/css” > < !- html,body,div#myAlternativeContent { width:100%; height:100%; } body { margin:0px; padding:0px; } - > < /style > < /head > < body > < div id=”myAlternativeContent” > < h1 > CSIS Seven Revolutions < /h1 > < h2 > CSIS 7 Rev Introduction by Mike Lively< /h2 > < p > Leadership is compressed. Greater connectivity across the world means broader perspectives are more important than ever before, but leaders no matter what their sector have far fewer opportunities to think beyond their short term priorities and immediate responsibilities. < /p > </p><p>< h1 > POPULATION < /h1 > < h2 > CSIS Revolution 1 < /h2 > < p > By the time Christopher Columbus reached the New World, global population had reached about 500 million. By July 1, 2005, population had increased by a factor of thirteen, to 6.5 billion the majority of this growth having occurred between the end of the Second World War and the present. By 2025, global population will probably reach 7.9 billion; and by 2050, there will be around 9.2 billion people on Earth. < /p > </p><p>< h1 > RESOURCE < /h1 > <h2 > CSIS Revolution 2 < /h2 > <p > Global trends in population growth, economic development, industrialization, and food production, among others, are placing increasing stress on the most precious finite natural resource: water. < /p> </p><p><h1 > TECHNOLOGY < /h1 > <h2 > CSIS Revolution 3 < /h2 > <p > Scientists have already achieved significant successes in the micro-miniaturization of sensors, activators, and actuators. < /p> </p><p>486 Chapter 12: Developing 3D Websites</p><p>< h1 > INFORMATION < /h1 > < h2 > CSIS Revolution 4 < /h2 > < p > The information revolution is also facilitating the decentralization and proliferation of media. < /p > </p><p>< h1 > ECONOMIC < /h1 > < h2 > CSIS Revolution 5 < /h2 > < p > According to a report from Goldman Sachs, if they can consolidate conditions conducive to structural growth, the total GDP of the BRIC economies by the year 2025 could equal half the aggregate level of the G-6 countries (United States, Japan, Germany, UK, France and Italy). < /p > </p><p>< h1 > CONFLICT < /h1 > < h2 > CSIS Revolution 6 < /h2 > < p > This new era of conflict can be characterized by asymmetric warfare. < /p > </p><p>< h1 > GOVERNANCE < /h1 > < h2 > CSIS Revolution 7 < /h2 > < p > Increasingly, companies face the challenge of juggling a triple bottom line from shareholders, management, and the public. < /p > </p><p>< a href=”http://www.adobe.com/go/getflashplayer” > < img src=”http://www.adobe.com/images/shared/download_buttons/get_flash_ player.gif” alt=”Get Adobe Flash player” / > </p><p>< /a > < /div > < /body > < /html > </p><p>6. Add the required CSS to get your percentages working in FireFox. </p><p>< style type=”text/css” > < !- html,body,div#myAlternativeContent { width:100%; height:100%; } body { margin:0px; padding:0px; } - > < /style > </p><p>7. Name your html wrapper file index and place it in your Flash folder or Flex bin - release folder. 8. Finally, upload it to your server using a program like Filezilla and text it by disabling the Flash player and JavaScript. </p><p>487 Part III: Building Games and Websites</p><p>Though you have the steps given above, which aren ’ t that difficult, content like this is best demonstrated using video. So make sure that you check out the book ’ s video on this topic. It walks you through the steps in a lively way. </p><p>SEO is an art that ’ s ever changing. Paying attention to SEO while you build your site will save you the frustration and expense of reprogramming it later. Don ’ t approach SEO as an afterthought. Make it a primary goal of every web project you develop and place on the web. </p><p>Summary In this chapter, you converted the CSIS 2D site to a 3D site. You created custom tree, cloud, and collage classes, and built a 3D navigation system from scratch using PV3D components. You examined a few importing reality checks when it comes to building websites: </p><p>❑ Using a design doc ❑ Learning Photoshop ❑ Combining 2D with 3D to get an optimized 3D experience </p><p>Finally, you learned how to optimize your website for search engines by adding html text to your html - swf wrapper file and by using the SWFObject. </p><p>488 Part IV: Extending PV3D and Beyond</p><p>Chapter 13: Making 3D Movies</p><p>Chapter 14: Taking Virtual Tours</p><p>Chapter 15: Adding Services</p><p>Chapter 16: Flash & Math: Exploring Flash 10 and Beyond Part IV: Extending PV3D and Beyond</p><p>In Part IV, you find out how to take PV3D to the next level of performance by using Flash CS4, Photoshop3D, Flash Catalyst, and Flash Builder. You add augmented reality and services, and examine a number of approaches to rendering 3D models in CS4. </p><p>490 Making 3D Movies </p><p>When Flash was born everyone was fascinated with how easily they could make objects “ tween ” across the stage or morph into other objects. Such concepts as masking, timeline, and framerate were subsequently brought into the stagnant world of HTML, and a new era in web design was born. But there was a problem. Making things move realistically using the Flash timeline took some work and a sleight- of - hand referred to as “ frame - skipping ” . But with the advent of 3D Flash, a new era of animation has begun, one which isn’ t bound by a mechanical timeline or 2D environment. </p><p>In this chapter, you review many of the new tools being used in making the next generation of Flash 3D movies such as Flash Professional CS4, Photoshop 3D, Flash Catalyst, and Flash Builder. You create a number of applications to demonstrate their use, build an animation recording application, and discuss building a PV3D studio. </p><p>Houston . . . We’ve Got a Problem Things are moving really fast in the programming world for the Adobe Flash Platform, perhaps too fast. These aren ’ t just code enhancements, but complete architectural overhauls that render previous codes obsolete and non - functional. It ’ s as if your economy was based on dollars, and one day America just starts using rupees as its major currency. Then, a week later, Americans adopt the Yen – imagine how you ’ d feel if you were a stockbroker by trade. The rapidly changing world of architectural methodologies and design patterns makes it difficult for developers to write software with staying power. </p><p>If you paid close attention to the CSIS Seven Revolutions section in the last chapter, this is a key problem with the information age. Because technology is changing so fast it becomes difficult for planners to plan much beyond a week, and in many cases it ’ s day by day. This is true of software development as well; complete development schedules and projects are scrapped with each new programming architecture release. </p><p>Why change architecture? Typically a new architecture release carries with it a significant performance enhancement and gives you a decisive edge over your competitors. Part IV: Extending PV3D and Beyond</p><p>If you want to “ get into the business ” of software development don ’ t start at the end of the line. Start with the newest technology (such as Flash Catalyst and Flash Builder). As everything is moving so rapidly, you can become an expert on it in less than two years. </p><p>You may be wondering when it ’ s all going to stop – it ’ s not! Change is going to accelerate. It ’ s “ infodarwinism. ” Due to worldwide connectivity, you now have millions of developers (and designers) thrown into the mix, contributing to the process around the clock. </p><p>So what ’ s the solution? Ride the rollercoaster, love what you do, and contribute to the open source community whenever possible. Grab on, and don ’ t let go!!! You start this chapter by examining one of the new kids on the block (a real PV3D 2.0 killer) – Flash CS4. </p><p>Using Flash CS4 When Flash 10 first came out, many of the PV3D 2.0 developers were quick to say that it wasn ’ t that great and that not much performance enhancement had been obtained by its release. However, Flash & Math were quick to say that their performance benchmarks improved by a factor of five as a result of these enhancements. However, frames - per - second is not the only measurement to judge by when evaluating a new framework. </p><p>Usability also plays a key role. And with the release of CS4 and its native 3D tools, your development productivity can be improved by as much as a factor of ten. Oh, sure . . . you aren ’ t bringing in 3D Collada models, but not because you can ’ t, but because the classes to do so have not yet been written (see Chapter 16). And literally thousands of lines of code for 3D are now native to the Flash 10 player ’ s engine, significantly bringing down the code bulk required to render a 3D object. </p><p>In Flash CS4, you have a native z component that gives you the ability to give elements 3D translation and 3D rotations. You can do this using the 3D Rotation Tool and 3D Translation tool found in the Flash CS4 Tools panel, as shown in Figure 13.1. </p><p>Figure 13-1</p><p>You can find full tutorials on using Flash CS4 on this book ’ s web site. So instead of going through the basics of Flash CS4, you ’ re about to apply Flash Professional to reconstruct the CSIS Seven Revolutions that was discussed in the previous chapter. Then, you compare the development cycle of PV3D 2.0 to Flash CS4. </p><p>Rebuilding CSIS Seven Revolutions in Flash CS4 Taking the Seven Revolutions project from the previous chapter and creating an equivalent experience in Flash CS4 (as opposed to PV3D) is a real eye-opener. Figure 13.2 demonstrates the difference between the two. </p><p>492 Chapter 13: Making 3D Movies</p><p>Figure 13-2</p><p>Comparing PV3D and Flash CS4 In all fairness, the Flash CS4 application is a scaled - down version of the PV3D application (no clouds or trees). But regardless of how you slice it, two hours versus three days of development to get a similar experience really shakes the tree. While you ’ re fiddling around with creating classes, the competition just robbed you of your contract three days ago. That ’ s the power of new development tools. Placing a TextArea on a 3D Panel In addition, as CS4 has a native z - component, all Flash components (such as textbox, buttons, and video) are readily introduced into your 3D Flash arena. Figure 13.3 demonstrates this by showing a text box at the top of the integration 5 - plane panel. Using the native z component the textbox was placed in the plane. The textbox and plane are rotated together in 3D around the y - axis with no distortion to the textbox; dropping text components into 3D and rotating them as in the example isn ’ t easily done in PV3D. But in Flash CS4, it ’ s simple. </p><p>Figure 13-3</p><p>493 Part IV: Extending PV3D and Beyond</p><p>In addition, as a result of the native - z component in Flash CS4, you can construct your navigation system without writing a single line of code. Building Your Navigation Panel The navigation panel in Figure 13.4 consists of eight buttons that are aligned in a row. In Flash CS4, you can convert your images to buttons by choosing Modify on the main menu bar, Convert to Symbol . . . and Button from the Type menu. And the buttons are styled (as shown in Figure 13.4) with a bevel effects found in the properties panel. All of this without a single line of code and all in 3D (or with a native z component)! </p><p>Figure 13-4</p><p>You create the button states when converting your images to buttons and tool tips are easily created by putting text in the roll over frame (or state) of your button as shown in Figure 13.5. Rolling over each navigation button causes the button to lift, change color, and its text tooltip to appear. This behavior is created using the typical Flash button time line shown in 13.5, but now it ’ s in 3D, which means you can translate it and rotate it in x, y, and z. </p><p>Figure 13-5</p><p>The buttons are controlled using listeners as shown in the following code snippet: </p><p> btn0.addEventListener(MouseEvent.CLICK, clickSelection); </p><p>494 Chapter 13: Making 3D Movies</p><p>As each button is clicked on, a clickSelection method is fired which identifies the button based on its name using its target.name property ( evtObj.target.name ). This name is sent to a switch statement, which selects the appropriate TweenLite method based on the button ’ s name. </p><p>//Button Name Variable var mySwitchVar:String=evtObj.target.name; //Switch Case (Only two of eight cases shown) switch(mySwitchVar) { case “btn0”: //Start State TweenLite.to(mySlideLayer, 1.2, {x:1735, y:790, z:-441,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:1735-cntr}); break; case “btn1”: //Rev 1 - tween to position TweenLite.to(mySlideLayer, 1.2, {x:565, y:522, z:-77,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:765-cntr}); break; . . . default: trace(“Not in data”); break; } </p><p>A similar saving was obtained when creating the sliding display object that ’ s shown in Figure 13 - 6. Creating a Sliding Display Object The image panels in Figure 13 . 6 all exist in a single movie clip with the instance name mySlideLayer . Each of the TweenLite methods discussed above contains the appropriate coordinates that orient the mySlideLayer Movie Clip into the correct position to bring the appropriate panel to its correct location. </p><p>The appearance of the image panels in the mySlideLayer movie clip (in Figure 13.6) is a little deceptive. It looks as if the image panels have been placed side by side, with the alternating panels sized smaller in x and y. But actually, these smaller - looking panels have been pushed back in the z direction. </p><p>Figure 13-6</p><p>So what you ’ re actually seeing is a change in depth. </p><p>In fact, the image panels (in Figure 13.6) could have been placed anywhere with any orientation using the translation and rotation tools that were shown previously in Figure 13.1. But for simplicity of discussion, they were lined up in a row with alternating depth. </p><p>495 Part IV: Extending PV3D and Beyond</p><p>Here ’ s an important tip: as your scenes become more complex using the orientation tools, the tools that appeared previously in Figure 13.1 will become cumbersome to use. You ’ ll find using the control panels that appear in Figure 13.7 much easier to use for controlling x, y, z, and rotational orientation. </p><p>Figure 13-7</p><p>Use the X , Y , and Z parameters in the 3D Position and View panel to change your translation and the Y parameter in the 3D Rotation panel to change your orientation. Don ’ t change the values under 3D Center point. In addition, the panel controls turn into sliders when you roll over them and you can preview and slide your image panels as you position them. 3DSMax provides a very similar functionality. </p><p>Finally, this entire sliding movie clip ( mySlideLayer ) sits on top of the image collage. Reflection is created graphically (all in Flash) by inverting an image and reducing its alpha value. The entire application is put together on the layered time line shown in Figure 13.8. </p><p>Figure 13-8</p><p>In Figure 13.8, ActionScript code is placed on the first layer (Actions). The navigation panel is on the second layer (buttons). The sliding movie clip (mySlideLayer ) is on the third layer (slidingDO). And the image collage is on the fourth layer (bckGrnd). Figure 13.9 shows all the graphical elements together. </p><p>496 Chapter 13: Making 3D Movies</p><p>Figure 13-9</p><p>The entire documented code is given here: </p><p> stop();</p><p>//TweenLite import gs.TweenLite; var cntr:Number=2000;</p><p>//Text for Seven Revolutions Text Area Boxes mySlideLayer.rev0.rev0txt.text=”SEVEN REVOLUTIONS is a project led by (CSIS) . . . ”; mySlideLayer.rev1.rev1txt.text=”By the time Christopher Columbus reached the New World . . . “; mySlideLayer.rev2.rev2txt.text=”Global trends in population growth, economic development . . . ”; mySlideLayer.rev3.rev3txt.text=”Scientists have already achieved significant successes . . . ”; mySlideLayer.rev4.rev4txt.text=”The information revolution is also facilitating the decentralization and proliferation of media . . . ”; mySlideLayer.rev5.rev5txt.text=”According to a report from Goldman Sachs, if they can consolidate conditions . . . “; mySlideLayer.rev6.rev6txt.text=”This new era of conflict can be characterized . . . ”; mySlideLayer.rev7.rev7txt.text=”Increasingly, companies face the challenge of juggling a triple bottom line . . . ”;</p><p>//Sound var sound:Sound= new Sound(); var soundControl1:SoundChannel=new SoundChannel(); soundControl1=(new ambientSound() as Sound).play(0,100); //Handle button events btn0.addEventListener(MouseEvent.CLICK, clickSelection); btn1.addEventListener(MouseEvent.CLICK, clickSelection); btn2.addEventListener(MouseEvent.CLICK, clickSelection); btn3.addEventListener(MouseEvent.CLICK, clickSelection); btn4.addEventListener(MouseEvent.CLICK, clickSelection);</p><p>(continued)</p><p>497 Part IV: Extending PV3D and Beyond</p><p>(continued) btn5.addEventListener(MouseEvent.CLICK, clickSelection); btn6.addEventListener(MouseEvent.CLICK, clickSelection); btn7.addEventListener(MouseEvent.CLICK, clickSelection);</p><p>//Switch case for Buttons Navigation function clickSelection(evtObj:MouseEvent){ //Button Name Variable var mySwitchVar:String=evtObj.target.name; switch(mySwitchVar) { case “btn0”: //Start State TweenLite.to(mySlideLayer, 1.2, {x:1735, y:790, z:-441,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:1735-cntr}); break; case “btn1”: //Rev 1 - tween to position TweenLite.to(mySlideLayer, 1.2, {x:565, y:522, z:-77,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:765-cntr}); break; case “btn2”: //Rev 2 - tween to position TweenLite.to(mySlideLayer, 1.2, {x:448, y:783, z:-600,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:448-cntr}); break; case “btn3”: // Rev 3 - tween to position TweenLite.to(mySlideLayer, 1.2, {x:-144, y:512, z:-168,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:-144-cntr}); break; case “btn4”: // Rev 4 - tween to position TweenLite.to(mySlideLayer, 1.2, {x:-728, y:786, z:-746,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:-728-cntr}); break; case “btn5”: // Rev 5 - tween to position TweenLite.to(mySlideLayer, 1.2, {x:-856, y:512, z:-256,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:-856-cntr}); break; case “btn6”: // Rev 6 - tween to position TweenLite.to(mySlideLayer, 1.2, {x:-1984, y:780, z:-892,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:-1384-cntr+400}); break; case “btn7”: // Rev 7 - tween to position TweenLite.to(mySlideLayer, 1.2, {x:-1564, y:506, z:-347,rotationY:-7}); TweenLite.to(bkGnd, 2, {x:-1564-cntr+400}); break;</p><p> default: trace(“Not in data”); break; }} </p><p>You may be wondering: why study PV3D 2.0 at all? PV3D is still superior in how it handles 3D objects and the camera object. To surpass this performance, classes need to be written for CS4. That will be discussed further in Chapter 16. Finally, you need to add a preloader. </p><p>498 Chapter 13: Making 3D Movies</p><p>Adding a Preloader If a user comes to your site and doesn ’ t see anything, chances are that he ’ ll hop right off thinking it ’ s broken. Having something pop up immediately is essential to keeping your business. That usually means using a preloader. A preloader is a graphical way of telling your clients that your site is loading. To create a preloader in Flash just move your application to frame 2 and put your preloader on frame 1 as shown in Figure 13.10. </p><p>Figure 13-10</p><p>Preloaders can get very elaborate. The one in Figure 13.10 is as simple as they get. It ’ s just a dynamic textbox that holds the percentage of how much of the Flash movie has been loaded. At the core of the preloader is the loaderInfo class which lets you monitor the loading progress event. As the movie loads, the updateProgress method is updated and your loaded movie percentage is calculated. Upon loading 100 per cent, you advance to your application frame using nextFrame . </p><p>The preloader code follows: </p><p> stop();</p><p>//Code for Preloader loaderInfo.addEventListener(ProgressEvent.PROGRESS, updateProgress);</p><p> function updateProgress(evtObject:ProgressEvent):void { //Calculate percent loader var myPercent:Number = Math.floor((evtObject.bytesLoaded*100)/evtObject .bytesTotal); loadingTxt.text = myPercent+”%”; //Go to next frame when 100% loaded if (myPercent==100){ //Go to application frame nextFrame(); } } </p><p>As easy as Flash CS4 makes it, building interactivity in Flash websites can be coding intensive and with each new release of the Flash player the chasm between developer and designer is growing. But there ’ s hope! Adobe has released Flash Catalyst to help bridge this growing gap. </p><p>499 Part IV: Extending PV3D and Beyond Bridging the Gap with Flash Catalyst As wonderful as the new era of 3D Flash is, it comes with the high price of learning OOP. The transition from Flash 8 (AS2) to 9 (AS3) was phenomenal, requiring a factor of 10 in programming skills and yielding a factor of 10 in performance. But it also created a gap between designer and developer and Adobe is seeking to remedy this problem with the release of Flash Catalyst as shown in Figure 13.11. </p><p>Figure 13-11</p><p>Flash Catalyst transforms Illustrator and Photoshop designer artwork into interactive applications which can be brought into Flash Builder. As a designer creates a visual interactive experience in Flash Catalyst, code is being written in the background that can be finished off by a developer in Flash Builder. Creating a Flash application using Flash Catalyst consists of the following three steps: </p><p>1. Create your visual design in Photoshop or Illustrator. 2. Import your design into Flash Catalyst and make it interactive. 3. Finish off your application in Flash Builder. </p><p>Taking your CSIS 7 Revs example, you start by laying it all out in Photoshop. </p><p>Laying it out in Photoshop As illustrated in Figure 13.12 you can lay the CSIS 7 Revs site in four Photoshop layers. The bottom layer is the ambient black color. The next layer is the background group which is the collage background with its reflection. The next layer group is panels, which has eight images in it. And finally the buttons layer group sits on top, with eight navigation images. </p><p>Figure 13-12</p><p>500 Chapter 13: Making 3D Movies</p><p>You can lay all this out in about five minutes in Photoshop and then you ’ re ready to import it into Flash Catalyst. Choose Flash Catalyst Create New Project from Design File From Adobe Photoshop PSD File. </p><p>Creating Code Automatically in Flash Catalyst As soon as you ’ ve brought your Photoshop (or Illustrator) files into Flash Catalyst you can add interactivity and even produce a project for the web. It ’ s important to note that Flash Catalyst has already been writing code for you automatically. If you ’ re an Adobe Flex developer you ’ ll understand this immediately. Just as Flex automatically writes code as graphical elements are brought to the stage, so does Flash Catalyst. But Flash Catalyst goes a step further and writes methods and properties, not just MXML container code. </p><p>Switching from Design to Code view, from your simple import from Photoshop you can examine the code already written in Flash Catalyst : </p><p>< ?xml version=’1.0’ encoding=’UTF-8’? > < s:Application xmlns:d=”http://ns.adobe.com/fxg/2008/dt” xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:s=”library://ns.adobe.com/flex/spark” width=”900” height=”700” backgroundColor=”#000000”> </p><p>< s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/BckGndAmbient.png’)” resizeMode=”scale” d:userLabel=”BckGndAmbient”/ > </p><p>< fx:DesignLayer d:userLabel=”BackGround” > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/Bottom.png’)” resizeMode=”scale” d:userLabel=”Bottom” x=”-324” y=”324” alpha=”0.55”/> < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/Top.png’)” resizeMode=”scale” d:userLabel=”Top” x=”-324” y=”0”/ > < /fx:DesignLayer > </p><p>< fx:DesignLayer d:userLabel=”panels” > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/plane7web256.png’)” resizeMode=”scale” d:userLabel=”plane7web256” x=”126” y=”252”/> < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/plane6web256.png’)” resizeMode=”scale” d:userLabel=”plane6web256” x=”126” y=”252”/> < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/plane5web256.png’)” resizeMode=”scale” d:userLabel=”plane5web256” x=”126” y=”252”/> < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/plane4web256.png’)” resizeMode=”scale” d:userLabel=”plane4web256” x=”126” y=”252”/> < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/plane3web256.png’)” resizeMode=”scale” d:userLabel=”plane3web256” x=”126” y=”252”/> < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/plane2web256.png’)” resizeMode=”scale” d:userLabel=”plane2web256” x=”126” y=”252”/> < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/plane1web256.png’)” resizeMode=”scale” d:userLabel=”plane1web256” x=”126” y=”252”/> < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/plane0web256.png’)” resizeMode=”scale” d:userLabel=”plane0web256” x=”126” y=”252”/> < /fx:DesignLayer > (continued)</p><p>501 Part IV: Extending PV3D and Beyond</p><p>(continued) < fx:DesignLayer d:userLabel=”buttons” > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/rev0web.png’)” resizeMode=”scale” d:userLabel=”rev0web” x=”72” y=”54”/ > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/rev1web.png’)” resizeMode=”scale” d:userLabel=”rev1web” x=”162” y=”54”/ > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/rev2web.png’)” resizeMode=”scale” d:userLabel=”rev2web” x=”252” y=”54”/ > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/rev3web.png’)” resizeMode=”scale” d:userLabel=”rev3web” x=”342” y=”54”/ > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/rev4web.png’)” resizeMode=”scale” d:userLabel=”rev4web” x=”432” y=”54”/ > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/rev5web.png’)” resizeMode=”scale” d:userLabel=”rev5web” x=”522” y=”54”/ > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/rev6web.png’)” resizeMode=”scale” d:userLabel=”rev6web” x=”612” y=”54”/ > < s:BitmapImage source=”@Embed(‘/assets/CSIS7Revs/rev7web.png’)” resizeMode=”scale” d:userLabel=”rev7web” x=”702” y=”54”/ > < /fx:DesignLayer > < /s:Application > </p><p>From examining this code, there are several very important points: </p><p>❑ The code has been optimized to work with Photoshop and Illustrator and even contains group layer names. ❑ Flash Catalyst is both a streamlined development tool and a teaching tool that can be used to bring developers rapidly up to speed in coding. </p><p>While working at NKU the author trained a number of graphic designers to become developers. Over a three - year period he noticed that there were two bottlenecks in the process: getting graphic designers to stop designing for print (and design for the web), and bringing them rapidly up to speed on coding. Flash Catalyst solves both of these problems. It forces designers to start designing for the web, and it enables them to see the code they generated in the process. </p><p>So not only does Flash Catalyst bridge the gap between designer and developer, but it ’ s also a powerful teaching tool that enables teachers to show designers how to code by enabling them to see the results of their work in Flash Catalyst. </p><p>❑ If all you did at this point was bring the file generated from Flash Catalyst into Flash Builder, you ’ d have saved a significant amount of development time already; enough to make Flash Catalyst worth using, especially if you ’ re working with a team of developers. </p><p>You have a number of options with Flash Catalyst; you can add interactivity, upload to the web for your client to review, or create a file for import into Flash Builder for further development. At this point, you import your Flash Catalyst file into Flash Builder (to see a complete example of FC to FB check out Chapter 15). </p><p>502 Chapter 13: Making 3D Movies</p><p>Sending Your Work to Flash Builder Bringing Flash Catalyst into Flash Builder is an easy process. However, one of the problems with FC at the time of writing this book was that you could not “ round trip. ” What is round tripping? It ’ s taking files from Flash Catalyst into Flash Builder and then from Flash Builder back into Flash Catalyst (as you can do with Illustrator files). Round tripping between FC and FB is an important and powerful feature not yet implemented in the beta version (used in this book) of Flash Builder. Round tripping between FB and FC is within the scope of Adobe ’ s development plan but given its complexity still may be somewhat in the future or limited in its capability. Bringing FC files into FB is a simple two - step process: </p><p>1. Save your Flash Catalyst file. The file saves as a FXP file, which is the same format that Flash Builder uses. 2. Import your FXP file into Flash Builder using the “ Import Flex Project (FXP) ” item found in the File menu item. On importing your project into Flash Builder you should notice that Flash Builder has created the appropriate file structure for the code generated by Flash Catalyst as shown in Figure 13.13. Specifically, the image resources are placed in an assets folder which is referenced by the @Embed tags. </p><p>Figure 13-13</p><p>You should also notice that Flash Builder has replaced the Flex Navigator panel with the Package Explorer panel. The Package Explorer is an enhanced version of the Flex Navigator that existed in Flex Builder 3. There are a number of new features in Flash Builder that you find out about in the next section (to see the completion of the project above check out the book ’ s website). </p><p>Pushing the Envelope with Flash Builder When Adobe purchased Macromedia, many of us held our breath in horror and choked down rumors that Adobe would kill Flash. Then Flex emerged and we all closed our eyes and hoped it would go away. And in the transition from Flash 8 to Flash 9 many of us lost huge reservoirs of code that would no longer work in the new Flash display object environment. </p><p>503 Part IV: Extending PV3D and Beyond</p><p>The author had written a 60,000 line Flash 8 learning management system (comparable to the Navy’s) that would no longer function. </p><p>Here are a number of new features that Flash Builder offers: </p><p>❑ Skinnable “ Spark ” components ❑ New namespaces and text controls ❑ New Package Explorer ❑ Moving and Refactoring of classes ❑ Automatic code generation for getters and setters and event handlers ❑ Flash Components directly from Flash Builder ❑ Customizable file templates ❑ New data connectivity tools (super easy to connect, and offers variety) ❑ Uses the SwiftObject to deploy sites ❑ Can choose project ’ s SDK from the initial project development screen ❑ Beefed - up view state syntax ❑ Beefed - up CSS ❑ New effects Animation Engine (any filter including Pixel Bender) ❑ Beefed - up Animation Engine (including 3D) ❑ Documentation with code hinting ❑ World - class text layout system </p><p>These are a few of the many new features in Flash Builder that enhance your work flow by a factor of 10. One big timesaver is the “ Flash Components directly from Flash Builder ” feature. In Flex 2 and 3 it was difficult to bring Flash animations into Flex and control them. It required embedding the swf animation files and creating a special class and placing that class in Flex. With the new Flash Components feature this entire process is streamlined and a controlling SWC file is automatically created for you in FB. </p><p>Birthing a Spark With Flash Builder comes the birth of a new component library: Spark! What is Spark? Spark is the new skinning architecture for Flex 4. Spark components separate the graphics from the code; where the old MXML components were one big lump combining both graphics and code. And why do we need a new component type? Weren ’ t the old components good enough? </p><p>In the evolution of Flash Catalyst, the Flex SDK team realized that for Flash Catalyst to achieve its ambitious work flow that there needed to be a major overhaul of the Flex Framework: namely the creation of the Spark architecture. If you think about it for a moment it makes sense. If Flash Catalyst is all about generating graphical interfaces without code, having code in the component architecture poses a problem. In addition, Flex had another problem. It was really difficult to skin components. Using Spark it ’ s easy to do. Really easy! </p><p>504 Chapter 13: Making 3D Movies</p><p>With Spark you have separated a component ’ s display logic from the component ’ s business logic. A Spark component consists of an object class and a skin class and by associating the two together you get a full blown Spark component. </p><p>The new Spark architecture lets you apply graphic designs to your components easily. And since the code is written in MXML it ’ s easy to understand. An example of an MXML component file used to skin a Spark button is given here: </p><p><?xml version=”1.0” encoding=”utf-8”?> <s:Skin xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:s=”library://ns.adobe.com/flex/spark” xmlns:mx=”library://ns.adobe.com/flex/ halo” width=”400” height=”300”> <!—New Metadata tag—> <fx:Metadata> [HostComponent(“spark.components.Button”)] </fx:Metadata></p><p><s:layout> <s:BasicLayout/> </s:layout> <!—Various States for your component—> <s:states> <mx:State name=”up”/> <mx:State name=”down”/> <mx:State name=”over”/> <mx:State name=”disabled”/> </s:states> <!—New graphic Rect component—> <s:Rect left=”0” right=”0” top=”0” botton=”0” minWidth=”64” minHeight=”32”> <s:stroke> <!—Set color Stroke of enabled and disabled states—></p><p><s:SolidColorStroke color=”0xCC0022” color.disabled=”0x2200CC”/> </s:stroke> </s:Rect> </s:Skin></p><p>The biggest change that you see in skinning components is that there’s a new Metadata tag called HostComponent.</p><p><fx:Metadata> [HostComponent(“spark.components.Button”)] </fx:Metadata></p><p>It declares the fully qualified name of the component that will use the skin. Of course, with Flash Catalyst you don ’ t have to skin components from scratch, and when (and if) round tripping is instituted (between FC and FB), the entire process becomes iterative without writing a single line of code. </p><p>Animating in 3D Using Flash Builder Once again, Flash Catalyst became a driving force behind some of the new animation effects created by Adobe. Essentially, Flex needed to be able to target arbitrary objects. But there ’ s a problem. Once you </p><p>505 Part IV: Extending PV3D and Beyond</p><p> target (or point) an animation at a graphic object (as it ’ s not a component) you don ’ t have the ability to move it. It doesn ’ t have a move function that effects can play, so Adobe broadened the capability of effects so they could target the Spark graphical elements and other arbitrary types. </p><p>As a result of these changes, Flash Builder now has a new Animate Super class, which is a subclass of Effect and takes the place of Tween effect. It ’ s similar to the AnimateProperty function in Flex 3, which animated one property at a time, but now you can animate multiple properties at the same time. In addition, some of the new things you can do are: repeat behavior, reverse animation, animate color, and 3D effects such as translation and rotation. </p><p>As an example of how this new animation effects engine works in Flash Builder you ’ re going to build the animated panel shown in Figure 13.14. As you rollover each of the images in the panel, the image you roll over rotates around once and then reverses its animation. Reversing animation once was a really hard thing to do, but in Flash Builder it ’ s as easy as just setting a flag. </p><p>Figure 13-14</p><p>The program is very simple to create. All you essentially do is add a 3D rotation effect to each image element in Figure 13.14. The Rotate3D Spark effect, shown in the code snippet below, accomplishes the required behavior by simply setting flags: </p><p>< s:Rotate3D id=”rotateEffect1” target=”{targetImg1}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn1B=false;” effectEnd=”btn1B=true;” / > </p><p>Another ground - breaking element in Flash builder is its automatic code creation. Because each image element in Figure 13.14 needs to be made interactive, a rollover listener had to be added to each image. </p><p>< mx:Image id=”targetImg1” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg1_rollOverHandler(event)” x=”165” y=”70” rollOverEffect=”myRollOver1”/ > </p><p>Upon code completion of the rollOver , the option is given to automatically create the required listener code stub. </p><p>506 Chapter 13: Making 3D Movies</p><p> protected function targetImg1_rollOverHandler(event:MouseEvent):void { if(btn1B) rotateEffect1.play(); } </p><p>As a developer you just need to complete the code stub, which in this case plays the 3D rotation effect, which spins your image and reverses that spin. </p><p>Finally, in the Rotate3D effect, two flags have been set that disable and enable the effect on start and finish. Thus, the effect can ’ t be activated again until the entire animation has played. And that ’ s pretty cool, and saves you the hassle of having your animation messed up before it ’ s completed. </p><p>The entire code for the rotating image panel follows: </p><p>< ?xml version=”1.0”? > < !-behaviors\Spark3DRotateLayoutCenterTransform.mxml- > < s:Application xmlsn:fx=”http://ns.adobe.com/mxml/2009” xmlsn:mx=”library://ns.adobe.com/flex/halo” xmlsn:s=”library://ns.adobe.com/flex/spark” > </p><p>< fx:Script > < ![CDATA[</p><p>//Booleans for determining if animation is complete private var btn1B:Boolean=true; private var btn2B:Boolean=true; private var btn3B:Boolean=true; private var btn4B:Boolean=true; private var btn5B:Boolean=true; private var btn6B:Boolean=true; private var btn7B:Boolean=true; private var btn8B:Boolean=true; private var btn9B:Boolean=true;</p><p>//Automatically generate rollOver Listeners protected function targetImg1_rollOverHandler(event:MouseEvent):void { if(btn1B) rotateEffect1.play(); } protected function targetImg2_rollOverHandler(event:MouseEvent):void { if(btn2B) rotateEffect2.play(); } protected function targetImg3_rollOverHandler(event:MouseEvent):void { if(btn3B) rotateEffect3.play(); } protected function targetImg4_rollOverHandler(event:MouseEvent):void { if(btn4B) rotateEffect4.play(); } protected function targetImg5_rollOverHandler(event:MouseEvent):void (continued)</p><p>507 Part IV: Extending PV3D and Beyond</p><p>(continued) { if(btn5B) rotateEffect5.play(); } protected function targetImg6_rollOverHandler(event:MouseEvent):void { if(btn6B) rotateEffect6.play(); } protected function targetImg7_rollOverHandler(event:MouseEvent):void { if(btn7B) rotateEffect7.play(); } protected function targetImg8_rollOverHandler(event:MouseEvent):void { if(btn8B) rotateEffect8.play(); } protected function targetImg9_rollOverHandler(event:MouseEvent):void { if(btn9B) rotateEffect9.play(); }</p><p>]] > </p><p></fx:Script></p><p><!—3D Rotations—></p><p><fx:Declarations> <s:Rotate3D id=”rotateEffect1” target=”{targetImg1}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn1B=false;” effectEnd=”btn1B=true;” /></p><p>< s:Rotate3D id=”rotateEffect2” target=”{targetImg2}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn2B=false;” effectEnd=”btn2B=true;” / > </p><p><s:Rotate3D id=”rotateEffect3” target=”{targetImg3}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn3B=false;” effectEnd=”btn3B=true;” / > </p><p>508 Chapter 13: Making 3D Movies</p><p>< s:Rotate3D id=”rotateEffect4” target=”{targetImg4}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn4B=false;” effectEnd=”btn4B=true;” / ></p><p>< s:Rotate3D id=”rotateEffect5” target=”{targetImg5}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn5B=false;” effectEnd=”btn5B=true;” / ></p><p>< s:Rotate3D id=”rotateEffect6” target=”{targetImg6}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn6B=false;” effectEnd=”btn6B=true;” / > </p><p><s:Rotate3D id=”rotateEffect7” target=”{targetImg7}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn7B=false;” effectEnd=”btn7B=true;” / ></p><p>< s:Rotate3D id=”rotateEffect8” target=”{targetImg8}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn8B=false;” effectEnd=”btn8B=true;”</p><p>/ > <s:Rotate3D id=”rotateEffect9” target=”{targetImg9}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn9B=false;” effectEnd=”btn9B=true;” / > (continued)</p><p>509 Part IV: Extending PV3D and Beyond</p><p>(continued) </fx:Declarations></p><p><!—Interactive Images with automatically generated listeners—></p><p><mx:Image id=”targetImg1” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg1_ rollOverHandler(event)” x=”165” y=”70” rollOverEffect=”myRollOver1”/> <mx:Image id=”targetImg2” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg2_ rollOverHandler(event)” x=”238” y=”70”/> <mx:Image id=”targetImg3” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg3_ rollOverHandler(event)” x=”309” y=”70”/> <mx:Image id=”targetImg4” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg4_ rollOverHandler(event)” x=”165” y=”136”/> <mx:Image id=”targetImg5” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg5_ rollOverHandler(event)” x=”238” y=”136”/> <mx:Image id=”targetImg6” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg6_ rollOverHandler(event)” x=”309” y=”136”/> <mx:Image id=”targetImg7” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg7_ rollOverHandler(event)” x=”165” y=”204”/> <mx:Image id=”targetImg8” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg8_ rollOverHandler(event)” x=”238” y=”204”/> <mx:Image id=”targetImg9” source=”@Embed(source=’assets/rev0web.png’)” rollOver=”targetImg9_ rollOverHandler(event)” x=”309” y=”204”/> </s:Application></p><p>The preceding code isn ’ t optimized, but is given to illustrate how easy it is to create behaviors that in the past were very difficult to do in programs like PV3D. The entire animation program took about 30 minutes to write, and in PV3D would have taken much longer and with much more code required to do so. </p><p>Finally, did Adobe kill Flash? Absolutely not – it elevated it to the next level in the competitive market place and integrated it in the powerful open source community; of which the author is an avid supporter and contributor. </p><p>The hard lesson for developers is that technology is moving forward at an extreme pace and it requires much diligence to keep up. Your ability to smoothly transition as technology advances and expand your skill sets along the way will ensure your continual employment in an ever - changing technology environment. </p><p>Adobe has moved Flash from being an animation engine to a full platform with a complete set of integrative technologies. It ’ s now on TV, the web, in Smart Homes , Phones , New York Times , Facebook , and in Hollywood movies. And there ’ s more. Adobe has released a very powerful set of 3D tools in Photoshop. You can even create Collada files in Photoshop for import into PV3D. </p><p>510 Chapter 13: Making 3D Movies Going 3D with Photoshop One of the big themes of Adobe Flash Camp (Adobe ’ s “ unconference ” style training) is that “ there ’ s more ” . Adobe is really pushing the envelope of web technology, especially in the area of 3D. And not just with Flash, but a number of other integrative technologies, such as Photoshop. </p><p>The CS4 Photoshop engine is totally new. You have the ability to model in Photoshop. And in addition, you can paint directly onto the surface of your 3D models (you can even paint on effects such as bump maps) – something you can ’ t do in 3DSMax. You no longer have to worry about UV coordinates – just paint directly on your surfaces. Photoshop also has a full lighting system, layered meshes, and a powerful ray - tracing render engine. </p><p>Getting Started Tips Here are a few getting started tips: </p><p>❑ Open up 3D objects in Photoshop in the same way that you would open up any other file. There are a number of 3D file formats that you can import: WavefrontOBJ (great for models, not for materials); U3D (good, but not as widely adopted as Collada); KMZ (huge resources available from Google Warehouse); Collada (accurate, best format), and 3DS (smoothes things, but not always accurate) files. ❑ Add 3D models to 2D files by creating a 3D layer. Choose 3D then New Layer from 3D file and select your 3D file. This creates a 3D layer that you can superimpose on your 2D layers. Also, optimally 3D files can be viewed from Adobe Bridge and brought into Photoshop just by clicking on them. ❑ Once you ’ ve created your file , you can save it as a psd file (for a 3D creation, it ’ s a self - contained file). Exporting your 3D model to a 3D program such as Papervision3D is easily done by selecting the layer you want to export, going to your 3D menu and selecting Export 3D Layer and selecting the file format you want to export. For PV3D you choose Collada . </p><p>As opposed to trying to go through a complete tutorial on 3D Photoshop, you now go through a simple but very important example. More examples can be found on the book ’ s website. </p><p>Unlocking the Power of Google 3D Warehouse Google 3D Warehouse is a huge reservoir of material that can potentially be used in PV3D. But one of the problems with working with SketchUp models is getting those models into PV3D. It ’ s not that PV3D can ’ t handle most low polygon models; it ’ s just that getting models into the right format for PV3D to read can at times be a really big and painful chore. Two problems arise when working with SketchUp material:</p><p>❑ Many SketchUp models have too many vertices. ❑ SketchUp models are difficult to bring into PV3D. </p><p>However, using Photoshop 3D can help alleviate this problem, as any KMZ file can be readily imported into Photoshop. Once you ’ ve brought that model into Photoshop, you can then export it as a Collada file for use in PV3D. Or you can import it into another 3D application, as shown in Figure 13.15, where you can reduce its polygon count, retexture it, and export it for use in PV3D. </p><p>511 Part IV: Extending PV3D and Beyond</p><p>Figure 13.15 shows a KMZ SketchUp lamp model that ’ s been imported into Photoshop and then exported as an obj file and imported into Maya. Once in Maya the lamp can be retextured, reduced, and exported for PV3D. </p><p>Figure 13-15</p><p>Recording 3D Animations Recording 3D animations in Flash is one of the most powerful but yet most underutilized areas of development in Flash. Its application in education is immense, especially in the area of student evaluation. For a long time educators have struggled with how to evaluate student learning properly. And with the advent of distance learning the burning issue of evaluation has become a central theme and yet little real progress has been made. </p><p>As part of the solution Joseph Novac, Professor of Education and Biological sciences at Cornell University, introduced concept mapping (also called mind - mapping). Concept mapping is a way of graphically organizing and representing knowledge. According to Novac, concepts are perceived regularity in events or objects represented by a label (such as a word). Creating a concept map is accomplished by linking concepts with prepositions, where prepositions are units of meaning. So typically concepts are placed in boxes and the lines linking the concepts are prepositions. </p><p>Novac found that by using this graphical technique he could more fully evaluate what students knew about their subject. He found that typical multiple choice questions, the mainstream of educational evaluation, were flawed. Students answered such questions by rote, and many times correctly for the wrong reasons. But by examining the complexity of a student ’ s concept map he could accurately predict how much the student knew about the subject. By recording animations you can take concept mapping to the next level. </p><p>Recording a student making a concept map not only demonstrates what he knows about a subject, but also examines his cognitive processes. In addition, by using technology like the FMS you can get a live stream, allowing for additional audio, video, and interactive shared objects. And because it ’ s animation you can catalog and fast forward or rewind through the process: something that can ’ t be done in the traditional classroom setting. </p><p>Creating an Animation Recorder in PV3D Building an animation recording system in Flash is a simple process. And there ’s a trick! You don’ t have to build a screen recording that records a video of everything you do, but an application that time stamps </p><p>512 Chapter 13: Making 3D Movies what you click on. You’ ve already seen this in the chapter on the FMS. When synchronizing two cars on a race track you only had to transfer the acceleration data. The program receiving that data knew what to do with it and executed the appropriate physics. </p><p>So taking the concept of recording “ only what you clicked on ” you can create a Flash animation system by time stamping those clicks and feeding them back to a player program. The player program repeats what you clicked on in the correct time sequence, as illustrated in Figure 13.16. This is extremely easy and as opposed to recording terabytes of video screen capture data, you ’ re saving very small XML files that hold what you clicked on and its timestamp. Which means you can have hours of animation play back that only require a few kilobytes of storage space. </p><p>Recorder Program</p><p>XML Time Stamp</p><p>Player Program</p><p>Figure 13-16</p><p>Figure 13.17 illustrates the concept. A jet fighter is flown around the screen. </p><p>Figure 13-17</p><p>The jet motion is controlled by the following 12 key commands: </p><p>❑ Right Arrow — Turn Right ❑ Left Arrow — Turn Left ❑ Up Arrow — Move Forward ❑ Down Arrow — Move Backwards ❑ Page Up — Move Up </p><p>513 Part IV: Extending PV3D and Beyond</p><p>❑ Page Down — Move Down ❑ a — pitch plus ❑ s — yaw plus ❑ d — roll plus ❑ z — pitch minus ❑ x — yaw minus ❑ c — roll minus </p><p>To start a recording, you input a file name and press the recording button. Then you fly your jet around the screen using the key described above. Each time you press a key, the key you pressed, and when you pressed it, is recorded and sent to an XML file. (Actually all 12 key states are recorded with the time interval.) When your animation is complete you hit the save button and your animation is saved to an XML file. </p><p>To play the recorded animation you just click on the name of your animation ’ s file and hit play. Your time stamped clicks are then sent to a player program which parses the XML animation, starts a timer and selects the buttons to be pressed “ programmatically ” from a switch statement (or series of if statements for simultaneous motion). </p><p>The savings in producing animation in this way are tremendous. For this particular system there are 12 key states and upon each transition (key press or key release) the press state of each key is recorded, where “ 0 ” means not pressed and “ 1 ” means pressed. Having the condition of all states recorded with each individual key press allows for multiple key presses at once. </p><p>So for example, you can pitch and dive at the same time by holding down the Page Down and a key simultaneously. It ’ s really a lot of fun! The XML data generated with each key action (press or release) are given below. It shows that with each key action the condition of all key states and the time at which that condition occurred are recorded. </p><p>You ’ ve already seen this approach to programming before and it ’ s commonly referred to as a state engine. With 12 possible keys and two possible conditions for each key (press or release), the total possible states that your system can have are 2 to the 12th power or 4096 possible states. Thinking of software systems as state engines can reduce the amount of programming work you need to do. For example, as opposed to representing your number as 12 separate key press/release entities (shown in the following XML) you could also represent them as a single 3 - digit hexadecimal number (B68 for example), saving a little space and work. </p><p>< ?xml version=”1.0” encoding=”utf-8”? > < jetsystem > < keyaction > < myState > 0,0,0,0,0,0,0,0,0,0,0,0 < /myState > < starttime > 0 < /starttime > < /keyaction > < keyaction > < myState > 0,1,0,0,0,0,0,0,0,0,0,0 < /myState > < starttime > 4000 < /starttime > < /keyaction > </p><p>514 Chapter 13: Making 3D Movies</p><p>< keyaction > < myState > 0,0,1,0,0,0,0,0,0,0,0,0 < /myState > < starttime >6000 < /starttime > < /keyaction > < keyaction > < myState > 0,0,1,0,0,0,0,0,1,0,0,0 < /myState > < starttime >10000 < /starttime > < /keyaction > < /jetsystem > </p><p>The data is sent to the web using Flex ’ s HTTPService method. </p><p>< mx:HTTPService id=”setPostFile” url=”MyXML/MyFiles/phpBlogNewTopic.php” method=”POST” > </p><p>< mx:request > < myFileName > {MyTopicData} < /myFileName > < myState > {myStateData} < /myState > < starttime > {myStartTime} < /starttime > < /mx:request > < /mx:HTTPService > </p><p>Each time a key is pressed or released Flex runs the setPostFile.send method which executes the HTTPService command and sends the name of the file to be accessed (MyTopicData ), the key state information (myStateData ), and the time of that state (myStartTime ) to a PHP file. The PHP file uses DOM programming to store that data on the web in an XML file. </p><p>Storing Your Results Using the DOM So far in this book you ’ ve created programs that store data in a variety of ways using MySQL, AIR, and Flash 10. But every once in a while you ’ re going to be asked to store data to the web without the use of a database. Now at first this may sound a little ridiculous, but there are some advantages to doing so. One is that you can create maintenance - free applications (or apps that don ’ t require a database programmer to install). You just drop your application on the web into its folder and it runs and stores data hassle free. </p><p>A number of educators who run short courses and have little knowledge of computers find such an option extremely appealing. In the Jet animation program above, all of its data are being recorded to the web using DOM programming. </p><p>DOM programming is not difficult, but may seem a little unusual to you at first. In the code below a new DOM document is created and passed the variable name from Flex (myFileName ), which opens an XML file to be appended with the new “ jet state ” data. Using the appendChild method, two pairs of nodes are inserted at the end of your XML data: one set of nodes for your key action state and one set of nodes for your begin time for that state. The createElement method creates the XML tags and the createTextNode creates the text element (or data) to be inserted between those tags. </p><p>These pairs of nodes are sandwiched by keyaction tags that are created by using the createElement(‘keyaction’) method. So essentially what you ’ re doing is recreating the structure of the XML jet data file given above. Finally, your data is saved as XML to the web. </p><p>515 Part IV: Extending PV3D and Beyond</p><p>< ?php $dom = new DomDocument; $dom- > Load($_POST[‘myFileName’].”.xml”); $jetsystem = $dom- > getElementsByTagName(‘jetsystem’)- >item(0); </p><p>$newElement = $dom - > createElement(‘keyaction’); $jetsystem- > appendChild($newElement);</p><p>// create child element **myKey** $item = $dom- > createElement(“myState”); $newElement- > appendChild($item);</p><p>// create another text node $text = $dom- > createTextNode($_POST[‘myState’]); $item- > appendChild($text);</p><p>// create child element **myPlace** $item = $dom- > createElement(“starttime”); $newElement- > appendChild($item);</p><p>// create another text node $text = $dom- > createTextNode($_POST[‘starttime’]); $item- > appendChild($text);</p><p>$dom- > save($_POST[‘myFileName’].”.xml”);</p><p>? > </p><p>You have a number of ways of approaching this problem. The method above is strictly for illustrative purposes. Each programming scenario typically has a different approach depending on what ’ s required to optimize your code. But as you can see, programming the DOM can come in handy when you don ’ t want to use a database to save data to the web. And it ’ s commonly done; there are even entire open - source wikis that use this approach. </p><p>A great place to learn more about the DOM (in addition to the book ’ s website) is from IBM. IBM has a number of superb web tutorials and their DOM articles can be found at http://www.ibm.com/ developerworks/library/os-xmldomphp/ . </p><p>You can download the code (and other similar examples) for the jet animation project from the book ’ s website.</p><p>Animating Spark The Spark effect classes hide much of the underlying effects implementation to simplify the use of effects. But if you want to grab control and create your own animations you can use motion paths. </p><p>What ’ s a motion path? It ’ s the collection of keyframes required to produce an effect. And it ’ s implemented using the AnimateTransform class. </p><p>In addition to the new Animate Super class, which targets properties, there ’ s an animate transform effect class that supports key frames and motion paths. Using this class enables you to perform the big three in x, y and z: movement, rotation, and scale. The code below demonstrates the use of the </p><p>516 Chapter 13: Making 3D Movies</p><p>AnimateTransform class to translate a logo in x, y, z, rotate it in y, and scale it in x. It ’ s similar to the jet animation project presented earlier where you play back property and time values, as opposed to state and time values, in order to create the animation sequence: </p><p>< ?xml version=”1.0”? > < !-behaviors\SparkXFormKeyFrame.mxml- > < s:Application xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:mx=”library://ns.adobe.com/flex/halo” xmlns:s=”library://ns.adobe.com/flex/spark” > </p><p>< fx:Declarations > < s:AnimateTransform id=”bounceEffect” target=”{myImage}” > < s:translationX > < s:MotionPath > < s:KeyFrame time=”250” value=”20”/ > < s:KeyFrame time=”500” value=”40”/ > < s:KeyFrame time=”750” value=”60”/ > < s:KeyFrame time=”1000” value=”80”/ > < s:KeyFrame time=”1250” value=”100”/ > < s:KeyFrame time=”3250” value=”400”/ > < s:KeyFrame time=”4250” value=”0”/ > < /s:MotionPath > < /s:translationX > < s:translationY > < s:MotionPath > < s:KeyFrame time=”250” value=”20”/ > < s:KeyFrame time=”500” value=”40”/ > < s:KeyFrame time=”750” value=”60”/ > < s:KeyFrame time=”1000” value=”80”/ > < s:KeyFrame time=”1250” value=”100”/ > < s:KeyFrame time=”3250” value=”120”/ > < s:KeyFrame time=”4250” value=”0”/ > < /s:MotionPath > < /s:translationY > < s:translationZ > < s:MotionPath > < s:KeyFrame time=”250” value=”40”/ > < s:KeyFrame time=”500” value=”220”/ > < s:KeyFrame time=”750” value=”40”/ > < s:KeyFrame time=”1000” value=”0”/ > < /s:MotionPath > < /s:translationZ > < s:rotationY > < s:MotionPath > < s:KeyFrame time=”250” value=”20”/ > < s:KeyFrame time=”500” value=”40”/ > < s:KeyFrame time=”750” value=”60”/ > < s:KeyFrame time=”1000” value=”80”/ > < s:KeyFrame time=”1250” value=”100”/ > < s:KeyFrame time=”3250” value=”120”/ > < s:KeyFrame time=”4250” value=”0”/ > (continued)</p><p>517 Part IV: Extending PV3D and Beyond</p><p>(continued) < /s:MotionPath > < /s:rotationY > < s:scaleX > < s:MotionPath > < s:KeyFrame time=”250” value=”1”/ > < s:KeyFrame time=”500” value=”2”/ > < s:KeyFrame time=”750” value=”1”/ > < s:KeyFrame time=”1000” value=”1”/ > < s:KeyFrame time=”1250” value=”4”/ > < s:KeyFrame time=”3250” value=”1”/ > < s:KeyFrame time=”4250” value=”1”/ > < /s:MotionPath > < /s:scaleX > </s:AnimateTransform > < /fx:Declarations > </p><p>< mx:Image id=”myImage” source=”@Embed(source=’assets/rev0web.png’)” click=”bounceEffect.end();bounceEffect.play();”/> < /s:Application > </p><p>Using such state - animation engines to play back recorded events can be a powerful tool in education. In addition to hand - coding the spark animation above, Flash Catalyst creates such animation through graphical techniques. We demonstrate this in upcoming chapters. Closely related to the idea of state engines, saving XML to the web, and creating 3D environments, is PV3D studio, created by Gary Stasiuk. </p><p>Building a PV3D Studio Every PV3D developer ’ s dream is to create a Studio Max - like website builder for Papervision3D. Such an application would let you manipulate 3D objects, place textures on them, and assign interactivity. Upon completion of your project, a single click would upload it to the web without even writing a single line of code. </p><p>Though this may sound a little farfetched, Gary Stasiuk of Juxt Interactive has made a worthy attempt with his creation of VizualPV3D. You can download VizualPV3D at http://www.juxtinteractive .com/work/vizualpv3d/ . </p><p>VizualPV3D VizualPV3D has a Flash GUI interface that makes creating and manipulating objects in 3D scenes easy to do (see Figure 13.18). </p><p>518 Chapter 13: Making 3D Movies</p><p>Figure 13-18</p><p>The big deal is that session data is exported as XML and sent to your clip board. You can then save this data as XML and open up your session again later. And if you port this program over to Flash 10 you can save this data directly to your hard drive. </p><p>This idea of creating web - ready interfaces from graphical UIs is exactly what Flash Catalyst is doing. The next advent of 3D technology will be one that takes an interface like ’ s Gary ’ s and reprograms it to interact with Flash Builder in the same way that Flash Catalyst does. </p><p>Summary This chapter highlights one of the greatest challenges developers face today – the rapid change in programming architecture and platforms. With such fast - paced changes, literally once an application is written it’ s obsolete. This was demonstrated by building the Seven Revolutions project from the previous chapter in Flash CS4. Next, Flash Catalyst and its integration into Flash Builder were examined. Finally, the new Animate Super Effects class was used to create a panel animation, Photoshop3D was examined, and a PV3D animation engine which saves its results to the DOM developed. </p><p>Keeping up with technology changes and actively incorporating them was the keynote of this chapter as you examined Flash Catalyst and Flash Builder, Adobe ’ s two newest products. </p><p>519</p><p>Taking Virtual Tours </p><p>In the 1980s, military doctrine was pretty simple. Whoever controlled the next medium controlled the world and that next medium was Space. Space was the last of the four great media (land, sea, air, and space). And that was all there was, but since the 1980s a number of new global factors have come into play, namely those mentioned in the Seven Revolutions of Chapter 12. And in this new millennium two new media will dominate the world stage: nanotechnology and virtual reality. </p><p>As our technology crazed world moves forward, the benefits and threats of new technologies emerge. World planners wrestle with where to invest their resources in order to gain the greatest advantage for society. With technology changing so rapidly, programming interfaces must be built that generate code at a blazing pace to keep up. With the advent of Adobe ’ s new Flash Catalyst and Flash Builder, code is now being generated automatically – and that ’ s a huge leap forward. </p><p>In this chapter, you take a brief look at how the military is using virtual technology, augmented reality, and building 3D worlds. </p><p>Hello “Uncle Sam ” Applying virtual reality to military development has two sides: the yin and the yang. The yin part is the soft side of “ military - virtual ” reality and it looks really good to policy makers: acceleration of development schedules and fewer people getting killed in deployment testing. The yang part is the hard side of “military - virtual” reality and it sounds really good to the public: bloodless battle, and economic stimulus for communities that are building the next level of virtually enhanced warfare. Part IV: Extending PV3D and Beyond</p><p>Embracing the Yin of VR The U.S. military is embracing VR in a number of areas such as training, weaponry development, field command, droid deployment, and control platforms. And here are some of the reasons why: </p><p>❑ VR offers a factor of 20 in savings. The military doesn ’ t have to spend a million dollars when an easy entry VR system can be purchased for US$50,000. ❑ Battles are staged with VR simulators, which are becoming more realistic with each new iteration of the technology. It ’ s a military fact that soldiers in the heat of battle perform by rote what they ’ ve been trained to do, and VR simulators are successfully training them. ❑ Conceptual designs are more easily tested using VR, eliminating the need to build physical prototypes and ship them overseas to see how they perform in realistic battle terrain. ❑ Early feedback eliminates lethal design flaws closer to production, making design phase 1 more productive and less politically driven. Such feedback could also potentially eliminate dead systems, accelerating the prototype to deployment by a number of years, and making such systems safer for soldiers to operate. ❑ Bench marking military prototypes by VR technology will eliminate wasteful congressional spending: where emotional and economic factors have in the past played more of a role in weaponry funding than potential weapon effectiveness. ❑ Road mapping of military technologies becomes more effective, as the result of weapon technology enhancements can be demonstrated without actually building prototypes. And it reduces the time it takes to integrate new technologies by a number of years. </p><p>Not only does VR shorten development time of advanced weaponry, it also performs a significant role in a number of other areas such as training exercises. </p><p>Using Drones – the Yang of VR The U.S. military has envisioned systems from as simple as UAVs (unmanned aerial vehicles) dropping pinpoint accurate missiles, to entire droid armies being controlled by space satellites from remote locations using virtual reality. Figure 14.1 shows the predator drone loaded with a Hellfire missile. </p><p>Figure 14-1</p><p>522 Chapter 14: Taking Virtual Tours</p><p>As drones become more widely used in warfare it will become apparent that VR will become an important military medium in playing a decisive role in global conflicts of the future: conflicts that ’ ll be seen as bloodless by the side that ’ s controlling the VR medium. </p><p>The next topic was a delightful mind blower for many PV3D developers – augmented reality. </p><p>Creating Augmented Reality Generating augmented reality in PV3D is done using the FLARToolKit . The FLARToolKit was created from the Java version of NyARToolKit, which was created from the C version of ARToolKit. </p><p>So what ’ s augmented reality anyway? It ’ s the overlay of virtual imagery on the real world. As shown in Figure 14.2, the creator of FLARToolKit, Japanese coder Saqoosha (at http://saqoosha.net/en/ ), has a two - level boxed hole in his head with firework - like graphics shooting out of it. </p><p>Figure 14-2</p><p>Real - time blending of real - world footage and computer - generated graphics blurs the line between reality and the virtual world by enhancing what you perceive. It ’ s a field that ’ s wide open for developers and will amaze your clients every time they view one of your FLARToolkit applications. Here are the steps for getting started with the FLARToolKit in PV3D: </p><p>❑ Download the FLARToolKit starter file from http://saqoosha.net/en/flartoolkit/ start-up-guide/ and unzip it. This folder contains everything you need to create your first FLART experiment. ❑ Or better yet, to keep your work up to date you can download the starter file from the SVN address at http://www.libspark.org/svn/ (just surf down to the appropriate folder). ❑ After unzipping the starter file, print a copy of the flarlogo - marker pdf found in the Data folder. The flarlogo is shown on the left side of figure 14.3. This FLAR (Flash Augmented Reality) application throws a 3D cube on top of this pattern when you run the program. ❑ Make sure that your computer ’ s web cam is hooked up and active. ❑ Finally, open up the SimpleCube fla in Flash and run the program. Your webcam then becomes active, and when you put your flarlogo - marker pattern in front of it a PV3D cube appears on top of the pattern as shown on the right side of Figure 14.3. </p><p>523 Part IV: Extending PV3D and Beyond</p><p>Figure 14-3</p><p>Basically, you create your FLAR scene just as you would any other PV3D application except with one big difference: PV3D uses an augmented left - handed coordinate system and FLARToolKit uses a right - handed coordinate system as shown in Figure 14.4. </p><p>Figure 14-4</p><p>The code required to render the cube in Figure 14.3 (on the right side) is rather simple. After the PV3D imports are declared, a Simple Cube class is created which extends the PV3DARApp (PV3D augmented reality) class. Then two primitives are declared: a plane and a cube. The plane is a red wire frame and overlays the FLART pattern. This gives an indication to the user that the program is working. The cube is the intended object that sits on top of your pattern. </p><p>Next the FLARToolKit needs to be initiated. This is done using the following line of code. </p><p> this.init(‘Data/camera_para.dat’, ‘Data/flarlogo.pat’); </p><p>The first argument is the path for the camera corrective file. You can leave it as default. The second argument is a definition file for your marker pattern. You need to define the pattern for the marker (for </p><p>524 Chapter 14: Taking Virtual Tours example, flarlogo.pat is for flarlogo - marker.pdf ). Later in this chapter you learn how to make your own markers. </p><p>Once the init() method is completed, the onInit() method is called. This method sets up your wireframe, point light source, cube material, and cube. It then adds them to your baseNode . Using the following code, you add your wireframe and cube to the FLAR pattern using _baseNode as opposed to adding your objects to your scene, which is typically done in PV3D. </p><p>This line adds your wireframe to FLAR: </p><p> this._baseNode.addChild(this._plane); </p><p>This line adds your cube to FLAR: </p><p> this._baseNode.addChild(this._cube); </p><p>The _baseNode container is a special object that follows the marker automatically. The complete documented code is given here: </p><p> package { //Papervision3D Imports import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.primitives.Cube; import org.papervision3d.objects.primitives.Plane;</p><p>//Public class extends the PV3D Augmented Reality Class public class SimpleCube extends PV3DARApp { //Declare plane and cube properties private var _plane:Plane; //Wireframe Plane private var _cube:Cube; //Projected Cube</p><p> public function SimpleCube() { /* Initialize FLARToolKit. The 1st argument is a path for the camera corrective file. You can leave it as default. The 2nd argument is a definition file for your marker pattern. You need to define the pattern for the marker. e.g. flarlogo.pat is for flarlogo-marker.pdf. */ this.init(‘Data/camera_para.dat’, ‘Data/flarlogo.pat’); }</p><p>/* Once init call is successfully over, this function is called. You can adjust 3D objects here. If you can’t call this function and an error message comes out, you should check the availability of pattern-file OR condition of your Webcam. */ (continued)</p><p>525 Part IV: Extending PV3D and Beyond</p><p>(continued) protected override function onInit():void { super.onInit();</p><p>/* This code discriminates if the marker works fine or not. They publish the plane as an overlay of the marker pattern, as the same size of the marker. */</p><p> var wmat:WireframeMaterial = new WireframeMaterial(0xff0000, 1, 2); this._plane = new Plane(wmat, 80, 80); // 80mm x 80mm /* Papervision3D and FLARToolKit have an opposite coordinate system. This code adjusts the PV3D coordinate system. */ this._plane.rotationX = 180; //Adds dummy wireframe plane used to see if FLARTooKit is working this._baseNode.addChild(this._plane); //Instantiates the PV3D light source for your material var light:PointLight3D = new PointLight3D(); light.x = 0; light.y = 1000; light.z = -1000;</p><p>// Instantiates your cube and creates its materials var fmat:FlatShadeMaterial = new FlatShadeMaterial(light, 0xff22aa, 0x75104e); this._cube = new Cube(new MaterialsList({all: fmat}), 40, 40, 40); this._cube.z = 20; //Add the cube to the baseNode /* Papervision3D normally uses addChild to add an object to a Scene3D, FLARToolKit needs to use addChild to add an object to a special node which controls the 3D object following the marker. The special node is _baseNode. Once you use addChild to add your object to _baseNode, the object follows the marker automatically. */ this._baseNode.addChild(this._cube); }}} </p><p>Now that you ’ ve gone through Saqoosha ’ s basic tutorial, it ’ s time to do something a little more elaborate. Such as play a game in augmented reality. </p><p>526 Chapter 14: Taking Virtual Tours</p><p>Playing a Game in Augmented Reality Before you begin putting your game into augmented reality there ’ s a little work to be done to Saqoosha ’ s example code above. In its present form, it ’ s not very useful as starter code. You need to put it into BasicView format so you can use it for any PV3D application more easily. In the following section, you fix it up so you can use it to start any PV3D project. Creating BasicView Starter Code Transforming Saqoosha ’ s code into PV3D BasicView starter code takes a little work. Essentially, five basic methods are involved: </p><p>❑ setupFlar — this method takes your marker and camera correction files and creates the Flar camera. It also creates your Flar node and adds it to your PV3D scene. ❑ setupWebCam — this method creates your camera and camera display object and attaches your camera to that display object. It then attaches your camera to the viewport and sets its mode. ❑ setupBitmap — this method is the heart of the Flar process. On every frame you take a bitmap data shot of the webcam and pass it to the Flar tool kit and the Flar tool kit detects your pattern in that data. ❑ setupPV3D — this method sets up your PV3D objects. It also places those objects in a display object class which is then placed in the Flar node. ❑ startRendering — this method captures your webcam data in a bitmapData object and sends it to your Flar detector which finds its pattern. It then gets the transformation data from the FLARToolKit and applies it to your 3D scene. </p><p>Sorting all this out takes a little work, but it ’ s not any more complicated than anything you ’ ve already done in this book. Essentially, you integrate the FLARToolKit into PV3D using the methods above. The rest is just good old PV3D, and you can add animation to your objects as shown in Figure 14.5 (where the cube is both rotating and expanding). </p><p>Figure 14-5</p><p>527 Part IV: Extending PV3D and Beyond</p><p>As shown in the code snippet here, everything is PV3D as usual once you integrate the FLARToolKit using the methods discussed previously. However, there ’ s a big difference in the render method. The PV3D camera is replaced by the Flar camera. So what you see comes from the eyes of Flar. And that ’ s pretty cool! </p><p>//Add a little Animation to your Cube oscParam+=4; oscParam%=360; this._cube.rotationZ+=4; this._cube.rotationY+=3; this._cube.scaleZ=2+Math.sin(oscParam*Math.PI/180); this._cube.scaleY=this._cube.scaleX=2-Math.sin(oscParam*Math.PI/180);</p><p>//Render your scene renderer.renderScene(scene, flarCam3D, viewport); </p><p>The following code has been extensively documented and is meant as starter code for any typical BasicView Flar project in PV3D: </p><p> package { //Flash Imports import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; import flash.media.Camera; import flash.media.Video; import flash.utils.ByteArray;</p><p>//FLAR Imports import org.libspark.flartoolkit.core.FLARCode; import org.libspark.flartoolkit.core.param.FLARParam; import org.libspark.flartoolkit.core.raster.rgb.FLARRgbRaster_BitmapData; import org.libspark.flartoolkit.core.transmat.FLARTransMatResult; import org.libspark.flartoolkit.detector.FLARSingleMarkerDetector; import org.libspark.flartoolkit.pv3d.FLARBaseNode; import org.libspark.flartoolkit.pv3d.FLARCamera3D;</p><p>//PV3D Imports import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.*; import org.papervision3d.view.BasicView;</p><p>//Create your SWF metadata [SWF(width=”640”, height=”480”, backgroundColor=”#000000”, frameRate=”30”)] public class BasicViewFLAR extends BasicView { //Embed your Marker and Camera correction data [Embed(source=”assets/data/camera_para.dat”, mimeType=”application/octet-stream”)]</p><p>528 Chapter 14: Taking Virtual Tours private var CamParams:Class; //Marker that your FLARToolKit will detect [Embed(source=”assets/data/flarlogo.pat”, mimeType=”application/octet-stream”)] private var MarkerPat:Class; //Create a display object to hold your PV3D objects private var myDisplayObject:DisplayObject3D= new DisplayObject3D(); //Camera properties private static const WIDTH:Number = 640; private static const HEIGHT:Number = 480; private static const FRAMES_PER_SECOND:int = 60; private var camParams:FLARParam; private var markerPat:FLARCode; private var raster:FLARRgbRaster_BitmapData; private var detector:FLARSingleMarkerDetector; private var webCam:Camera; private var camDisplay:Video; private var capture:BitmapData; //FLAR properties private var flarCam3D:FLARCamera3D; private var flarNode:FLARBaseNode; private var transResult:FLARTransMatResult; private var oscParam:Number=0; //PV3D primitives private var _plane:Plane; private var _cube:Cube; //Start up BasicView and initiate your program public function BasicViewFLAR():void { super(WIDTH * 2, HEIGHT * 2, false); init(); } //Run your initiation functions private function init():void { setupFlar(); setupWebCam();</p><p>/*Set Up Bitmap is the heart of the Flar process. On every frame you take a bitmap data shot of the webcam and pass it to the FLARToolKit and the FLARToolKit detects your pattern in that data.*/ setupBitmap(); setupPV3D(); startRendering(); } //Set up FLAR private function setupFlar():void { camParams = new FLARParam(); camParams.loadARParam(new CamParams() as ByteArray); markerPat = new FLARCode(16, 16); (continued)</p><p>529 Part IV: Extending PV3D and Beyond</p><p>(continued) markerPat.loadARPatt(new MarkerPat()); //Instantiate your FLAR camera and node flarCam3D = new FLARCamera3D(camParams); flarNode = new FLARBaseNode(); //Add your FLAR node to your PV3D scene scene.addChild(flarNode); transResult = new FLARTransMatResult(); }</p><p>//Set up your Camera private function setupWebCam():void { //Instantiate your camera camDisplay = new Video(); camDisplay.width = WIDTH; camDisplay.height = HEIGHT; webCam = Camera.getCamera(); //Attach Camera camDisplay.attachCamera(webCam); //Add video component to the stage addChildAt(camDisplay, getChildIndex(viewport)); webCam.setMode(WIDTH / 2, HEIGHT / 2, FRAMES_PER_SECOND); } //Capture your Camera scene private function setupBitmap():void { capture = new BitmapData(camDisplay.width, camDisplay.height, false, 0x0); //Draw video object that contains your webcam capture.draw(camDisplay); //Two import FLAR objects that will let you detect your pattern raster = new FLARRgbRaster_BitmapData(capture); //Detect where the pattern is found detector = new FLARSingleMarkerDetector(camParams, markerPat, 80); }</p><p>//Create your PV3D objects private function setupPV3D():void {</p><p>//Viewport fix from Saqoosha due to camera corrections viewport.x = -4; //Create your wireframe plane that will overlay your pattern var wmat:WireframeMaterial = new WireframeMaterial(0xff0000, 1, 2); this._plane = new Plane(wmat, 80, 80); // 80mm x 80mm </p><p>//Instantiate the PV3D light source for your material var light:PointLight3D = new PointLight3D(); light.x = 0; light.y = 1000; light.z = -1000;</p><p>// Instantiate your cube and create its materials var fmat:FlatShadeMaterial = new FlatShadeMaterial(light, 0xff22aa, 0x75104e);</p><p>530 Chapter 14: Taking Virtual Tours</p><p> this._cube = new Cube(new MaterialsList({all: fmat}), 40, 40, 40); this._cube.z = 20; //Add your cube and plane to your display object myDisplayObject.addChild(_plane); myDisplayObject.addChild(_cube); //Rotate your plane into position this._plane.rotationX = 180; //Add your displayobject to your FLAR Node flarNode.addChild(myDisplayObject);</p><p>}</p><p>//Start rendering your PV3D scene override protected function onRenderTick(event:Event = null):void { //Capture bitmap data from the webcam it’s now stored inside of bitmapdata capture.draw(camDisplay); //Add a try-catch to keep errors from shutting you down try { //Pass in the raster object which has the bitmapdata and passes it to FLAR if(detector.detectMarkerLite(raster, 80) & & detector.getConfidence() > 0.5) { //Get the transformation from the FLARToolKit and apply it to the 3D scene detector.getTransformMatrix(transResult); flarNode.setTransformMatrix(transResult);</p><p> flarNode.visible = true; }else { flarNode.visible = false; }} catch(e:Error){trace(“Catch a falling error and put it in your pocket!”);}</p><p>//Add a little Animation to your Cube oscParam+=4; oscParam%=360; this._cube.rotationZ+=4; this._cube.rotationY+=3; this._cube.scaleZ=2+Math.sin(oscParam*Math.PI/180); this._cube.scaleY=this._cube.scaleX=2-Math.sin(oscParam*Math.PI/180);</p><p>//Render your scene renderer.renderScene(scene, flarCam3D, viewport); }}} </p><p>The marker used for this project (shown in Figure 14.3) is pretty boring and most likely you ’ ll want to do some corporate branding for your FLAR projects. Generating Your Own Markers When you open up a marker file you see four sets of 16 � 48 matrices, which is your marker from four different directions. Your marker is seen by FLAR as 16 � 16 barcode. The reason the matrix is 16 � 48 is that you need to triple its size to account for each RGB color: that ’ s 16 � 16 three times or 16 � 48. Luckily there have been a few marker generators released on the web that help you create your own markers automatically. </p><p>531 Part IV: Extending PV3D and Beyond</p><p>Tarotaro has a great marker maker on their blog at http://flash.tarotaro.org/blog/2008/ 12/14/artoolkit-marker-generator-online-released/ . </p><p>It runs online so there is no AIR installation (which means you need the Flash 10 player to save the marker file to your hard drive). The application gives you a preview of your marker, lets you choose marker mode or load a marker image from your hard drive, has a “get pattern ” button, and lets you adjust your marker segments and sides as shown in Figure 14.6. </p><p>Figure 14-6</p><p>Here ’ s how you use it: </p><p>1. Design your original marker and print it. 2. Open ARToolKit Marker Generator Online. 3. Set segments and marker size. 4. Point your webcam at the printed marker, or load a file generated in Photoshop (for example). 5. Push Get Pattern button when a red line encloses the marker. 6. Preview your marker image. 7. Push Save button to save your pattern file *.pat . You must add the .pat extension to your file. </p><p>Here are two important tips: </p><p>When you use this program you must make sure that you add the extension .pat to your file when saving it. Flash 10 doesn ’ t add the file extension automatically when you save. Also, when loading an image file make sure that you have your large black rectangle surrounded by white, as shown in Figure 14.6, or you won ’ t get a correct marker capture. </p><p>532 Chapter 14: Taking Virtual Tours</p><p>In addition to using the online version, you can obtain the marker generator source code from the SVN given earlier and create your own generator. The code is located in the a3/ARToollKitMarkerGenerator folder. </p><p>You now add Jiglib physics to FLAR – thanks to polymorphism! Adding Jiglib Physics to FLAR Employing the starter code above makes it really easy to add Jiglib physics to FLAR. All you have to do is place everything into your display object (myDisplayObject ), which is then placed into your FLAR Node ( flarNode ) object. </p><p>As an example of how this works, you add the Jiglib Hello World example of Chapter 9 to FLAR. Here ’ s what you do: </p><p>1. Create a new project with your FLAR starter code and open up the Jiglib Hello World example from Chapter 9 as a reference. You’ ll be transferring code from this app to your FLAR starter code. 2. Transfer the Jiglib folder so you have all the physics classes in your starter code. 3. Transfer all the imports, embed statement(s), and properties from the Jiglib Hello World to your FLAR starter code. Go ahead and test your program to see if there are any errors. Every time you make a change to Flash or Flex you should test it to make sure that you haven ’ t introduced an error. Making five or six changes and then testing may leave you wondering which change created the error. 4. Add interactivity to your super method by changing its third augment to true. </p><p> super(WIDTH * 2, HEIGHT * 2, true, false);</p><p>5. Add all the appropriate function calls, key listeners, point light(s), and the physics PV3D plugin to your constructor function as shown in the following code snippet: </p><p> public function JiglibFLAR():void { super(WIDTH * 2, HEIGHT * 2, true, false); //KeyListeners stage.addEventListener( KeyboardEvent.KEY_DOWN, myKeyDown ); stage.addEventListener( KeyboardEvent.KEY_UP, myKeyUp ); //Set Light sceneLight = new PointLight3D(true, true); sceneLight.x = 10; sceneLight.y = 300; sceneLight.z = -400;</p><p>// Initialize the Papervision3D physics plugin physics = new Papervision3DPhysics(myDisplayObject, 7); //Initiate Listeners createFloor();</p><p> createHelloSphere();</p><p> init(); }</p><p>533 Part IV: Extending PV3D and Beyond</p><p>And here ’ s the big trick! In the Papervision3DPhysics method, replace the scene with myDisplayObject . This puts all your Jiglib objects into your display object. Then the display object is placed into your flarNode , which is placed into your scene. physics = new Papervision3DPhysics(myDisplayObject, 7);</p><p>In seeing how easy this is, you should really gain an appreciation of how powerful polymorphism truly is. 6. Now place all the appropriate methods from your Jiglib code into your starter code (key methods, create floor method, and createHelloSphere method). 7. Finally, everywhere you see scene.addChild in your code, change it to myDisplayObject. addChild . That ’ s all there is to it. When you run the program and place your marker in front of your webcam you now have the key controlled Jiglib Hello World in FLAR as shown in Figure 14.7. </p><p>Figure 14-7</p><p>The entire source code is as follows: </p><p> package { //Flash Imports import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; import flash.media.Camera; import flash.media.Video;</p><p>534 Chapter 14: Taking Virtual Tours import flash.utils.ByteArray; import flash.events.KeyboardEvent; import flash.ui.Keyboard;</p><p>//Jiglib Flash import jiglib.geometry.JSphere; import jiglib.math.JNumber3D; import jiglib.physics.RigidBody; import jiglib.plugin.papervision3d.Papervision3DPhysics; import jiglib.plugin.papervision3d.Pv3dMesh;</p><p>//FLAR Imports import org.libspark.flartoolkit.core.FLARCode; import org.libspark.flartoolkit.core.param.FLARParam; import org.libspark.flartoolkit.core.raster.rgb.FLARRgbRaster_BitmapData; import org.libspark.flartoolkit.core.transmat.FLARTransMatResult; import org.libspark.flartoolkit.detector.FLARSingleMarkerDetector; import org.libspark.flartoolkit.pv3d.FLARBaseNode; import org.libspark.flartoolkit.pv3d.FLARCamera3D;</p><p>//PV3D Imports import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.*; import org.papervision3d.cameras.CameraType; import org.papervision3d.cameras.SpringCamera3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.view.layer.util.ViewportLayerSortMode; import org.papervision3d.view.BasicView;</p><p>//Create your SWF metadata [SWF(width=”640”, height=”480”, backgroundColor=”#000000”, frameRate=”30”)] public class JiglibFLAR extends BasicView {</p><p>[Embed(source=”assets/helloearth.jpg”)] public var EarthTexture:Class;</p><p>//Variable Declaration private var physics:Papervision3DPhysics; private var sphereObject:Sphere; private var helloSphere:RigidBody; private var moveForce:Number = 6; private var springCamera:SpringCamera3D; private var cameraTarget:DisplayObject3D; private var sceneLight:PointLight3D; (continued)</p><p>535 Part IV: Extending PV3D and Beyond</p><p>(continued) //Key Listeners private var keyRight:Boolean = false; private var keyLeft:Boolean = false; private var keyForward:Boolean = false; private var keyReverse:Boolean = false; private var keyUp:Boolean = false;</p><p>//Embed your Marker and Camera correction data [Embed(source=”assets/data/camera_para.dat”, mimeType=”application/octet-stream”)] private var CamParams:Class; //Marker that your FLARToolKit will detect [Embed(source=”assets/data/flarlogo.pat”, mimeType=”application/octet-stream”)] private var MarkerPat:Class; //Create a display object to hold your PV3D objects private var myDisplayObject:DisplayObject3D= new DisplayObject3D(); //Camera properties private static const WIDTH:Number = 640; private static const HEIGHT:Number = 480; private static const FRAMES_PER_SECOND:int = 60;</p><p> private var camParams:FLARParam; private var markerPat:FLARCode; private var raster:FLARRgbRaster_BitmapData; private var detector:FLARSingleMarkerDetector;</p><p> private var webCam:Camera; private var camDisplay:Video; private var capture:BitmapData; //FLAR properties private var flarCam3D:FLARCamera3D; private var flarNode:FLARBaseNode;</p><p> private var transResult:FLARTransMatResult; private var oscParam:Number=0; //PV3D primitives private var _plane:Plane; private var _cube:Cube;</p><p>//Start up BasicView and initiate your program public function JiglibFLAR():void { super(WIDTH * 2, HEIGHT * 2, true, false); //KeyListeners stage.addEventListener( KeyboardEvent.KEY_DOWN, myKeyDown ); stage.addEventListener( KeyboardEvent.KEY_UP, myKeyUp ); //Set Light sceneLight = new PointLight3D(true, true); sceneLight.x = 10; sceneLight.y = 300; sceneLight.z = -400;</p><p>// Initialize the Papervision3D physics plugin physics = new Papervision3DPhysics(myDisplayObject, 7);</p><p>536 Chapter 14: Taking Virtual Tours</p><p>//Initiate Listeners createFloor(); createHelloSphere(); init(); }</p><p>//Create your floor private function createFloor():void { var myFloor:Plane = new Plane(new WireframeMaterial(0xFFFFFF), 10000, 10000, 10000*0.001, 10000*0.001); myFloor.rotationX = 90; myFloor.y = -150; myDisplayObject.addChild(myFloor); //Generate ground from PV3D Plugin physics.createGround(new WireframeMaterial(0xFFFFFF, 0), 1800, 0);</p><p>}</p><p>//Create Hello World Sphere private function createHelloSphere():void { //Define Material for Hello World Sphere var helloWorld:BitmapMaterial = new BitmapMaterial(Bitmap(new EarthTexture()). bitmapData, true); helloWorld.smooth = true; sphereObject = new Sphere(helloWorld, 120, 12, 12); myDisplayObject.addChild(sphereObject); //Instantiate Jiglib sphere and attach PV3D Sphere helloSphere = new JSphere(new Pv3dMesh(sphereObject), 100); helloSphere.y = 180; helloSphere.rotationY+=60; helloSphere.restitution = 3; helloSphere.mass = 1 physics.addBody(helloSphere); //Define DisplayObject3D cameraTarget cameraTarget = new DisplayObject3D(); cameraTarget.copyPosition(sphereObject); myDisplayObject.addChild(cameraTarget); }</p><p>//Key Handler Methods //myKeyUp Method private function myKeyUp(event:KeyboardEvent):void { switch(event.keyCode) {</p><p>(continued)</p><p>537 Part IV: Extending PV3D and Beyond</p><p>(continued) case Keyboard.UP: keyForward = false; break;</p><p> case Keyboard.DOWN: keyReverse = false; break;</p><p> case Keyboard.LEFT: keyLeft = false; break;</p><p> case Keyboard.RIGHT: keyRight = false; break; case Keyboard.SPACE: keyUp=false; }}</p><p>//myKeyDown Method private function myKeyDown(event:KeyboardEvent):void { switch(event.keyCode) { case Keyboard.UP: keyForward = true; keyReverse = false; break;</p><p> case Keyboard.DOWN: keyReverse = true; keyForward = false; break;</p><p> case Keyboard.LEFT: keyLeft = true; keyRight = false; break;</p><p> case Keyboard.RIGHT: keyRight = true; keyLeft = false; break;</p><p> case Keyboard.SPACE: keyUp = true; break; }}</p><p>//Run your initiation functions private function init():void {</p><p>538 Chapter 14: Taking Virtual Tours setupFlar(); setupWebCam();</p><p>//Set Up Bitmap is the heart of the FLAR process. setupBitmap(); setupPV3D(); startRendering(); } //Set up FLAR private function setupFlar():void { camParams = new FLARParam(); camParams.loadARParam(new CamParams() as ByteArray); markerPat = new FLARCode(16, 16); markerPat.loadARPatt(new MarkerPat()); //Instantiate your FLAR camera and node flarCam3D = new FLARCamera3D(camParams); flarNode = new FLARBaseNode(); //Add your FLAR node to your PV3D scene scene.addChild(flarNode); transResult = new FLARTransMatResult(); }</p><p>//Set up your Camera private function setupWebCam():void { //Instantiate your camera camDisplay = new Video(); camDisplay.width = WIDTH; camDisplay.height = HEIGHT; webCam = Camera.getCamera(); //Attach Camera camDisplay.attachCamera(webCam); //Add video component to the stage addChildAt(camDisplay, getChildIndex(viewport)); webCam.setMode(WIDTH / 2, HEIGHT / 2, FRAMES_PER_SECOND); } //Capture your Camera scene private function setupBitmap():void { capture = new BitmapData(camDisplay.width, camDisplay.height, false, 0x0); //Draw video object that contains your webcam capture.draw(camDisplay); //Import FLAR objects that will let you detect your pattern //Pass in the Camera Data raster = new FLARRgbRaster_BitmapData(capture); //Detect whether the pattern is found detector = new FLARSingleMarkerDetector(camParams, markerPat, 80); }</p><p>//Create your PV3D objects private function setupPV3D():void { (continued)</p><p>539 Part IV: Extending PV3D and Beyond</p><p>(continued) //Viewport fix from Saqoosha due to Camera corrections viewport.x =100;</p><p>//Add your displayobject to your FLAR Node this.myDisplayObject.rotationX=90; flarNode.addChild(myDisplayObject); myDisplayObject.z=-500;</p><p>}</p><p>//Start rendering your PV3D scene override protected function onRenderTick(event:Event = null):void { //Capture bitmap data from the webcam, it’s now stored inside of bitmapdata capture.draw(camDisplay); //Add a try-catch to keep errors from shutting you down try {//pass in raster object which has the bitmapdata and passes it to FLAR if(detector.detectMarkerLite(raster, 80) & & detector.getConfidence() > 0.5) { //Get transformation from FLARToolKit and apply (set) it to the 3D scene detector.getTransformMatrix(transResult); flarNode.setTransformMatrix(transResult);</p><p> flarNode.visible = true; }else { flarNode.visible = false; }} catch(e:Error){trace(“Catch a falling error and put it in your pocket!”);}</p><p>//Jiglib Code</p><p> if(keyLeft) { helloSphere.addWorldForce(new JNumber3D(0, 0, moveForce), helloSphere.currentState.position); } if(keyRight) { helloSphere.addWorldForce(new JNumber3D(0, 0,-moveForce), helloSphere.currentState.position); } if(keyForward) { helloSphere.addWorldForce(new JNumber3D(moveForce,0,0 ), helloSphere.currentState.position); } if(keyReverse) {</p><p>540 Chapter 14: Taking Virtual Tours</p><p> helloSphere.addWorldForce(new JNumber3D(-moveForce, 0, 0 ), helloSphere.currentState.position); } if(keyUp) { helloSphere.addWorldForce(new JNumber3D(0, 2*moveForce, 0), helloSphere.currentState.position); }</p><p>//Execute rendering process physics.step(); //myDisplayObject.copyPosition(helloSphere);</p><p>//Render your scene renderer.renderScene(scene, flarCam3D, viewport); }}} </p><p>The orientation of your pattern influences the direction of your ball directional keys. For example, if you rotate your marker by 180 degrees your keys work in an opposite way to how they did before. </p><p>If you ’ re bored with just one marker at a time, don ’ t fret … you can have multiple markers. And the place to go to find out more (besides the book ’ s website) is www.squidder.com . Creating Multiple Markers Generating multi - FLAR markers is similar to creating a particle system. You extend the single marker FLAR class, create an array of markers, and iterate over that array each iteration of your scene. The results are shown in Figure 14.8. </p><p>Figure 14-8</p><p>541 Part IV: Extending PV3D and Beyond</p><p>The code snippet that generates the results array for the multiple markers is as follows: </p><p> override protected function _detectMarkers(): void {</p><p>_resultsArray = _flarDetector.updateMarkerPosition( _flarRaster, 80, .5 );</p><p> for ( var i: int = 0; i < _resultsArray.length; i ++ ) { var subResults: Array = _resultsArray[ i ]; for ( var j: * in subResults ) { _flarDetector.getTransmationMatrix( subResults[ j ], _resultMat ); if ( _cubes[ i ][ j ] != null ) transformMatrix( _cubes[ i ][ j ], _resultMat ); }} </p><p>You can download the complete source code and read a brief explanation of its creation from http:// www.squidder.com/2009/03/06/flar-how-to-multiple-instances-of-multiple-markers/. </p><p>Well – after all of that, you ’ re finally ready to add a game to FLAR. Adding Pong to FLAR Putting Pong into FLAR is accomplished in the same way that you put the Jiglib Hello World above into FLAR. Start by opening up the Pong app from Chapter 10 on games and the FLAR Jiglib Hello World app above simultaneously in Flex. This way you can copy and paste the needed methods from one app to the other. Then go through the following steps: </p><p>1. Rename the FLAR Jiglig app to PongFLAR . 2. Transfer the Lively folder that contains the Skybox and Arena classes and place the appropriate import statements in the PongFLAR code. </p><p>//Lively Imports import org.lively3d.jiglib.Arena; import org.lively3d.jiglib.Skybox;</p><p>3. Transfer the Embed assets files and code. </p><p>//Sound [Embed(source=”assets/sound/dink.mp3”)] public var hitSoundN:Class; [Embed(source=”assets/sound/collision.mp3”)] public var scoreSound:Class; [Embed(source=”assets/sound/win.mp3”)] public var wonSound:Class; [Embed(source=”assets/sound/gameOver.mp3”)] public var lostSound:Class;</p><p>4. Add the appropriate Pong game properties. </p><p>//Pong Game public var score1:int=0; public var score2:int=0; public var arena: Arena; public var yaw: Number = 0;</p><p>542 Chapter 14: Taking Virtual Tours</p><p> public var targetYaw: Number = 0; public var pitch: Number = 0; public var targetPitch: Number = 0;</p><p> public var isMouseDown: Boolean = false; public var lastMousePoint: Point;</p><p> public const SCREEN_WIDTH: int = 800; public const SCREEN_HEIGHT: int = 500;</p><p> public const GAME_WIDTH:int = 600; public const GAME_HEIGHT:int = 400; private var mySkybox:Skybox = new Skybox();</p><p> private var mylight:PointLight3D;</p><p> private var myHeight :Number=300; private var myWidth :Number=600; private var myDepth :Number=400; private var ballBody:RigidBody; private var bar1:RigidBody; private var bar2:RigidBody; private var scorePlayer1:int=0; private var scorePlayer2:int=0; private var hitOnce:Boolean=true;</p><p> private var shadeMateria:FlatShadeMaterial; private var vplObjects:ViewportLayer;</p><p> private var materiaList:MaterialsList; private var checkSpeed:Timer; private var oldX1:Number=0; private var oldX2:Number=0; private var myNum1:int=0; private var myNum2:int=0;</p><p> private var targetX:Number; private var targetY:Number; private var easing:Number=7;</p><p> private var myTextField:TextField; private var hisTextField:TextField; private var myMessage:TextField; private var levelInt:int=1; private var ballSpeedIs:Number=60;</p><p>5. Add the method required to initiate all the PV3D game objects. Change the scene.addChild method to myDisplayObject.addChild and make sure that the PV3D physics objects reference the myDisplayObject as opposed to the scene object. </p><p>543 Part IV: Extending PV3D and Beyond</p><p> private function initObjects():void { // Create all the game objects arena = new Arena(myWidth, myDepth); //Change from scene.addChilde to myDisplayObject.addChild myDisplayObject.addChild(arena); //Change the scene reference to myDisplayObject physics = new Papervision3DPhysics(myDisplayObject, 8);</p><p> shadeMateria = new FlatShadeMaterial(mylight, 0x77ee77); var myWire:WireframeMaterial=new WireframeMaterial(0x000000); myWire.doubleSided=true;</p><p> physics.createPongBoundingBox(myWire, myWidth, myDepth, myHeight,0,-10,0);</p><p> vplObjects = new ViewportLayer(viewport,null); vplObjects.layerIndex = 2; vplObjects.sortMode = ViewportLayerSortMode.Z_SORT; viewport.containerSprite.addLayer(vplObjects); shadeMateria = new FlatShadeMaterial(mylight, 0xff6666); //Create the ball shadeMateria.interactive = true; ballBody = physics.createSphere(shadeMateria, 16, 12, 12); ballBody.mass = 1; ballBody.friction=.2; ballBody.restitution=.8;</p><p> ballBody.moveTo(new JNumber3D( -100, 0, -100)); vplObjects.addDisplayObject3D(physics.getMesh(ballBody));</p><p> shadeMateria = new FlatShadeMaterial(mylight, 0xffff44);</p><p> materiaList = new MaterialsList(); materiaList.addMaterial(shadeMateria,”all”); //Create the deflection bars bar1 = physics.createCube(materiaList, 16, 86, 32); bar1.moveTo(new JNumber3D(-280, 0, 0)); bar1.movable=false; bar1.restitution=.8; bar1.friction=.2;</p><p> vplObjects.addDisplayObject3D(physics.getMesh(bar1));</p><p> bar2 = physics.createCube(materiaList, 16, 86, 32); bar2.moveTo(new JNumber3D(280, 0, 0)); bar2.movable=false; bar2.restitution=.8; bar2.friction=.2;</p><p>544 Chapter 14: Taking Virtual Tours</p><p> vplObjects.addDisplayObject3D(physics.getMesh(bar2)); //Add a ball to a random position ballBody.addWorldForce(new JNumber3D(300-600*Math.random(),0,100-200*Math .random()),ballBody.currentState.position); }</p><p>6. Add the textbox method that keeps score and shows the game level. Keep the addChild method the same – don ’ t add them to your display object as was done previously. This adds your textboxes to the stage as opposed to the game display object. </p><p> private function addTextFields():void{</p><p> myTextField=new TextField(); hisTextField = new TextField(); myMessage = new TextField();</p><p> myMessage.y=20; myMessage.x=400; myMessage.scaleX=1.5; myMessage.scaleY=1.5; myMessage.textColor=0xffffff; myMessage.text=”Level “+String(levelInt)+” Game Goes to 21”;</p><p> myTextField.y=hisTextField.y=50; myTextField.x=400; hisTextField.x=520; myTextField.textColor=0xffffff; hisTextField.scaleX=1.5; hisTextField.scaleY=1.5; myTextField.scaleX=1.5; myTextField.scaleY=1.5; myTextField.textColor=0xffffff; hisTextField.textColor=0xffffff;</p><p> myTextField.text=”My Score: “+String(score1); hisTextField.text=”Computer Score: “+String(score2);</p><p> addChild(myTextField); addChild(hisTextField); addChild(myMessage); myMessage.maxChars=400; myMessage.width=400; }</p><p>545 Part IV: Extending PV3D and Beyond</p><p>7. Add your ball collision method. </p><p> private function checkCollisions():void {</p><p> var myTang:Number=Math.atan2(ballBody.currentState.linVelocity.z,ballBody.currentState. linVelocity.x); ballBody.currentState.linVelocity.x=Math.cos(myTang)*ballSpeedIs; ballBody.currentState.linVelocity.z=Math.sin(myTang)*ballSpeedIs; if(ballBody.hitTestObject3D(bar1)){</p><p> if(hitOnce){ hitOnce=false; (new hitSoundN() as Sound).play(); }}</p><p> if(ballBody.hitTestObject3D(bar2)){ if(hitOnce){ hitOnce=false; (new hitSoundN() as Sound).play(); }}</p><p> if(ballBody.x < myWidth/2-100 & & ballBody.x > 100){ changeBoolean(); }}</p><p> private function changeBoolean():void{ hitOnce=true; }</p><p>8. Add your paddle methods. </p><p> private function moveAiPaddle():void { //Very simple AI for the computer-controlled paddle. </p><p> targetY=(ballBody.z+43);</p><p> bar2.z +=(targetY-bar2.z)/easing; bar2.x=280; bar2.rotationY=0; }</p><p> public function movePlayerPaddle():void { if((bar1.z < myDepth/2-34)||(bar1.z > -myDepth/2+34)){</p><p> targetX=myDepth/2-mouseY+43; bar1.z +=(targetX-bar1.z)/easing;</p><p> bar1.x =-280; bar1.rotationY=0;</p><p>}} 546 Chapter 14: Taking Virtual Tours</p><p>9. In this final stage you have to do a little work. You need to extract from your game loop the appropriate code to insert into your onRenderTick method. This is the heart of your game engine: </p><p>//Pong Jiglib Code to be placed in your onRenderTick method</p><p> moveAiPaddle(); movePlayerPaddle();</p><p> checkCollisions(); //Iterate your physics engine physics.engine.integrate(0.2);</p><p> if(ballBody.x > myWidth/2){ score1++; myTextField.text=”My Score: “+String(score1); }</p><p> if(ballBody.x < -myWidth/2){ score2++; hisTextField.text=”Computer Score: “+String(score2); }</p><p> if(ballBody.x > myWidth/2||ballBody.x < -myWidth/2){ ballBody.addWorldForce(new JNumber3D(-600*Math.random(),0,200-400*Math .random()),ballBody.currentState.position);</p><p> ballBody.x=ballBody.y=ballBody.z=0;</p><p>(new scoreSound() as Sound).play();</p><p>}</p><p> if(score1==21){</p><p> levelInt++; (new wonSound() as Sound).play();</p><p> myMessage.text=”Up to Level “+String(levelInt)+” You Won: “+String(score1)+”/”+String(score2); score1=score2=0; ballSpeedIs+=10; easing-=1; if(levelInt==7){</p><p> score1=score2=0; myTextField.text=”My Score: “+String(score1); hisTextField.text=”Computer Score: “+String(score2); ballSpeedIs=60; easing=7; (new wonSound() as Sound).play(); (continued)</p><p>547 Part IV: Extending PV3D and Beyond</p><p>(continued) myMessage.text=”Level “+String(levelInt)+” You Beat the Computer: “+String(score1)+”/”+String(score2);</p><p> levelInt=1;</p><p>}}</p><p> if(score2==21){ levelInt=1; (new lostSound() as Sound).play();</p><p> myMessage.text=”Back to Level “+String(levelInt)+” You Lost: “+String(score1)+”/”+String(score2); score1=score2=0; myTextField.text=”My Score: “+String(score1); hisTextField.text=”Computer Score: “+String(score2); ballSpeedIs=60; easing=7; }</p><p>Now that was a bunch of code. But really all you did was copy and paste most of it directly from the Pong game to your PongFLAR application: when you run it, your Pong game should appear in augmented reality as shown in Figure 14.9. In the same way you can transfer practically any game over to FLAR. </p><p>Figure 14-9</p><p>In running the game, you may find that it lags a little, because you ’ ve really loaded your CPU. </p><p>548 Chapter 14: Taking Virtual Tours</p><p>Making FLAR Run Faster One of the problems with FLAR is that it ’ s very processor - intensive. And as you add more items to your renderer your fps slows down. Why? Because essentially, you ’ re taking an image capture of each video frame, analyzing that video frame to find a marker signature, and then rendering your display object on top of that marker. On his website, Saqoosha has suggested a number of ways to speed things up such as more efficient algorithms, use of Pixel Bender, and so on. The bottom line is that you ’ ve got to reduce your processing cycles while increasing your code execution efficiency. </p><p>The Pong game above was created using primitives. But for most games you ’ re going to import 3D models into FLAR and that ’ s easy to do. Putting a 3D Model into FLAR Bringing 3D models into FLAR is easy using the starter code developed earlier. In Figure 14.10, a Quake 2 (or md2) model is brought into FLAR. You use the same procedure for any 3D model format such as the Collada. </p><p>Figure 14-10</p><p>First, you import the appropriate parser class and then instantiate the parsed element. </p><p>//Import the appropriate classes import org.papervision3d.objects.parsers.MD2; import org.papervision3d.materials.BitmapFileMaterial; //Instantiate the parsed element private var item:MD2 = new MD2(); </p><p>Then you load the skin and model into the instantiated MD2 object (called item in this case), and finally you attach that object to your display object and attach that display object to your flarNode . </p><p>549 Part IV: Extending PV3D and Beyond</p><p>//Load your 3D model and skin var myHeliSkin:BitmapFileMaterial = new BitmapFileMaterial(‘assets/heli/ helicopter1.png’, true); //The load method includes (url string, material, framerate, scale) item.load(‘assets/heli/helicopter.md2’, myHeliSkin, 10, 3); item.scale=.3; // Add models to your display object myDisplayObject.addChild(item); //Add your displayobject to your FLAR Node flarNode.addChild(myDisplayObject); </p><p>You can also animate your MD2 object simply by calling it by name in your animation loop and applying a rotation. </p><p> this.item.rotationZ+=2; </p><p>Using the base code provided in this chapter you can bring practically anything into augmented reality just by attaching it to a display object and attaching that display object to your flarNode . Augmented reality is one of those super fun mind - blowers. It ’ s impressive and easy to use in PV3D. </p><p>Building 3D Worlds A few attempts have been made to create 3D multiplayer worlds in PV3D (similar to Second Life) using the Flash Media Server (or Red 5) technology. The key advantage to using the FMS is that you can easily inject chat, audio, video, and shared objects into a multiplayer stream (actually easier than you can in Second Life). But the downside is that as opposed to Second Life, Flash is a poor 3D animation engine, and even small polygon counts (around 2000) lug your processor. </p><p>To circumvent a few of the problems with vertex number, Sean McCracken, part of the Influxis team, has written Massive3d shown in Figure 14.11. Massive3d is a highly customizable 3D avatar chat environment which features: </p><p>❑ 3d Asset Management ❑ Fully customizable UI ❑ 3d and 2d User Chat ❑ Member List ❑ Collision Detection for City Assets </p><p>To get Massive3d you have to get an Influxis account at http://www.influxis.com/ . Influxis hosts the applications for you and provides you with a number of source files that you can use to customize it. In the development of Massive3d, Sean has used a number of optimization techniques that can be used in any such project of this magnitude. </p><p>550 Chapter 14: Taking Virtual Tours</p><p>Figure 14-11</p><p>Here are some of the key optimization features Sean used: </p><p>❑ The avatar assets were separated from the city assets for loading purposes and are encapsulated in swf files. The Flash FLA files are provided by Influxis so the avatars can be customized. Two animation sequences “ idle ” and “ walk ” are currently available. ❑ The city assets are loaded in the background while the user is selecting/personalizing their avatar (thus making a shorter load process for your app). ❑ Proximity based billboarding is used. What this does is, if the frames - per - second of the client drops too low, Massive3d starts to switch out the avatars from 3D to 2D based on Proximity to the user ’ s avatar. Basically, this system keeps swapping out the 3D avatars to 2D avatars until the system is at a level that can handle it. ❑ Background assets like trees, flowers, mountains, and clouds are created using 2D sprites. </p><p>Programs like Massive3d have a huge potential in education. They give you the ability to tailor a role - playing game (RPG) to enhance student interaction and accelerate learning. </p><p>Summary As technology advances, Virtual Reality is playing a key role. In this chapter, you examined the way VR is used in the military; the use of augmented reality, and how to build 3D worlds. The majority of the chapter treated augmented reality using the FLARToolkit that was created by Saqoosha. Saqoosha ’ s starter kit code was examined and extended to create PV3D BasicView starter code. Using this starter code, a number of items were imported in FLAR including the Jiglib Pong game created in the chapter on games. </p><p>551</p><p>Adding Services </p><p>Adding web services to your applications has never been easier than with Flash Builder. Flash Builder is a “ data - centric ” animal and connects services with just a click and a drag, eliminating much of the laborious programming required by past development tools. Adding Flash Catalyst to the mix enables you to build world - class interactive experiences in a matter of minutes. </p><p>So what ’ s a web service? In a nutshell, it ’ s essentially any piece of software that makes itself available over the Internet through XML. The basic web services platform is XML + HTTP, where XML provides the language that is used to communicate to different platforms. XML is used because it ’ s the most universally accepted cross - platform communication language. As tons of introductory articles about services are available on the web, the basics aren ’ t covered here. Instead, you jump right into the heart of the matter and start connecting up your services. </p><p>In this chapter you learn how to build interfaces using Flash Catalyst and then use Flash Builder to add connectivity. You start by building a simple Twitter app, then a Flickr app, then a weather app, and finally a CNN news reader. </p><p>You won ’ t spend much time building PV3D applications in this chapter, but instead learn how to institute 3D using Flash Catalyst from the Flickr app. </p><p>Tweeting The workflow advantage that can be achieved in Flash Catalyst is unprecedented. Literally, what used to take days to develop in Flash or Flex can be put together in just a matter of minutes in Flash Catalyst. In this section, you go straight to building a 3D twitter search application. In addition, a great place to learn the basics of Flash Catalyst and Flash Builder (beside the book ’ s website) is at Adobe Labs: http://labs.adobe.com/technologies/flash/videos/.</p><p>The work flow for this project goes first from Photoshop, then to Flash Catalyst, and finally to Flash Builder. You start by building your layout in Photoshop. Part IV: Extending PV3D and Beyond</p><p>Creating Your Layout in Photoshop Creating a twitter search application begins in Photoshop, where you lay out the elements as shown in Figure 15.1. Your design is fairly simple; it consists of a cloud application background and a list box. Photoshop has no automatic list box, so you need to layout the application graphically in layers: including all of its elements such as scroll bar track, scroll button, and control arrows. The complete layout for the application consists of the following layers: </p><p>❑ Search Text ❑ Search Button ❑ Item Renderer ❑ Collapsible Items ❑ Scroll Bar ❑ Backgrounds </p><p>Everything in the layers and their folders are just simple images or text elements created from Photoshop tools. </p><p>Figure 15 -1 </p><p>Now that you have all your graphics arranged in Photoshop, you import them into Flash Catalyst and make them interactive. </p><p>Adding Interactivity in Flash Catalyst Once you ’ ve laid out your site in Photoshop (or Illustrator) it ’ s pretty easy to bring it into Flash Catalyst. After opening up Flash Catalyst, select From Adobe Photoshop PSD File from the default menu that ’ s displayed and import the Photoshop creation named CloudTheme. The PSD importer is found in the Create New Project from Design File menu as shown in Figure 15.2. </p><p>554 Chapter 15: Adding Services</p><p>Figure 15 -2 </p><p>When Flash Catalyst imports your PSD file, it completely reproduces your application ’ s layer structure and graphical layout, and it creates all the necessary to reproduce your project in Flash Builder. </p><p>In addition to reproducing your exact Photoshop layout, a few important distinctions need to be made: </p><p>The items in your layout can now be clicked on and dragged around the screen just as you would do in Flash. As you click on an item, its layer is highlighted. And if you select an item in a folder, the selected item is highlighted in the folder structure as shown in Figure 15.3. </p><p>Figure 15 -3 </p><p>And here ’ s the kicker – in addition to a Flash - like environment, complete with layers and drawing tools, there ’ s an event - based state engine, which is like states on steroids. It comes complete with a HUD (heads up display), effects timeline, Photoshop layers, and a properties inspector. You now investigate each one of these more closely by adding interactivity to your application. Creating a Scroll Bar Your scroll bar is made up of the four elements found inside the ScrollBar layer: up arrow, down arrow, scroll track, and a scroll button. To create your scroll bar you select all the scroll bar elements simultaneously by selecting the ScrollBar folder, and clicking on Convert Artwork to Component in the HUD. Then select Vertical Scrollbar as shown in Figure 15.4. </p><p>555 Part IV: Extending PV3D and Beyond</p><p>Figure 15-4</p><p>At this point your component has been created but its individual pieces need to be assigned. You do this by double - clicking your scroll bar, which takes you into the scroll state, and then selecting each element of the scroll bar. Once you ’ ve selected an element, you can assign it to the proper part of the component by using the HUD and choosing Convert Artwork to Vertical Scrollbar Part and choosing the respective part. </p><p>Figure 15-5</p><p>In Figure 15.5, the up arrow graphic is associated with the component Up Button by selecting it using the HUD. Creating a List Box Next, select the item Renderer folder (which automatically selects the text title, image, and Lorem Ipsum text in your list box). Group these items by using the shortcut for Windows (Ctrl+G) or Mac (@@cmd+G), or right - click on the selected items in the layers panel and choose Group. When you ’ ve selected the grouped items, select the scroll bar as well and from the HUD choose Convert Artwork to Component and choose data list. As before, the component is created but its parts need to be assigned. </p><p>The scroll bar is automatically assigned, but the repeated group needs to be assigned by selecting Convert Artwork to Data List Part and selecting Repeated Item as shown in Figure 15.6. </p><p>556 Chapter 15: Adding Services</p><p>Figure 15-6</p><p>After selecting the repeated item, your dummy group (or “ design - time data ” ) fills the list box. You can then adjust its spacing from the properties panel. And from the design - time data panel you can add a few more rows of data if necessary. In addition, you can make changes to individual graphics by drilling down and working at the component level. Adding Search Next you need to turn your button graphic into a button and your input graphic into an input container as shown in Figure 15.12. You do this in the same way as before – just select Convert Artwork to Component from your HUD: choose button for the search button and input text for the text search graphic.</p><p>Button After you ’ ve converted your button graphic and its label into a button component, double - click your button component to bring up its state and convert its text into a label. This lets you treat your button label dynamically. Next, go to the over state and change your graphic to indicate that you ’ re in a roll - over state. You can do this in the properties panel or by round tripping in Illustrator. Then navigate back into your Main application and set the label property of your button text to “ Search ” . You now have a fully skinned button for import into Flash Builder. </p><p>Search Input Having converted your input box graphic into a text input component, choose edit parts from the HUD, click on the text and adjust its properties. Test your application and see if everything is running correctly. </p><p>What you ’ re going to do next is pretty amazing . . . Collapsing Your Entire Panel Using Flash Catalyst, you can make your interfaces really fluid. To demonstrate this, collapse your entire list box into a small clickable button square, shown in Figure 15.7, and then reanimate your list box when clicking on the button again. </p><p>557 Part IV: Extending PV3D and Beyond</p><p>Figure 15-7</p><p>Here are the steps for making your List box collapsible: </p><p>Start by duplicating your main application and renaming it Collapsed as shown in Figure 15.8. Animation is accomplished in Flash Catalyst by animating between states. Things are made to appear and disappear just by changing the visibility in the layers panel. </p><p>Figure 15-8</p><p>Turn the small square graphic shown in Figure 15.7 into a button. The animation keyframe sequence is created in its button panel, shown in Figure 15.9. </p><p>558 Chapter 15: Adding Services</p><p>Figure 15-9</p><p>In the Button HUD shown in Figure 15.9, first choose On Click Interaction and then Play transition to state then Main to Collapsed. This creates the closing animation. Then choose Play transition to state again and Collapse to Main. This creates the opening animation. At this point, you ’ ve created a two keyframe animation sequence. </p><p>Stretch out the animation sequence bars and move the fade and resize bars to get a desired animation tween in the effects timeline. An example of the closing animation is shown in Figure 15.10. </p><p>Figure 15-10</p><p>You did it, and now it ’ s time to add connectivity in Flash Builder. You ’ re going to bring this application into Flash Builder and hook up your list box to the live data coming off the twitter search API. </p><p>Building Connectivity in Flash Builder Bringing a Flash Catalyst file into Flash Builder is easy, as Flash Catalyst saves its files in the Flash Builder FXP format. After opening up Flash Builder, all you need to do is import the Flash Catalyst file that you created in the last section. </p><p>559 Part IV: Extending PV3D and Beyond</p><p>When you ’ ve done that, the main MXML file is then contained in the src folder in the default package. The imported file looks and runs just like the file did in Flash Catalyst. </p><p>Switching to code view reveals the code generated automatically by Flash Catalyst, shown here: </p><p>< ?xml version=’1.0’ encoding=’UTF-8’? > < s:Application xmlns:d=”http://ns.adobe.com/fxg/2008/dt” xmlns:fx=”http://ns.adobe. com/mxml/2009” xmlns:s=”library://ns.adobe.com/flex/spark” width=”900” height=”575” backgroundColor=”#ffffff” xmlns:th=”http://ns.adobe.com/thermo/2009”> < fx:Script > < ![CDATA[ protected function Button_click():void { var state:String = currentState; if ( state == ‘Collapsed’ ) { currentState=’Main’; } if ( state == ‘Main’ ) { currentState=’Collapsed’; } } ]] > </p><p>< /fx:Script > < s:transitions > < s:Transition fromState=”Main” toState=”Collapsed” autoReverse=”true”> < s:Parallel > < s:Parallel target=”{bitmapimage3}” > < s:Move autoCenterTransform=”true” duration=”300” startDelay=”250”/> < s:Resize duration=”300” startDelay=”250”/> < /s:Parallel > < s:Parallel target=”{bitmapimage1}” > < s:Resize duration=”300” startDelay=”250”/> < /s:Parallel > < s:Parallel target=”{bitmapimage2}” > < s:Resize duration=”300” startDelay=”250”/> < /s:Parallel > < s:Parallel target=”{list1}” > < s:Fade duration=”350”/ > < /s:Parallel > < s:Parallel target=”{button1}” > < s:Fade duration=”350”/ > < /s:Parallel > < s:Parallel target=”{textinput1}” > < s:Fade duration=”350”/ > < /s:Parallel > < /s:Parallel > < /s:Transition > < s:Transition fromState=”Collapsed” toState=”Main” autoReverse=”true”> < s:Parallel > < s:Parallel target=”{bitmapimage3}” > < s:Move autoCenterTransform=”true” duration=”550”/> < s:Resize duration=”550”/ > < /s:Parallel > </p><p>560 Chapter 15: Adding Services</p><p>< s:Parallel target=”{bitmapimage1}” > < s:Resize duration=”550”/ > < /s:Parallel > < s:Parallel target=”{bitmapimage2}” > < s:Resize duration=”550”/ > < /s:Parallel > < s:Parallel target=”{list1}” > < s:Fade duration=”350” startDelay=”450”/ > < /s:Parallel > < s:Parallel target=”{button1}” > < s:Fade duration=”350” startDelay=”450”/ > < /s:Parallel > < s:Parallel target=”{textinput1}” > < s:Fade duration=”350” startDelay=”450”/ > < /s:Parallel > < /s:Parallel > < /s:Transition > < /s:transitions > < s:states > < s:State name=”Main”/ > < s:State name=”Collapsed”/ > < /s:states > < s:BitmapImage source=”@Embed(‘/assets/CloudTheme/Background.png’)” resizeMode=”scale” d:userLabel=”Background” d:locked=”true”/> < s:BitmapImage source=”@Embed(‘/assets/CloudTheme/List_Bckgnd.png’)” d:userLabel=”List Bckgnd” x=”28” y=”95” y.Collapsed=”52.35” id=”bitmapimage3” x.Collapsed=”29.35” width.Collapsed=”9” height.Collapsed=”10” resizeMode=”scale”/> < fx:DesignLayer d:userLabel=”ScrollBar” > < /fx:DesignLayer > < fx:DesignLayer d:userLabel=”Collapsible Items”> < s:BitmapImage source=”@Embed(‘/assets/CloudTheme/Search_Bckgnd.png’)” d:userLabel=”Search Bckgnd” x=”28” y=”53” width.Collapsed=”10” height. Collapsed=”10” id=”bitmapimage1” resizeMode=”scale”/> < s:BitmapImage source=”@Embed(‘/assets/CloudTheme/Top_Bar.png’)” d:userLabel=”Top Bar” x=”28” y=”53” width.Collapsed=”10” height.Collapsed=”10” resizeMode=”scale” id=”bitmapimage2”/ > < /fx:DesignLayer > < fx:DesignLayer d:userLabel=”item Renderer” th:visible.Page2=”false”> < s:List x=”34” y=”93” skinClass=”components.DataList1” th:origVisible.Page2=”true” visible.Collapsed=”false” id=”list1” > < s:ArrayCollection > < fx:Object image1=”@Embed(‘/assets/CloudTheme/faceImage.png’)” text1=”Text Title” text2=”Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.”/> < fx:Object image1=”@Embed(‘/assets/CloudTheme/faceImage.png’)” text1=”Text Title” text2=”Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.”/> </p><p>(continued)</p><p>561 Part IV: Extending PV3D and Beyond</p><p>(continued) . . . repeat fx:Object as many times as needed . . . </p><p>< /s:ArrayCollection > < /s:List > < /fx:DesignLayer > < fx:DesignLayer d:userLabel=”Search Btn” th:visible.Page2=”false”> < s:Button x=”188” y=”71” skinClass=”components.Button1” label=”Search” th:origVisible.Page2=”true” visible.Collapsed=”false” id=”button1”/> < /fx:DesignLayer > < fx:DesignLayer d:userLabel=”Search Text” th:visible.Page2=”false”> < s:TextInput x=”35” y=”70” skinClass=”components.TextInput2” text=”” id=”textinput1” th:origVisible.Page2=”true” visible.Collapsed=”false”/> < /fx:DesignLayer > < s:Button skinClass=”components.Button2” click=”Button_click()” x=”26” y=”51”/> < /s:Application > </p><p>Examining the code above you should be able to identify all the graphical elements (including their layers) used to build your collapsible list box. This code in Flash Builder is set up a little differently to what you typically see in Flex 3: you now have transitions and states blocks that contain the code generated by Flash Catalyst. But if you were used to working with states and transitions in Flex 3 this won ’ t look too strange to you. The rest of the code is very similar to what you ’ ve done in Flex 3, just arranged a little differently. </p><p>It ’ s amazing how easy it is to create complex interfaces in Flash Catalyst: Adobe has really raised the bar on user experience design. Next, you hook up the data to your code. Creating a Data Provider In order to get live data into your list, you need to provide it with a data provider. The code for the list component generated by Flash Catalyst is given here: </p><p>< s:List x=”34” y=”93” skinClass=”components.DataList1” th:origVisible.Page2=”true” visible.Collapsed=”false” id=”list1” > </p><p>In order to retrieve data from a server you need to use the HTTPService class. But to get HTTPService into Flash Builder you must first create a declarations tag and inside of that you put your HTTPService. Place your declarations tags before your script block, and give your HTTPService an “ id ” and result handler. You should recognize that, as you create your results handler, you ’ re given the option upon code completion to automatically generate the code stub necessary for your listener ’ s event handler, as shown in Figure 15.11. </p><p>Figure 15-11</p><p>562 Chapter 15: Adding Services</p><p>When you accept the automatic code generation option, the following snippet is generated in your script block for you to complete. </p><p> protected function myService_resultHandler(event:ResultEvent):void { // TODO Auto-generated method stub } </p><p>Flex doesn ’ t work directly with record sets and query results; it likes working with arrays and objects, so in order to grab the twitter data you need to stuff the results into an ArrayCollection . Do this by first putting a Bindable twitter ArrayCollection in your script block. </p><p>[Bindable] private var twitterAC:ArrayCollection;</p><p>Remember, if you use code completion, the appropriate import declaration is added automatically to Flash Builder. </p><p>Grabbing Data from the Web In order to populate the twitter array collection you ’ ve got to grab the data from the twitter site based upon your search selection. You do this by putting text into your search textbox and clicking the search button shown in Figure 15.12. </p><p>Figure 15-12</p><p>But right now, your search button and text input aren ’ t hooked to anything and you need to go into the code and hook them up. </p><p>< fx:DesignLayer d:userLabel=”Search Btn” th:visible.Page2=”false”> < s:Button x=”188” y=”71” skinClass=”components.Button1” label=”Search” th:origVisible.Page2=”true” visible.Collapsed=”false” id=”button1”/> < /fx:DesignLayer > < fx:DesignLayer d:userLabel=”Search Text” th:visible.Page2=”false”> < s:TextInput x=”35” y=”70” skinClass=”components.TextInput2” text=”” id=”textinput1” th:origVisible.Page2=”true” visible.Collapsed=”false”/> < /fx:DesignLayer > </p><p>Do this by first adding the click event to the button code shown in the snippet above; as you do that you ’ re asked to generate the event handler code stub automatically. In the generated code stub add the HTTPService call, and set the URL dynamically with the twitter URL plus the text input from your input textbox of Figure 15.12. </p><p> protected function button1_clickHandler(event:MouseEvent):void { //Dynamically load data myService.url=”http://search.twitter.com/search.atom?”+textinput1.text; //Run HTTPService myService.send(); } </p><p>563 Part IV: Extending PV3D and Beyond</p><p>Each time you type in a search query and press the search button, info will be loaded into your HTTPService object dynamically. That information then needs to be placed in your twitter array collection using the completed HTTPService code stub here: </p><p> protected function myService_resultHandler(event:ResultEvent):void { twitterAC = service.lastResult.feed.entry as ArrayCollection; } </p><p>The array collection twitterAC is your dataProvider . So to implement it, first remove that design - time ArrayCollection and add the twitterAC array collection as your data provider as shown in the following code snippet: </p><p>< fx:DesignLayer d:userLabel=”item Renderer” th:visible.Page2=”false” > < s:List =” x=”34” y=”93” skinClass=”components.DataList1” th:origVisible. Page2=”true” visible.Collapsed=”false” id=”list1” > < /s:List > < /fx:DesignLayer > </p><p>You need to do one more thing, and that ’ s to fix your item renderer. The item renderers are in the components package. You need to make the following change in your item renderer (named RepeatedItem1 ): </p><p>1. Change the Bitmap image to a Flex Image component, and change the source to data. link[a].href , which gets you the user ’ s avatar from Twitter. Note that Flash Builder lets you run both Halo and Spark items together. Halo components are the older Flex 3 components, which have both code and graphics grouped together. Eventually, these components will be retired but until that day you can still run them in conjunction with the newer Spark components. 2. Change: </p><p>< s:BitmapImage source=”{data.image1}” resizeMode=”scale” d:userLabel=”faceImage” y=”18”/ > </p><p>To: </p><p>< mx:Image source=”{data.link[1].href}” resizeMode=”scale” d:userLabel=”faceImage” y=”18”/ > </p><p>3. Change the RichText tags to reference the Twitter data such as the author name and Twitter title: </p><p>< s:RichText d:userLabel=”Text Title” fontFamily=”Myriad Pro” color=”0x020100” fontSize=”14” kerning=”off” whiteSpaceCollapse=”preserve” =”2” text=”{data.author. name}” > < /s:RichText > < s:RichText d:userLabel=”Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo </p><p>564 Chapter 15: Adding Services</p><p> consequat.” fontFamily=”Myriad Pro” color=”0x010000” fontSize=”10.65” kerning=”off” width=”173” height=”64” whiteSpaceCollapse=”preserve” =”42” y=”18” text=”{data. title}” > < /s:RichText > </p><p>4. Finally, after running your application you may find that the images and text aren ’ t aligned properly; if so just align them correctly in design view. </p><p>Running the application grabs Twitter images, titles, and summary text. The complete application can be downloaded from the book ’ s website. </p><p>Adding 3D to Your Flickr Menu In this section, you build a Flickr search application complete with a 3D effects menu. Just as you did in the Twitter application you ’ re able to type in a search name and get back a list of items. But when you click on one of the Flickr thumbnails, the thumbnail spins in 3D and downloads the higher resolution image into your application for full viewing. </p><p>The workflow goes from Photoshop, to Flash Catalyst, to Flash Builder. </p><p>Using Photoshop To make things a little more interesting, you throw out standard (ugly) scroll bars and buttons and use free - form graphic components from the portrait of the Mona Lisa shown in Figure 15.13. The images for the components can be grabbed using an image grabber such as from Techsmith.com . Jing is free and let ’ s you highlight the portion of the image you want to snag (or grab). Using Jing you create the search button from Mona Lisa ’ s mouth, the scroll track from a strip of her picture, the scroll thumb from her nose, and the thumbnail data list image and high resolution image from her full portrait (as shown in Figure 15.13). </p><p>Figure 15-13</p><p>565 Part IV: Extending PV3D and Beyond</p><p>Having laid out the image elements in Photoshop, import them into Flash Catalyst so that you can add interactivity. </p><p>Adding 3D in Flash Catalyst In Flash Catalyst you create the data list just as you did in the section on Twitter (so you won ’ t cover that again here). But in addition to this standard data list you ’ re going to create menu items that spin 360 degrees when clicked upon. You do this by using the new Flash Builder Effects class. </p><p>To add 3D to your data list items do the following: </p><p>1. After creating your data list, drill down to the base thumbnail item by clicking on it repeatedly. 2. In this base item group, you should see three states (Normal, Hovered, Selected). Click on the Hovered state. In the TIMELINES panel, select the hovered > selected menu item. 3. At the bottom of the TIMELINES panel click on the Add Action button and select Rotate 3D, then stretch out your Rotate3D to 0.5 seconds. Make sure that you save your changes .</p><p>Okay, that ’ s it . . . you ’ve just added 3D to your data list in less than one minute. If you ’ ve ever spent an “ all - nighter ” (as the author has!) working on item renderers for a data list, you ’ ll really appreciate how easy this is to do!!! </p><p>Now that you ’ ve set everything up in Flash Catalyst, it ’ s time to hook it up in Flash Builder. </p><p>Hooking it up in Flash Builder Hooking up the Flickr application is very similar to what you did in the Twitter example, except you add the ability to download a high resolution item when a photo thumbnail is clicked on. Here are the steps for hooking up the application: </p><p>1. Add your HTTPService method with your base Flickr URL at http://api.flickr.com/ services/feeds/photos_public.gne . </p><p>< s:HTTPService id=”myPhotoService” url=”http://api.flickr.com/services/feeds/ photos_public.gne” result=”myPhotoService_resultHandler(event)” fault=”myPhotoService_faultHandler(event)” / > </p><p>2. Create a Bindable ArrayCollection to hold the results of your HTTPService query. </p><p>[Bindable] private var myPhotoFeed:ArrayCollection; </p><p>3. Add a request method to your Search button. </p><p>// Request photos based on keyword criteria you insert</p><p>566 Chapter 15: Adding Services</p><p> private function requestPhotos(event:Event):void { // if the photoService is still loading we cancel to prevent errors // then we call again. myPhotoService.cancel(); var params:Object = new Object(); params.format = ‘rss_200_enc’; params.tags = searchTerms.text; myPhotoService.send(params); } </p><p>4. Add a Button_click method to your data list, which sends the index of the clicked thumbnail item to your image loading method. </p><p>< s:List x=”98” y=”72” id=”myList” dataProvider=”{myPhotoFeed}” skinClass=”components.DataList2” enabled=”true” allowMultipleSelection=”false” click=”Button_click(event)” > < /s:List > </p><p>5. Add the feed source of your Flash Catalyst high resolution image component. </p><p>//Add a Button_click method which tracks which index was clicked on</p><p> protected function Button_click(event:MouseEvent):void { //Start at index 0 if no index is supplied (-1) if(myList.selectedIndex <0) myList.selectedIndex=0; //Send selected image to Flash Catalyst generated component customcomponent11.mySource=myPhotoFeed[myList.selectedIndex].content.url; //Add image source link to top of screen in myID textbox myID.text=String(customcomponent11.mySource); } </p><p>6. Open up the components folder and hook up the Flash Catalyst components by opening them and changing their bindings: 1. Bind the image source to data.thumbnail.url and the RichText text to data.credit.</p><p>< mx:Image source=”{data.thumbnail.url}” d:userLabel=”monasmall” /> < s:RichText d:userLabel=”MonaText” fontFamily=”Myriad Pro” color=”0x020100” fontSize=”14” kerning=”off” whiteSpaceCollapse=”preserve” x=”8” y=”82” width=”59” height=”14” text=”{data.credit}” > </p><p>2. Add a bindable mySource variable, and set the image source to the mySource variable in the CustomComponent1 created in Flash Catalyst. </p><p>567 Part IV: Extending PV3D and Beyond</p><p>< ?xml version=”1.0” encoding=”utf-8”? > < s:Group xmlns:mx=”library://ns.adobe.com/flex/halo” xmlns:s=”library://ns.adobe. com/flex/spark” xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:d=”http://ns.adobe. com/fxg/2008/dt” xmlns:th=”http://ns.adobe.com/thermo/2009” width=”500” height=”500” maxHeight=”500” maxWidth=”500” > </p><p>< fx:Script > < ![CDATA[</p><p>[Bindable] public var mySource:String;</p><p>]] > < /fx:Script > </p><p>< s:states > < s:State name=”State1” th:color=”0xcc1500”/ > < /s:states > < mx:Image id=”bitmapimage1” source=”{mySource}” showBusyCursor=”true” d:userLabel=”monalarge” width.State1=”500” height.State1=”400” scaleContent. State1=”true” maxHeight.State1=”300” maxWidth.State1=”500” width=”426” height=”400” maxHeight=”400” maxWidth=”500”/ > < /s:Group > </p><p>After making these changes, run your application. If you hooked everything up correctly you should get the results shown in Figure 15.14. When you search on dogs, up comes a thumbnail list of dogs. When you click on a thumbnail of the dog of your choice, you download its high resolution image. </p><p>Figure 15-14</p><p>The document source for the main Flickr application is given here. Examining the code, you should be impressed by the small amount of code that accomplishes so much. </p><p>568 Chapter 15: Adding Services</p><p>< ?xml version=’1.0’ encoding=’UTF-8’? > < s:Application xmlns:mx=”library://ns.adobe.com/flex/halo” xmlns:d=”http://ns. adobe.com/fxg/2008/dt” xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:s=”library:// ns.adobe.com/flex/spark” width=”900” height=”700” backgroundColor=”#ffffff” xmlns:c omponents=”components.*” xmlns:th=”http://ns.adobe.com/thermo/2009”> </p><p>< fx:Declarations > < s:HTTPService id=”myPhotoService” url=”http://api.flickr.com/services/feeds/ photos_public.gne” result=”myPhotoService_resultHandler(event)” fault=”myPhotoService_faultHandler(event)” / > < /fx:Declarations > </p><p>< fx:Script > < ![CDATA[ import mx.events.ModuleEvent; import mx.collections.ArrayCollection; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.controls.Alert; import mx.events.FlexEvent;</p><p>//Create a Photo Array [Bindable] private var myPhotoFeed:ArrayCollection;</p><p>//Add a Button_click method which tracks which index was clicked on protected function Button_click(event:MouseEvent):void { if(myList.selectedIndex <0) myList.selectedIndex=0; //Send selected image to Flash Catalyst generated component customcomponent11.mySource=myPhotoFeed[myList.selectedIndex].content.url; //Add image source link to top of screen in myID textbox myID.text=String(customcomponent11.mySource);</p><p>} protected function myPhotoService_resultHandler(event:ResultEvent):void { myPhotoFeed = event.result.rss.channel.item as ArrayCollection; }</p><p>//Error handling protected function myPhotoService_faultHandler(event:FaultEvent):void { Alert.show(“Can’t load or reach photos from services”,”Error”); } private function addListenerToControls():void { searchTerms.addEventListener(FlexEvent.ENTER,requestPhotos); } (continued)</p><p>569 Part IV: Extending PV3D and Beyond</p><p>(continued)</p><p>// Request photos based on keyword criteria you insert private function requestPhotos(event:Event):void { // if the photoService is still loading we cancel to prevent errors // then we call again. myPhotoService.cancel(); var params:Object = new Object(); params.format = ‘rss_200_enc’; params.tags = searchTerms.text; myPhotoService.send(params); }</p><p>//Code Stub for deactivate Handler protected function myList_deactivateHandler(event:Event):void { // TODO Auto-generated method stub }</p><p>]] > < /fx:Script > < s:states> < s:State name=”Main” th:color=”0xcc1500”/> < /s:states > < s:BitmapImage source=”@Embed(‘/assets/Flickr/Background.png’)” resizeMode=”scale” d:userLabel=”Background”/ > < components:CustomComponent1 x=”258” y=”76” id=”customcomponent11” height=”417”> < /components:CustomComponent1 > < s:List x=”98” y=”72” id=”myList” dataProvider=”{myPhotoFeed}” skinClass=”components.DataList2” enabled=”true” allowMultipleSelection=”false” click=”Button_click(event)” > < /s:List> < s:TextInput x=”53” y=”30” id=”searchTerms” skinClass=”components.TextInput1”/> < s:Button x=”204” y=”27” skinClass=”components.Button2” label=”Search” click=”reque stPhotos(event)” / > < mx:Label x=”305” y=”30” text=”Address” width=”438” id=”myID”/> < mx:Label x=”310” y=”5” text=”Double click a thumb nail to see its image!”/> < /s:Application > </p><p>You now learn how to hook up services graphically using Flash Builder ’ s Data/Services tab. Start by connecting Yahoo ’ s weather service. </p><p>Mining Yahoo ’s Treasure Trove Yahoo has a treasure trove of web services that you can tap into for free. To find them (and instructions on how to access them) go to http://developer.yahoo.com/ . At the bottom of the page under “ Yahoo! APIs and Web Services ” you ’ ll find more than 30 services that you can tap into for free. Some require that you acquire a free Yahoo Application ID and tack it onto your request string. </p><p>In this discussion you create a weather widget that doesn ’ t require a Yahoo ID. </p><p>570 Chapter 15: Adding Services</p><p>Checking Your Local Weather Clicking the Yahoo Web Services Weather link takes you to Yahoo ’ s Weather RSS Feed page at http://developer.yahoo.com/weather/ . Here you can find all the information you need to build your query. The Weather RSS feed is a dynamically generated feed based on zip code or Location ID. </p><p>According to Yahoo ’ s documentation, the weather RSS feed request follows simple HTTP GET syntax: start with a base URL and then add parameters and values after a question mark (?). Multiple parameters are separated by an ampersand ( & ). For the Weather RSS feed there are two parameters: </p><p>❑ p for location ❑ u for degree units (Fahrenheit or Celsius). </p><p>The location parameter p is required. Use this parameter to indicate the location for the weather forecast as a zip code or Location ID. </p><p> http://weather.yahooapis.com/forecastrss?p=location </p><p>For example, to get weather for Yahoo! Headquarters in Sunnyvale, CA, use the zip code for Sunnyvale (94089): </p><p> http://weather.yahooapis.com/forecastrss?p=94089 </p><p>Or use the Location ID for Sunnyvale (USCA1116): </p><p> http://weather.yahooapis.com/forecastrss?p=USCA1116 </p><p>The location parameter can be a US zip code or a location ID. To find your location ID, browse or search for your city from the Yahoo weather home page. The weather ID is in the URL for the forecast page for that city. </p><p>The optional u (units) parameter indicates the degree units for the weather forecast. By default, Yahoo! Weather returns temperature information in degrees Fahrenheit. Use the u parameter to explicitly specify the degree units in Fahrenheit (f) or Celsius (c). The units parameter is case sensitive. For example, to get the weather forecast in Sunnyvale, CA in Celsius: </p><p> http://weather.yahooapis.com/forecastrss?p=94089 & u=c </p><p>It ’ s important that you understand the details of how you query the information. You use the http://weather.yahooapis.com/forecastrss as your base HTTPService URL and pull the zip and degree information from components built in Flash Catalyst. </p><p>571 Part IV: Extending PV3D and Beyond</p><p>Examining Your Weather XML If you enter in the query string discussed above you get back a web page that contains the location and weather conditions for Sunnyvale, CA. But you can ’ t stop here, you need to right - click and perform a view source on the page so you can access the RSS Feed. In doing so, you get the following XML code: </p><p><?xml version=”1.0” encoding=”UTF-8” standalone=”yes” ?> <rss version=”2.0” xmlns:yweather=”http://xml.weather.yahoo.com/ns/rss/1.0” xmlns: geo=”http://www.w3.org/2003/01/geo/wgs84_pos#”> <channel></p><p><title>Yahoo! Weather—Sunnyvale, CA http://us.rd.yahoo.com/dailynews/rss/weather/Sunnyvale__CA/*http://weather. yahoo.com/forecast/USCA1116_c.html Yahoo! Weather for Sunnyvale, CA en-us Wed, 08 Jul 2009 3:56 pm PDT 60 Yahoo! Weather 142 18 http://weather.yahoo.com http://l.yimg.com/a/i/us/nws/th/main_142b.gif Conditions for Sunnyvale, CA at 3:56 pm PDT 37.39 -122.03 http://us.rd.yahoo.com/dailynews/rss/weather/Sunnyvale__CA/*http://weather. yahoo.com/forecast/USCA1116_c.html Wed, 08 Jul 2009 3:56 pm PDT Current Conditions:
Fair, 26 C

Forecast:
Wed—Mostly Clear. High: 24 Low: 13
Thu—Partly Cloudy. High: 23 Low: 12

Full Forecast at Yahoo! Weather

(provided by The Weather Channel)
]]>

572 Chapter 15: Adding Services

USCA1116_2009_07_08_15_56_PDT

It ’ s important that you study this XML carefully. You need to understand its structure so you can properly build your weather widget. This is true for any service that you ’ ll be parsing. Now that you ’ ve examined your XML structure you can properly create your widget interface in Flash Catalyst. Creating Your Web Service Code In the previous section on Flickr, you hand - coded your HTTPService . But in this section, you use Flash Builder ’ s new Data/Services feature to grab all of the XML above automatically and bring it into a Flex Builder project. Here are the steps:

1. Create a Flex project and call it MyWeatherData . 2. Click on Flash Builder ’ s Data/Services Tab and Select Connect to Data/Service 3. From the graphical menu shown in Figure 15.15, select HTTPService.

Figure 15-15

4. In the next panel input the Service name as myWeatherCode , the Operation as getMyData , and the URL as http://weather.yahooapis.com/forecastrss?p=94089 & u=c . This enables Flex to analyze your data, and set up your parameters as shown in Figure 15.16. When you hit the Finish button, Flex creates your service and reminds you that you still need to configure and bind your data.

573 Part IV: Extending PV3D and Beyond

Figure 15-16

If you open up the services package that you just generated (services.myweathercode ) in the package explorer, you ’ ll find two files (_Super_MyWeatherCode and MyWeatherCode ). Opening up _ Super_MyWeatherCode you ’ ll find the main constructor function here:

// Constructor public function () { // initialize service control _serviceControl = new HTTPMultiService(); var operations:Array = new Array(); var operation:Operation; var argsArray:Array;

operation = new Operation(null, “getMyData”); operation.url = “http://weather.yahooapis.com/forecastrss”; operation.method = “GET”; argsArray = new Array(“p”,”u”); operation.argumentNames = argsArray; operation.resultType = Object; operations.push(operation);

_serviceControl.operationList = operations; model_internal::initialize(); }

You should recognize your input parameters (p and u), your base URL, your GET method, and a new HTTPMultiService() method that takes the place of your hand - coded HTTPService method.

1. Next you create a custom class that enables you to generate a return form. In the Data/Services tab right - click on the getMyData method and choose Configure Return Type . . . and in the panel text field create a new custom data type, input a name for your return class (MyWeatherReturn ). Then click Next. 2. In the New Data Type panel you can enter in data values in the Enter Value column (but your old values should be there) and hit the Next button.

574 Chapter 15: Adding Services

3. In the Modify Properties or Return Type panel shown in Figure 15.17, you can limit what ’ s returned. In this case, choose channel, which lets you view all data.

Figure 15-17

4. Finally, you can generate a form automatically that you can use to query your Web Service and examine its complete set of XML returns. You do this by right - clicking on getMyData . . . MyWeatherReturn object in the Data/Services tab and choose Generate Form. All the defaults should be fine except that you deselect Make result form editable, as you won ’ t be editing the data. Click the Finish button to generate the entry and return forms. 5. In Design view, you ’ ll find both an entry and return form. They may be overlapping so move them apart by clicking and dragging on them and extend your Flex window so you can view all the returned data. 6. Run the programs and input your zip code – and you get the very ugly return form shown in Figure 15.18.

P 41042 MyWeatherReturn U 1 Title Yahoo! Weather - Florence, KY GetMyData Link http//:hus.rd.yahoo.com/dailynews/rss/weather/florence, Description Yahoo! Weather for Florence, KY Language on-us LastBuildDate Thu, 09 Jul 2009 10:52 am EDT Tl 60 Yweather_location City Florence Region KY Country US Figure 15-18

575 Part IV: Extending PV3D and Beyond

Now at this point, if you ’ re a designer, you may be thinking . . . “ Why bother – this is really ugly ” . But if you ’ re a developer and you ’ ve ever had to hand - code all of this in Flex you ’ re now singing the Hallelujah Chorus . This is truly amazing: what would have taken you hours to hand - code you ’ ve just accomplished in less than five minutes without writing a single line of code.

You can now spend your time beautifying it! And when round tripping is instituted you ’ ll be able to take your FB programs back and forth between FB and FC, hooking up interactive interfaces to code. Making Your Widget Interface If you don ’ t have Photoshop (or Illustrator) don ’ t despair, you can build complete interfaces in Flash Catalyst using Wireframe Components, adding interaction as needed, and then pass that interactive widget into Flash Builder where you can throw on a few graphics where needed. Or you could go ahead and just build the entire interface in Flash Builder.

For the weather app, here ’ s your design doc. Your widget consists of two interchangeable panels (input on one and output on the other). After entering in the appropriate data you hit a send button and your panel will fly out, revealing the result of your request. Your panel sizes will be 240x320.

Input panel Your input panel has the following components:

❑ Input Textbox for your zip code ❑ Radio buttons for selecting Fahrenheit or Celsius selection ❑ Send request button

Output panel Your output panel has the following components:

❑ Description textbox (city name and zip code) ❑ Temperature ❑ Place for cloud graphic ❑ Conditions textbox ❑ Two - day forecast ❑ Refresh, Full Forecast, and Return buttons

The panel configuration above can be created in Flash Catalyst in less than five minutes and is shown here in Figure 15.19. Now it ’ s time to run down the hall and get your graphic designer. It ’ s functional, but not pretty!!!

576 Chapter 15: Adding Services

Figure 15-19

At this point all you have to do is import your Flash Catalyst file into Flash Builder and hook up the code you generated in the last section. This is left as an exercise to the reader and the finished application can be downloaded from the book ’ s website.

Building a CNN News Feed As in the case of Yahoo, CNN has a treasure trove of news feeds ranging from top stories to insider blog posts. You can access them at http://www.cnn.com/services/rss/?iref=rsssvcs . And for the purposes of this discussion you create a simple “ Top Stories ” news feed (at http://rss.cnn.com/rss/ cnn_topstories.rss ) using just Flash Builder.

As before, you want to go to the news feed link ( http://rss.cnn.com/rss/cnn_topstories.rss ) for top stories and view it on the web, and then perform a view source to take a look at its XML. This helps you select the correct node for parsing in Flash Builder.

Connecting Your Service The procedure for creating the HTTPService is identical to the last section on the weather widget, except you don ’ t have any query parameter. Here are the steps for creating the Service connection:

1. Create your Flex Builder application and name it ACNNewsFeed . 2. Click on Data/Services, select Connect to Data/Service, select HTTPService, and click Next. 3. In the Configure HTTP Service panel in the Service name field, type in CNNewsFeed , and in the Operation input box under Operation type in getFeed and for URL paste in the top stories rss link http://rss.cnn.com/rss/cnn_topstories.rss and click the Finish button. 4. You now need to configure a return class. In the Data/Services panel right - click on the getFeed() object and select Configure Return Type.

577 Part IV: Extending PV3D and Beyond

5. In the Configure Operation Return Type panel in the Create a new custom data type input box type myNewsFeed and click Next. 6. In the Create New Data Type panel select the Enter complete URL including parameters radio button and in the URL to fetch input box paste in the news feed link from step 3 and click Next. 7. In the Modify Properties of Return Type panel you can now select the node you want to bring in. Select item from the drop down combo box and select finish. In the Data/Services panel your getFeed() object should now have the MyNewsFeed return class attached to it.

After connecting your web service you need to bind it to a list component.

Binding Your Data You now need to bind your data to a list component that you ’ ll use to display your top story titles, and when selecting a time its news feed will appear. Here are the steps:

1. Go to design view, open up your components panel and drag a list box to the stage from the data controls folder and size it appropriately. 2. Right - click on the list box and choose Bind to Data . . . accept the New service call and in the Bind to field drop down box select title. This populates your list box with the title of your feed. Then click OK. A binding icon should appear at the top of your list box. 3. Right - click on your list box and choose Generate Details Form. Deselect Make form editable, and select Make a new service call to get details and click Next. 4. In the Property control mapping panel select and rearrange the links that you want to appear in your news feed (title, pubDate, description, and link), and click Finish. The feed form is generated in design view. Reposition the form appropriately. Try running the program.

When you run your form you ’ ll find that when you click on any item in the list only the first item gets fed to your form. You need to configure your data to get it running properly. If you ’ re a designer, it ’ s time to run screaming down the hall to get your developer to code this side for you.

Configuring Your Data Hooking up your data isn ’ t difficult; you just need to look at it from the eyes of a developer. To this point you ’ ve been spoiled – the application has practically been built without writing a single line of code. But to finish it you ’ ve got to put on your Flex hacking cap. Start by identifying the problem and follow your nose: The Problem The problem is that when you click any item in your list box only the first news feed shows up. This means that your list change handler method needs a little work.

578 Chapter 15: Adding Services

Code before The list change handler is shown here:

protected function list_selectionChangedHandler(event:IndexChangedEvent):void { getFeedResult2.token = cNNewsFeed.getFeed(); }

This method calls a responder which gets the news data.

< s:CallResponder id=”getFeedResult2” result=”myNewsFeed = getFeedResult2. lastResult[0] as MyNewsFeed”/ >

But the responder method shown above only grabs the zero index of the lastResult , which is your first news feed. In order to get other news feeds you need to change the lastResult index to correspond to what you ’ ve clicked on in your list.

Code after To change the lastResult index start by creating a new private variable called myIndex .

private var myIndex:int=0;

Then set the myIndex variable in the lastResult responder method as shown here:

< s:CallResponder id=”getFeedResult2” result=”myNewsFeed = getFeedResult2. lastResult[myIndex] as MyNewsFeed”/ >

Next you need to set the myIndex variable to the index of what you ’ ve clicked on. In the list change handler place the following line of code:

myIndex=list.selectedIndex;

The complete list change handler method is shown here:

protected function list_selectionChangedHandler(event:IndexChangedEvent):void { //Set List Selected Index myIndex=list.selectedIndex; //Get Feed by calling responding method getFeedResult2.token = cNNewsFeed.getFeed(); } Doing a little Styling Now that you have all this data hooked up you want to do a little styling to your application.

1. First in the description section (shown in Figure 15.20), put a TextArea component and link it to the news feed description.

579 Part IV: Extending PV3D and Beyond

Figure 15-20

2. Next create a Full Story link button (shown in Figure 15.21) and attach to it the URL method.

Figure 15-21

580 Chapter 15: Adding Services

When you click on this link button Click for Full Story a full - story html page is brought into your browser.

private function getMyURL(event:MouseEvent):void{

var req:URLRequest = new URLRequest(myNewsFeed.link);

try { navigateToURL(req); } catch (e:Error) { trace(“No URL Available”); } }

This method uses your feed link as a dynamic URL request parameter, and catches bad URLs in its catch statement.

The complete source code for the application is given here:

< ?xml version=”1.0” encoding=”utf-8”? > < s:Application xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:s=”library://ns. adobe.com/flex/spark” xmlns:mx=”library://ns.adobe.com/flex/halo” minWidth=”1024” minHeight=”768” xmlns:cnnewsfeed=”services.cnnewsfeed.*”> < fx:Script > < ![CDATA[ import mx.events.IndexChangedEvent; import mx.events.FlexEvent; import com.adobe.serializers.utility.TypeUtility; import mx.controls.Alert; //Add URL imports import flash.net.navigateToURL; import flash.net.URLRequest;

//Add Index for selected results private var myIndex:int=0;

//After list is created add data protected function list_creationCompleteHandler(event:FlexEvent):void { getFeedResult.token = cNNewsFeed.getFeed(); getFeedResult2.token = cNNewsFeed.getFeed(); }

//Change selected index protected function list_selectionChangedHandler(event:IndexChangedEvent):void {

myIndex=list.selectedIndex; getFeedResult2.token = cNNewsFeed.getFeed(); }

581 Part IV: Extending PV3D and Beyond

//Get URL function private function getMyURL(event:MouseEvent):void{

var req:URLRequest = new URLRequest(myNewsFeed.link); //Grab URL catch if error try { navigateToURL(req); } catch (e:Error) { trace(“No URL Available”); } }

]]>

582 Chapter 15: Adding Services

If you ’ ve ever written an RSS news feed before you ’ ll certainly be impressed with how easy this was to create.

When running the program you get the results shown in Figure 15.22.

Figure 15-22

The results shown in Figure 15.22 are functional. Whenever you click on a link in the list box on the left the appropriate feed appears on the right. But it’ s really ugly. So run down the hall and get your designer.

Summary In this chapter, you learned to hook up Web Services using Flash Catalyst and Flash Builder. You built a Twitter viewer, Weather checker, Flickr picker, and CNN news reader. But there ’ s more . . . WSDL, PHP, Coldfusion, BlazeDS, and LCDS (a full gamut of wondrous click - and - drag connectivity). To cover all these topics thoroughly would take an entire book (or more). But in this chapter you got the basics and with Flash Builder, data connectivity is made easy, giving you more time for development. The next generation of creative Flash applications is on its way!

An awesome place to go to find out more, beside the book ’ s website, is Sujit Reddy ’ s blog at http://sujitreddyg.wordpress.com/ . Sujit has a large number of video tutorials on hooking up Web Services to Flash Builder.

583

Flash & Math: Exploring Flash 10 and Beyond

When Flash 10 was released, the Away3D team immediately embraced it and updated their application to take advantage of some of its features. But just enhancing your 3D application so that it uses the Flash 10 features isn ’ t enough to make anyone excited. The present 3D work flow for software like Away3D and PV3D is too time consuming and complex. The next successful 3D Flash engine won ’ t just concern greater speed but will also be about enhanced work flow.

Adobe has got it right . . . programs like Flash Catalyst are models of what the next generation of 3D software must do: create sites and games using techniques that accelerate work flow by generating code through graphical techniques. Like Flash Catalyst, which generates code by graphical techniques, there needs to be something like a “ Papervision Catalyst ” that generates 3D websites just as easily.

In this chapter, you start with taking the pool shooting game developed in Chapter 10 and recreate it using Flash Catalyst and Flash Builder. You discuss optimizing the PV3D engine to run more efficiently in the Flash 10, take a look at faster options, and develop other possible schemes using drawTriangle class. Finally, you take a look at Flash Builder ’ s new layout functionality.

Importantly, no chapter like this is ever complete. Technology is moving so fast that as soon as a topic is published about the newest thing – it ’ s not! To help address this problem the book ’ s website is constantly being updated on the newest technology. Part IV: Extending PV3D and Beyond Rebuilding the 3D Pool “Shooting” Rebuilding the pool shooting game from Chapter 10 in Flash Builder begins with laying out its states in Flash Catalyst, then importing Flash Catalyst into Flash Builder, and laying PV3D onto those states. Flash Catalyst takes the chore out of programming states in Flex, and adds transitions and interactivity using the graphical user interface instead.

Laying out Your Game’s Framework Start by laying out the graphics that you need for the game’ s states in Photoshop as shown in Figure 16.1: five buttons, large outer- space back ground, and gun panel screen. You don’ t have to worry about laying out your textboxes in Photoshop. You can use the wireframe components in Flash Catalyst for your text.

Figure 16 -1

After you lay out your graphics, save your Photoshop file and open it up in Flash Catalyst. Checking the code view, you ’ ll find that the following code has been automatically generated for you, which is also shown here:

< ?xml version=’1.0’ encoding=’UTF-8’? > < s:Application xmlns:d=”http://ns.adobe.com/fxg/2008/dt” xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:s=”library://ns.adobe.com/flex/spark” width=”900” height=”700” backgroundColor=”#ffffff”> < s:BitmapImage source=”@Embed(‘/assets/ShootingPoolLayout/Background_Image. png’)” resizeMode=”scale” d:userLabel=”Background Image”/> < s:BitmapImage source=”@Embed(‘/assets/ShootingPoolLayout/imageGame.png’)” resizeMode=”scale” d:userLabel=”imageGame” x=”242” y=”211”/> < fx:DesignLayer d:userLabel=”Buttons” > < s:BitmapImage source=”@Embed(‘/assets/ShootingPoolLayout/Btn1.png’)” resizeMode=”scale” d:userLabel=”Btn1” x=”99” y=”30”/> < s:BitmapImage source=”@Embed(‘/assets/ShootingPoolLayout/Btn2.png’)” resizeMode=”scale” d:userLabel=”Btn2” x=”261” y=”30”/> < s:BitmapImage source=”@Embed(‘/assets/ShootingPoolLayout/Btn3.png’)” resizeMode=”scale” d:userLabel=”Btn3” x=”416” y=”30”/> < s:BitmapImage source=”@Embed(‘/assets/ShootingPoolLayout/Btn4.png’)” resizeMode=”scale” d:userLabel=”Btn4” x=”573” y=”30”/> < s:BitmapImage source=”@Embed(‘/assets/ShootingPoolLayout/Btn5.png’)” resizeMode=”scale” d:userLabel=”Btn5” x=”733” y=”30”/> < /fx:DesignLayer > < /s:Application >

586 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

You should recognize that, in the code above, all your graphics have been automatically embedded.

At the heart of the Flash Catalyst process is FXG. FXG stands for Flash XML graphics and is a subset of MXML (including only graphics tags). It ’ s a declarative syntax for defining graphics in Flex. Why FXG? The purpose of FXG is to develop “ componentized graphics ” for Flex. Flex has a great XML framework, so it ’ s a natural extension to building a graphically driven MXML framework. What Adobe did was to build a set of ActionScript objects that represented all the graphics in Flash and mapped them into XML for Flex using the rules of MXML. Out of this approach came two great benefits:

Other programs such as Photoshop and Illustrator can produce FXG graphics which can easily be brought into Flex. Separating graphics from code makes it easier to skin components and mix graphics with data (such as adding data binding and even handlers), extending present capabilities and ease of connection. As a result, this makes it much easier to develop rich Internet applications (RIAs).

Finally, how does FXG relate to Spark? FXG is used in Spark skins to draw the visual elements of the class. An FXG file consists of a single definition and an optional library that ’ s contained within a root < Graphic > tag. An FXG document fragment can include zero or more containers and graphic elements. An FXG file can be a standalone *.FXG or *.MXML file. The following is a simple example of an FXG document fragment inside an MXML file:

< ?xml version=”1.0” encoding=”utf-8”? > < !-FXG/SimpleLineExample.mxml- > < s:Application xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:mx=”library://ns.adobe.com/flex/halo” xmlns:s=”library://ns.adobe.com/flex/spark” >

< mx:Panel title=”Line Graphic Example” height=”75%” width=”75%” layout=”horizontal” paddingTop=”10” paddingBottom=”10” paddingLeft=”10” paddingRight=”10”>

< s:Line xFrom=”0” xTo=”100” yFrom=”0” yTo=”100” > < s:stroke > < s:SolidColorStroke color=”0x000000” weight=”1”/> < /s:stroke > < /s:Line >

< /mx:Panel > < /s:Application >

The application above (taken from Adobe docs) draws a diagonal line on the screen from the (0,0 coordinate to (100, 100) inside of a panel using a FXG fragment.

It ’ s FXG that enables Flash Catalyst to do what it does. With that bit of a detour into FXG, you now get back to the business of building your game by adding game states.

587 Part IV: Extending PV3D and Beyond

Adding Game States in Flash Catalyst The 3D pool shooting game consists of the following states:

❑ Intro State ❑ Play State ❑ Win State ❑ Lose State ❑ Cancel State

To create the five states shown in Figure 16.2, all you have to do in Flash Catalyst is hit the Duplicate State button in the PAGES/STATE panel. Then give each state an appropriate name. One of the great things about Flash Catalyst is that it ’ s so easy to create transitions between states graphically, whereas Flex 3 required you to hand - code each transition.

Figure 16 -2

As you ’ re graphically creating your states, the following code is being built for you in the background.

< s:states > < s:State name=”Intro”/ > < s:State name=”Play”/ > < s:State name=”Win”/ > < s:State name=”Lose”/ > < s:State name=”Cancel”/ > < /s:states >

Just as you did in the previous chapter, you want to create your buttons and all the interactivity required for each of the five states. But you ’ ve got a problem. Each time you run your program it takes you back to your original state. So how do you test the button interactivity of the other states? You ’ ve got to change the Default State.

Figure 16-3

588 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

As shown in Figure 16.3, to change your default state, all you have to do is right - click on the state you want to start at and select Set as Default State. Next time you run your program you ’ ll be able to test the interactivity of that state. After testing, make sure that you change your default state back to Intro.

Once everything has been laid out in Flash Catalyst, all the required interactivity added, and the appropriate textboxes added, you ’ re ready to import it into Flash Builder. All of this is accomplished in the same way as in the previous chapter so there ’ s no need to cover it again here. To see the completed application, check out the book ’ s video tutorial on this section.

Adding Code in Flash Builder After importing your file into Flash Builder, code the button ’ s interactivity, and the 3D pool shooting game of Chapter 10

Here are the steps:

1. Add Jiglib, org (PV3D/Lively3d), and caurina class folders to your application. 2. Add your assets folder and LineOut class. 3. Migrate all your import statements and variables. 4. In the Play_click() method add the code required to take you to the play state and the appropriate initialization statements.

protected function Play_click():void {

var state:String = currentState; if ( state == ‘Intro’ ) { currentState=’Play’; initPV3D(); myGameState(1); myTester.y=-1000; }}

5. Add an initialization method to your application tag.

creationComplete=”init()”

When your program starts up it automatically goes to the Intro state (since that ’ s how you set it in Flash Catalyst) and runs the init() method which starts your initial sound playing. 6. Add your init() method and your sound method to your program.

private function init():void{ myGameState(0); }

7. Then transfer your methods (testing as needed). As you bring over various methods you ’ ll start getting errors because your text boxes aren ’ t hooked to your score, timer and other items. Connect these as you go. Once everything is connected your code should run great!

589 Part IV: Extending PV3D and Beyond

After performing all these steps you should be running your game with no problem. You ’ re now running your Flash 9 game in Flash 10. To speed things up a little you need to optimize this application so it can take advantage of the new features available in Flash 10.

Optimizing PV3D for Flash 10 It may surprise you that PV3D runs in Flash 10 without any problems, and as a result it ’ s easy to port PV3D into Flash builder and layer 3D Collada scenes onto your Flash Builder programs. But, as in the case of the pool shooting game, doing so doesn ’ t give you a performance enhancement. That ’ s right – the Flash 10 engine is useless, unless you take advantage of its Vector3D, native z, and Matrix3D, and other classes. At this point, you ’ ve probably got a few questions about the process. What do you do? The porting process of optimizing PV3D for Flash 10 is simple. The process is essentially one of moving your Vector3D and Matrix3D classes into your PV3D core, incorporating your native z advantage, and eliminating superfluous code: of which there ’ s a large amount as much of the processes are now encapsulated in the Flash 10 player. How long will it take? If you really know what you ’ re doing (and have nothing else to do) it ’ s a two week development activity. Will you get a performance boost? Not really! The author has done extensive testing on this port and hasn ’ t found a significant enhancement (or at least not enough to write home about). The pot of 3D gold isn ’ t just a port of PV3D but a complete architectural shift that takes advantage of technologies like Pixel Bender and Alchemy – which run at optimized x5 or x10 performance speeds.

Ralph Hauwert, the driving force behind PapervisionX, has already demonstrated that you can push around +300,000 3D particles, in real - time on the screen, using Alchemy and Pixel Bender. You can check out his web demo (shown in Figure 16.4) at http://www.unitzeroone.com/labs/alchemy PushingPixels/ .

Figure 16-4

590 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

But there ’ s a problem . . . Alchemy and Pixel Bender don ’ t run on the same architectural platform as many of the new 3D Flash 10 native rendering classes such as Vector3D , Matrix3D , and Perspective Projection. You can ’ t use these features in combination with Alchemy – and that ’ s a bummer. So the appropriate architecture to produce results like those obtained in Figure 16.4, on a scale the same as PV3D, has to be created. Development activities like this take months of work.

As a result, you have another problem . . . developers are reluctant, in such a fluid environment, to pour hours of work into technologies that might disappear or be significantly enhanced in the near future, causing their work to become obsolete overnight. Sometimes it ’s a wait - and - see game as architectures shift and change. To learn more about Alchemy, check out Adobe Labs at http://labs.adobe.com/ technologies/alchemy/.

Until these 3D rendering models are developed in Pixel Bender (or another C slave) and Alchemy, you ’ ll be using Adobe ’ s native 3D classes (which are really amazing). And in the next section, you take a look at a number of examples using the newest framework.

Rendering 3D Models in Flash 10 When you first started working with Flash CS4 you were probably amazed at how easy it was to create flying post card applications. And as long as everything remained flat (and you create the appropriate sorting algorithms) you had no complaints. But as time went on you probably started wondering how you could bring in your 3D models. The real hallmark of PV3D is its ability to bring in 3D models. And why can ’ t Flash 10 do the same? Isn ’ t it supposed to be better than Flash 9?

Well, it ’ s a little more complicated than that. Flash 10 was a complete architectural shift that incorporated a native z component and various 3D packages which, at first, no one knew how to use. It wasn ’ t that Flash 10 couldn ’ t import and render your Collada models, it was just that no one had written the classes to do it yet.

In the following section, you go through a series of 3D examples ranging from rendering flying post cards to rendering full 3D models, illustrating the use of the many new 3D classes in Flash 10. A full treatment of this topic would take several chapters of this book, but there ’ s enough here to get you started. Unfortunately, the information is a little compressed. But for each example given, a corresponding tutorial video has been created on the book ’ s website.

Creating a 3D Image Ball The image ball in Figure 16.5 is an enhanced Flex 4 version of the one created by Flash & Math (at http://www.flashandmath.com/ ). When you click on the images in the image ball, its high resolution version is brought to the screen for viewing. The port to Flex 4 was non - trivial, but the big thing about having it in Flex (MXML and ActionScript) was that you can drag in Flex components, letting you harness data connectivity easily in order to build more complex applications.

591 Part IV: Extending PV3D and Beyond

Figure 16-5

The following discussion demonstrates some of the challenges present when porting a CS4 Flash program over to MXML. The CS4 Flash code from Flash and Math is thoroughly documented and you should download it and go through their version of the documentation to understand how it was created. Here you only deal with the port itself, not how the image ball works. Here are the highlights of the port from the Flash & Math version to Flex 4.

1. You can ’ t use addChild to add a sprite directly to the stage of a Flex MXML project as you do in Flash CS4. So create a canvas (cs4Canvas ) and add your sprite to that canvas using rawChildren .

cs4Canvas.rawChildren.addChild(board);

2. Flash and Math pull their thumbnail image assets from the Flash library using linkage. Since Flex doesn ’ t have a Flash Library you can ’ t take this approach, so one approach is to use the embed tag for the thumbnails (all 46 of them – remember you can use Flash Catalyst to generate all the embed statements for you automatically).

[Embed(source=”thumbs/pic1.jpg”)] private var Small1:Class;

3. In the Flex version of the code, the embed method that Flash & Math uses (setUpPics() ) won ’ t work, so you must use the BitmapAsset class to create a thumbnail array. And then you must exchange your embed class for their movie class.

thumbsArray[0]=[new Small1() as BitmapAsset];

4. You must rewrite the Flash and Math Vector array declarations using a push method to load your vector arrays – their method of loading these arrays doesn ’ t work in Flex.

592 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

thetaStep.push(0,60,36,30,36,60,0); jLen.push(1,6,10,12,10,6,1); phiTilt.push(-90,-60,-30,0,30,60,90)

5. Just as with a formal class structure, you must make sure that your variables have the private declaration in front of them, and that all declarations are contained in a function (within your script tags). In addition, you must create an init() method and call it from your MXML tags (this acts like a constructor function).

< mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” creationComplete=”init()” layout=”absolute” >

6. You must add an image forward and backwards button to your program so that once the user double - clicks on a thumbnail image its high resolution image is called. Using these buttons your client can now surf the images (without going back and forth between image and image thumbnails as is the case in the Flash and Math version). This is a big improvement to the code. Implementing this requires that you write a small string routine that extracts the numbers from the image name so you could start at that image and go sequentially.

var myStringIs:String = picsArray[i][j]; myStringIs=myStringIs.split(“.”).join(); myStringIs=myStringIs.split(“c”).join(); splitArray=myStringIs.split(“,”); stringNumIs=Number(splitArray[1].toString());

In addition, you need to create the navigation code (shown here) for your move forward and back buttons to call the correct images.

private function moveForward(e:MouseEvent):void { stringNumIs++; stringNumIs=stringNumIs%47; if(stringNumIs==0)stringNumIs=1; loader.load(new URLRequest(“images/pic”+stringNumIs+”.jpg”)); }

private function moveBackwards(e:MouseEvent):void { stringNumIs-; if(stringNumIs==0)stringNumIs=46; loader.load(new URLRequest(“images/pic”+stringNumIs+”.jpg”)); }

This works fine as long as your images are sequentially numbered, but who can remember which image is which? So in the next iteration (which is left as an exercise for the reader) you need to extend this routine to handle any image name.

The complete code can be downloaded from the book ’ s website.

593 Part IV: Extending PV3D and Beyond

Using drawTriangles to Build a Shaded TIE Fighter When Flash 10 was first released there were a number of posts speculating whether 3D models could be brought into CS4. The problem was that there were a bunch of powerful tools, but they worked a little differently compared to what most programmers were familiar with.

The key to rendering models in 3D is using the drawTriangles class. In this section you render a Blender TIE fighter model. It ’ s great starter code, but there ’ s still much work to be done to create a full blown model renderer. The good news is that you don ’ t have to start from scratch; PV3D has laid the foundation.

Figure 16.6 shows a shaded TIE fighter built completely in CS4. It was created in Blender, and uses drawTriangles to render it. The shaders come directly from Ralph Hauwert ’ s fast light maps and the background perlin noise clouds come from flepstudio (at http://www.flepstudio.org).

Figure 16-6

This program culls, has its own light class with brightness (here it alternates in brightness), depth sorts, and displays concavity with no problems. Flat Shading in CS4 Here ’ s how flat shading works in CS4. The light class is a point light with brightness (contained in the org/lively3d/lights/ folder of your download). It gives you the position of your light source, brightness, light color, ambient color, and specular level. Now doing real light calculations in 3D is very CPU intensive. To get a good light effect and avoid all this processing, there ’ s a trick.

594 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

The trick The lightColor variable is the color of your light source. The ambientColor variable is not true ambient light. It ’ s the first and second parameter in a gradient fill (the third one is your light color), and it governs the color that your object will take on. The specularLevel variable adjusts the number of shades between your middle ambient color and light color, and gives the appearance of making your surface shiny at low numbers – hey, it ’ s a great hack!

How the gradient fill works The beginGradientFill method has several parameters, but the only ones used here are type, color, alphas, ratios, and matrix:

beginGradientFill(type, colors, alphas, ratios, matrix)

They do the following:

❑ type — specifies which gradient type to use: linear or radial ❑ colors — an array of color values used in the gradient ❑ alphas — an array of alpha values corresponding to the colors ❑ ratios — define the percentage of the width where the color is sampled at 100 per cent. ❑ matrix — a transformation matrix which controls scale, skewing, and location of your gradient appearance.

How the light map is implemented A BitmapData object is created named tempmap , which is just a simple strip 255 pixels long and 1 pixel wide. The gradient is calculated using the ambient and light colors, and the gradient is drawn into the tempmap and returned.

The process described above is demonstrated in the code here, which comes from Ralph Hauwert ’ s LightMaps class and is used to create the light maps for flat, cell, phong, and gouraud:

var tempmap:BitmapData = new BitmapData(255,1,false,0); var s:Sprite = new Sprite(); var m:Matrix = new Matrix(); m.createGradientBox(255,1,0,0,0); s.graphics.beginGradientFill(GradientType.LINEAR, [ambientColor,ambientColor,lightColor],[1,1,1],[0,255-specularLevel,255],m); s.graphics.drawRect(0,0,255,1); s.graphics.endFill(); tempmap.draw(s); return tempmap;

595 Part IV: Extending PV3D and Beyond

Now that you understand how a simple light map is created, the steps to creating a flat shaded model are easy.

Creating a flat shaded model You create your light map in this way:

_colors = LightMaps.getFlatMapArray(lightColor, ambientColor, specularLevel );

You follow these steps:

1. Return a cosine (zd) from the dot product of your light vector with your triangle normal vector. This gives the cosine of the angle of your triangle face normal to your light source. If your light source is at 90 degrees to your object triangle (no light hitting it) then the cosine value (zd) is zero. If your triangle is directly facing the light source then your cosine is 1 (completely lit).

AVect[0]=new Vector3D(dispVec[facesVec[curFace][0]].x,dispVec[facesVec[curFace][0]] .y,dispVec[facesVec[curFace][0]].z); AVect[1]=new Vector3D(dispVec[facesVec[curFace][1]].x,dispVec[facesVec[curFace][1]] .y,dispVec[facesVec[curFace][1]].z); AVect[2]=new Vector3D(dispVec[facesVec[curFace][2]].x,dispVec[facesVec[curFace][2]] .y,dispVec[facesVec[curFace][2]].z);

AVect[3]= AVect[0].subtract(AVect[1]); AVect[4]= AVect[1].subtract(AVect[2]);

AVect[5]=AVect[4].crossProduct(AVect[3]); AVect[6]=new Vector3D(light.x, light.y, light.z);

var mag1:Number=AVect[5].length; var mag2:Number=AVect[6].length;

var zd:Number = AVect[6].dotProduct(AVect[5])/(mag1*mag2);

In CS4, you totally eliminate PV3D ’ s Numbers class, as all the calculations provided by that class are now internal to CS4. So for example, a dot product of two matrices in CS4 is given by: var zd:Number = AVect[6].dotProduct(AVect[5])/(mag1*mag2);

2. Calculate the index value from your zd by multiplying it by 255 (hex 0xff) and grab the respective color from your _colors array. Essentially, as your triangle turns away from your light source, the hex value is reduced – effectively darkening your pixels. zAngle = zd*0xff; //0xff if hex for 255 currentColor = _colors[zAngle];

596 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

3. Render your triangle and fill it with your calculated light map color as shown in Figure 16.7.

spObjImage.graphics.beginFill(currentColor); spObjImage.graphics.drawTriangles(vertices);

The code above uses drawTriangles exclusively, where PV3D draws individual lines. Theoretically, using drawTriangles should be faster.

Find Light Map Render Triangle Index and Color

Calculate Cosine Create Light Map

Figure 16-7

Using drawTriangles The heart of the code is the drawTriangles method, which can be used to place individual triangles on the stage and fill those triangles with shades, gradients, or materials. The TIE fighter is created by building up, sorting, and fitting together a large number of these triangles. The position and connecting data for these triangles are generated from a Blender XML data file. This data file is created from a Blender XML exporter (created by the author) found on the book ’ s website.

The abbreviated Blender XML data file, shown here, consists of vertex position and triangle connection (or triangle face) data.

< myPrimSet > < myVertices > 0.631775,0.036783,1.471956,0.629989,-1.278971,0.707213,0.626437,-1.274558,-0.814632 ,0.624669,0.045608,-1.571733,0.772240,1.168502,- . . . more position values go here . . . < /myVertices > < myFaces > 19,18,24,20,19,24,21,20,24,22,21,24,23,22,24,18,23,24,31,26,25,26,27,25,27,28,25,28 ,29,25,29,30,25,30,31,25,50,9,10,4,50,10,50,3,9,51,10,11,5, . . . more face values go here . . . < /myFaces > < myParams > 0.000000,0.000000,0.000000,0.000000,0.000000,-0.000000,1.000000,1.000000,1.000000 < /myParams > < /myPrimSet >

597 Part IV: Extending PV3D and Beyond

This data is parsed in the main program and used to construct your model ’ s triangles. The method for parsing the vertex and face data is given here:

private function setVertices(myData_xml:XML):void { //Parse vertex and face data var mySlitVert:Array=myData_xml.myPrimSet[0].myVertices.split(“,”); var mySlitFace:Array=myData_xml.myPrimSet[0].myFaces.split(“,”); numVertices=(mySlitVert.length-1)/3; var myScale:Number=20;

for(i=0;i < =numVertices;i++){ //Push data into your vertex array verts Vec[i]= new Vector3D(mySlitVert[i*3]*myScale,mySlitVert[i*3+1]*myScale,mySlitVert[i *3+2]*myScale); }

numFaces = (mySlitFace.length-1)/3; for(j=0;j < =numFaces;j++){ //Push data into your face array facesVec[j]=[mySlitFace[j*3],mySlitFace[j*3+1],mySlitFace[j*3+2]]; facesColors[j]=Math.random()*0xffffff; }}

Once the parsing of the Blender XML file is complete you end up with two arrays, one which contains triangle vertex data (vertsVec ) and the other which contains your triangle face data (facesVec ). In order to shade your model you need to assign a different color (or shade) to each triangle of your model. You do this by iterating over each of your triangles, every cycle of your animation engine.

A current face parameter ( curFace ) is created from your faceVec array, and is used to create the vertex data for your drawTriangles method as shown in the following code snippet:

if(zd > 0){ //Define your vertices Vector var vertices:Vector. < Number >=new Vector. < Number > (); //Push in the appropriate vertex using curFac vertices.push(dispVec[facesVec[curFace][0]].x, dispVec[facesVec[curFace][0]].y); vertices.push(dispVec[facesVec[curFace][1]].x, dispVec[facesVec[curFace][1]].y); vertices.push(dispVec[facesVec[curFace][2]].x, dispVec[facesVec[curFace][2]].y); //Create your lineStyle (use 0 to keep a line from being drawn) spObjImage.graphics.lineStyle(0);\ //Put in the color of your triangle (this is your shade) spObjImage.graphics.beginFill(currentColor, .8); //Draw your triangle using the drawTriangles method spObjImage.graphics.drawTriangles(vertices); }}

The zd parameter acts as a culling mechanism. If zd is less than zero, your triangle is facing away from you and there ’ s no need to draw it and the drawTriangles method is skipped – otherwise it ’ s drawn.

598 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

This may seem a lot to absorb, and much of the discussion above is designed to follow the website ’ s video tutorial (so go check it out on the book ’ s website). But the good news is that the rest of the shaders follow pretty much the same process.

The full source code can be downloaded from the book ’ s website.

An Orbiting Ship using drawTriangles One of the problems with the previous method is that in every case you have to render an entire triangle, not just the required edges to draw it. So you end up with extra drawing duty by drawing overlapping edges. But because they exactly overlap, you don ’ t see them. It would be nice to have a method that only draws the triangle piece that you need, eliminating overlapping edges. And you can, if you ’ re willing to do a little mathematical mapping. The resulting parser works really well for convex structures, but has difficulty with concave objects, due to sorting issues. This is a great exercise if you ’ re a mathematician, but to get this working correctly you ’ ve got to do a little more work on the class. The method presented in the previous sections works for all cases but isn ’ t as efficient as this one. So what ’s the problem? The problem with rendering in CS4 (not requiring triangle overlap as was done in the previous section) has to do with properly mapping uv coordinates to the appropriate vertex values. In this discussion, you develop a very simple parser which allows you to bring Blender models into CS4. It uses the drawTriangles method, the Blender XML Exporter created by the author, and a one - to - one mapping scheme of vertex and uv data points. The orbiting shuttle shown in Figure 16.8 was created using this method.

Figure 16-8

To create a Blender parser using the drawTriangles method, you ’ ve got to get your mapping right. It must be one - to - one, which means that each vertex must have one unique uv point. But in modeling software, such as Blender, that ’ s not true. You can have multiple uv points for one vertex point as shown in the figure 16.9.

599 Part IV: Extending PV3D and Beyond

Figure 16-9

When you unfold a pyramid as in Figure 16.9, uv mapping of the top 3D vertex point becomes four 2D points. In this case, the Blender data gives four uv data points for the apex of your pyramid above (point 4: 0,0,0 -> uv:points: 00, 01, 10, 11). But drawTriangles doesn ’ t know what to do with this. It wants one uv point for each vertex. The way around this problem is to collect all similar uv data points per vertex and assign a new vertex number for non - unique points. So for example:

(point 4: 0,0,0 - > uv:points: 00, 01, 10, 11)

becomes:

point 4: 0,0,0 - > 00 point 5: 0,0,0 - > 01 point 6: 0,0,0 - > 10 point 7: 0,0,0 - > 11

Points 4 through 7 are your unwrapped points and points 0 through 3 are the four base pyramid points, which aren ’ t unwrapped and as a result don ’ t need to be assigned new vertex numbers.

The new vertex numbers have the same vertex coordinates as shown above – only the point assignment for the triangular fan is changed.

The number of extra points you need to add is entirely dependent on how you unfold your object. If you use the hole punch method, you only need four extra vertices for an entire figure. As in anything, there ’ s no free lunch. You always give up something to gain something. How the Code Works The code sorts points into unique vertices and uv numbers (creating a one - to - one mapping). If unique data can ’ t be found, then new vertices are assigned, creating a one - to - one mapping.

The whole process starts by creating a Blender XML file and Photoshop image. This is done by using the Blender XML Exporter discussed on the book ’ s website. The Blender XML file is imported into the

600 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond program, parsed, and sorted. Then the following sorter code is used to create the one - to - one mapping scheme discussed above. Here are the steps:

1. The code starts by grabbing the index and vertex data created from the Blender XML export. for (var i:int = 0; i < myFaceNum; i++) {

//Grab Indices from Blender XML indices.push(int(mySplitFace[12*i])); indices.push(int(mySplitFace[12*i+1])); indices.push(int(mySplitFace[12*i+2]));

//Grab UV Data from Blender XML myUVDatArray.push(Number(mySplitFace[12*i+3])); myUVDatArray.push(Number(1-mySplitFace[12*i+4]));

myUVDatArray.push(Number(mySplitFace[12*i+5])); myUVDatArray.push(Number(1-mySplitFace[12*i+6]));

myUVDatArray.push(Number(mySplitFace[12*i+7])); myUVDatArray.push(Number(1-mySplitFace[12*i+8])); }

2. The raw data above is sorted and a one - to - one mapping scheme is created. //Sorting Program myFertsNum=indices.length; myVertsNum=verts.length/3-1;//iteration number

for (var j:int = 0; j < myFertsNum; j++) { for (var k:int = j+1; k < myFertsNum; k++) { if (indices[j]==indices[k]) { if ( (myUVDatArray[2*j]==myUVDatArray[2*k]) & & (myUVDatArray[2*j+1]==myUVDatArray[2 *k+1]) ) { } else { verts.push(verts[3*indices[k]],verts[3*indices[k]+1],verts[3*indices[k]+2] ) myVertsNum++; indices[k]=myVertsNum; }}}}

3. The uvtData is extracted for input into the drawTriangles method.

//Sort uvtData (verts match uvtData-the golden rule for drawTriangles) for (var m:int = 0; m < verts.length/3; m++) { for (var n:int = 0; n < indices.length; n++) { if (indices[n]==m) { uvtData.push(myUVDatArray[2*n], myUVDatArray[2*n+1], 0); break; }}}.

The drawTriangle code is fully encapsulated using a Blender Primitive created by the author, and closely resembles the PV3D coding approach. A Blender Primitive is declared and an animation loop created inside the wrapper file.

blenderShuttle = new BlenderPrim(“assets/data/shuttle.xml”, bmp, 300.0, 300.0, 10);

601 Part IV: Extending PV3D and Beyond

The parameters of the constructor function above are xml , bitmap , width and height . And the Blender Primitive lives in the org/lively3d/objects/primitives folder. This is the same as PV3D ’ s primitive path, except for the lively3d part, which is used to distinguish the different approaches.

The complete documented source code for the Blender Primitive is shown here:

package org.lively3d.objects.primitives{ //Flash Imports import flash.net.*;

import flash.display.*; import flash.geom.*; import flash.events.Event; import flash.utils.Timer; import flash.events.TimerEvent;

//Constructor Class public class BlenderPrim extends Sprite {

public var w:Number=800; public var h:Number=600;

public var bitmapData:BitmapData=new BitmapData(1,1,true,0xFFFFFFFF);

//Declare Primitive properties private var primitive:Sprite; private var paraVec:Vector3D; private var bmp:BitmapData; private var myXML:String; public var myAngle:Number=0; public var xmlLoaded:Boolean=false; public var focalLength:Number=250;

//Declare Vertex and UVT data properties private var verts:Vector. < Number >=new Vector. < Number > ; private var indices:Vector. < int >=new Vector. < int > ; private var uvtData:Vector. < Number >=new Vector. < Number > ; private var projectedVerts:Vector. < Number > ; //Declare 3D perspective and projection Matrix properties private var perspective:PerspectiveProjection; private var projectionMatrix:Matrix3D = new Matrix3D();

//Declare XML properties private var xmlReq:URLRequest; private var xmlLoader:URLLoader; private var myData_xml:XML; private var iFacNum:Number; //Declare vertex and face parsing properties private var myFertsNum:int; private var myVertsNum:int; private var stayAWhile:Number;

602 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

//Create Analysis Arrays private var myIndexArray:Vector. < int >=new Vector. < int > ; private var myUVDatArray:Vector. < Number >=new Vector. < Number > ;

//Create 3D Model Constructor Method public function BlenderPrim(myXMl:String, bmp:BitmapData, w:Number, h:Number, myAngle:Number) {

//Model Variables myAngle=myAngle; myXML=myXMl; bitmapData=bmp; this.w=w; this.h=h; var externalXML:XML; var loader:URLLoader = new URLLoader(); var request:URLRequest=new URLRequest(myXMl);

//Load XML 3D Model Data loader.load(request); loader.addEventListener(Event.COMPLETE, onComplete);

//Once data is loaded parse data function onComplete(event:Event):void { var loader:URLLoader=event.target as URLLoader; if (loader!=null) { externalXML=new XML(loader.data); //Build your 3D Model buildBlender(externalXML); xmlLoaded=true; } else { trace(“loader is not a URLLoader!”); }}}

//Load 3D model XML data private function buildBlender(myData_xml:XML):void {

//Parse vertex and triangle face data var myVerts:Array=myData_xml.myPrimSet[1].myVertices.split(“,”); var mySplitFace:Array=myData_xml.myPrimSet[1].myFaces.split(“,”);

//Arrays created to make the analysis easier and the program portable for (var iverts:int = 0; iverts < myVerts.length-1; iverts++) { //Push in your vertex data verts.push( myVerts[iverts]);

} var myFaceNum:int = (mySplitFace.length-1)/12; for (var i:int = 0; i < myFaceNum; i++) { (continued)

603 Part IV: Extending PV3D and Beyond

(continued)

//Set Index Data indices.push(int(mySplitFace[12*i])); indices.push(int(mySplitFace[12*i+1])); indices.push(int(mySplitFace[12*i+2]));

trace(int(mySplitFace[12*i])+” “+int(mySplitFace[12*i+1])+” “+int(mySplitFace[12*i+ 2]));

//Load UV Data into arrays

myUVDatArray.push(Number(mySplitFace[12*i+3])); myUVDatArray.push(Number(1-mySplitFace[12*i+4]));

myUVDatArray.push(Number(mySplitFace[12*i+5])); myUVDatArray.push(Number(1-mySplitFace[12*i+6]));

myUVDatArray.push(Number(mySplitFace[12*i+7])); myUVDatArray.push(Number(1-mySplitFace[12*i+8]));

}

//Ferts Sorting Program (sorts your face data) myFertsNum=indices.length; myVertsNum=verts.length/3-1;//iteration number

trace(myVertsNum+ “my vet number minus one”);

for (var j:int = 0; j < myFertsNum; j++) {

for (var k:int = j+1; k < myFertsNum; k++) { if (indices[j]==indices[k]) {

if ( (myUVDatArray[2*j]==myUVDatArray[2*k]) & & (myUVDatArray[2*j+1]==myUVDatArray[2 *k+1]) ) {

} else { verts.push(verts[3*indices[k]],verts[3*indices[k]+1],verts[3*indices[k]+2]);

myVertsNum++;

indices[k]=myVertsNum;

}}}}

//Sort uvtData (verts match uvtData-the golden rule for drawTriangles) for (var m:int = 0; m < verts.length/3; m++) {

for (var n:int = 0; n < indices.length; n++) {

if (indices[n]==m) {

604 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

var myZIs:Number= focalLength/(focalLength+verts[m+2]); uvtData.push(myUVDatArray[2*n], myUVDatArray[2*n+1], 0); break;

}}}

//Instantiate parameters projectedVerts = new Vector. < Number > ();

//Remove primitive from stage before adding the next one addChild(primitive= new Sprite());

//Set perspective perspective= new PerspectiveProjection(); perspective.fieldOfView=35.0;// camera angle, in degrees

//Initialize projected vertices for (var p:int = 0; p < verts.length/3; p++) { projectedVerts.push(0.0,0.0); } }

//Primitive animation loop public function animLoop():void { // Set up a viewpoint: if(xmlLoaded==true){ projectionMatrix=perspective.toMatrix3D();

//projectionMatrix.prependTranslation(.1,.1,0.4); myAngle+=.1; //Rotate Primitive projectionMatrix.prependRotation( 90,new Vector3D(0,0,1)); projectionMatrix.prependRotation( 90,new Vector3D(0,1,0)); projectionMatrix.prependRotation( -180,new Vector3D(1,0,0)); projectionMatrix.prependTranslation((2+Math.sin(myAngle)),4*(2+Math.cos(myAngle))- 8,0); projectionMatrix.prependRotation(myAngle*180/Math.PI+90,new Vector3D(0,0,1));

//Update projection vectors Utils3D.projectVectors(projectionMatrix, verts, projectedVerts, uvtData); //Draw the primitive with (primitive.graphics) { clear(); beginBitmapFill(bitmapData,null, false,false); drawTriangles(projectedVerts, indices, uvtData,TriangleCulling.NEGATIVE); endFill(); }}}}}

605 Part IV: Extending PV3D and Beyond

Encapsulated approaches like this one really reduce the amount of code needed in the main program, which greatly simplifies the chore of programming 3D models. The entire source code can be downloaded from the book ’ s website.

Taking drawTriangles to the Next Level One of the leading blogs that deals with drawTriangles is Pixelero by Petri Leskinen, found at http://pixelero.wordpress.com . It ’ s a blog you want to bookmark and visit routinely. You always find something amazing that seems to take Pixel Bender and drawTriangles to the next level. In Figure 16.10 Petri uses drawTriangles to create a burning sphere.

Figure 16-10

Creations like this one and their complete source code can be found on Pixelero. They make a great starter for more advanced projects, a good example of that being the Google Wormhole application created by the author using Petri ’ s tunnel class.

Building a Google Wormhole Using Petri ’ s tunnel class found on Pixelero, you can create the Google Map wormhole shown in Figure 16.11. The realtime animation is created by iterating forward in Google map coordinates. It ’ s more than just an iteration of uvtData (as is done on Pixelero), but both longitude and latitude coordinates are moving forward on the Google map itself. The change is picked up by drawing the iterated movie into a BitmapData object and throwing that on to Petri ’ s Tunnel class.

Figure 16.11 shows actual map data coming at you – not just a jpeg being recycled.

606 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

Figure 16-11

Bringing Google map data into CS4 or Papervision3D is really easy, as discussed in Chapter 7. In this case, you use Gumbo (Flex 4), not PV3D. Flex 4 performs better than PV3D with Google maps and doesn ’ t distort button positions on standard Google control buttons as PV3D does.

Here are the steps required to get your Google map Wormhole working:

1. Create a movie clip to hold your Google map.

movie.graphics.beginFill(0xFFFFFF); movie.graphics.drawRect(0,0,300,300);

2. Add the Google map to your movie.

movie.addChild(Application.application.map); movie.graphics.endFill();

3. Create a BitmapData and draw your movie into it.

_bmpd=new BitmapData(300,300, false); _bmpd.draw(movie);

4. Instantiate the Tunnel class (adding in the bitmapdata material) and add the tunnel to a canvas container on your Flex stage.

tunnel = new Tunnel(_bmpd, 800.0, 600.0, 32, 32); mapHolder.rawChildren.addChild(tunnel);

5. Redraw the movie and iterate Latitude and Longitude coordinates in the onEnterFrame animation loop.

607 Part IV: Extending PV3D and Beyond

_bmpd.draw(movie);

myLat=myLat-.0002*sin; myLng=myLng-.0002;

Because of cross domain policy issues the code only works as an AIR application. The entire document AIR code is show here:

< ?xml version=”1.0” encoding=”utf-8”? > < mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” > < maps:Map xmlns:maps=”com.google.maps.*” id=”map” mapevent_mapready=”TunnelDemo(event)” width=”100%” height=”100%” url=”http://www.professionalpapervision.com/demos/projects/tour/” key=”ABQIAAAA4LpFEu2In0mhf90kMq6mnRS-6gvR7plocG5jEAw_XBK5krTJ_ RRxjCMUXsM0nZ0Gam53OPR26zHU5w”/ >

< mx:Script > < ![CDATA[ //Flash Imports import flash.events.Event; import flash.display.*; import flash.geom.*; import mx.core.Application; //Google Maps API import com.google.maps.*; import com.google.maps.controls.*; //Import Petri’s Tunnel class import Tunnel;

private var tunnel:Tunnel; public var movie:MovieClip = new MovieClip(); public var movieParent:MovieClip = new MovieClip(); private var _bmpd:BitmapData;

private var count:Number = 0.0; //Starting longitude and latitude values private var myLat:Number=40.736072; private var myLng:Number=-73.992062;

public function TunnelDemo(event:Event):void {

this.map.setCenter(new LatLng(myLat,myLng), 14, MapType.HYBRID_MAP_TYPE);

//Create a movie clip to hold your Google map movie.graphics.beginFill(0xFFFFFF); movie.graphics.drawRect(0,0,300,300) //movie.buttonMode=true //Add Google map to your movie movie.addChild(Application.application.map); movie.graphics.endFill();

608 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

// Create a bitmapdata and draw your movie into it _bmpd=new BitmapData(300,300, false); _bmpd.draw(movie);

/*Instantiate the Tunnel class (adding in the bitmapdata material) and add the tunnel to a canvas container on your Flex stage. */

tunnel = new Tunnel(_bmpd, 800.0, 600.0, 20, 20); //spherePano.rotationX=180;

mapHolder.rawChildren.addChild(tunnel); //Add Listener for animation loop addEventListener(Event.ENTER_FRAME, update);

} //Animation Loop private function update(e:Event):void {

/* Redraw the movie and iterate Latitude and Longitude coordinates in the onEnterFrame animation loop*/

_bmpd.draw(movie);

count += 0.03; var sin:Number = Math.sin(count);

//move forward in Longitude and Latitude

myLat=myLat-.0002*sin; myLng=myLng-.0002;

//tunnel.step(0.01,0.01*sin); this.map.setCenter(new LatLng(myLat,myLng));

//Add tunnel, bending the tunnel tunnel.xBending = 0.02*(sin-Math.cos(2.5*count)); tunnel.yBending = 0.01*(Math.sin(2.5*count)+sin); //Update your tunnel tunnel.update(); }

]] > < /mx:Script > < mx:Canvas x=”400” y=”300” width=”800” height=”600” id=”mapHolder”> < /mx:Canvas > < /mx:WindowedApplication >

The code is pretty straightforward. It places a Google map in a movie clip and takes a snapshot of it. That snapshot is then placed on the tunnel created by Petri ’ s tunnel class.

The 3D techniques demonstrated in this book have tremendous application potential. In the next section, you build a molecule viewer that can be used to teach chemistry.

609 Part IV: Extending PV3D and Beyond

Creating a Molecule Viewer Viewing molecules on the web usually requires some type of special download, which parses a special molecule file called a Molfile. In this section, you learn how to create a Molfile molecule viewer in Flex 4 (Gumbo). As shown in Figure 16.12, you ’ re able to view any Molfile in 3D just by selecting the Molfile of your choice.

Figure 16-12

Realizing Huge Time and Code Savings in CS4 Using CS4 over PV3D really reduces the amount of code you need to write, accelerates development time, and enhances application performance.

A previous version of the 3D molecule viewer was created by the author in PV3D. The PV3D version was about 1400 lines of code, required rewriting the Molfile into xml, and didn ’ t show molecular bonds. This CS4 version took only 350 lines of code, rendered bonds (single, double, triple, aromatic), and parsed the Molfile directly.

As there ’ s already so much Molfile content freely available on the web, creating a Molfile parser is central to building a successful web - molecule viewer. Molfile Parser Parsing the Molfile is not that difficult. And by the way . . . what ’ s a Molfile anyway?

A Molfile is a text file (using a .mdl extension) which holds information about the atoms, bonds, connectivity and coordinates of a molecule. As an example, benzene ’ s Molfile is given here:

benzene ACD/Labs0812062058 6 6 0 0 0 0 0 0 0 0 1 V2000 1.9050 -0.7932 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1.9050 -2.1232 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7531 -0.1282 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 0.7531 -2.7882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0

610 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

-0.3987 -0.7932 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -0.3987 -2.1232 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 0 3 1 2 0 0 0 0 4 2 2 0 0 0 0 5 3 1 0 0 0 0 6 4 1 0 0 0 0 6 5 2 0 0 0 0 M END $$$$

The first two lines are unimportant for your purposes – they just hold the chemical name and company ID. The third line holds information about the number of atoms and bonds and the version number which you ’ ll use to tell you where your array should start counting. The next six lines are the atom table, one line per atom. The first four columns are the atom ’ s x, y and z coordinates and atom type. Because this was generated from a 2D drawing program (such as ChemWindow), all z coordinates are zero.

The next six lines comprise the bond table, one bond per line. The first two columns list which two atoms are connected by the bond. The third column indicates whether the bond is single (1), double (2), triple (3) or aromatic (4).

Once you ’ ve selected your Molfile, you bring it into Flex and parse it.

Here are the parsing steps:

1. Bring the (Molfile) text file into Flex 4 using the HTTPService class just as you would any XML file, but don ’ t use the e4x format here.

< mx:HTTPService id=”molService” url=”assets/0420.mol” result=”initMoleFile(event)” / >

2. Convert the file into a string, take out the special characters, split the contents, stuff it into an array, and remove all empty array elements.

// Convert to string myString=molService.lastResult.toString();

//Remove special characters myString = myString.split(“\n”).join(“ “); myString = myString.split(“\r”).join(“ “);

//Stuff into an Array myArray = myString.split(/ /);

//Filter out blank array elements myArray = myArray.filter(filterMe); function filterMe(element:String, index:int, array:Array):Boolean{ return (element != “” ); }

After stuffing your Molfile into an array, you need to know where to go (a focus point) in that array so you can start counting off your data elements. A good focus point is the version number of the Molfile (it ’ s always there). Using the forEach method, you grab its index value as shown in the following code

611 Part IV: Extending PV3D and Beyond

snippet. The forEach method is an array method that executes a function on each item in an array. In the case here, it executes the myStart function on each element of the myArray array . This is an extremely useful and compact array method that saves you tons of coding.

myArray.forEach(myStart); function myStart(element:String, index:int, array:Array):void{ if(element == “V2000”||element == “V3000”){ myStartVert=index;}

The index value of the version number is the heart of your Molfile parser; it tells you where you are so you count off and access the correct data values from your version numbers (V2000 or V3000). This index value takes you to any piece of data that you need in order to display your molecule. From this point on, it ’ s just a big counting game; everything is referenced to your version index value. Creating an Atom File To display your molecule appropriately, its atoms have to have a size, color, and fill based upon the atom type. This is accomplished by creating an Atom class that encapsulates all the properties of each atom and draws the atom ’ s billboard. The Atom class consists of two parts:

❑ A switch statement that holds each atom ’ s properties (color, and radius) and selects the atom to be drawn based upon the atom being rendered from the Molfile.

case “C”: //myColor = “colors/grey.jpg”; myColor = 0xbfbfbf; radius = 70; break;

❑ A graphical piece which draws the atom ’ s billboard and gradient using the CURVETO and drawPath methods.

//Draws the element’s billboard

radius=radius/5;

commands.push(GraphicsPathCommand.MOVE_TO); data.push(-radius/1.9, radius/1.9);

data.push(0, radius); data.push(radius/1.9, radius/1.9);

data.push(radius, 0); data.push(radius/1.9, -radius/1.9);

data.push(0, -radius); data.push(-radius/1.9, -radius/1.9);

data.push(-radius, 0); data.push(-radius/1.9, radius/1.9);

commands.push(GraphicsPathCommand.CURVE_TO); commands.push(GraphicsPathCommand.CURVE_TO);

612 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

commands.push(GraphicsPathCommand.CURVE_TO); commands.push(GraphicsPathCommand.CURVE_TO);

this.graphics.beginGradientFill(myType, [myColor, 0x000022],[1,1], [1, 40]); graphics.drawPath(commands, data);

Putting these two together gives you the Atom class that follows:

package objects{ import __AS3__.vec.Vector; //Flash Imports import flash.display.GraphicsPathCommand; import flash.display.Sprite; import flash.display.GradientType; import flash.geom.Matrix;

public class Atom extends Sprite { //Vector Arrays private var commands:Vector. < int > = new Vector. (); private var data:Vector. < Number > = new Vector. < Number > (); //Atom’s physical properties private var type:String; private var radius:Number; private var myColor:Number;

public var xpos:Number = 0; public var ypos:Number = 0; public var zpos:Number = 0; public var vx:Number = 0; public var vy:Number = 0; public var vz:Number = 0; public var mass:Number = 1;

//Gradient properties private var myType:String = GradientType.RADIAL; private var alphas:Array = [1, 1]; private var ratios:Array = [1, 100];

private var matrix:Matrix = new Matrix();

//var colors:Array = [0x00FF00, 0x000088];

public function Atom(type:String) { this.type = type; init(type); }

public function init(type:String):void {

(continued)

613 Part IV: Extending PV3D and Beyond

(continued) matrix.createGradientBox(200, 40, 0, 0, 0);

//Atom’s switch case method. Selects the atom under consideration switch(type) { case “H”: //myColor = “colors/white.jpg”; myColor = 0xffffff; radius = 25;

break; case “C”: //myColor = “colors/grey.jpg”; myColor = 0xbfbfbf; radius = 70; break; case “O”: //myColor = “colors/red.jpg”; myColor = 0xe60501; radius = 60; break; case “N”: //myColor = “colors/lightblue.jpg”; myColor = 0x8F8FFF; radius = 65; break;

. . . Rest of the Chemical Elements go here, see the book’s source code . . .

default: //myColor = “colors/deeppink.jpg”; myColor = 0xF71994; radius = 120; break; }

//Draws the element’s billboard uses CURVE_TO and drawPath methods

radius=radius/5;

commands.push(GraphicsPathCommand.MOVE_TO); data.push(-radius/1.9, radius/1.9);

data.push(0, radius); data.push(radius/1.9, radius/1.9);

data.push(radius, 0); data.push(radius/1.9, -radius/1.9);

data.push(0, -radius); data.push(-radius/1.9, -radius/1.9);

data.push(-radius, 0); data.push(-radius/1.9, radius/1.9);

614 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

commands.push(GraphicsPathCommand.CURVE_TO); commands.push(GraphicsPathCommand.CURVE_TO); commands.push(GraphicsPathCommand.CURVE_TO); commands.push(GraphicsPathCommand.CURVE_TO);

this.graphics.beginGradientFill(myType, [myColor, 0x000022],[1,1], [1, 40]); graphics.drawPath(commands, data);

}}}

The Atom class only takes one parameter – a string that comes from the Molfile – giving it an atom type. So after the Molfile is parsed, atoms are created by iterating over all your atoms in your molecule and passing the appropriate atom type to your Atom class as shown in the following code snippet:

for(var j:int = 0; j < myNumAtoms; j++) { //Instantiate your atom and pass in its assignment string var atom:Atom = new Atom(myArray[myStartVert+4 +j*16]);

//Treat your atoms as particles by pushing them into an atoms array atoms.push(atom); marks.push(atom);

//Position your atoms atom.xpos = myArray[myStartVert+1 +j*16] * myScale-offsetx; atom.ypos = myArray[myStartVert+2 +j*16] * myScale-offsety; atom.zpos = myArray[myStartVert+3 +j*16] * myScale-offsetz;

//Place your atoms in their molecule holder myHolder.addChild(atom); }

Now that your atom sub - class is completed, it ’ s time to start building molecules. Creating Molecules and Bonds There are two parts to creating molecules: placing atoms in 3D, and creating their bonds. The first part of a Molfile gives you the atom position values, and the second part gives the bond relationships (which atoms are connected to which) and types. Placing Atoms Essentially, all you have to do is replace the random placement of circles from the post on drawPaths to the atomic positions found in your Molfile:

atom.xpos = myArray[myStartVert+1 +j*16] * myScale - offsetx; atom.ypos = myArray[myStartVert+2 +j*16] * myScale - offsety; atom.zpos = myArray[myStartVert+3 +j*16] * myScale - offsetz;

myHolder.addChild(atom);

You get to those positions by counting one position forward for x, two for y, and three for z, from the version number index, as shown above.

615 Part IV: Extending PV3D and Beyond

The offset is just an average of the molecular positions and gives you the ability to spin your molecule around its center. Everything else pretty much follows what was done in the drawPaths post. Creating Chemical Bonds Creating bonds is easy as well, with one big conceptual change from the drawPaths post. Double processing is eliminated by just duplicating the atom and placing it into a marks array. The lines follow the duplicated atom in the marks array and the other atom which exists in the atoms array is used for sorting.

atoms.push(atom); marks.push(atom);

The big problem in creating bonds is figuring out how to create double, triple, and aromatic bonds. It turns out to be just a big counting game, coupled with offsetting lines. It starts with figuring out what type of bond you have and using that information in a switch statement.

The information for the type of bond you have is carried in the second part of your Molfile which starts at:

startBondArray=myStartVert+myNumAtoms*16+1;

Adding two to this number gives you the bond type location (OK once again it ’ s a big counting game – check out the Molfile description under the read more button to see the Molfile structure).

So, each time you create a double, triple, or aromatic bond you have to keep track of where all the data is in your array. This was accomplished by adding the following counting tracker to your command and data arrays:

commands[2*k+2*dB+4*tB+2*aB] mydata[4*k+4*dB+8*tB+4*aB]

This is needed for the drawPath command that ’ s shown here:

myHolder.graphics.drawPath(commands, mydata);

The variables dB , tB , and aB are iterated by one each time you create a double, triple, or aromatic bond respectively. These values are then zeroed after each molecular drawing iteration and the process is restarted on each onEnterFrame tick.

Creating the bond offsets isn ’ t very sophisticated as shown here:

mydata[4*k+4*dB+8*tB+4*aB] = marks[myArray[startBondArray+7*k]-1].x-bond2Sep; mydata[4*k+1+4*dB+8*tB+4*aB] = marks[myArray[startBondArray+7*k]-1].y-bond2Sep;

mydata[4*k+4+4*dB+8*tB+4*aB] = marks[myArray[startBondArray+7*k]-1].x+bond2Sep; mydata[4*k+5+4*dB+8*tB+4*aB] = marks[myArray[startBondArray+7*k]-1].y+bond2Sep;

You just subtract or add an offset value ( bond2Sep ) as shown above for the double bond case. The complete code can be downloaded from the book ’ s website. The next level for code developed for a project like this is to incorporate chemical bonding information and a molecular version of Jiglib so your

616 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

molecules can interact and bond. The source code for this project can be downloaded from the book ’ s website.

If you love mathematics and mapping you ’ ll love this next section. It ’ s all about mapping.

Mapping a Single Image onto an Octahedron Mapping a sphere to a cube becomes a fundamental issue in CS4, where vertex to UV data is a one - to - one map. Typical UV data brought in from Blender or 3DSMax, which have multiple uv data points per vertex (or triangle fan), won ’ t work with the drawTriangles method of CS4 (unless you do complete triangle mapping). There are workarounds, but eventually it all boils down to creating some type of one - to - one mapping scheme.

In this section you use the polar parametric equations of an octahedron to map a single image onto its surface, as shown in Figure 16.13. You won ’ t need to break your image up into eight separate sides.

Figure 16-13

To accomplish this mapping you first need to derive the octahedron parametric equations. Polar Octahedron Derivation The generalized parametric mapping equations for the octahedron are simple, and for the 2D case boil down to:

x = r( )*cos( ) y = r( )*sin( )

and in 3D:

x = r( , Φ )*cos( )sin(Φ ) y = r( , Φ )*sin( )sin(Φ ) z = r( , Φ )*cos(Φ )

where r is function of angle (not constant as in the spherical case).

617 Part IV: Extending PV3D and Beyond

Solving for the 2D case, r( ) is given by:

: 0 to 90, r( ) = 1/(sin( ) + cos( )) : 90 to 180, r( ) = 1/(sin( ) - cos( )) : 180 to 270, r( ) = -1/(sin( ) + cos( )) : 270 to360, r( ) = -1/(sin( ) - cos( ))

These solutions are easily obtained by substituting x = r( )*cos( ), and y = r( )*sin( ) into the linear equations (see Figure 16.14) and solving for r( ).

y X1 y X1

theta

y X1y X1

Figure 16-14

The generalized solution is:

r(a, b, ) = a/(sin( ) + b*cos( ))

where

: 0 to 90 (a=1, b=1), : 90 to 180 (a=1, b=-1), : 180 to 270 (a=-1, b=-1), : 270 to360 (a=-1, b=-1)

Interestingly, the values of the a and b parameters correspond to all possible combinations of 1 and - 1, which is similar to tossing two coins which gives:

(HH, HT, TH, TT) where H=1, and T=-1. 3D Case Following the approach above, for the 3D case (where eight possible planes correspond to the eight sides of the polar cube), the general solution is given by:

r(a, b, c, , Φ ) = a/(cos(Φ )+b*sin( )sin(Φ )+c*cos( )sin(Φ ))

where the parameters a , b , c correspond to flipping three coins at the same time with eight possible outcomes:

618 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

(HHH, HHT, HTH, HTT, THH, THT, TTH, TTT or 111, 11-1, 1-11, 1-1-1, -111,- 11-1, -1- 11, -1-1-1)

corresponding to the eight sides of our polar cube.

The eight possibilities used to map the vertices of the cube are:

: 0 to 90, Φ :0 to 90 (-1,-1,-1) : 90 to 180, Φ :0 to 90 (-1,-1,1) : 180 to 270, Φ :0 to 90 (-1,1,1) : 270 to360, Φ :0 to 90 (-1,1,-1) : 0 to 90, Φ :90 to 180 (1,1,1) : 90 to 180, Φ :90 to 180 (1,1,-1) : 180 to 270, Φ :90 to 180 (1,-1,-1) : 270 to360, Φ :90 to 180 (1,-1,1)

Using these possibilities you can now code your parametric equations. Coding the Parametric Equations The coding is straightforward. The cube ’ s vertices are derived from the parametric form given above and its indices are derived from a one - to - one mapping of a sphere onto a cube. That ’ s why the spherically mapped image, shown in the image above, lies on the cube without distortion.

for (var i:int = 0; i!=rows; i++) { ix= i/(rows-1)*Math.PI*2.0;

for (var j:int = 0; j!=cols; j++) { iy= (j/(cols-1)-0.5)*Math.PI;

// 8 planes 8 cases

if(ix > =0 & & ix < 2*Math.PI/4){

// : 0-90, Φ : 0-90, (-1,-1,-1) if(iy > =-2*Math.PI/4 & & iy < 0){ varMyrIs=rFunc(-1,-1,-1,ix,iy); }else{

// : 0-90, Φ : 90-180, (1,1,1) varMyrIs=rFunc(1,1,1,ix,iy);} }

if(ix > =2*Math.PI/4 & & ix < 2*Math.PI/2){

// : 90-180, Φ : 0-90, (-1,-1,1) if(iy > =-2*Math.PI/4 & & iy < 0){ varMyrIs=rFunc(-1,-1,1,ix,iy); }else{

// : 90-180, Φ : 90-180, (1,1,-1) varMyrIs=rFunc(1,1,-1,ix,iy);} } (continued)

619 Part IV: Extending PV3D and Beyond

(continued)

if(ix > =2*Math.PI/2 & & ix < 6*Math.PI/4){

// : 180-270, Φ : 0-90, (1,-1,1) if(iy > =-2*Math.PI/4 & & iy < 0){ varMyrIs=rFunc(-1,1,1,ix,iy); }else{

// : 180-270, Φ : 90-180, (-1,-1,1) varMyrIs=rFunc(1,-1,-1,ix,iy);} }

if(ix > =6*Math.PI/4 & & ix < =2*Math.PI){

// : 270-360, Φ : 0-90, (-1,1,-1) if(iy > =-2*Math.PI/4 & & iy < 0){

varMyrIs=rFunc(-1,1,-1,ix,iy); }else{

// : 270-360, Φ : 90-180, (1,-1,1) varMyrIs=rFunc(1,-1,1,ix,iy);} }

//Polar Cube paraVec = new Vector3D( varMyrIs*Math.cos(iy)*Math.cos(ix), varMyrIs*Math.sin(iy), varMyrIs*Math.cos(iy)*Math.sin(ix));

//Collect vertices in the verts Vector array verts.push(radx*paraVec.x,rady*paraVec.y,radz*paraVec.z);

//Load uvt data uvtData.push( i/(rows-1),j/(cols-1), 0.0); //Initialize projected vertices projectedVerts.push(0.0,0.0); } }

The entire source code can be downloaded from the book ’ s website.

Using ILayoutElement As soon as you thought carousels were dead, Ryan Campbell at http://www.bobjim.com/ has taken them to a new level using Flash Builder ’ s new layout class. His “ 3D Layouts for Flex 4 ” application is a great illustration of how powerful and easy it is to use the new tools in the Flash 10 player (as shown in

620 Chapter 16: Flash & Math: Exploring Flash 10 and Beyond

Figure 16-15

Figure 16.15). His program implements the new Flex 4 ILayoutElement class which provides a ton of methods for managing child element layout.

Ryan ’ s application demonstrates how elegantly Flash Builder skips between multiple 3D layouts such as CoverFlow, Carousel, Spiral, Time Machine, and Awesome. Each design is controlled by its own set of sliders, and Ryan uses the perspectiveProjection property to create a camera effect very similar to what is being used in Away3D.

Summary Flash 3D coding has come a long way, and over the next few years will transcend even optimistic expectations. 3D coding architectures of the future will not only be fast (incorporating such technologies as Pixel Bender and Alchemy), but will also generate code through graphical design, thus significantly enhancing work flow. Full 3D websites including 3D models will be created using graphical user interfaces similar to that of Flash Catalyst. In this chapter, you scratched the surface of such technologies by rebuilding your 3D pool shooting game in Flash Catalyst, and visiting a number of CS4 rendering examples.

621

Pocket Reference

This appendix is a listing by chapter of important code snippets and formulas. It ’ s meant to be a reference for the code snippets. The electronic form is on the book ’ s companion website so you can arrange it into any format that you ’ d like.

Chapter 1: Understanding Flash3D Summary: Flash 3D is one of the fastest - moving areas in web technology. In this chapter, you learned how to build a simple 3D engine in both CS3 and CS4. Using what you learned about 3D engines you created a torus worm, carousel, and image ball. Finally, you examined Disney ’ s rules for creating realistic animation, and converted a timeline animation into ActionScript.

Helpful Links http://www.mathworld.wolfram.com

http://www.sebleedelisle.com

http://flintparticles.org

http://www.flashandmath.com

Perspective Scaling T = scale = focal length/(focal length + z) scale=focal length/(focal length + z); x=x*scale; y=y*scale; Appendix A: Pocket Reference

Generating Random Colors Math.random()*0xffffff

3D Engine in 13 Lines of Code import flash.display.Sprite;//imports sprite class var myAngle:Number =0;//Angle of ball var ball:Sprite = new Sprite();//instantiates ball sprite ball.graphics.beginFill(0xFF0000);//Assign a ball color ball.graphics.drawCircle(0, 0, 40);//draws your ball at 0,0 ball.graphics.endFill();//ends the fill addChild(ball);//adds the ball to the stage addEventListener(Event.ENTER_FRAME, myonEnterFrame);//loops equations function myonEnterFrame(event:Event):void{ myAngle=myAngle+.1;//iterates angle ball.x = 300*Math.sin(myAngle); //ball orbit x ball.y = 300*Math.cos(myAngle); //ball orbit y ball.z = 2000*Math.sin(myAngle/10);} //increments z and changes sign

Torus Parametric Equations x=(c+a*cos(v))cos(u) y=(c+a*cos(v))sin(u) z=a*sin(v)

Simple CS4 Depth Sorting //Sorting function sortParticles():void { particlesArray.sortOn(“z”, Array.DESCENDING|Array.NUMERIC); for(var i:int = 0; i < particlesArray.length; i++) { addChildAt(particlesArray[i] as Sprite, i); } }

Keith Peters ’ Depth Sorting //Sorting function sortParticles():void { particlesArray.sort(particleZSort); for(var i:int = 0; i < particlesArray.length; i++) { myDisplayObject.addChildAt(particlesArray[i] as Sprite, i); } } //Sorting Function function particleZSort(particle1:DisplayObject,particle2:DisplayObject):int {

624 Appendix A: Pocket Reference

var zpos1:Vector3D = particle1.transform.matrix3D.position; zpos1 = myDisplayObject.transform.matrix3D.deltaTransformVector(zpos1); var zpos2:Vector3D = particle2.transform.matrix3D.position; zpos2 = myDisplayObject.transform.matrix3D.deltaTransformVector(zpos2); return zpos2.z-zpos1.z; }

Flash & Math Depth Sorting private function zSortPics():void { var distArray:Array=[]; var dist:Number; var i:int; var j:int; var k:int; var curMatrix:Matrix3D; var curMid:Vector3D; curMatrix=spSphere.transform.matrix3D.clone(); while(spSphere.numChildren > 0) { spSphere.removeChildAt(0); } for(i=0;i < 7;i++){ for(j=0;j < jLen[i];j++){ curMid=curMatrix.deltaTransformVector(midsArray[i][j]); dist=curMid.z; distArray.push([dist,i,j]); }} distArray.sort(byDist); for(k=0;k < distArray.length;k++){ spSphere.addChild(holdersArray[distArray[k][1]][distArray[k][2]]); holdersArray[distArray[k][1]][distArray[k][2]].alpha=Math.max(k/(distArray. length-1),0.5); }} private function byDist(v:Array,w:Array):Number { if (v[0] > w[0]){ return -1; } else if (v[0] < w[0]){ return 1; } else { return 0; }}

3D Rotation Equations xnew = xold*cos(angleZ)–yold*sin(angleZ) ynew = xold*sin(angleZ) + yold*cos(angleZ) xnew = xold*cos(angleY)–zold*sin(angleY) znew = xold*sin(angleY) + zold*cos(angleY) ynew = yold*cos(angleX)–zold*sin(angleX) znew = yold*sin(angleX) + zold*cos(angleX)

625 Appendix A: Pocket Reference

Degree/Radian Conversions Radians = Degrees*PI/180 Degrees = Radians*180/PI

drawRec Method for Creating Planes var plane:Sprite = new Sprite();//instantiates plane sprite plane.graphics.beginFill(Math.random()*0xffffff);//Assign a plane color plane.graphics.drawRect(-60, -60, 80, 80);//draws your plane at 0,0 plane.graphics.endFill();//ends the fill

2D Trig plane.x = Math.cos(angle) * 200; plane.z = Math.sin(angle) * 200;

3D Trig plane.x = radius*Math.sin(i*pStep)*Math.sin(j*tStep); plane.z = -radius*Math.sin(i*pStep)*Math.cos(j*tStep); plane.y =-radius*Math.cos(i*pStep);

Chapter 2: Getting Started With Papervision3D Summary: In this chapter, you were introduced to the basics of Papervision3d, Painter ’ s Algorithm, and the view frustum. You learned about culling and clipping and examined the guts of Papervision3d. You instantiated your first primitive (a sphere) and added a wireframe material. You extended your application using BasicView, and examined the different ways to run an application.

Helpful Links http://www.syncrosvnclient.com

http://tortoisesvn.tigris.org/

http://papervision3d.googlecode.com/svn/trunk/

http://code.google.com/p/Papervision3D/source/checkout

Adding a Viewport to Flex pv3Canvas.rawChildren.addChild(viewport);

626 Appendix A: Pocket Reference

Simple Extended Class package { import flash.display.Sprite; public class Papervision3DClass extends Sprite { Public function Papervision3DClass() { trace(“Hello World”); initPV3D(); } private function initPV3D():void { trace(“Initialize Papervision3D Here”); } } }

BasicView Starter Code package { //Flash Imports import flash.display.BitmapData; import flash.events.Event; import flash.display.StageAlign; import flash.display.StageScaleMode; //Papervision3D Imports import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.view.BasicView; //Declare the Hello Plane class that extends BasicView public class HelloPlane extends BasicView { //Declare the plane, bitmapdata, and material variables protected var myPlane:Plane; protected var planeBitmapData:BitmapData; protected var planeMaterial:BitmapMaterial; //Create the Hello Plane Constructor function public function HelloPlane() { super(1, 1, true, false); initPV3D(); } //Set up stage and plane private function initPV3D():void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; opaqueBackground = 0; //Set up plane (continued)

627 Appendix A: Pocket Reference

(continued) planeBitmapData = new BitmapData(512,256,false,0); planeBitmapData.perlinNoise(512,256,4, 123456, true,false); planeMaterial = new BitmapMaterial(planeBitmapData); myPlane = new Plane(planeMaterial,300,300, 10,10); scene.addChild(myPlane); startRendering(); } //Override protected function override protected function onRenderTick(event:Event=null):void { //Animation goes here myPlane.rotationY+=4; super.onRenderTick(event); } } } Chapter 3: Rezzing Primitives Summary: In this pivotal chapter, you examined how prims were made in both Papervision3D and CS4. You created your own custom prim by examining the parametric equations of different prims. And in the case of creating a Second Life tree, you analyzed the potential savings between using Papervision3D and CS4. You learned about the powerful new classes and methods in the CS4 library (such as Matrix3D, drawTriangles, and vectors). And you were able to create your own CS4 super prim, using a switch case and timer.

Helpful Links http://maps.jpl.nasa.gov/

http://code.google.com/p/panosalado/

http://away3d.com/

Embedding an Image [Embed(source=”worldmapCourtesyNASAJPL.jpg”)] private var YourImage:Class;

Create a Timer //Declare and start Timer var myTimer = new Timer(1800); myTimer.addEventListener(“timer”, timerHandler); myTimer.start();

628 Appendix A: Pocket Reference

Plane Grid Code for ( var ix:int = 0; ix < gridX + 1; ix++ ) { for ( var iy:int = 0; iy < gridY1; iy++ ) { var x:Number = ix * iW-textureX; var y:Number = iy * iH-textureY; vertices.push( new Vertex3D( x, y, 0 ) ); } }

Prim Template Code package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; //import Papervision3D import org.papervision3d.materials.WireframeMaterial; 1. [Your Primitive Import Statement Goes Here] import org.papervision3d.view.BasicView; //Create WirePlane class public class WirePlane extends BasicView { //Instantiate primitive and wireframe material 2. [Your Primitive Name Type Declaration Goes Here] private var planeMaterial:WireframeMaterial; public function WirePlane() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); } //Initiate Papervision protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0; // Create a wireframe material for the plane planeMaterial = new WireframeMaterial(0xFFFFFF); planeMaterial.doubleSided=true; //Add your wireframe to the scene 3. [Your Primitive is Instantiated Here] // Add Your Primitive to the Scene (continued)

629 Appendix A: Pocket Reference

(continued) 4. [Your AddChild Statement Goes Here] } // override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Apply an action to your primitive 5. [Your Action Statement Goes Here] //Call the super.onRenderTick function super.onRenderTick(event); }}}

Material List (for a Cube) var myMaterialsList:MaterialsList = new MaterialsList(); myMaterialsList.addMaterial( wireframeMaterial, “front” ); myMaterialsList.addMaterial( wireframeMaterial, “back” ); myMaterialsList.addMaterial( wireframeMaterial, “left” ); myMaterialsList.addMaterial( wireframeMaterial, “right” ); myMaterialsList.addMaterial( wireframeMaterial, “top” ); myMaterialsList.addMaterial( wireframeMaterial, “bottom” );

Cylindrical Ellipsoid Parametric Equations x=a*cos(u) z=b*sin(u) y=v

Spherical Ellipsoid Parametric Equations x=a*cos(u)*sin(v) z=b*sin(u)sin(v) y=c*cos(v)

Pinched Sphere fX=fX*(fZ)/fRadius; fY=fY*(fZ)/fRadius;

Pyramid Grid Code // Vertices for ( var iu:int = 0; iu < gridU1; iu++ ) { for ( var iv:int = 0; iv < gridV1; iv++ ) { var vertex:Vertex3D = new Vertex3D(); vertex[v] = iv * incV-textureV; vertex[u] = (iu*incU-textureU)*rev*Math.abs(1-(iv*incV)/height); vertex[w] = depth*Math.abs(1-(iv*incV)/height);

630 Appendix A: Pocket Reference

vertices.push(vertex); planeVerts.push(vertex); } }

Curved Plane Grid Code for ( var ix:int = 0; ix < gridX + 1; ix++ ) { for ( var iy:int = 0; iy < gridY1; iy++ ) { var x:Number = ix * iW-textureX; var y:Number = iy * iH-textureY; //Curvature var z:Number = -(myCurvatureX*x*x+myCurvatureY*y*y)/ ((width*width+height*height)/2); vertices.push( new Vertex3D( x, y, z ) ); }}

Elliptical Torus Parametric Equations x=(c+a*cos(v))cos(u) y=(c+a*cos(v))sin(u) z=b*sin(v)

Cone Parametric Equations x=((h-u)/h)*r*cos(v) y=((h-u)/h)*r*sin(v) z=u

Switch Case switch (number) { case 0: //Plane paraVec = new Vector3D(-.5*(2-ix), .5*(iy),0); break; case 1: //Cylinder paraVec = new Vector3D( Math.cos(ix), iy, Math.sin(ix)); break; case 2: //Cone paraVec = new Vector3D( (iy)*Math.cos(ix), Math.sin(iy), (iy)*Math.sin(ix)); break; case 3: //Sphere paraVec = new Vector3D( (continued)

631 Appendix A: Pocket Reference

(continued) Math.cos(iy)*Math.cos(ix), Math.sin(iy), Math.cos(iy)*Math.sin(ix)); break; case 4: //Torus paraVec = new Vector3D( (1+.4*Math.cos(iy))*Math.cos(ix), .4*Math.sin(iy), (1+.4*Math.cos(iy))*Math.sin(ix)); break; default: trace ( “ no case tested true “ ) }

Chapter 4: Adding Materials Summary: In this chapter you turned the corner from 3D techno babble to application building. You learned the basics of how materials are used to create objects and about their lighting. You learned how to add brightness to a Papervision3D light source, create shades, and make bump maps. And you extended these concepts to CS4.

Helpful Links http://professionalpapervision.wordpress.com/

Triangle in Flash graphics.beginFill( fillColor, fillAlpha ); graphics.moveTo( x0, y0 ); graphics.lineTo( x1, y1 ); graphics.lineTo( x2, y2 ); graphics.lineTo( x0, y0 ); graphics.endFill();

Texture Template Code package { //Flash imports import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; //Import Papervision3D classes 1. [Your Material Import Statement Goes Here] import org.papervision3d.objects.primitive.Sphere; import org.papervision3d.view.BasicView; //Create WirePlane class public class MaterialSphere extends BasicView

632 Appendix A: Pocket Reference

{ //Instantiate primitive and material 2. [Declare Your Material Instance and Data Type] private var sphere:Sphere; public function MaterialSphere() { //Call super super(1, 1, true, false); initPV3D(); startRendering(); } //Initiate Papervision protected function initPV3D():void { //Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; //Set the background to black opaqueBackground = 0; // Create a material for the Sphere 3. [Instantiate Your Material Here and Add Parameters] material .doubleSided=true; //Add your material and primitive to the scene sphere = new Sphere(material , 100, 8, 8); // Add Your Primitive to the Scene scene.addChild(sphere);

} // override onRenderTick so you can execute code override protected function onRenderTick(event:Event=null):void { //Apply an action to your primitive sphere.yaw(2); //Call the super.onRenderTick function super.onRenderTick(event); }}}

Affine Transformation Xnew = Xold*a + Yold*c + tx Ynew = Xold*b + Yold*d + ty

Simple Image Loader // Add an image for your environment map var img:Loader = new Loader(); img.load(new URLRequest(“assets/envmap15.jpg”)); img.contentLoaderInfo.addEventListener(Event.COMPLETE, initenv);

633 Appendix A: Pocket Reference

Bitmap Video Capture public override function updateBitmap ():void { try { // copies the scale properties of the video var myMatrix:Matrix = new Matrix(); myMatrix.scale( this.video.scaleX, this.video.scaleY ); // Fills the rectangle with a background color this.bitmap.fillRect ( this.bitmap.rect, this.fillColor ); // Due to security reasons the BitmapData cannot access RTMP content like a NetStream using a FMS server. // The next three lines are a simple but effective workaround to get past the Flash security sandbox. this.video.attachNetStream ( null ); this.bitmap.draw( this.video, myMatrix, this.video.transform.colorTransform ); this.video.attachNetStream ( this.stream ); }catch(e:Error) { // }}

Connecting Video to a Netstream private function loadMyVideo():void { //Set up Net Connection myConnection = new NetConnection(); //Not on the media server myConnection.connect(null); myStream = new NetStream(myConnection); //Set buffer time-at lease 2 seconds for some machines myStream. bufferTime = 2; //Set up My Stream for client myStream.client = new Object(); //Instantiate the video myVideo = new Video(320, 240); myStream.play(“assets/eternity.flv”); //Attach to local client side video myVideo.attachNetStream(myStream); }

Adding Video to a Primitive videoMaterial = new VideoStreamMaterial(myVideo, myStream); //videoMaterial.interactive = true; videoMaterial.doubleSided = true ; //plane = new Plane(material applied to object, width, height, wSegments, hSegments); planeCurve = new CurvedPlane(videoMaterial,32,24,8,8,10); scene.addChild(planeCurve);

634 Appendix A: Pocket Reference

Shader Code Template package { // Flash imports import flash.display.BitmapData; import flash.display.Loader; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.net.URLRequest; // Papervision3D imports import org.papervision3d.lights.PointLight3D; import org.papervision3d.core.proto.MaterialObject3D; [Your Shade Import Statement Goes Here] import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.*; import org.papervision3d.view.BasicView; public class FiveShadersSphere extends BasicView { // Declare Cylinder variables private var cylinder:Cylinder; //Constructor Statement goes here public function FiveShadersSphere() { super(0, 0, true, false); // Initialise Papervision3D initPV3D(); // Create your objects createObjects(); } private function initPV3D():void { // Set Stage stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; // Set the background to black opaqueBackground = 0xffffff; // Place your camera camera.x = 0; camera.y = 400; camera.z = -100; } private function createObjects():void { // Create your point light source var light:PointLight3D = new PointLight3D(true); light.x = 100; light.y = 1000; light.z = -400; // Create your flat shade material and apply it to your cylinder 2. [Your Shade type Material Statement Goes Here] // Place Shade on Cylinder cylinder = new Cylinder(yourShadedMaterialhere, 50, 10, 20, 10); cylinder.x = -240;

(continued)

635 Appendix A: Pocket Reference

(continued)

3. [If Using Environment Map, Required Statements go Here] // Add your Cylinder and light to the scene scene.addChild(cylinder); scene.addChild(light); // Start rendering the scene startRendering(); } override protected function onRenderTick(event:Event=null):void { cylinder.yaw(2); super.onRenderTick(event); } } }

Splitting Color into its RGB for ( var i:int = 0; i < = 255; i++) { rlut[i] = (i-(i % Math.round(256/steps))) < < 16; glut[i] = (i-(i % Math.round(256/steps))) < < 8; blut[i] = (i-(i % Math.round(256/steps))); }

Getter/Setter Method public function get brightness():Number { return this._brightness; } public function set brightness(value:Number):void { this._brightness = value; }

Creating a Bump Map //Create Assets for EnvMap and Bump Map var bumpmap:BitmapAsset = new graphic() as BitmapAsset; var envmap:BitmapAsset = new envmap() as BitmapAsset; var bumpmapc:BitmapData = bumpmap.bitmapData.clone(); //Add Blur to bump map clone bumpmapc.applyFilter(bumpmapc, bumpmapc.rect, new Point(), new BlurFilter(3,3,3)); //Create shade material for the bump Sphere var envShaded:EnvMapShader = new EnvMapShader(pointLight, envmap.bitmapData, envmap.bitmapData,0,bumpmapc); var shadedMaterial:ShadedMaterial = new ShadedMaterial(new BitmapMaterial(bumpmap. bitmapData),envShaded); //Instantiate your sphere and place your shaded Material on it. bumpSphere = new Sphere(shadedMaterial, 400, 16,16); scene.addChild(bumpSphere);

636 Appendix A: Pocket Reference

Rotate Sphere Method private function rotateXY(ball:Sphere, angleX:Number, angleY:Number):void { var cosX:Number = Math.cos(angleX*Math.PI/180); var sinX:Number = Math.sin(angleX*Math.PI/180); var cosY:Number = Math.cos(angleY*Math.PI/180); var sinY:Number = Math.sin(angleY*Math.PI/180); var myX:Number = 1000 * sinY*cosX; var myZ:Number = 1000 * cosY*cosX-1000; var myY:Number = 1000 * sinX; ball.x = myX; ball.z = myZ; ball.y = -myY; }}}

Net Stream in CS4 private var myConnection: NetConnection; private var myStream: NetStream; private var myVideo: Video; bitmapData.draw( this .myVideo); this .myVideo.attachNetStream ( this .myStream );

Follow these steps:

1. Declare your camera and matrix variables.

private var myCam:Camera; private var matrix:Matrix;

2. Eliminate your loadMyVideo() method and add the following code to grab your local camera.

myCam=Camera.getCamera(); myCam.setMode(320, 240, 15); myVideo = new Video(320, 240); myVideo.attachCamera(myCam);

3. Add a matrix method to flip your camera around the x - axis so you get your mirror image back. Otherwise, your video is reversed.

matrix = new Matrix(-1,0,0,1,bitmapData.width,0);

4. Add this matrix method to your bitmapData draw method (where - 1 in the matrix flips your video on the x - axis, and the other parameters reposition it).

bitmapData.draw( this.myVideo, matrix);

637 Appendix A: Pocket Reference

Creating a Composite Material //Instantiate your ColorMaterial and WireframeMaterial wireMaterial = new WireframeMaterial(0x676767); colorMaterial = new ColorMaterial(0x434343); //Instantiate your CompositeMaterial as myCompMaterial myCompMaterial = new CompositeMaterial(); //Add your Composite Material using the addMaterial method myComposite.addMaterial(colorMaterial); //Add the Wireframe Material using the addMaterial method myComposite.addMaterial(wireMaterial); //Add your Composite Material to your 3D object mySphere = new Sphere(composite, 200, 8, 8);

Chapter 5: Creating 3D Models Summary: In this chapter, you started with modeling and ended up with Pixel Bender. Regardless of how PV3D changes over time the principles presented in this chapter will be around for a while. You ’ ll still need parsers to bring in vertex data regardless of your software environment. And Pixel Bender, the new kid on the block, will obviously become the cornerstone of any new 3D package hitting the scene.

Helpful Links http://www.blender.org

http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro

http://www.md2.sitters-electronics.nl/index.html

http://chumbalum.swissquake.ch/

http://telias.free.fr/models_md2_menu.html

http://sketchup.google.com/3dwarehouse/

http://labs.adobe.com/technologies/pixelbender/

Adding StatsView Import the StatsView class:

import org.papervision3d.view.stats.StatsView;

638 Appendix A: Pocket Reference

Declare and add StatsView to your scene:

addChild(new StatsView(renderer));

Frames -per-second protected function onFrame(event:Event): void { currentFrameTime = getTimer(); fps = 1000/(currentFrameTime-lastFrameTime); lastFrameTime = currentFrameTime; }

Memory Usage System.totalMemory/1024/1024).toFixed(2)

Adding MD2 to PV3D private function initMD2():void { // Instantiate your skin as a BitmapFileMaterial var myMD2Skin:BitmapFileMaterial = new BitmapFileMaterial(“assets/mySkin.jpg”, true); // Instantiate an MD2 object (use true for animation, false otherwise) var myMD2Object:MD2 = new MD2(true); // Give your object a name so you can control it in the display list myMD2Object.name = “md2Object1”; //Load your MD2 model and its skin myMD2Object.load(‘assets/myModel.md2’, mySkin, framerate, scale); // Add your MD2 object to your scene using addChild scene.addChild(myMD2Object); }

MD2 Models as Particles for(var j:uint = 0; j < markersCount; j++) { var item:MD2 = new MD2(); particles_ary.push(item); }

Master Morphing Equation Vnext=V0+ frameAlpha* (V1-V0)

639 Appendix A: Pocket Reference

Morphing Code Snippet for ( var i:int = 0; i < target.geometry.vertices.length; i++){ var u:Vertex3D = target.geometry.vertices[i]; var v:Vertex3D = curOutput[i]; var w:Vertex3D = nxtOutput ? nxtOutput[i]: curOutput[i]; //Implement Vnext = V0+ frameAlpha*(V1-V0) u.x = v.x + frameAlpha * (w.x-v.x); u.y = v.y + frameAlpha * (w.y-v.y); u.z = v.z + frameAlpha * (w.z-v.z); } }

Referencing Collada Elements by Name //Get a reference to earth earth = collada.getChildByName( “earth” ); //Get a reference to moon moon = collada.getChildByName( “moon” ); //Remove collada from the collada object. collada.removeChild(moon); //Create a new pivot point for the moon. pivot = new DisplayObject3D(); //Add the moon to the pivot point pivot.addChild(moon); //Add the pivot point to collada collada.addChild(pivot); //Add the collada to the scene. scene.addChild(collada);

Steps to Adding a Collada File Follow these steps:

1. Import the Collada parser.

import org.papervision3d.objects.parsers.Collada;

2. Declare collada var and datatype it to Collada.

private var collada:Collada;

3. Set up the MaterialsList Method. This method defines the material to be applied to the multi/ sub object and each image is tagged to the appropriate polygon set using the Collada file.

var materialsList:MaterialsList = new MaterialsList(); materialsList.addMaterial( bitmapFileMaterial1, “image1” ); materialsList.addMaterial( bitmapFileMaterial2, “image2” ); materialsList.addMaterial( bitmapFileMaterial3, “image3” ); . . .

640 Appendix A: Pocket Reference

4. Instantiate the Collada class and fill in the appropriate parameters.

collada = new Collada(myDAE.dae file, materialsList, scale);

5. Add your collada to the scene using the addChild method.

scene.addChild(collada);

Grabbing Vehicle Parts from a Collada File // Steering disc for front wheels steerFR = collada.getChildByName( “ steerFR” , true ); steerFL = collada.getChildByName( “ steerFL” , true ); steerFR.rotationX=steerFL.rotationX=-90; // Rotation of wheels wheelFR = collada.getChildByName( “ FRWheel” , true ); wheelFL = collada.getChildByName( “ FLWheel” , true ); wheelRR = collada.getChildByName( “ RRWheel” , true ); wheelRL = collada.getChildByName( “ RLWheel” , true );

Steering Car Wheels steerFR.rotationY = steer; steerFL.rotationY = steer;

Rolling Car Wheels var roll:Number = speed*.4; wheelFR.yaw( roll ); wheelFL.yaw( roll ); wheelRL.yaw( roll ); wheelRR.yaw( roll );

Translating Using Local Coordinates public function translate( distance:Number, axis:Number3D ): void { var vector:Number3D = axis.clone(); if ( this ._transformDirty ) updateTransform(); Matrix3D.rotateAxis( transform, vector ); this .x += distance * vector.x; this .y += distance * vector.y; this .z += distance * vector.z; }

Loading an Animated Collada File dae = new helperDAE( false, null ); dae.addEventListener(FileLoadEvent.ANIMATIONS_COMPLETE, onAnimationsComplete); dae.load( “MyAnimated.DAE” ); scene.addChild(dae);

641 Appendix A: Pocket Reference

Loading an Animated Collada File with Material List //Add Materials bitmapFileMaterial1 = new BitmapFileMateria( “MyTexture.png”); wireframe:WireframeMaterial = new WireframeMaterial(0); //Use a Composite Material to enhance your surface composite.addMaterial(bitmapFileMaterial); composite.addMaterial(wireframe); //Add your DAE and Initiate materialsList.addMaterial(composite, “ all” ); dae = new DAE( true ); dae.scale = 100; dae.load( “MyAnimated.DAE”, materialsList); scene.addChild(dae);

Pixel Bender Starter Code [Part 1 Identifier and Kernel] < languageVersion: 1.0; >

//Kernel Definition kernel NewFilter < namespace: “Your Namespace”; vendor: “Your Vendor”; version: 1; description: “your description”; > [Part 2 Your filter code] { input image4 src; output pixel4 dst; void evaluatePixel() { dst = sampleNearest(src,outCoord()); } }

Gray Scale Pixel Bender (PB) pixel1 shadeVal=(colorChannel.r+colorChannel.g+colorChannel.b) /3.0; colorChannel.r = colorChannel.g = colorChannel.b = shadeVal;

Complement PB colorChannel.r = 1.0-colorChannel.r; colorChannel.g = 1.0-colorChannel.g; colorChannel.b = 1.0-colorChannel.b;

642 Appendix A: Pocket Reference

Brightness PB colorChannel.r += brightParam*(2.0-colorChannel.r); colorChannel.g += brightParam*(2.0-colorChannel.g); colorChannel.b += brightParam*(2.0-colorChannel.b);

Exposure PB dst.rgb = pow( colorChannel.rgb, float3(1.0-exposureParam)); dst.a = colorChannel.a;

Color Balance PB colorChannel.r += redParam*(1.0-colorChannel.r); colorChannel.g += greenParam*(1.0-colorChannel.g); colorChannel.b += blueParam*(1.0-colorChannel.b);

Binary Contrast PB pixel1 shadeVal=(colorChannel.r+colorChannel.g+colorChannel.b)/3.0; if(shadeVal > shadeParam) { colorChannel.r = colorChannel.g = colorChannel.b = 1.0; } else{ colorChannel.r = colorChannel.g = colorChannel.b = 0.0; }

Simple Pixelate PB float2 pixelCorner = floor(outCoord()/dimAsFloat); pixelCorner = dimAsFloat*pixelCorner; outputPixel = sampleNearest(inputImage, pixelCorner); Simple Blur PB float2 pos = outCoord(); pixel4 color = sampleNearest(src,pos); color+=0.75*sampleNearest(src, pos+float2(0.5*blurParam, 0))+0.25*sampleNearest(src , pos+float2(blurParam, 0)); color+=0.75*sampleNearest(src, pos-float2(0.5*blurParam, 0))+0.25*sampleNearest(src , pos-float2(blurParam, 0)); color+=0.75*sampleNearest(src, pos+float2(0, 0.5*blurParam))+0.25*sampleNearest(src , pos+float2(0, blurParam)); color+=0.75*sampleNearest(src, pos-float2(0, 0.5*blurParam))+0.25*sampleNearest(src , pos-float2(0, blurParam)); dst = color/5.0;

643 Appendix A: Pocket Reference

Sharpen PB float4 left = sampleLinear(src, coord-float2(radius, 0.0)) * amount; float4 right = sampleLinear(src, coord + float2(radius, 0.0)) * amount; float4 top = sampleLinear(src, coord-float2(0.0, radius)) * amount; float4 bottom = sampleLinear(src, coord + float2(0.0, radius)) * amount; dst.rgb += (top.rgb); dst.rgb -= (bottom.rgb); dst.rgb += left.rgb; dst.rgb -= right.rgb;

Simple Point Light PB float2 outcoord = outCoord(); float attn = pow(attnMult/pow(distance(outcoord, center), attnDecay), brightness);

dst = attn* sampleNearest(src, outcoord);

Bloom Brightness PB float4 current = sample(src, outCoord());

if (length(current.xyz) < threshold) { dst = pixel4(0.0, 0.0, 0.0, 1.0); } else { current.xyz *= exposure; dst = current; }

Cross Fade PB // Grab the pixel values from both images float4 topPixel = sampleNearest(topImage, outCoord()); float4 bottomPixel = sampleNearest(bottomImage, outCoord()); dst = (1.0-intensity)*topPixel+ bottomPixel*intensity;

Subtraction PB // Grab the pixel values from both images pixel4 topPixel = sampleNearest(topImage, outCoord()); pixel4 bottomPixel = sampleNearest(bottomImage, outCoord()); dst.rgb=topPixel.rgb-(1.0-intensity)*bottomPixel.rgb; dst.a=1.0;

644 Appendix A: Pocket Reference

Average PB dst.rgb=(topPixel.rgb-(1.0-intensity)*bottomPixel.rgb)/2.0;

Simplified Lively Light PB float2 outcoord = outCoord(); float attn = (brightness/((distance(outcoord, center)+radius))); dst = attn* sampleNearest(src, outcoord); dst.a=1.0

Creating a Pixel Bender Bitmap Material Make a copy of the PV3D BitmapMaterial class and name it BitmapPixelMaterial. Add the appropriate import statements and the pbj and image embed methods and create a shader as shown here:

//Embed your images [Embed (source=”filters/livelyLight.pbj”, mimeType=”application/octet-stream”)] private var ShaderClass:Class; [Embed (source=”assets/images/gainesHouse512.jpg”)] private var myImage:Class; private var shader:Shader

Next incorporate the following code to get your shader and image working together:

shader = new Shader(new ShaderClass()); shader.data.center.value = [400, 306]; shader.data.brightness.value = [150]; shader.data.radius.value = [100]; var image:Bitmap = new myImage(); image.filters = [new ShaderFilter(shader)]; shader.data.src.input = image.bitmapData

Finally you must change your fill method to a shader fill method with a matrix method:

graphics.beginShaderFill(shader,_localMatrix)

Double Lights PB float2 outcoord = outCoord(); float attn = (brightness1/((distance(outcoord, center1)+radius1)))+(brightness2/ ((distance(outcoord, center2)+radius2))); dst = attn* sampleNearest(src, outcoord); dst.a=1.0;

645 Appendix A: Pocket Reference

Animating PB Light Sources private function loop(e:Event):void{ //Increment your oscillation parameter osc++; //Calculate sine and cosine var cos:Number=150*Math.cos(osc/10); var sin:Number=150*Math.sin(osc/10); //Update your light positions shader.data.center1.value = [sin+400, cos+180]; shader.data.center2.value = [cos+200, sin+180]; //Apply the update to your image image.filters = [filter]; //Rotating your image holder holder.rotationY+=.25; }

Adding and Removing Particles movieParent.removeChild(particles_ary[prevIndex]); particles_ary[prevIndex]. load(myDataGrid.selectedItem.address, mySkin, 15, 1); movieParent.addChild(particles_ary[prevIndex])

Chapter 6: Working with Particle Systems Summary: This chapter gave a broad overview of particles in both PV3D and CS4. You started with the PV3D particle system and added some of your own particles to it and created a starry panorama. You built a 3D particle system from scratch and created a Flash CS4 glowworm. You learned how to slice and explode particle systems, and how to use them to interact with video. You took a look at the great work that Plug - in Media is doing and learned how to incorporate FLINT into PV3D.

Helpful Links http://flintparticles.org

http://labs.adobe.com/technologies/alchemy

http://www.sebleedelise.com

http://www.pv3d.org

Adding a PV3D Particle System Follow these steps:

1. Import the required classes. import org.papervision3d.materials.special.ParticleMaterial; import org.papervision3d.objects.special.ParticleField;

646 Appendix A: Pocket Reference

2. Declare a particle material, a particle field variable, and data type.

private var particlemat:ParticleMaterial; private var stars:ParticleField;

3. Instantiate your particle material and particle field and then add it to your scene by using the addChild method.

particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_CIRCLE); stars = new ParticleField(particlemat, 300,3, 2000,2000,2000); scene.addChild(stars);

Adding Additional Particles to PV3D switch (shape) { case SHAPE_SQUARE: graphics.drawRect(renderrect.x, renderrect.y, renderrect.width, renderrect.height); break; case SHAPE_CIRCLE:

graphics.drawCircle(renderrect.x+renderrect.width/2, renderrect.y+renderrect. width/2, renderrect.width/2); break; case SHAPE_STAR: var points:int=6; var innerRadius:Number=renderrect.width/10; var outerRadius:Number=renderrect.width*2; var count:int = Math.abs(points); if (count > =2) { // calculate distance between points var step:Number = (Math.PI*2)/points; var halfStep:Number = step/2; // calculate starting angle in radians var start:Number = (20/180)*Math.PI; graphics.moveTo(renderrect.x+(Math.cos(start)*outerRadius), renderrect.y-(Math.sin (start)*outerRadius)); // draw lines for ( var i:int=1; i < =count; i++) { graphics.lineTo(renderrect.x+Math.cos(start+(step*i)-halfStep)*innerRadius, renderrect.y-Math.sin(start+(step*i)-halfStep)*innerRadius); graphics.lineTo(renderrect.x+Math.cos(start+(step*i))*outerRadius, renderrect.y-Math.sin(start+(step*i))*outerRadius); } } break; case SHAPE_GEAR: (continued)

647 Appendix A: Pocket Reference

(continued)

[Gear code goes here] break ; case SHAPE_WEDGE: [Wedge code goes here] break ; case SHAPE_POLY: [Poly code goes here] break ; case SHAPE_BURST: [Burst code goes here] break ; default : trace ( “warning-Particle material has no valid shape.”); break; }

Adding Sets of Stars //First set of stars particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_CIRCLE); stars = new ParticleField(particlemat, 300,3, 2000,2000,2000); scene.addChild(stars); //Second set of stars particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_STAR); stars = new ParticleField(particlemat, 300,3, 2000,2000,2000); scene.addChild(stars); //Third set of stars particlemat = new ParticleMaterial(0xffffff,1,ParticleMaterial.SHAPE_BURST); stars = new ParticleField(particlemat, 300,3, 2000,2000,2000); scene.addChild(stars);

CS4 Particle Starter Code package org.lively3d.particles { import flash.display.*; public class Particle3D extends Sprite { //Part 1, Define Properties public var particleLife:Number; public var velocityX:Number; public var velocityY:Number; public var velocityZ:Number; public var gravity:Number; public var friction:Number; public var myAlpha:Number; public function Particle3D() { //Part 2, Add and define property values particleLife=0; velocityX = 0; velocityY = 0;

648 Appendix A: Pocket Reference

velocityZ = 0; gravity = 0; friction = 1; myAlpha = 1; } //Part 3, Update Properties by looping public function myUpdate():void { //Add Looping variables plus native z component this.x += velocityX; this.y += velocityY; this.z += velocityZ; particleLife+=1; velocityX *= friction; velocityY *= friction; velocityZ *= friction; velocityY += gravity; this.alpha *= myAlpha; }}}

Emitter at Mouse myParticle.ypos = mouseY-vpY; myParticle.xpos = mouseX-vpX;

Particle Position Based on Velocity myParticle.xpos += myParticle.vx; myParticle.ypos += myParticle.vy; myParticle.zpos += myParticle.vz;

Gravity myParticle.vy += gravity;

Bounce myParticle.bounceNum++; myParticle.vy *= bounce =-.6;

Explosion Starter Code package org.lively3d.particles { import flash.display.*; //Particle Parameters public class ParticleBlowUp { public var velocityX:Number; (continued)

649 Appendix A: Pocket Reference

(continued) public var velocityY:Number; public var velocityZ:Number; public var gravity:Number; public var friction:Number; public var fade:Number; public var autoRotate:Boolean; public var spinX:Number; public var spinY:Number; public var spinZ:Number; public var object:DisplayObject; public function ParticleBlowUp(obj:DisplayObject) { //Set Parameters object = obj; velocityX = 0; velocityY = 0; velocityZ = 0; gravity = 0; friction = 1; fade = 1; spinX = 0; spinY = 0; spinZ = 0; } //Update Parameters public function myUpdate():void { velocityX *= friction; velocityY *= friction; velocityY += gravity; object.x += velocityX; object.y += velocityY; object.z += velocityZ; //Update rotation object.rotationX += spinX; object.rotationY += spinY; object.rotationZ += spinZ; } } }

Random Number Generator function randRange(min:Number, max:Number) { var randomNum:Number = (Math.random() * (max-min )) + min; return randomNum; }

650 Appendix A: Pocket Reference

Book Slicing Code // loop through all pieces for(var x:uint=0;x < 6;x++) { for (var y:uint=0;y < 4;y++) { // create new map piece bitmap var newmapPieceBitmap:Bitmap = new Bitmap(new BitmapData(pieceWidth,pieceHeight)); newmapPieceBitmap.bitmapData.copyPixels(image.bitmapData,new Rectangle(x*pieceWidth ,y*pieceHeight,pieceWidth,pieceHeight),new Point(0,0)); // create new sprite and add bitmap data to it var newmapPiece:Sprite = new Sprite(); newmapPiece.addChild(newmapPieceBitmap); mapPiece=new MapPiece(newmapPiece,x,y); DragDropStatic.DragDrop(mapPiece); // set location mapPiece.x = x*(pieceWidth+5)+20; mapPiece.y = y*(pieceHeight+5)+20; mapPiece.addEventListener(MouseEvent.CLICK,clickmapPiece); // add to stage movie.addChild(mapPiece); }}

Polymorphism package objects.primitives { import flash.display.Sprite; public class MapPiece extends Sprite { private var mapPiece:Sprite; private var locX:uint; private var locY:uint; private var myXPos:Number; private var myYPos:Number; //Transfer map piece into function public function MapPiece(mapPiece:Sprite,locX:uint,locY:uint) { this.mapPiece=mapPiece; this.locX=locX; this.locY=locY; addChild(mapPiece); }

Generalized Slicing Code Using an Array Collection protected function loaderComplete( event:Event ):void { //For 40 images var rowIndex:Number = 0; var colsIndex:Number = 0; var myWidth:Number = 80; var colNum:int=10; (continued)

651 Appendix A: Pocket Reference

(continued) var rowNum:int=4; var myImageNum:Int=40 var myHeight:Number = 100; var myImageArray:Array = []; var myImage:BitmapData; var myBitMap:BitmapData = Bitmap( loader.content ).bitmapData; for ( var i:int = 0; i < myImageNum; i++ ) { myImage = new BitmapData( myWidth, myHeight ); myImage.copyPixels(myBitMap, new Rectangle(colsIndex * myWidth, rowIndex * myHeight, myWidth, myHeight ), new Point( 0, 0 ) ); myImage.draw(myImage, new Matrix() ); myImageArray.push( new Bitmap(myImage ) ); //For 3 rows if ( i == colNum-1 || i == 2*colNum-1 || i == 3*colNum-1 ) { rowIndex++; colsIndex = 0; } else { colsIndex++; } } myList.dataProvider = new ArrayCollection(myImageArray); myList.selectedIndex = 19; }

Dragging and Dropping Images package utils { import flash.display.Sprite; import flash.events.MouseEvent; public class DragDropStatic { public function DragDropStatic() { } //Drag and Drop static method using mouse listeners public static function DragDrop(mySprite:Sprite):void { mySprite.addEventListener(MouseEvent.MOUSE_DOWN, dragMe); mySprite.addEventListener(MouseEvent.MOUSE_UP, dropMe); function dragMe(event:MouseEvent):void { mySprite.startDrag(); } function dropMe(event:MouseEvent):void { mySprite.stopDrag(); }}}}

652 Appendix A: Pocket Reference

Removing a Particle Follow these steps:

1. Use the shift command to remove a particle from the particles array.

myParticle = particles.shift();

2. Use the removeChild command to remove a particle from the stage.

stageContainer.removeChild(myParticle);

3. Use null to make sure that all references are removed from your particle.

myParticle = null;

Add and Remove Listener To add:

myParticle.addEventListener(MouseEvent.CLICK,clickmapPiece); To remove:

myParticle.removeEventListener(MouseEvent.CLICK,clickmapPiece);

Mipmapping The factors - of - 2 principle works for Flash filters as well. They ’ re optimized by using filter numbers that are factors of 2 such as 2, 4, 8, 16, 32 . . .

Christmas Ornaments for ( var i:int = 0; i < total; i++) { var material:ColorMaterial; if ((i & 1) == 0) material = new ColorMaterial(0xaa0000); else material = new ColorMaterial(0x00aa00); material.doubleSided = true ; //Add a sphere instead var sphere:Sphere = new Sphere(material, 10, 2,2); sphere.x = Math.cos(i) * (radius-i * 3); sphere.z = Math.sin(i) * (radius-i * 3); sphere.y = i / total * height; myHolder.addChild(sphere); }

653 Appendix A: Pocket Reference

3D Text //IT’S A var itsaMaterial:Letter3DMaterial = new Letter3DMaterial(0x00cc00); var itsa:Text3D = new Text3D(“IT’S A” , rockwell, itsaMaterial); //SNOWY var snowyMaterial:Letter3DMaterial = new Letter3DMaterial(0xcc0000); var snowy:Text3D = new Text3D( “SNOWY” , rockwell, snowyMaterial); //CHRISTMAS var christmasMaterial:Letter3DMaterial = new Letter3DMaterial(0x00cc00); var christmas:Text3D = new Text3D( “CHRISTMAS” , rockwell, christmasMaterial);

Chapter 7: Geocoding, XML, and Data Bases Summary: In this chapter, you turned the corner from learning the inner workings of PV3D to using it to build data - driven web applications. You built a number of applications in Air, Flash CS4, and Flex that illustrated the use of XML, PHP, and MySQL. You learned how to use the Flex data wizard to automatically create PHP code, which was used to make server requests.

Helpful Links http://www.professionalpapervision.com/demos/web/geocoding

http://www.nofs.navy.mil/nomad

http://www.wampserver.com/en

http://code.google.com/apis/maps/documentation/flash/

Adding a Google Map to a PV3D Prim Follow these steps:

1. Turn Google Maps into a Movie

movie.graphics.beginFill(0xFFFFFF); movie.graphics.drawRect(0,0,800,600) movie.addChild(Application.application.map); movie.graphics.endFill(); mat = new MovieMaterial(movie, false, true,true,new Rectangle(0,0,800,600));

2. Place that movie onto a primitive sphere = new Sphere(mat, 600, 32, 32);

654 Appendix A: Pocket Reference

Adding a Google Map to CS4 //Place your map in a movie so you can position its pivot movie.addChild(map); //Set your map pivot map.x=-map.width/2; map.y=-map.height/2; //Put your movie in a Flex Canvas mapHolder.rawChildren.addChild(movie);

Switching Button Labels //Pause or rotate button for your map private function pauseRotate(event:MouseEvent): void { if (mouseBtn.label== ”Stop” ){ mouseBtn.label= ”Start” ; stopRotation = true ; } else { mouseBtn.label= ”Stop” ; stopRotation = false ; } }

Flex Slider Code //Map Slider private function mySliderHere(event:SliderEvent): void{ rotationValue=-mySliderValue.value; }

E4X Expressions To retrieve info in between element tags (or get a text node) using e4x, you just use dot syntax:

dataXML.client[0].email //Retrieves the text node

To retrieve info from an attribute use the @ symbol as follows:

dataXML.client[1].@id //Retrieves the value

Or to drill down deeper into your XML structure you could use the following dot syntax:

dataXML.client[0].projects.project[1].endDate //Retrieves Week

Loading XML public function getXml():void { var urlRequest:URLRequest = new URLRequest(“data/markersTour.xml”); var xmlLoader:URLLoader = new URLLoader(urlRequest); xmlLoader.addEventListener(“complete”, readXml); xmlLoader.load(urlRequest);//For this case, this step is optional } 655 Appendix A: Pocket Reference

Splitting CSV Data public function readXml(event:Event): void { var markersXML:XML = new XML(event.target.data); starArray=markersXML.myStars.split( “,” ); initPV3D();}

Toggle Audio private function leaveStage():void{ if(soundChannel){ //Stop audio when the component leaves the stage and set the label. soundChannel.stop(); SoundBtn.label=”Play Audio”; }}

Adding Stars to a Planetarium //Iterate only over a quarter of the values var myPartNum:Number = starArray.length/4; for( var i:Number = 0; i < myPartNum; i++ ) { //Distribute your star based on Spherical Coordinates var myX:Number=skyRadius*(Math.cos(2*Math.PI*starArray[4*i+1]/24)*Math.sin((90- starArray[4*i+2])*Math.PI/180)); var myZ:Number=-skyRadius*(Math.sin(2*Math.PI*starArray[4*i+1]/24)*Math.sin((90- starArray[4*i+2])*Math.PI/180)); var myY:Number=skyRadius*Math.cos((90-starArray[4*i+2])*Math.PI/180) //Change size of your star based on brightness var mySize:Number=size*(3-starArray[(4*i+3)]); addParticle(new Particle(material as ParticleMaterial, mySize, myX, myY, myZ));

Creating Map Marker acMarker = event.result.response.data.row as ArrayCollection; //Create your markers public function createMarkers(): void { var i:Number; //Iterate over all the markers in your data base for (i=0; i < acMarker.length; i++) { var latlng:LatLng = new LatLng(acMarker[i].lat, acMarker[i].lng); map.addOverlay(createMarker(latlng,acMarker[i].name )); } }

Data Base Switch Case switch (@$_REQUEST[“method”]) { case “FindAll”: //Read $ret = findAll();

656 Appendix A: Pocket Reference

break; case “Insert”: //Create $ret = insert(); break; case “Update”: //Update $ret = update(); break; case “Delete”: //Delete $ret = delete(); break; case “Count”: $ret = rowCount(); break; }

Chapter 8: Gliding on Air Summary: In this chapter, you built your first editor in Adobe Air. During the development process you mastered the use of a few basic Flex components that you ’ ll use again to create other editors. Accessing your local PC file system using Air and Flash10, you saved your editor results to your local hard drive. You learned about creating grayscale height maps and using the “ geometry.vertices ” property to bring those maps into PV3D. Using this you created a PV3D terrain viewer to view your heightmaps. Extending your editor you captured your webcam, programmatically changed it into grayscale, and brought it into your PV3D terrain viewer.

Helpful Links http://papervision2.com/page/3/

http://papervision2.com/downloads/heightmap/Heightmap.as

http://en.wikipedia.org/wiki/Heightmap

http://www.planetside.co.uk/terragen/productmain.shtml

http://www.daz3d.com/i.x/software/bryce

http://www.cleoag.ru/2007/04/13/another-voxel-terrain-demo/

http://www.bukisa.com/articles/34551_papervision-3d-programming-tutorial-terrain

657 Appendix A: Pocket Reference

Arranging Cubes with geometry.vertices for(var i:int = 0; i

Grayscale for Elevation Map //Calculate gray scale elevation value public function getHeight(myColor:uint):Number { var red:Number=0; var green:Number=0; var blue:Number=0; var hexColor:String = myColor.toString(16); if (hexColor.length == 1) hexColor = “ 000000” ; //Calculate RGB values else if (hexColor.length == 4) hexColor= ”00” +hexColor; red = parseInt(hexColor.substr(0, 2), 16); green = parseInt(hexColor.substr(2, 2), 16); blue = parseInt(hexColor.substr(4, 2), 16); return ((red/255) + (green/255) + (blue/255))/3; }

Displacing Vertices //Iterate over plane for ( var ix:int = 0; ix < gridX + 1; ix++ ) { for( var iy:int = 0; iy < gridY+1; iy++ ) { //Calculate elevation based on bitmap grayscale vertices[vertexIndex].z =-getMyZValue(); vertexIndex++; } }}

658 Appendix A: Pocket Reference

Adding Displacement Color for ( var ra:uint = 0;ra < 256;ra++) { //Grab gradient value from photoshop color map var myPixelData:Number = grad.bitmapData.getPixel(10, 256-ra); var hexColor:String=myPixelData.toString(16); if (hexColor.length == 1) {hexColor = “ 000000” ;} //Set R, G, B arrays for map texture creation paletteArrayR[ra] = “ 0x00”+hexColor.substr(0, 2)+ ”0000” ; paletteArrayG[ra] = “ 0x0000”+hexColor.substr(2, 2)+ ”00” ; paletteArrayB[ra] = “ 0x000000”+hexColor.substr(4, 2); }

Changing Pixel Color myBitmapData.setPixel(oldX +i , oldY+j, myColor);

Perlin Noise private function addPerlinNoise(): void { var seed:Number = Math.floor(Math.random()*10); var channels:uint = BitmapDataChannel.RED|BitmapDataChannel. GREEN|BitmapDataChannel.BLUE; myBitmapData.perlinNoise(512, 512, 6, seed, true , true, channels, true , null ); }

Key Listeners //Assign your key up and key down listeners addEventListener(KeyboardEvent.KEY_UP, myKeyUp); addEventListener(Event.ENTER_FRAME, frameLoop); //Keyboard actions //My key down method private function myKeyDown(event:KeyboardEvent): void { if (isChar(event.charCode, “ a” )){ myAKey= true ; } else if (isChar(event.charCode, “ d” )){ myDKey= true ; } } //My key up method private function myKeyUp(event:KeyboardEvent): void { if (isChar(event.charCode, “ a” )){ myAKey= false ; } else if (isChar(event.charCode, “ d” )){ myDKey= false ; } }

659 Appendix A: Pocket Reference

Key Action Animation Loop Listener //Frame Looper private function frameLoop(e:Event): void { //Increase (AKey) or decrease (DKey) greyscale if (myAKey== true) {colorScale.value++;}; if (myDKey== true) {colorScale.value-;}; }

JPEG/PNG Encoders //JPEG encoder var jpgenc:JPEGEncoder= new JPEGEncoder(80); imageByteArray=jpgenc.encode(myBitmapData); //PNG encoder var pngenc:PNGEncoder= new PNGEncoder(); imageByteArray=pngenc.encode(myBitmapData);

Saving in Flash 10 var saveFileRef:FileReference = new FileReference(); saveFileRef.save(imageByteArray); Chapter 9: Incorporating 3D Physics Summary: In this chapter, you examined a number of approaches to bring physics into PV3D. You started by creating a spring camera, and beefing up the DisplayObject3D class to add gravity effects, to create orbiting planets. You created custom classes for oscillation and learned how to make particles interactive using the interactive scene manager (or ISM). You learned how to build large - scale applications using states, modules, and porting. You examined both WOW and Jiglibflash physics engines. Finally, you built a Jiglib Hello World example and a Jiglib example viewer.

Helpful Links http://www.youtube.com/mikenku

http://www.gotoandlearn.com/

http://agit8.turbulent.ca/bwp

http://code.google.com/p/flex3cookbook3/

http://seraf.dediabox.fr/wow-engine

660 Appendix A: Pocket Reference

http://code.google.com/jiglib/flash/

http://code.google.com/p/as3dmod

http://code.google.com/p/as3dmod/wiki/AS3Dmod_Tutorial

http://www.gamasutra.com/blogs/ReidKimball/20090312/867/Key_Questions_for_Choosing_a_ Camera_Perspective.php

Acceleration from Velocity Equations vx += ax; vy += ay;

Velocity from Position Equations Ball.x += vx; Ball.y += vy;

Adding Physics to a Camera //Adding Stiffness to get Hooke’s restoration force _stretch.x = (x-_desiredPosition.x) * -stiffness; _stretch.y = (y-_desiredPosition.y) * -stiffness; _stretch.z = (z-_desiredPosition.z) * -stiffness; //Multiply velocity by damping to get damping force _dv.x = _velocity.x * damping; _dv.y = _velocity.y * damping; _dv.z = _velocity.z * damping; //Subtract damping force from restoration force to get the force on your camera _force.x = _stretch.x-_dv.x; _force.y = _stretch.y-_dv.y; _force.z = _stretch.z-_dv.z; //Divide force by mass to get acceleration _acceleration.x = _force.x * (1 / mass); _acceleration.y = _force.y * (1 / mass); _acceleration.z = _force.z * (1 / mass); //Calculate velocity _velocity.plusEq(_acceleration); //Step velocity down to position of your camera _xPosition.x = x + _velocity.x; _xPosition.y = y + _velocity.y; _xPosition.z = z + _velocity.z;

661 Appendix A: Pocket Reference

Changing Camera View switch (cameraSwitch) { //Switch to First person case 0: renderer.renderScene(scene, firstPersonCam, viewport); break; //Switch to Second person case 1: renderer.renderScene(scene, secondPersonCam, viewport); break; //Switch to third person case 2: renderer.renderScene(scene, thirdPersonCam, viewport); break; //Switch to Top view case 3: renderer.renderScene(scene, topViewCam, viewport); break; default: trace (“Not in my list!”); }

Object Navigation //Increase Nav Object Speed myObject.extra.angle += mySpeed; //Place the nav cylinder on the next position around the navPath var point:Number3D = navPath(myObject.extra.angle); var pointNav:Number3D = navPath(myObject.extra.angle+(0.4));

Gravitational Acceleration Physics //Iterate through i and j particles for ( var j:int=i+1;j < numParticles;j++){ //Find delta x, y, z for i and j particles var ddx:Number =(particleArray[j].x-particleArray[i].x); var ddy:Number =(particleArray[j].y-particleArray[i].y); var ddz:Number =(particleArray[j].z-particleArray[i].z); //Calculate distance var d:Number =(Math.pow(ddx,2)+Math.pow(ddy,2)+Math.pow(ddz,2)); //Calculate force of gravity between particles var mainF:Number=(gravitationalConstant*particleArray[j].mass*particleArray[i]. mass)/d; //Force times distance var xc:Number = mainF*ddx; var yc:Number = mainF*ddy; var zc:Number = mainF*ddz; //Set acceleration for i and j particles particleArray[i].accelerationX+=xc; particleArray[i].accelerationY+=yc; particleArray[i].accelerationZ+=zc; particleArray[j].accelerationX+=-xc;

662 Appendix A: Pocket Reference

particleArray[j].accelerationY+=-yc; particleArray[j].accelerationZ+=-zc; }

Pendulum Pinning myPendulum1.rotationZ = Osc; myPendulum2.rotationZ = Osc*1.721; myPendulum2.x = myPendulum1.getPin(Osc).x; myPendulum2.y = myPendulum1.getPin(Osc).y; public function getPin(myRot:Number):Point { var angle:Number = myRot* Math.PI / 180; var xPos:Number = this.x + Math.sin(angle) * myLength; var yPos:Number = this.y - Math.cos(angle) * myLength; return new Point(xPos, yPos); }

Interactive Scene 3D Event (x, y, z Gizmo) //Create a Coordinates axis private function Coords():void{ /*Create axis Gizmo-in this case cylinders are used but default line material Lines3D object can be used as well*/ colormaterialR = new ColorMaterial(0xFF0000, .8, true); colormaterialG = new ColorMaterial(0x00FF00, .8, true); colormaterialB = new ColorMaterial(0x0000FF, .8, true); //Set x-axis axisX = new Cylinder(colormaterialR, 5, 120, 4, 4); axisX.rotationZ=90; //Set y-axis axisY = new Cylinder(colormaterialG, 5, 120, 4, 4); //Set z-axis axisZ = new Cylinder(colormaterialB, 5, 120, 4, 4); axisZ.rotationX=90; //Put axis x,y,and z in a coordinate container coordContainer.addChild(axisX); coordContainer.addChild(axisY); coordContainer.addChild(axisZ); //Add ISM listeners to the x, y, z cylinder axis axisX.addEventListener(InteractiveScene3DEvent.OBJECT_OVER,function(e: InteractiveScene3DEvent):void {axisX.scale=1.5;}); axisX.addEventListener(InteractiveScene3DEvent.OBJECT_OUT,function(e: InteractiveScene3DEvent):void {axisX.scale=1;}); axisY.addEventListener(InteractiveScene3DEvent.OBJECT_OVER,function(e: InteractiveScene3DEvent):void {axisY.scale=1.5;}); axisY.addEventListener(InteractiveScene3DEvent.OBJECT_OUT,function(e: InteractiveScene3DEvent):void {axisY.scale=1;}); axisZ.addEventListener(InteractiveScene3DEvent.OBJECT_OVER,function(e: InteractiveScene3DEvent):void {axisZ.scale=1.5;}); axisZ.addEventListener(InteractiveScene3DEvent.OBJECT_OUT,function(e: InteractiveScene3DEvent):void {axisZ.scale=1;}); particleContainer.addChild(coordContainer); }

663 Appendix A: Pocket Reference

Pairing WOW and PV3D Objects //Pair objects together for ( var i:uint = 0; i < pv3DObjects.length; i++) { apply3DToPhy(pv3DObjects[i], wowObjects[i]); }}

Jiglib BasicView Starter Code package { //Flash, Jiglib, PV3D imports import flash.events.Event; import jiglib.plugin.papervision3d.Papervision3DPhysics; import org.papervision3d.view.BasicView; //Jiglib Starter Code public class StarterCode extends BasicView { //Instantiate Papervision3DPhysics parameter private var physics:Papervision3DPhysics; //Starter Code Constructor public function StarterCode() { super(0, 0, true); //Start Physics and PV3D Engine initPhysics(); startRendering(); } //Start Up Physics Engine private function initPhysics():void { //Set scene and physics engine speed physics = new Papervision3DPhysics(scene, 7); } //BasicView Animation Loop override protected function onRenderTick(event:Event = null):void { //update the engine in each frame physics.step(); super.onRenderTick(event); } }}

Jiglib Plane Shortcut private function createGround():void { var myMaterial :WireframeMaterial = new WireframeMaterial(0xCCCCCC); var floor:RigidBody = _physics.createGround(myMaterial , 1024 , 0); }

664 Appendix A: Pocket Reference

Jiglib Cube Shortcut private function createBox():void { var myMaterials:MaterialsList = new MaterialsList(); myMaterials.addMaterial(new WireframeMaterial(0xFF4444), “all”); var myBox:RigidBody = _physics.createCube(myMaterials, 64, 64, 64, 4, 4, 4); }

Jiglib Sphere Shortcut private function createSphere():void { var mySphere:RigidBody = _physics.createSphere(myMaterial, 64, 16, 8); }

Module Code < ?xml version=”1.0” encoding=”utf-8”? > < mx:Module xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”100%” height=”100%” > < mx:Script > < ![CDATA[ //Put the Application Here!!! ]] > < /mx:Script > < /mx:Module >

Stack Modifier var noise:Noise = new Noise(20); mstack.addModifier(noise); mstack.apply();

Chapter 10: Building 3D Games for Wii Summary: Building games is great fun and in this chapter you learned the basics of building game systems, which include game states and multi - levels. This chapter explored two different games: pool “ shooting ” and Pong. In the pool game you learned to build an entire game system based on Flex View States. And in the Pong game you learned to build a multi - level Wii controlled game. Both games were built in Flex, but the Pong game could have easily been built in Flash since it ’ s an ActionScript package. You built bounding boxes for your games by hacking the Jiglib PV3D plugin class and created a skybox.

Helpful Links http://blog.zupko.info

http://wiiflash.bytearray.org/

http://wiiflash.bytearray.org/?page_id=50

665 Appendix A: Pocket Reference

http://johnnylee.net/projects/wii

http://www.senocular.com/flash/tutorials

Adding a Flex Background Image < mx:Application xmlns:mx=”://www.adobe.com/2006/mxml” layout=”vertical” backgroundImage=”assets/image/side08.jpg” horizontalScrollPolicy=”off” verticalScro llPolicy=”off” >

Flex ViewStates < ?xml version=”1.0” encoding=”utf-8”? > < mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”vertical” current State=”introState” > < mx:states > < mx:State name=”introState”/ > < mx:State name=”playState”/ > < mx:State name=”winState” > < /mx:State > < mx:State name=”lostState”/ > < mx:State name=”cancelState”/ > < /mx:states > < mx:Canvas x=”0” y=”0” width=”550” height=”505”> < mx:Label x=”10” y=”10” text=”Space Ray Pool Game” id=”label1”/> < /mx:Canvas > < /mx:Application >

Center a Panel startPanel.x=stateCanvas.width/2-startPanel.width/2; startPanel.y=stateCanvas.height/2-startPanel.height;

Set ViewState (currentState) //Move to Play State, Initiate PV3D, and Start Music private function cancelClickHandler(event:MouseEvent): void { //Go to playState currentState= ”playState” ; initPV3D(); myGameState(1); }

666 Appendix A: Pocket Reference

Custom Skybox package org.lively3d.jiglib { //PV3D import statements import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.primitives.Cube; //Public class extends the cube public class Skybox extends Cube { //Embed your image assets [Embed (source=”/./assets/skybox/top01.jpg”)] private var BMTop: Class; [Embed (source=”/./assets/skybox/side10.jpg”)] private var BMBottom: Class; [Embed (source=”/./assets/skybox/side08.jpg”)] private var BMFront: Class; [Embed (source=”/./assets/skybox/bottom02.jpg”)] private var BMBack: Class; [Embed (source=”/./assets/skybox/side09.jpg”)] private var BMRight: Class; [Embed (source=”/./assets/skybox/top02.jpg”)] private var BMLeft: Class; //Skybox Constructor public function Skybox() {var bmTop: BitmapMaterial = new BitmapMaterial(new BMTop().bitmapData); var bmBottom: BitmapMaterial = new BitmapMaterial(new BMBottom().bitmapData); var bmFront: BitmapMaterial = new BitmapMaterial(new BMFront().bitmapData); var bmBack: BitmapMaterial = new BitmapMaterial(new BMBack().bitmapData); var bmRight: BitmapMaterial = new BitmapMaterial(new BMRight().bitmapData); var bmLeft: BitmapMaterial = new BitmapMaterial(new BMLeft().bitmapData); //Set your material to double side so you’ll see the inside of your skybox bmTop.doubleSided = true; bmBottom.doubleSided = true; bmFront.doubleSided = true; bmBack.doubleSided = true; bmRight.doubleSided = true; bmLeft.doubleSided = true; //Create your material list var materials: MaterialsList = new MaterialsList(); //Add Your Materials to your Materials list materials.addMaterial(bmTop, “top”); materials.addMaterial(bmBottom, “bottom”); materials.addMaterial(bmFront, “front”); materials.addMaterial(bmBack, “back”); materials.addMaterial(bmRight, “right”); materials.addMaterial(bmLeft, “left”);

// Add your materials, large sides, and low segments to your super class super(materials,10000,10000,10000,8,8,8); }}}

667 Appendix A: Pocket Reference

Jiglib Bounding Box public function createBoundingBox(material:MaterialObject3D, myWidth:Number, myDepth:Number, myHeight:Number, myX:Number=0, myY:Number=0, myZ:Number=0):void{ //Top Bottom createPlane(material, myWidth, myDepth, new Number3D(myX,myHeight/2+myY,myZ), new Number3D(-90,0,0)); createPlane(material, myWidth, myDepth, new Number3D(myX,-myHeight/2+myY,myZ), new Number3D(90,0,0)); //Left Right createPlane(material, myHeight, myWidth, new Number3D(myX,myY,-myDepth/2+myZ), new Number3D(0,180,90)); createPlane(material, myHeight, myWidth, new Number3D(myX,myY,myDepth/2+myZ), new Number3D(180,180,270)); //Front Back createPlane(material, myDepth, myHeight, new Number3D(myWidth/2+myX,myY,myZ), new Number3D(180,270,0)); createPlane(material, myDepth, myHeight, new Number3D(-myWidth/2+myX,myY,myZ), new Number3D(180,90,0)); }

Draw a Bullet Hole //draw the bullet hole var mat:MovieMaterial = event.renderHitData.material as MovieMaterial; var hole: Bitmap = Sprite(mat.movie).addChild( new bulletAsset()) as Bitmap; hole.blendMode = “ overlay” ; hole.x = event.renderHitData.u-hole.width*0.5; hole.y = event.renderHitData.v-hole.height*0.5; //Use drawBitmap to draw the hole mat.drawBitmap();

Rack Balls private function rackBalls():void{ //Iterate over your racking sphere for (var i:int = 0; i < myNum; i++) { jBall[i].moveTo(new JNumber3D(rackSphere.geometry.vertices[i].x, rackSphere. geometry.vertices[i].y, rackSphere.geometry.vertices[i].z)); // start position jBall[i].hitNumber=0; jBall[i].movable=true; }}

Stop Game Method private function showTime():void{ gameTime=getTimer()-getStartTime; if(gameTime > 60000||numHitSank==10){ //Remove processes tickTimer=false; removeSight(); yesShoot=false;

668 Appendix A: Pocket Reference

numHitSank=0; if(myScore < =0){ currentState=”loseState”; myGameState(3); }else{ currentState=”winState”; myGameState(2); }} gameTimeField.text = “Time “+ clockTime(gameTime); }

Time Conversion (milliseconds to seconds and minutes) //Convert milliseconds to seconds and minutes public function clockTime(ms:int):String{ var seconds:int = Math.floor(ms/1000); var minutes:int = Math.floor(seconds/60); seconds-=minutes*60; var timeString:String = minutes+”:”+String(seconds+100).substr(1,2); return timeString; }

Adding a Sight private function addSight():void{ sight.visible = true; Mouse.hide(); }

Sound Method private function myGameState(gameState:int):void{ switch(gameState) { case 0: //Start State soundControl1=(new startSound() as Sound).play(0,20); break; case 1: //Play State soundControl1.stop(); soundControl2=(new gameSound() as Sound).play(0,100); break; case 2: //Win State soundControl2.stop(); (new wonSound() as Sound).play(); break; case 3: //Lose State soundControl2.stop(); (new lostSound() as Sound).play(); break; case 4: //Restart State break; default: trace(“Not in data”); break; }} 669 Appendix A: Pocket Reference

Wii Starter Code package { import flash.display.Sprite; import org.wiiflash.Wiimote; import org.wiiflash.events.ButtonEvent; import org.wiiflash.events.WiimoteEvent; public class MyWiiBase extends Sprite { private var MyWiiIs:Wiimote; public function MyWiiBase() { //Instantiate your Wii controller MyWiiIs = new Wiimote(); MyWiiIs.connect(); WiiListeners(); } //Add your Wii listeners private function WiiListeners():void{

//Pitch, Yaw, Roll MyWiiIs.addEventListener( WiimoteEvent.UPDATE, onUpdated ); //Buttons MyWiiIs.addEventListener( ButtonEvent.A_PRESS, onAPressed ); MyWiiIs.addEventListener( ButtonEvent.A_RELEASE, onAReleased); MyWiiIs.addEventListener( ButtonEvent.LEFT_PRESS, onLeftPressed ); MyWiiIs.addEventListener( ButtonEvent.LEFT_RELEASE, onLeftReleased); MyWiiIs.addEventListener( ButtonEvent.RIGHT_PRESS, onRightPressed ); MyWiiIs.addEventListener( ButtonEvent.RIGHT_RELEASE, onRightReleased); MyWiiIs.addEventListener( ButtonEvent.UP_PRESS, onUpPressed ); MyWiiIs.addEventListener( ButtonEvent.UP_RELEASE, onUpReleased); MyWiiIs.addEventListener( ButtonEvent.DOWN_PRESS, onDownPressed ); MyWiiIs.addEventListener( ButtonEvent.DOWN_RELEASE, onDownReleased); MyWiiIs.addEventListener( ButtonEvent.B_PRESS, onBPressed ); MyWiiIs.addEventListener( ButtonEvent.B_RELEASE, onBReleased); MyWiiIs.addEventListener( ButtonEvent.MINUS_PRESS, onMinusPressed); MyWiiIs.addEventListener( ButtonEvent.MINUS_RELEASE, onMinusReleased); MyWiiIs.addEventListener( ButtonEvent.PLUS_PRESS, onPlusPressed); MyWiiIs.addEventListener( ButtonEvent.PLUS_RELEASE, onPlusReleased); MyWiiIs.addEventListener( ButtonEvent.HOME_PRESS, onHomePressed); MyWiiIs.addEventListener( ButtonEvent.HOME_RELEASE, onHomeReleased); MyWiiIs.addEventListener( ButtonEvent.ONE_PRESS, onOnePressed); MyWiiIs.addEventListener( ButtonEvent.ONE_RELEASE, onOneReleased); MyWiiIs.addEventListener( ButtonEvent.TWO_PRESS, onTwoPressed); MyWiiIs.addEventListener( ButtonEvent.TWO_RELEASE, onTwoReleased); } //Add your accelerometer and battery methods private function onUpdated ( pEvt:WiimoteEvent ):void { //trace(pEvt.target.pitch +” pitch”); //trace(pEvt.target.roll +” roll”); //trace( pEvt.target.yaw +” yaw”); //trace(pEvt.target.sensorX +” x”); //trace(pEvt.target.sensorY +” y”); //trace(pEvt.target.batteryLevel +” battery level”);

670 Appendix A: Pocket Reference

} //Add your button press methods private function onAPressed ( pEvt:ButtonEvent ):void { trace(“pressed A”); } private function onAReleased ( pEvt:ButtonEvent ):void { trace(“released A”); } private function onMinusPressed ( pEvt:ButtonEvent ):void { trace(“onMinusPressed”); } private function onMinusReleased ( pEvt:ButtonEvent ):void { trace(“onMinusReleased”); } private function onPlusPressed ( pEvt:ButtonEvent ):void { trace(“onPlusPressed”); } private function onPlusReleased ( pEvt:ButtonEvent ):void { trace(“onPlusReleased”); } private function onHomePressed ( pEvt:ButtonEvent ):void { trace(“Home Pressed”); } private function onHomeReleased ( pEvt:ButtonEvent ):void { trace(“Home Released”); } private function onOnePressed ( pEvt:ButtonEvent ):void { trace(“onOnePressed”); } private function onOneReleased ( pEvt:ButtonEvent ):void { trace(“onOneReleased”); } private function onTwoPressed ( pEvt:ButtonEvent ):void { trace(“onTwoPressed”); } private function onTwoReleased ( pEvt:ButtonEvent ):void { trace(“onTwoReleased”); } private function onBPressed ( pEvt:ButtonEvent ):void { (continued)

671 Appendix A: Pocket Reference

(continued) trace(“onBPressed”); } private function onBReleased ( pEvt:ButtonEvent ):void { trace(“onBReleased”); } private function onUpPressed ( pEvt:ButtonEvent ):void { trace(“onUpPressed”); } private function onUpReleased ( pEvt:ButtonEvent ):void { trace(“onUpReleased”); } private function onLeftPressed ( pEvt:ButtonEvent ):void { trace(“onLeftPressed”); } private function onLeftReleased ( pEvt:ButtonEvent ):void { trace(“onLeftReleased”); } private function onRightPressed ( pEvt:ButtonEvent ):void { trace(“onRightPressed”); } private function onRightReleased ( pEvt:ButtonEvent ):void { trace(“onRightReleased”); } private function onDownPressed ( pEvt:ButtonEvent ):void { trace(“onDownPressed”); } private function onDownReleased ( pEvt:ButtonEvent ):void { trace(“onDownReleased”); }}}

Get Sensor Bar IR Values private function getIRValues(even:WiimoteEvent):void{ if(!isNaN(_wiimote.ir.point1.x)){ dataIRX=(stage.width-_wiimote.ir.point1.x*stage.width);} if(!isNaN(_wiimote.ir.point1.y)){ dataIRY=(-_wiimote.ir.point1.y*stage.height); } //Calibration provided if needed. _virtualMouse.x = dataIRX+calibration.x; _virtualMouse.y = dataIRY+calibration.y; }

672 Appendix A: Pocket Reference

Pong Bounding Box public function createPongBoundingBox(material:MaterialObject3D, myWidth:Number, myDepth:Number, myHeight:Number, myX:Number=0, myY:Number=0, myZ:Number=0):void { //Bottom createPhongPlane(material, myWidth, myDepth, new Number3D(myX,0+myY,myZ), new Number3D(90,0,0)); //Sides createPhongPlane(material, myHeight, myWidth, new Number3D(myX,myY,-myDepth/2+myZ), new Number3D(0,180,90)); createPhongPlane(material, myHeight, myWidth, new Number3D(myX,myY,myDepth/2+myZ), new Number3D(180,180,270)); }

Constant Velocity Ball //Calculate the arctangent of your ball Var myTang:Number=Math.atan2(ballBody.currentState.linVelocity.z,ballBody.currentState. linVelocity.x); //Increase x direction of your ball proportioanely ballBody.currentState. linVelocity.x=Math.cos(myTang)*ballSpeedIs; //Increase y direction of your ball proportionately ballBody.currentState. linVelocity.z=Math.sin(myTang)*ballSpeedIs;

Paddle AI private function moveAiPaddle():void { // AI based on easing targetY=(ballBody.z+43); //Split the difference bar2.z +=(targetY-bar2.z)/easing; //Freeze bar x position and rotation bar2.x=280; bar2.rotationY=0; }

Adding Textboxes private function addTextFields():void{ //Instantiate your score and message textboxes myMessage = new TextField(); //Message Textbox properties myMessage.y=20; myMessage.x=400; myMessage.scaleX=1.5; myMessage.scaleY=1.5; myMessage.textColor=0xffffff; myMessage.text=”Level “+String(levelInt)+” Game Goes to 21”; myMessage.maxChars=400; myMessage.width=400; //Add Box to stage addChild(myMessage); } 673 Appendix A: Pocket Reference Chapter 11: Integrating the Flash Media Server Summary: Creating Rich Internet Applications has long been the goal of Macromedia (now Adobe). An integral part of that has been using the Flash Media Server to create interactive web experiences. In this chapter, you learned how to get your users interacting with 3D objects using remote shared objects. You also created the starter code for a slot car racing game. Finally, you examined alternatives to the FMS such as Red 5, Wowza, and the Flash Collaboration Service.

Helpful Links http://www.influxis.com

http://www.sothink.com

http://www.flashkit.com

http://www.wowzamedia.com

http://blog.zupko.info

http://algorithmist.wordpress.com

http://code.google.com

http://osflash.org/red5

http://rockonflash.wordpress.com

http://labs.adobe.com/technologies/afcs

Connecting to the Flash Media Server (FMS) netConnect= new NetConnection(); netConnect.addEventListener(NetStatusEvent.NET_STATUS, connectStatus); netConnect.connect(“rtmp://localhost/ConnectingFMS”);

FMS Starter Code package { //Flash Imports import flash.display.Sprite; import flash.events.NetStatusEvent; import flash.net.NetConnection; import flash.text.TextField; //Connection Class public class ConnectingFMS extends Sprite {

674 Appendix A: Pocket Reference

//Communication Properties private var netConnect:NetConnection; private var textField:TextField; //Connection Constructor public function ConnectingFMS() { //Instantiate net connection and connect to rtmp netConnect= new NetConnection(); netConnect.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); netConnect.connect(“rtmp://localhost/ConnectingFMS”); } //Check connection Status public function netStatusHandler(event:NetStatusEvent):void { textField = new TextField(); addChild(textField); //Trace connection status trace(“connected is: “ + netConnect.connected ); trace(“event.info.level: “ + event.info.level); trace(“event.info.code: “ + event.info.code); //Use switch case to tab through different info messages switch (event.info.code) { case “NetConnection.Connect.Success”: textField.text=”You Are Connected!”; break; case “NetConnection.Connect.Rejected”: textField.text=”You Connection is Rejected!”; break; case “NetConnection.Connect.Failed”: textField.text=”Connection Failed!”; break; } } }}

XML Loaded RTMP Address //Communication Properties private var netConnect:NetConnection; private var textField:TextField; //Connection Constructor public function ConnectingFMSXML() { getXml(); } //Load your XML public function getXml():void{ var urlRequest:URLRequest = new URLRequest(“assets/data/rtmpAddress.xml”); var xmlLoader:URLLoader = new URLLoader(urlRequest); xmlLoader.addEventListener(Event.COMPLETE, readXml); } (continued)

675 Appendix A: Pocket Reference

(continued) //Parse your XML public function readXml(event:Event):void{ var readRtmp:XML=new XML(event.target.data); //Instantiate net connection and connect to rtmp netConnect= new NetConnection(); netConnect.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); netConnect.connect(readRtmp.rtmp.@path); }

Remote Shared Object and changeList // update clients when the shared object changes private function syncHandler(event:SyncEvent):void { //When a sync event is fired //Update the position of the ball for (var cList:uint; cList

Dashed Track Using Knot Theory //Draw upper half Circle var prevKnotX1Cir:Number=knotRadius; var prevKnotY1Cir:Number=knot2.y; //Iterate over half circle for(var knotPos1:uint=0;knotPos1 < 40;knotPos1++){ var knotX1Cir:Number= knotRadius*Math.cos(Math.PI*knotPos1/40);

676 Appendix A: Pocket Reference

var knotY1Cir:Number= knotRadius*Math.sin(Math.PI*knotPos1/40)+knot2.y; //Use modulo trick to skip lines and make strips if(knotPos1%2==1){ addNewSegmentedLine(4,1,prevKnotX1Cir, -2, prevKnotY1Cir, knotX1Cir, -2, knotY1Cir); } //Store previous value for next iteration starting point prevKnotX1Cir=knotX1Cir; prevKnotY1Cir=knotY1Cir; }

Chapter 12: Developing 3D Websites Summary: In this chapter you converted the CSIS 2D site to a 3D site. You created custom tree, cloud, and collage classes and built a 3D navigation system from scratch using PV3D components. You examined a few importing reality checks when it comes to building websites: using a design doc, learning Photoshop, and combining 2D with 3D to get an optimized 3D experience. Finally, you learned how to optimize your website for search engines by adding html text to your html - swf wrapper file and by using the SWFObject.

Helpful Links http://www.sebleedelisle.com/?p=455

http://www.professionalpapervision.com

http://www.ledjam.com

http://www.seomoz.org

http://www.webmasters.live.com

http://www.google.com/webmasters

http://dailypv3d.wordpress.com

http://gsi.csis.org

http://code.google.com/p/swfobject

Adding Reflection View public class CSIS extends ReflectionView

677 Appendix A: Pocket Reference

Cylindrical Panorama Class package org.lively3d.jiglib { //by Lively import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.primitives.Cylinder; public class SlidingCylinder extends Cylinder { //Embed your image assets [Embed (source=”/./assets/image/backGround.jpg”)] private var BMFront: Class; public function SlidingCylinder(radius:Number) { var bmFront: BitmapMaterial = new BitmapMaterial(new BMFront().bitmapData); //Set your material to double side so you’ll see the inside of your skybox bmFront.doubleSided = true; //Add your material, large sides, and low segments to your super method var myHeight:Number= 2*Math.PI*radius*256/4096; super(bmFront,radius,myHeight,20,4,radius, false, false); y = myHeight/2-8; }}}

Instantiating Clouds Using Mod cloudPlane = new Plane(myCould[i%6], 256, 256, 1, 1);

Randomly Distributing Clouds var myCould:Array=[cloud0, cloud1,cloud2, cloud3,cloud4, cloud5]; //Randomly distribute your clouds using the modulo method for(var i:int=0; i < cloudNum; i++) { cloudPlane = new Plane(myCould[i%6], 256, 256, 1, 1); cloudPlane.x = radius-Math.random()*2*radius; cloudPlane.z = radius-Math.random()*2*radius; cloudPlane.y=64-Math.random()*4; cloudPlane.rotationX=90; this.addChild(cloudPlane); }

Tree Class package org.lively3d.jiglib { //by Lively import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.primitives.Cylinder; public class SlidingCylinder extends Cylinder { //Embed your image assets [Embed (source=”/./assets/image/backGround.jpg”)]

678 Appendix A: Pocket Reference

private var BMFront: Class; public function SlidingCylinder(radius:Number) { var bmFront: BitmapMaterial = new BitmapMaterial(new BMFront().bitmapData); //Set your material to double side so you’ll see the inside of your skybox bmFront.doubleSided = true; //Add your material, large sides, and low segments to your super method var myHeight:Number= 2*Math.PI*radius*256/4096; super(bmFront,radius,myHeight,20,4,radius, false, false); y = myHeight/2-8; }}}

Scene and Panel Rotation //Panel Rotation private function togglerNumber(hitNumber:int):void { //This positions your panel into the correct x and z position var myZ:Number=panelArray[hitNumber].z; var myX:Number=panelArray[hitNumber].x; //Calculate the Distance of your panel from the origin var myDist:Number=Math.sqrt(myX*myX+myZ*myZ); //Calculate the angle pendulum pivot point before rotation var myAngle:Number = Math.atan2(-panelArray[hitNumber].z,-panelArray[hitNumber].x); //Grab the panel rotational orientation from the XML and convert it to radians var rotNeeded:Number = (panelArray[hitNumber].rotationY+30)*Math.PI/180; var rotNeeded2:Number = (panelArray[hitNumber].rotationY+30); //Calculate your total rotation by adding the two rotations together var totRot:Number=myAngle+rotNeeded; //Calculate your new x and y coordinates based upon your total rotation and trig var newMyX:Number = myDist*Math.cos(totRot); var newMyZ:Number = myDist*Math.sin(totRot); //Use the TweenLite engine to get a smooth transition to your new rotational values TweenLite.to(mySlideLayer, 1.5, {x:newMyX-100, z:-myRadius/2+newMyZ,rotationY:- rotNeeded2}); TweenLite.to(myCylinder,2,{rotationY:myAngle*180/Math.PI}); //Set the material for your button state myGameStateIs(hitNumber); }

Simple Navigation Panel private function createObjects():void{ for(var i:uint=0; i < 8; i++) { //Instantiate Plane material and make it interactive var bam:BitmapFileMaterial = new BitmapFileMaterial(“assets/button/rev”+i+”webBW. jpg”); bam.interactive = true; //Create, Position, and Scale Plane (Button Panel) p = new Plane(bam, 16, 16, 2, 2); p.x = 12*i-40; (continued)

679 Appendix A: Pocket Reference

(continued) p.z = -800; p.scale=.6; p.y=-2; //Add Plane to Scene scene.addChild(p); //Set your extra object p.extra={pIndex:i}; //Add Plane Listeners p.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, toggler); p.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, rollOver); p.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, rollOut); planeArray[i] = p; //Add Plane to Array } //Switch Button Material planeArray[0].material= new BitmapFileMaterial(“assets/button/rev0web.jpg”); } //RollOver and RollOut Methods private function rollOver(event:InteractiveScene3DEvent):void{ event.target.y=0; (new overSound() as Sound).play(); } private function rollOut(event:InteractiveScene3DEvent):void{ event.target.y=-2; }

Move Camera Backwards camera.moveBackward(1000); Chapter 13: Making 3D Movies Summary: This chapter highlights one of the greatest challenges developers face today – the rapid change in programming architecture and platforms. This was demonstrated by building the Seven Revolutions project from the previous chapter in Flash CS4. Next, Flash Catalyst and its integration into Flash Builder were examined. Finally, the new Animate Super Effects class was used to create a panel animation, Photoshop3D was examined, and a PV3D animation engine which saves its results to the DOM developed.

Helpful Links http://www.ibm.com/developerworks/library/os-xmldomphp/

http://www.juxtinteractive.com/work/vizualpv3d

680 Appendix A: Pocket Reference

Preloader Script stop(); //Code for Preloader loaderInfo.addEventListener(ProgressEvent.PROGRESS, updateProgress); function updateProgress(evtObject:ProgressEvent):void { //Calculate percent loader var myPercent:Number = Math.floor((evtObject.bytesLoaded*100)/evtObject. bytesTotal); loadingTxt.text = myPercent+”%”; //Go to next frame when 100% loaded if (myPercent==100){ //Go to application frame nextFrame(); } }

3D Effects Rotation < s:Rotate3D id=”rotateEffect1” target=”{targetImg1}” angleYBy=”360” duration=”1000” autoCenterTransform=”true” repeatCount=”2” repeatBehavior=”reverse” effectStart=”btn1B=false;” effectEnd=”btn1B=true;” / >

Chapter 14: Taking Virtual Tours Summary: As technology advances, Virtual Reality is playing a key role. In this chapter you examined the using of VR in the military, using augmented reality, and building 3D worlds. The majority of the chapter treated augmented reality using the FLARToolkit which was created by Saqoosha. Saqoosha ’ s starter kit code was examined and extended to create PV3D BasicView starter code. Using this starter code a number of items were imported in FLAR including the Jiglib Pong game created in the chapter on games.

Helpful Links http://www.libspark.org/svn/

http://www.squidder.com/2009/03/06/flar-how-to-multiple-instances-of-multiple-markers

http://saqoosha.net/en

http://saqoosha.net/en/flartoolkit/start-up-guide

http://flash.tarotaro.org/blog/2008/12/14/artoolkit-marker-generator-online-released

681 Appendix A: Pocket Reference

FLAR BasicView Starter Code package { //Flash Imports import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; import flash.media.Camera; import flash.media.Video; import flash.utils.ByteArray; //FLAR Imports import org.libspark.flartoolkit.core.FLARCode; import org.libspark.flartoolkit.core.param.FLARParam; import org.libspark.flartoolkit.core.raster.rgb.FLARRgbRaster_BitmapData; import org.libspark.flartoolkit.core.transmat.FLARTransMatResult; import org.libspark.flartoolkit.detector.FLARSingleMarkerDetector; import org.libspark.flartoolkit.pv3d.FLARBaseNode; import org.libspark.flartoolkit.pv3d.FLARCamera3D; //PV3D Imports import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.materials.WireframeMaterial; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.*; import org.papervision3d.view.BasicView; //Create your SWF metadata [SWF(width=”640”, height=”480”, backgroundColor=”#000000”, frameRate=”30”)] public class BasicViewFLAR extends BasicView { //Embed your Marker and Camera correction data [Embed(source=”assets/data/camera_para.dat”, mimeType=”application/octet-stream”)] private var CamParams:Class; //Marker that your FLARToolKit will detect [Embed(source=”assets/data/flarlogo.pat”, mimeType=”application/octet-stream”)] private var MarkerPat:Class; //Create a display object to hold your PV3D objects private var myDisplayObject:DisplayObject3D= new DisplayObject3D(); //Camera properties private static const WIDTH:Number = 640; private static const HEIGHT:Number = 480; private static const FRAMES_PER_SECOND:int = 60; private var camParams:FLARParam; private var markerPat:FLARCode; private var raster:FLARRgbRaster_BitmapData; private var detector:FLARSingleMarkerDetector; private var webCam:Camera; private var camDisplay:Video; private var capture:BitmapData; //FLAR properties private var flarCam3D:FLARCamera3D; private var flarNode:FLARBaseNode;

682 Appendix A: Pocket Reference private var transResult:FLARTransMatResult; private var oscParam:Number=0; //PV3D primitives private var _plane:Plane; private var _cube:Cube; //Start up BasicView and initiate your program public function BasicViewFLAR():void { super(WIDTH * 2, HEIGHT * 2, false); init(); } //Run your initiation functions private function init():void { setupFlar(); setupWebCam(); /*Set Up Bitmap is the heart of the Flar process. On every frame you take a bitmap data shot of the webcam and pass it to the FLARToolKit and the FLARToolKit detects your pattern in that data.*/ setupBitmap(); setupPV3D(); startRendering(); } //Set up FLAR private function setupFlar():void { camParams = new FLARParam(); camParams.loadARParam(new CamParams() as ByteArray); markerPat = new FLARCode(16, 16); markerPat.loadARPatt(new MarkerPat()); //Instantiate your FLAR camera and node flarCam3D = new FLARCamera3D(camParams); flarNode = new FLARBaseNode(); //Add your FLAR node to your PV3D scene scene.addChild(flarNode); transResult = new FLARTransMatResult(); } //Set up your Camera private function setupWebCam():void { //Instantiate your camera camDisplay = new Video(); camDisplay.width = WIDTH; camDisplay.height = HEIGHT; webCam = Camera.getCamera(); //Attach Camera camDisplay.attachCamera(webCam); //Add video component to the stage addChildAt(camDisplay, getChildIndex(viewport)); webCam.setMode(WIDTH / 2, HEIGHT / 2, FRAMES_PER_SECOND); } //Capture your Camera scene private function setupBitmap():void { (continued)

683 Appendix A: Pocket Reference

(continued) capture = new BitmapData(camDisplay.width, camDisplay.height, false, 0x0); //Draw video object that contains your webcam capture.draw(camDisplay); //Two import FLAR objects that will let you detect your pattern raster = new FLARRgbRaster_BitmapData(capture); //Detect where the pattern is found detector = new FLARSingleMarkerDetector(camParams, markerPat, 80); } //Create your PV3D objects private function setupPV3D():void { //Viewport fix from Saqoosha due to camera corrections viewport.x = -4; //Create your wireframe plane that will overlay your pattern var wmat:WireframeMaterial = new WireframeMaterial(0xff0000, 1, 2); this._plane = new Plane(wmat, 80, 80); // 80mm x 80mm

//Instantiate the PV3D light source for your material var light:PointLight3D = new PointLight3D(); light.x = 0; light.y = 1000; light.z = -1000; // Instantiate your cube and create its materials var fmat:FlatShadeMaterial = new FlatShadeMaterial(light, 0xff22aa, 0x75104e); this._cube = new Cube(new MaterialsList({all: fmat}), 40, 40, 40); this._cube.z = 20; //Add your cube and plane to your display object myDisplayObject.addChild(_plane); myDisplayObject.addChild(_cube); //Rotate your plane into position this._plane.rotationX = 180; //Add your displayobject to your FLAR Node flarNode.addChild(myDisplayObject); } //Start rendering your PV3D scene override protected function onRenderTick(event:Event = null):void { //Capture bitmap data from the webcam it’s now stored inside of bitmapdata capture.draw(camDisplay); //Add a try-catch to keep errors from shutting you down try { //Pass in the raster object which has the bitmapdata and passes it to FLAR if(detector.detectMarkerLite(raster, 80) & & detector.getConfidence() > 0.5) { //Get the transformation from the FLARToolKit and apply it to the 3D scene detector.getTransformMatrix(transResult); flarNode.setTransformMatrix(transResult); flarNode.visible = true; }else { flarNode.visible = false; }} catch(e:Error){trace(“Catch a falling error and put it in your pocket!”);}

684 Appendix A: Pocket Reference

//Add a little Animation to your Cube oscParam+=4; oscParam%=360; this._cube.rotationZ+=4; this._cube.rotationY+=3; this._cube.scaleZ=2+Math.sin(oscParam*Math.PI/180); this._cube.scaleY=this._cube.scaleX=2-Math.sin(oscParam*Math.PI/180); //Render your scene renderer.renderScene(scene, flarCam3D, viewport); }}}

Jiglib Key Code //Jiglib Code if(keyLeft) { helloSphere.addWorldForce(new JNumber3D(0, 0, moveForce), helloSphere.currentState. position); } if(keyRight) { helloSphere.addWorldForce(new JNumber3D(0, 0,-moveForce), helloSphere.currentState. position); } if(keyForward) { helloSphere.addWorldForce(new JNumber3D(moveForce,0,0 ), helloSphere.currentState. position); } if(keyReverse) { helloSphere.addWorldForce(new JNumber3D(-moveForce, 0, 0 ), helloSphere. currentState.position); } if(keyUp) { helloSphere.addWorldForce(new JNumber3D(0, 2*moveForce, 0), helloSphere. currentState.position); }

Adding Multiple Markers override protected function _detectMarkers(): void { _resultsArray = _flarDetector.updateMarkerPosition( _flarRaster, 80, .5 ); for ( var i: int = 0; i < _resultsArray.length; i ++ ) { var subResults: Array = _resultsArray[ i ]; for ( var j: * in subResults ) { _flarDetector.getTransmationMatrix( subResults[ j ], _resultMat ); if ( _cubes[ i ][ j ] != null ) transformMatrix( _cubes[ i ][ j ], _resultMat ); }}

685 Appendix A: Pocket Reference

Adding a 3D Model (MD2) to FLAR //Import the appropriate classes import org.papervision3d.objects.parsers.MD2; import org.papervision3d.materials.BitmapFileMaterial; //Instantiate the parsed element private var item:MD2 = new MD2(); //Load your 3D model and skin var myHeliSkin:BitmapFileMaterial = new BitmapFileMaterial(‘assets/heli/ helicopter1.png’, true); //The load method includes (url string, material, framerate, scale) item.load(‘assets/heli/helicopter.md2’, myHeliSkin, 10, 3); item.scale=.3; // Add models to your display object myDisplayObject.addChild(item); //Add your displayobject to your FLAR Node flarNode.addChild(myDisplayObject);

Chapter 15: Adding Services Summary: In this chapter you learned to hook up Web Services using Flash Catalyst and Flash Builder. You built a Twitter viewer, Weather checker, Flickr picker, and CNN news reader. Flash Builder has a full range of data connectivity options . . . WSDL, PHP, Coldfusion, BlazeDS, and LCDS. This makes data connectivity easier and gives you more time for creative development.

Helpful Links http://www.libspark.org/svn/

http://www.cnn.com/services/rss/?iref=rsssvcs

http://rss.cnn.com/rss/cnn_topstories.rss

http://rss.cnn.com/rss/cnn_topstories.rss

http://labs.adobe.com/technologies/flash/videos

http://search.twitter.com/search.atom? ” +textinput1.text

http://api.flickr.com/services/feeds/photos_public.gne

http://developer.yahoo.com

http://developer.yahoo.com/weather

http://weather.yahooapis.com/forecastrss

http://sujitreddyg.wordpress.com

686 Appendix A: Pocket Reference

HTTPService < s:HTTPService id=”myPhotoService” url=”http://api.flickr.com/services/feeds/ photos_public.gne” result=”myPhotoService_resultHandler(event)” fault=”myPhotoService_faultHandler(event)” / > // Request photos based on keyword criteria you insert private function requestPhotos(event:Event):void { // if the photoService is still loading we cancel to prevent errors // then we call again. myPhotoService.cancel(); var params:Object = new Object(); params.format = ‘rss_200_enc’; params.tags = searchTerms.text; myPhotoService.send(params); }

Dynamic Twitter URL Loading protected function button1_clickHandler(event:MouseEvent):void { //Dynamically load data myService.url=”http://search.twitter.com/search.atom?”+textinput1.text; //Run HTTPService myService.send(); }

HTTPService with fault and send Methods < fx:Declarations > < s:HTTPService id=”myPhotoService” url=”http://api.flickr.com/services/feeds/photos_public.gne” result=”myPhotoService_resultHandler(event)” fault=”myPhotoService_ faultHandler(event)” / >

List Selection Code Stub //Add a Button_click method which tracks which index was clicked on protected function Button_click(event:MouseEvent):void { if(myList.selectedIndex <0) myList.selectedIndex=0; //Send selected image to Flash Catalyst generated component customcomponent11.mySource=myPhotoFeed[myList.selectedIndex].content.url; //Add image source link to top of screen in myID textbox myID.text=String(customcomponent11.mySource); }

687 Appendix A: Pocket Reference

Calling a URL (using Try/Catch) private function getMyURL(event:MouseEvent):void{ var req:URLRequest = new URLRequest(myNewsFeed.link); try { navigateToURL(req); } catch (e:Error) { trace(“No URL Available”); } } Chapter 16: Exploring Flash 10 and Beyond Summary: Flash 3D coding has come a long way, and over the next few years will transcend even optimistic expectations. In this chapter you scratched the surface of such technologies by rebuilding your 3D pool “ shooting ” game in Flash Catalyst, and visiting a number of CS4 rendering examples.

Helpful Links http://www.unitzeroone.com/labs/alchemyPushingPixels

http://www.flashandmath.com

http://www.flepstudio.org

http://www.bobjim.com

http://labs.adobe.com/technologies/alchemy

http://pixelero.wordpress.com

FXG Fragment < s:Graphic x=”0” y=”0” > < s:Line xFrom=”0” xTo=”100” yFrom=”0” yTo=”100” > < s:stroke > < /s:stroke > < /s:Line >

688 Appendix A: Pocket Reference

Flash Catalyst States < s:states > < s:State name=”Intro”/ > < s:State name=”Play”/ > < s:State name=”Win”/ > < s:State name=”Lose”/ > < s:State name=”Cancel”/ > < /s:states >

State Toggle Code protected function Play_click():void { var state:String = currentState; if ( state == ‘Intro’ ) { currentState=’Play’; initPV3D(); myGameState(1); myTester.y=-1000; }}

Split & Join Methods var myStringIs:String = picsArray[i][j]; myStringIs=myStringIs.split(“.”).join(); myStringIs=myStringIs.split(“c”).join(); splitArray=myStringIs.split(“,”); stringNumIs=Number(splitArray[1].toString());

Forward -Backward Button Script private function moveForward(e:MouseEvent):void { stringNumIs++; stringNumIs=stringNumIs%47; if(stringNumIs==0)stringNumIs=1; loader.load(new URLRequest(“images/pic”+stringNumIs+”.jpg”)); } private function moveBackwards(e:MouseEvent):void { stringNumIs-; if(stringNumIs==0)stringNumIs=46; loader.load(new URLRequest(“images/pic”+stringNumIs+”.jpg”)); }

CS4 Dot Product var zd:Number = AVect[6].dotProduct(AVect[5])/(mag1*mag2);

689 Appendix A: Pocket Reference

XML Parser private function setVertices(myData_xml:XML):void { //Parse vertex and face data var mySlitVert:Array=myData_xml.myPrimSet[0].myVertices.split(“,”); var mySlitFace:Array=myData_xml.myPrimSet[0].myFaces.split(“,”); numVertices=(mySlitVert.length-1)/3; var myScale:Number=20; for(i=0;i <=numVertices;i++){ //Push data into your vertex array vertsVec[i]= new Vector3D(mySlitVert[i*3]*myScale,mySlitVert[i*3+1]*myScale,mySlitV ert[i*3+2]*myScale); } numFaces = (mySlitFace.length-1)/3; for(j=0;j <=numFaces;j++){ //Push data into your face array facesVec[j]=[mySlitFace[j*3],mySlitFace[j*3+1],mySlitFace[j*3+2]]; facesColors[j]=Math.random()*0xffffff; }}

Simple Culling if(zd > 0){ //Define your vertices Vector var vertices:Vector. < Number >=new Vector. < Number > (); //Push in the appropriate vertex using curFac vertices.push(dispVec[facesVec[curFace][0]].x, dispVec[facesVec[curFace][0]].y); vertices.push(dispVec[facesVec[curFace][1]].x, dispVec[facesVec[curFace][1]].y); vertices.push(dispVec[facesVec[curFace][2]].x, dispVec[facesVec[curFace][2]].y); //Create your lineStyle (use 0 to keep a line from being drawn) spObjImage.graphics.lineStyle(0);\ //Put in the color of your triangle (this is your shade) spObjImage.graphics.beginFill(currentColor, .8); //Draw you triangle using the drawTriangles method spObjImage.graphics.drawTriangles(vertices); }}

Remove Special Characters // Convert to string myString=molService.lastResult.toString(); //Remove special characters myString = myString.split(“\n”).join(“ “); myString = myString.split(“\r”).join(“ “); //Stuff into an Array myArray = myString.split(/ /);

690 Appendix A: Pocket Reference forEach Array Method myArray.forEach(myStart); function myStart(element:String, index:int, array:Array):void{ if(element == “V2000”||element == “V3000”){ myStartVert=index;}

Molecular drawPath commands[2*k+2*dB+4*tB+2*aB] mydata[4*k+4*dB+8*tB+4*aB] myHolder.graphics.drawPath(commands, mydata); Parametric Mapping Equations In 2D: x = r( )*cos( ) y = r( )*sin( ) Generalized 2D Solution: r(a, b, ) = a/(sin( ) + b*cos( )) In 3D: x = r( , )*cos( )sin( ) y = r( , )*sin( )sin( ) z = r( , )*cos( ) Generalized 3D Solution: r(a, b, c, , ) = a/(cos( )+b*sin( )sin( )+c*cos( )sin( ))

691

Companion Website

The companion website for Professional Papervision3D (at www.professionalpapervision.com ) is more than just an add - on or a place for distributing code, it ’ s an integral part of conveying the book ’ s contents. The days when code was small and easily explained in a few written pages are gone. Today, many Flash applications are thousands of lines of code with large supporting libraries. Using video to help convey the concepts behind such applications is the primary goal of the book ’ s website.

You ’ ll find a large set of video resources on the book ’ s website that are designed to help you come up to speed on the book ’ s topics. The website consists of:

❑ Book Chapter Videos on Each Major Topic ❑ Training Videos on Topics Supporting PV3D ❑ Chapter Code for Download and Viewing ❑ Additional Topics From the Book ❑ Bonus Examples and New Flash 3D Technologies ❑ Current 3D Topics from UCF

Using the book and the book ’ s training videos together should help you come up to speed rapidly in Flash 3D and its supporting technologies.

Book Chapter Videos Each major topic in the book has a video associated with it. This approach is designed to maximize your grasp of the content and convey concepts difficult to explain in text. In some cases, portions of the book were written to go hand in hand with its counterpart video content. To maximize your absorption of the contents, first read a section in the book, watch its video, try the code examples, and then try extending the code ’ s capability. Appendix B: Companion Website

All the video on this site is contained on YouTube as well, so if you have difficulty accessing them on the site you can go directly to them from the author ’ s channel at www.youtube.com/mikenku . The content on Youtube is not as well organized as the book ’ s site. But YouTube provides an alternative path to the content, and allows you to embed the videos in your own work if needed.

Training Videos Developing applications using software like Papervision3D requires that you also use a number of other supporting technologies. Just teaching Papervision3D isn ’ t enough to bring you up to speed on the topic, so the book ’ s website provides ten additional training courses patterned after Lynda.com. Each training video provides you with the ability to come up to speed quickly on a topic interest. You can of course find many more great topics on Lynda but not for free and not geared specifically for PV3D. You ’ ll find the following list of video training courses on the book ’ s website:

❑ Photoshop ❑ Illustrator (Basics) ❑ 3DS Max ❑ Blender ❑ SketchUp ❑ Flash CS4 ❑ Flash Catalyst ❑ Flash Builder (Gumbo) ❑ Papervision3D (Training Course) ❑ How the Book ’ s Website Was Made

The last two courses are geared to help you professionally. Papervision3D is a basic video training course that contains many of the topics presented in the seminars by the PV3D core team. The author has had the opportunity to attend a few of them. And if you ever get a chance to go to one you should. But if not, the material in this training course provides you with the necessary information.

How the Book ’ s Website was Made teaches you how to build a similar site by demonstrating the software and multimedia that were used to create the book ’ s website. The course covers:

❑ Building the book ’ s website in Flash Builder ❑ Creating a multimedia recording interface ❑ Using capture software, using bamboo drawing pad ❑ Audio recording with a Rhode microphone ❑ Hosting a professional quality curriculum for only pennies

Using this material you ’ ll be able to build your own multimedia training site.

694 Appendix B: Companion Website Current 3D Projects The author is currently simulating nano - technology at the University of Central Florida using PV3D and CS4. Current 3D projects in his work are displayed on the book ’ s website. If you ’ re a scientist you ’ ll find these simulations interesting. In addition to building nano - simulations, a number of projects requiring connectivity and data management are under development by the author as well.

Bonus Examples As technology changes, there ’ s a need for examples on how to use that technology. The book ’ s companion website catalogs extended examples (that are created by the author) on the newest Flash 3D technology hitting the scene.

695

Index Index

slot car racing game, having a wreck, 442–444 Numbers Wiimote accelerometer sensing, 399–400 1st person camera perspective, 323–324 access modifiers (scope), 48–49 2.5D, 6 Action Script 3.0 Game Programming University 2D (Rozenzweig), 238 billboarding surfaces to look 3D, 242–244 action tokens, 282–283 converting website to 3D. See CSIS (Center for ActionScript Strategic and International Studies) website code for particle systems, 230–231 rendering to 3D, 8–9 running Papervision3D in Flex as, 41–43 2nd person camera perspective, 323–324 simulating 3D objects in, 34 3D. See also Flash 3D, understanding turning Flash animation into, 334 adding to Flickr menu. See Flickr menu turning timeline animation into, 29–31 converting 2D to. See 2D addChild method creating Photoshop layer in, 511 adding models to stage, 184 Flash 10 engine in 13 lines of code, 14 adding sphere to scene, 57 Flash 9 engine. See Flash 9, building 3D adding sphere to stage, 54 engine in 18 lines of code in final base code, 55–56 models. See models, 3D formulating PV3D class, 52 physics. See physics, 3D setting viewport, 53 rotation. See rotation, 3D using rawChildren tag with, 43 Wall Carousel Course. See Wall Carousel addEventListener method, 17–19 Course, 3D coding animation, 11–12 wall course. See Wall Carousel Course, 3D creating cube in CS4, 116 websites. See Websites, developing 3D creating parametric particle system, 17–19 3DSMax addPropertyArray method, 31 billboarding surfaces to look 3D, 242–244 addWorldForce method, 390 building ray gun in, 377–378 Adobe converting file into MD2 format, 185–186 AIR application. See AIR creating model in, 178, 195 Flash Collaboration Service, 457–458 creating multiple images on single object, 191 Flex application. See Flex creating single and multiple objects with Photoshop. See Photoshop simple images, 188–191 affine transformations, 134–135 primitives, 75 AI (Artificial Intelligence), adding to Pong game, simple exporters, 204 408 3rd person camera perspective, 323–324 AIR, 285–317 building modeling program. See modeling program, PV3D A fixing Flash Sandbox security issue, 255–259 acceleration Google map wormhole created as, 608–609 adding to DisplayObject3D class, 326–328 overview of, 285 calculating for camera, 322–323 preventing insecurity of swf files, 422 slot car racing game, adding FMS, 444–456 sculpting prim with face3d, 313–316 AIR, building editor in

AIR, building editor in, 306–312 Ase, 179 accessing local file system, 307 asymptotic zero. See vanishing point (asymptotic deployment, 312 zero) overview of, 306–307 asynchronous file operations, AIR, 307 saving bitmap data, 307–310 AsyncToken class, 282–283 saving to local PC using Flash 10, 310–311 atoms, Molfile molecule viewer, 612–616 turning Webcam into PV3D heightmap, 311–312 attributes using SQLite, 312 particles in particle system, 227 Alchemy property access, 48 difficulty optimizing PV3D for Flash 10, 591 XML, 264 Flash importing C/C++ libraries using, 251 augmented reality, 523–550 information on, 201 adding Jiglib physics to FLAR, 533–541 Alert box, saving bitmap data, 308 adding PONG to FLAR, 542–548 TheAlgorithmist, 438 creating BasicView starter code, 527–531 alignment, stage, 65–66 creating multiple markers, 541–542 alpha property, ParticleMaterial, 222–223 defined, 523 ambientColor variable, CS4 flat shading, 595 generating own markers, 531–533 Animate Super class, Flash Builder, 506 making FLAR run faster, 549 AnimateTransform class, 517–518 overview of, 523–526 animation, 3D putting 3D model into FLAR, 549–550 Collada parsers and, 198–200 Away3D of exploding balls, 382–383 creating tube from Torus, 105–107 inside Collada files, 190 naming Torus tube, 107 of light source in Pixel Bender, 218 porting in, 361–362 loop, 140–141 porting torus from, 102–105 in MD2 and Collada, 186–187 of muzzles, 378–379 principles of, 28–29 B recording. See recording 3D animations backEnvMap, material for, 155–156 of skyboxes, 226–227 background image turning into ActionScript in Flash, 334 adding to 3D games for Wii, 368 using Flash Builder, 505–510 programming introState panel, 370–372 animation loops programming winState panel, 372–373 building Wii navigation system, 413 background music, game flow, 387 following game flow, 389 ball collision method, 546 animation recorder, in PV3D, 512–515 base state, 370 anti-aliasing, mipmapping reducing, 242 BasicView anticipation, as animation principle, 28 creating starter code for augmented reality, ArrayCollection 527–531 grabbing data from Twitter site, 563 encapsulation and, 69 hooking up Flickr application, 566–567 example of, 69–70 arrays, in depth sorting, 20 extending application using, 67 Artificial Intelligence (AI), adding to Pong game, JiglibFlash starter code using, 348–349 408 undulating sphere using, 69–72 AS3 beginBitmapFill method, 134 creating XML in, 264–265 beginFill method, 117–118 depth sorting in, 20 beginGradientFill method, 152–154, 595 manipulating objects in Collada files, 188 beginShaderFill method, 217 AS3 Exporter, 202–204 Big and Small TV show, 460–461 AS3Dmod, 360–362 billboarding

698 camera Index adding physics to DisplayObject3D, 328 using drawTriangles to build shaded TIE Massive3D proximity based, 551 fighter, 594–599 overview of, 242–244 blimp (spherical) ellipsoids, 95–96 saving CPU usage using, 460 bloom brightness, Pixel Bender, 214 binary contrast, with Pixel Bender, 212 blow-up point (singularity), scaling equation, 7–8 binary data, XML vs., 201 bluetooth USB connector, Wii, 393 Bind to Data, news feed connection, 578 blur filter Bitmap property, MaterialObject3D enhancing bump map, 163 class, 129 Pixel Bender, 212 BitmapAssetMaterial, 136–141 bonds, Molfile molecule viewer, 615–617 BitmapColorMaterial, 141–142 Booleans BitmapData class changing motion in animation loops, 339 creating Google map wormhole, 607 making Wii work with PV3D, 398 creating video on primitive with, 143, 145 bounce property, particle systems, 232–233 defined, 134 bounding boxes Flash Sandbox security issue, 255–259 creating Pong game, 406 flat shading with, 152–154 customizing, 375–376 implementing light map in Flash CS4, 595 limiting particle lifetime with, 233 integrating particles and video, 240 brightness, 157–160 saving bitmap data, 307–310 adding, 160–162 BitmapFileMaterial class adding to color, Pixel Bender, 211 bringing Pixel Bender into Flash, 216 bloom, Pixel Bender, 214 building 3D Wall Carousel Course, point light, 147–148 341–342 browser double-click problems, 337 building nav system for 3D website, 474 buildPlane method, cube prims, 85–87 overview of, 136–137 bullets putting it all together, 137–141 animating muzzle, 378–379 BitmapMaterial class, 134–136 creating pool balls with imprint from, 380–381 BitmapPixelMaterial class, 216 creating tracer, 379 bitmaps, 134–142 dropping imprint to optimize for Web, 391 adding skybox with motion, 225–226 bump maps, 162–168 BitmapAssetsMaterial, 136 creating, 162–163 BitmapFileMaterial. See overview of, 162 BitmapFileMaterial class putting inside panorama, 163–168 BitmapMaterial, 134–136 using Fog and composite materials, 168–169 BitmapViewportMaterial, 137–141 Button_Click method, Flickr, 567 color and wireframe, 141–142 buttons Pixel Bender instant, 207 3D site layout in Photoshop, 500–501 putting it all together, 137–141 navigation panel in Flash CS4, 494–495 using, 134 navigation system for 3D website, 474–476 BitmapViewportMaterial, 137–141 terrain editor, 296–297 black hat (deceptive) techniques, Web twitter search application, 557, 563–565 optimization, 483–484 Blender creating IPO curves in, 198 C creating pyramid in CS4 using, 117–118 camera creating simple parser for, 202–203 adding physics to, 322–323 as modeling application, 177 adding video from Web cam to CS4 prim, using drawTriangles to build orbiting ship, 146–147 599–606 creating CS4 super prim, 126

699 camera (continued)

camera (continued) formulating. See PV3D (Papervision3D), creating perspectives, 323–324 formulating class in cruising stars using, 324–326 obtaining, 33–34 defined, 40–41 client side formulating Papervision3D class, 53 adding database to Google maps, 280–284 using BasicView, 67–72 creating FMS application for, 419 view frustum, 38 clipping, 39 working with, 13–14 clockTime( ) method, game scoreboard, 384 cancelState clouds, 466–468 adding in Flash Catalyst, 588 CNN news reader, building, 577–583 defined, 370 adding style, 579–583 programming winState panel, 372–373 binding data, 578 carousels configuring data, 578–579 creating for rotation, 23–25 connecting service, 577–578 using ILayoutElement, 619–620 overview of, 577 Wall Carousel Course. See Wall Carousel Cocomo, 457–458 Course, 3D code reuse, 98 cars code view, programming panel containers, building interactive vehicles, 194–198 370–373 building race track. See slot car racing game coding importing MD2 models, 181–182 animation, 9–12 Wii virtual driving, 400–401 automatically in Flash Catalyst, 502 cartoon (cell) shading Flash CS4 saving time, 610 overview of, 149 COLLADA (COLLAborative Design Activity), 187. using CellMaterial, 156–160 See also Collada parsers C/C++ libraries, Alchemy for, 251 Collada, bringing 3D objects into PV3D with, CDATA tags, XML, 264 31–32 cell (cartoon) shading Collada parsers overview of, 149 defined, 179 using CellMaterial, 156–160 difficulties importing files into PV3D, 202 CellMaterial interactive objects and environments, 194–198 creating shaders, 156–160 introduction to, 187–188 updating with brightness parameter, 161 single and multiple objects with simple images, Center for Strategic and International Studies. 188–191 See CSIS (Center for Strategic and single object with multiple images, 191–194 International Studies) website understanding, 204–206 changeList, instantiating RSO, 425–426 collages, 465–466 chemical bonds, Molfile molecule viewer, color 616–617 balance, in Pixel Bender, 211 Chinese Goodbye( ) method, binary contrast in Pixel Bender, 212 GoodByeClass, 45–47 for low polygon models, 174 Chinese Hello( ) method, HelloClass, 45 optimizing for Web, 391 class package, running PV3D in Flash as, 41–43 for terrains, 289–293 class path, organizing classes in, 58 Color material classes adding to prim, 132–134 building own, 328–332 BitmapColorMaterial vs., 141–142 building skybox, 373–375 properties, 129 creating particles, 222 color property, ParticleMaterial, 222–223 customizing. See OOP (object oriented) class, complement of image, 211 building composite materials, 168–169

700 DAE parser Index concept mapping, recording 3D animation, CSIS (Center for Strategic and International 512–518 Studies) website, rebuilding in Flash CS4, cones 492–499 creating hourglass from, 107–108 adding preloader, 499 creating pyramid from, 98 building navigation panel, 494–495 generating, 93–95 comparing PV3D and Flash CS4, 493 connections creating sliding display object, 495–498 CNN news feeds, 577–578 overview of, 492–493 to Flash Media Server, 419–420 placing textarea on 3D panel, 493–494 hooking up Flickr application in Flash Builder, CSV (comma separated values), XML exporter, 566–570 203 tweeting application, using Flash Builder, cube prim 559–565 animating in CS4, 116–118 container-sorting creating custom puckered, 101–102 creating carousel for navigation, 23–24 creating custom Second Life Tree prim from, overview of, 20–21 108–110 controller, Wii, 393 creating in CS4, 114–116 coordinates rezzing to stage, 83–86 Flash 10 system of, 4–5 transforming into pyramid, 97–98 Hello World using unprojected, 436 cubes Copy Motion, 29–31 building interactive (face level), 332–333 copyPixels, 237 creating for Jiglib, 350 cosine, for flat shading, 153–154 curved plane prims createBoundingBox method, Jiglib, 374 creating, 100–101 createFloor( ) method, Jiglib Hello World, creating puckered cube, 101–102 351, 353–354 creating video on Papervision3D prim, createGun( ) method, 388 144–145 createHelloSphere( ) method, Jiglib Hello curved surfaces, for low polygon models, 174 World, 351, 353 custom classes createmarkersXMLList, 267 defined, 43 createObjects function, 56 extending with inheritance, 44–45 creationComplete( ) method, introState placing custom methods underneath, 47–48 panel, 370 writing, 43–44 CRUD acronym, databases Custom Components, generating game states, creating custom client-side application, 367 280–284 custom primitives. See primitives (prims), defined, 275 customizing saving data using SQLite, 312 cutTriangleMesh method, slicing sphere, CS4. See Flash CS4 89–91 CSIS (Center for Strategic and International cylinders Studies) website, 462–483 rezzing to stage, 91–93 building nav system, 474–476 transforming into cones, 93–95 complete commented code, 476–483 making clouds, 466–468 making collage, 465–466 D making it 3D, 463–464 DAE parser making trees, 468–470 defined, 179 overview of, 462–463 loading images onto object from, 199 placing panels with XML, 470–473 PV3D containing, 168 using reflection, 464 steps for using, 199

701 Daily Papervision

Daily Papervision, 462–463 building shaded TIE fighter, 594–599 dash, segmented line for track, 439–441 drawing in 3D using CS4, 114 data provider, tweeting, 562–565 rendering models in 3D, 594 databases, 275–284 taking to next level, 606 installing test server, 276–277 drones, as military VR medium, 522–523 integrating Flex with PHP. See PHP, automatically drop shadows, and Web optimization, 391 generating map maker database, 277–278 saving data using SQLite, 312 E understanding, 275 E4X (ECMAScript) using Flex, 276 creating map makers, 283 .db extension, 311–312 incorporating XML into PV3D, 259 Declaration of Independence port, 361–362 using, 264–265 Delisle, Seb Lee, 19 Eclipse, coding animation in, 9 deployment, AIR, 312 ECMAScript. See E4X (ECMAScript) depth sorting (z-sorting), 20–21, 23–24 edges, 35–36 descendant operator (.), E4X, 265 ellipsoids, 95–96 development environment, FMS, 419 Embed tag, 141, 435–436 dictionaries, for particle trash, 241–242 emitter property, particle systems, 232 digital certificates, AIR, 312 emotional (intuitive) programming, 246 Disney, Walt, 28–29 emotional stimulation, 3D, 460 display containers, 20–21, 23–24 encapsulation DisplayObject3D class overview of, 69–70 adding physics to, 326–328 slicing image using, 238–239 building 3D Wall Carousel Course, 341–344 environment creating simple pendulum, 329–330 building in 3D, 460 finding motion functions, 60 converting site from 2D to 3D, 463–464 move commands, 197 Environment map (reflective) shading, 149, overview of, 41 155–156 star cruising using cameras, 325–326 EnvMapMaterial updating and adding brightness, 160–161 creating shaders, 155–156 document class, 57–58 PhongMaterial extending, 156 documents, XML updating with brightness parameter, 161 importing into Flex and Flash, 267–268 EnvMapShader, 162–163 well-formed, 263–264 evaluatePixel function, Pixel Bender, DOM, storing recorded animations in, 515–516 209–210 donut radius, parametric equations, 16 EventDispatcher class, 3D games for Wii, dot syntax, E4X, 262–265 368 double pendulums, 330–331 events double-sided plane prims, customizing, 110 overview of, 50 double-sided property, MaterialObject3D virtual mouse, 404 class, 129 exaggeration, as animation principle, 28 dragging objects, 436–437 exclude faces parameter, cube prims, 86 drawBitmap( ) method, pool balls with bullet exploding imprint, 380–381 objects in Flash CS4, 233–237 drawing code, terrain editor in AIR, 294–295 in particle systems, 236 drawParticle method, 223 pool balls, 382–383 drawRect method, creating carousel, 23 exporting drawTriangles class 3D model from Photoshop, 511 building orbiting ship, 599–606 Pixel Bender, 215

702 Flash Builder Index exposure, adding in Pixel Bender, 211 creating Google Map wormhole, 606–609 extra property, 3D site navigation, 476 creating Molfile molecule viewer, 610–617 extrusions, creating with splines, 36 mapping single image onto octahedron, 617–620 overview of, 591 F taking drawTriangles to next level, 606 face indices, 77, 79 using drawTriangles to build orbiting ship, face level mode, ISM, 332–337 599–606 face3d, sculpting prim, 312–315 using drawTriangles to build shaded TIE faces fighter, 594–599 creating low polygon models, 174 Flash 3D as element in object, 36 bringing Pixel Bender into, 215 factors of 2 principle, 242 building 3D site, 460–461 far clip plane, view frustum, 38 importing XML into, 266 File class, 307–310 problems in PV3D, 344–345 file formats, importing into Photoshop, 511 programming in Flex vs., 42–43 FileReference class, 310–311 sandbox security issue, 255–259 FileStream class, 307–310 using Alchemy, 251 FillAlpha property, Color material, 129 using Flex Embed tag in, 435–436 FillColor property, Color material, 129 using Google maps, 254 filters validating XML structure before using in, 264 bringing Pixel Bender into Flash, 216 Flash 3D, understanding optimizing for Web, 391 building Flash 10 engine in 13 lines with CS4, Pixel Bender, 208–210 14 final base code, formulating PV3D classes, building Flash 9 engine. See Flash 9, building 54–56 3D engine in 18 lines of code FindAll action token, 283 coordinates in Flash 10, 4–5 1st person perspective, camera, 323–324 overview of, 3–4 FLARToolKit, 523–525 scaling, 28–32 FLARToolKit, playing game in augmented reality using rotation. See rotation, 3D adding Jiglib physics to FLAR, 533–541 using translation. See translation, 3D adding PONG to FLAR, 542–548 Flash 9, building 3D engine in 18 lines of code creating BasicView starter code, 527–531 adding camera, 13–14 creating multiple markers, 541–542 applying Thales Theorem, 5–6 generating own markers, 531–533 building Flash 10 engine in 13 lines with CS4, making FLAR run faster, 549 14 putting 3D model into FLAR, 549–550 coding animation, 9–11 Flash & Math, 591–593 deriving scaling equation, 6–8 Flash 10, 585–621 overview of, 5 building 3D engine in 13 lines of code, 14 rendering to screen, 8–9 optimizing PV3D engine for, 590–591 running code, 11–12 overview of, 585 setting vanishing point (asymptotic zero), 12–13 rebuilding 3D pool game in Flash Builder, Flash Builder 586–591 adding game code, 589–590 rotation in, 22–23 animating in 3D, 505–510 saving to local PC using, 310–311 building connectivity for tweeting, 559–565 using Flex or Flash components in, 345 features, 503–504 using ILayoutElement class, 620–621 importing file from Flash Catalyst into, 559 Flash 10, rendering 3D models, 591–620 making 3D movies, 503–510 creating 3D image ball, 591–593 new layout functionality of, 620–621

703 Flash Builder (continued)

Flash Builder (continued) building PV3D Google maps in. See Google rebuilding 3D pool game in, 586–591 maps sending files from Flash Catalyst to, 503 coding animation in, 9 using Spark, 504–505 Embed tag, using in Flash, 435–436 Flash Catalyst importing XML into, 266 adding 3D to Flickr menu, 566 problems using in PV3D, 344–345 adding tweeting interactivity, 554–559 programming in Flash vs., 42–43 creating code automatically, 501–502 surfing to classes in, 246 creating weather widget interface, 576–577 using with databases, 276 hooking up components in Flickr application, validating XML structure before using in, 264 567–568 Flex 4 (Gumbo) importing file into Flash Builder, 503, 559 creating molecule viewer in, 610–617 laying it out in Photoshop, 500–501 putting Google map on Web with, 259–262 laying out game framework in, 586–587 Flex Builder, running PV3D in, 57–60 as next generation 3D software model, 585 Flex data wizard overview of, 500 automatically generating PHP with, 278–279 Flash Communication Server, 417 creating custom client side application, Flash CS4 280–284 building Flash 10 engine in 13 lines of code, 14 PHP side, 280 building planetarium in, 273–275 Flickr menu, 565–570 creating primitives in. See primitives (prims), adding 3D in Flash Catalyst, 566 creating in CS4 adding 3D in Photoshop, 565–566 creating stunning 3D websites, 463 hooking it up in Flash Builder, 566–570 depth sorting in PV3D vs., 20–21 overview of, 565 early pioneer of, 120 Flint particles flat shading in, 594–597 making it snow for Christmas, 245–246 improving PV3D with, 72–73 overview of, 249–251 making 3D movies in, 492. See also CSIS floor function, Pixel Bender, 212 (Center for Strategic and International FMS (Flash Media Server), 417–458 Studies) website, rebuilding in Flash CS4 alternatives to, 456–458 making things explode in, 233–237 the big mistake, 421–422 native z component, 492 building race track, 437–456 putting Google map on, 259–262 checking program performance, 422–423 setting vanishing point, 14 configuring development environment, 419 time and code savings of, 610 connecting to, 419–420 video on prim in, 145–147 creating 3D multiplayer worlds using, 550 Flash Kit, 445 dragging objects, 436–437 Flash library, 113, 136 free version of Red 5, 456–457 Flash Media Server. See FMS (Flash Media getting started, 417–419 Server) overview of, 417 Flash XML graphics (FXG), 587 running RSO Hello World, 423–436 flat shading FMS Administration Console, 422–423 in Flash CS4, 594–597 focal length overview of, 149 application of Thales Theorem, 5–6 using FlatShadeMaterial, 152–154 defined, 3 FlatShadeMaterial, 152–154, 161 deriving scaling equation, 7–8 Flex focus, camera property, 53 adding motion with, 60–65 Fog Filter, 168–169 automatically generating PHP, 278–280 folders, materials, 128 building large-scale applications in, 345–346 follow-through, as animation principle, 28

704 HTTPService Index Foucault pendulum, 331–332 gradients Fps, StatsView, 176 adding color to terrain, 289–290 Full Story link button, Flickr, 581 drawing code for terrain editor, 294–295 functions, 43, 47–48 making trees for 3D site, 468–470 FXG (Flash XML graphics), 587 graphics, optimizing with mipmapping, 391 FXP file, 503 gravity adding physics to DisplayObject3D, 326–328 G particle systems, 232 game flow, 387–390 grayscale game play, 389 adding key listeners to terrain editor, game score, 385 299–306 game sight, 386 converting image from color to, 211 game time, for game scoreboard, 383–384 heightmaps using, 287–288 Geocoding, 253–262 Grden, John, 312–315 Flash Sandbox security issue, 255–259 Gumbo. See Flex 4 (Gumbo) overview of, 253–254 putting Google map on PV3D prim, 254–255 putting Google map on Web with Gumbo, H 259–262 hacking, in open source community, 102–103 geodesic sphere prims, 99–101 heightmap geometry.vertices property adding color to terrain, 289–293 defined, 285 overview of, 287–288 displacing vertices, 288–289 saving bitmap data, 307–310 placing particles at vertices, 286 turning Webcam into PV3D, 311–312 racking pool balls using, 381–382 understanding, 287–288 getHeight method, displacing vertices, Hello World 288–289 Chinese Goodbye( ) method, 45–47 getPin( ) function, double pendulums, Chinese Hello( ) method, 45 330–331 extending class with inheritance, 44–45 getPixel method, heightmaps, 288 JiglibFlash, 351–356 getRemote method, instantiating RSO, 424 programming in Flash vs. Flex, 42–43 getTimer( ) method, StatsView, 176 running Papervision3D, 41–42 getTimer method, scoreboard, 383–384 writing custom class, 43–44 getXml( ) method, 266 Hello World, running RSO, 423–436 GoodByeClass, 45–46 instantiating RSO, 424–435 Google, Flash API. See Google maps overview of, 423–424 Google 3D Warehouse, 511–512 unprojecting coordinates, 436 Google Earth, using KMZ, 180, 187 using Flex Embed tag in Flash, 435–436 Google Key, 256 HelloPlane. See BasicView Google maps hidden surface drawing, 37, 174 adding database to. See databases highway, creating virtual, 441–442 creating wormhole in Flash 10, 606–609 hitBall method, game play, 390 Flash Sandbox security issue, 255–259 hourglass prim, 107–108 Geocoding and, 253–254 HTML, Web crawlers using, 484 putting on Web with Flex 4 Gumbo, 259–262 HTTP, running FMS, 417 putting PV3D prim on, 254–255 HTTP GET syntax, checking local weather, 571 Google SketchUp. See SketchUp HTTPService Gouraud shading, 149, 154–155 building connectivity for tweeting in Flash GouraudMaterial, 154–155, 161 Builder, 562

705 HTTPService (continued)

HTTPService (continued) initPV3D function creating connection for CNN news feed, final base code, 56 577–578 following game flow, 387–388 creating custom client-side application, formulating PV3D class, 51 280–284 input command, Pixel Bender, 209 grabbing data from twitter site, 564 inside faces parameter, cube prim, 86 hooking up Flickr application, 566 installer files, AIR, 312 human-readability, of Collada format, 187 instantiating RSO, FMS Hello World, 430–435 assigning Sync event, 425 creating application, 426–435 I using changeList, 425–426 if statement using getRemote, 424 converting animation into ActionScript, 31 using setProperty, 424–425 creating cube prim, 85–87 integrating environments ILayoutElement class, 620–621 Flash Media Server. See FMS (Flash Media Illustrator, producing FXG graphics in, 587 Server) image ball, 3D Wii-PV3D game design, 404 creating for rotation, 25–28 interactive objects, Collada, 194–198 creating in Flash 10, 591–593 Interactive property, MaterialObject3D class, images 129 adding to prim in CS4, 120–121 Interactive Scene Manager. See ISM (Interactive building interactive cube (face level), 333–337 Scene Manager) creating heightmaps, 288 InteractiveScene3D object, 332 creating low polygon models by optimizing, 174 InteractiveScene3DEvent listener, 312–315 modifying source pixels with point processes, interactivity 210–215 building nav system for 3D site, 474–476 processing in Pixel Bender, 210–215 with objects in PV3D. See ISM (Interactive writing Pixel Bender filter, 209–210 Scene Manager) import statements slicing image using, 239–240 building wire frame sphere, 51 interface, weather widget, 576–577 obtaining, 50 internal access modifier, 48 for properties, 50 InterPOlation (IPO) curves, Blender animation, putting materials together in application, 137 198–199 importers, PV3D. See parsers introState importing files adding in Flash Catalyst, 588 from Flash Catalyst into Flash Builder, 559 defined, 370 XML into Flex and Flash, 266 programming panel containers, 370–372 indices, creating cube based on, 114–116 intuitive (emotional) programming, 246 Influxis IPO (InterPOlation) curves, Blender animation, getting Massive3D using, 550–551 198–199 getting started with Flash Media Server, IR data, paddle motion, 414–415 417–418 ISM (Interactive Scene Manager), 332–341 preventing insecurity of swf files, 421–422 browser double-click problems, 337 inheritance, 44–47 building 3D Wall Carousel Course, 341–344 init( ) method, 387 building interactive cube (face level), 332–337 init2D( ) method, 388 building interactive particles (object level), 338 init3D( ) method, 388 clicking on particles, 339–340 initialization, coding animation using, 10 overview of, 332 initP1 panel, introState panel, 370–371 rolling over x, y, z gizmo axes, 340–341 initP2 panel, winState panel, 372–373 rotating small sphere, 338–339

706 map, creating site Index FlatShadeMaterial, 153–154 J implementing flat shading in Flash CS4, JiglibFlash 595–596 adding objects, 349–351 PhongMaterial, 156 adding physics to FLAR, 533–541 light source, in Pixel Bender, 217–218 adding simple preloader, 360 light vectors, 148 BasicView starter code, 348–349 lightColor variable, flat shading, 595 building example viewer, 356–359 lighting creating Pong. See Pong game, with Jiglib adding brightness, 160–162 Hello World, 351–356 creating multiple light source with Pixel Bender, overview of, 348 217 pool shooting game, 366–367 creating shade materials. See shaders WOW vs., 346 understanding, 147–148 JiglibLoaderApp modules folder, 356–359 LineAlpha property, Wireframe material, 129 Jing, using, 565 LineColor property, Wireframe material, 129 JPEG encoder, saving bitmap data, 307–309 lines avoiding for low polygon models, 174 creating racing track, 439 K creating simple pendulums, 329–330 making bullet tracer, 379 key listeners removing blur for Web optimization, 391 building Wii navigation system, 411–413 lineStyle method, animating cube in CS4, creating terrain editor, 299–306 117–118 sculpting prim using face3d, 312–315 LineThickness property, Wireframe material, 129 keyboard listener, setting up for game, 389 linking, creating steering mechanism, KMZ parser 195–196 defined, 180 List box, twitter search application, 556–559 getting Collada files out of, 201 listeners overview of, 187 coding animation, 10–11 creating terrain editor with key, 299–306 L sculpting prim using face3d key, 312–315 slicing images, 239–240 LAMP test server, installing, 276–277 LiveSupport, 418 latitude, Google map wormhole, 606–609 local file system, AIR using, 307 layout longitude, Google map wormhole, 606–609 adding 3D to Flickr in Photoshop, 565–566 looping adding tweeting interactivity in Flash Catalyst, coding animation, 11 555–556 formulating PV3D class, 51 Flash Builder’s new functionality, 620–621 rendering PV3D class, 54 game framework in Flash Catalyst, 586–587 loseState for twitter search application in Photoshop, 554 adding in Flash Catalyst, 588 LEDJAM website, 463–464 defined, 370 Lee-Delisle, Seb, 244–246, 405, 461 programming winState panel, 372–373 Leskinen, Petri, 120 Lynda.com tutorial video service, for Letter3D material, 246–247 Photoshop, 461 libraries, Collada document, 195, 204–206 lifetime, limiting particle, 233 light maps M CellMaterial, 156–160 MAMP test server, installing, 276–277 creating, 152–153 map, creating site, 461–462

707 map maker database

map maker database, 277–278, 283 animation in, 186–187 mapping building ray gun, 377–378 single image onto octahedron, 617–620 creating files with MilkShape, 185–186 using drawTriangles to build orbiting ship, defined, 180 599–606 handling multiple models, 184–185 markers model pack, 181–184 bringing media into, 267–268 putting into PV3D, 180–181 feeding XML data into popup, 267 Media Interactive Server, 417 generating multi-FLAR, 541–542 Media Streaming Server, 417 generating own, 531–533 memory usage mass concept, physics, 321–322, 326–328 gauging with StatsView, 176 Massive3D, 418, 550–551 handling particle trash, 240–242 material editor, 178, 179 methods MaterialObject3D class, 129 creating, 47–48 materials, 127–169 defining scope for, 48–49 adding brightness, 160–162 drawing in 3D using CS4, 114 adding skybox with motion, 225–226 military virtual reality, 521–523 adding to prim, 130–134 MilkShape, 185–186, 377 applying textures, 127–128 mind-mapping, 512 bitmaps. See bitmaps mipmapping bump maps, 162–168 billboarding surfaces to look 3D, 242 creating custom Second Life Tree prim, creating low polygon models, 174 109–110 exporting Photoshop collage for 3D website for cube vs. other prims, 83 using, 465 defined, 40–41 optimizing for Web with, 391 Fog and composite, 168–169 overview of, 244 movie clips, 142–147 modal control, 3D games for Wii, 367–368 overview of, 127 model packs, MD2, 181–184 types of, 130 modeling program, PV3D, 285–293 understanding light, 147–148 modeling terrains, 287–293 using shaders. See shader materials overview of, 285 using simple, 128 placing particles at vertices, 286–287 MaterialsList class sculpting prim using face3d, 312–315 adding multiple materials with, 191 models, 3D, 173–220 adding skybox with motion, 225–226 bringing down count, 174–175 building interactive cube (face level), 333–337 bringing into FLAR, 549–550 creating cube prim, 85–87 choosing application, 176–179 loading images onto object from, 199–200 gauging performance in PV3D, 175–176 Math.random( )*Oxffffff method, 17 importers for PV3D. See parsers Matrix3D object overview of, 173–174 animating cube in CS4, 116–117 processing images. See Pixel Bender applying transformations all at once, 15, writing own parsers, 201–206 125–126 modolo function, 439–442 container-sorting, 21 modules creating CS4 super prim, 120–125 adding simple preloader, 360 defined, 4 building viewer in Jiglib, 356–359 drawing using CS4, 114 molecule viewer. See Molfile molecule viewer Max3DS parser, 180 Molfile molecule viewer, 610–617 Maya application, 178 creating Atom class, 612–615 MD2 parser, 180–187 creating chemical bonds, 616–617

708 objects Index creating molecules and bonds, 615 running PV3D in Flex as, 41–43 Molfile parser, 610 myAngle+i/20, 17–18 overview of, 610 myDisplay object name, carousels, 23 placing atoms, 615–616 mySlideLayer movie clip, 495–498 motion, adding, 60–65 MySQL database motion editor creating map maker, 277–278 converting animation into ActionScript, 29–31 defined. See also databases defined, 29 generating PHP for. See PHP, automatically video tutorial for, 29 generating motion paths, Spark, 516–518 installing test server, 276–277 mouse myTerrain vertices, 288–289 adding paddle motion, 414–415 adding virtual, 402–403 in screen coordinates, 436 N mouse listener, games, 389 Name property, MaterialObject3D class, 129 move commands, DisplayObject3D, 197 naming conventions moveForward command, skybox motion, 227 custom classes, 43–44 movie clips, 142–147 model in 3DSMax, 195 building interactive cube (face level), 333 new prims, 96 connecting to Particle3D class, 229–230 Pinched Sphere, 97 exploding particle system using, 234–235 NaN data, Wiimote, 414–415 Google map wormhole using, 607–608 National Treasure movie, 361–362 importing into PV3D, 142 navigation optimizing for Web, 391 3D site, 474–476 using MovieAssetsMaterial, 142 converting site from 2D to 3D, 474–476 using MovieMaterial class, 142 in Flash CS4 for 3D movies, 494–495 using VideoStreamMaterial, 143 laying out 3D site in Photoshop, 500–501 video on CS4 prim, 145–147 rotating small sphere with ISM, 338–339 video on PV3D prim, 144–145 star cruising using cameras, 324–326 MovieAssetMaterial, 142, 333 using view states, 368–369 MovieMaterial class Wii, 411–413 building interactive cube (face level), 333 near clip plane, view frustum, 38 creating pool balls with bullet imprint, 380–381 net connection Flash Sandbox security and, 255–259 connecting to FMS, 419–420 putting Google map on PV3D prim, 254–255 video on CS4 prim, 145 using, 142 NetStream class, 144–146 movies, making 3D, 491–519 news feed. See CNN news reader, building building PV3D studio, 518–519 Newtonian Mechanics, 322 recording 3D animations, 512–518 nochump folder, 34 technological changes and, 491–492 nodes, XML, 264 using Flash Builder, 503–510 NOMAD, 268 using Flash Catalyst, 500–503 nonconvex rigid body physics, 348. See also using Flash CS4, 492. See also CSIS (Center for JiglibFlash Strategic and International Studies) website, normal vector, 83 rebuilding in Flash CS4 null command, particle trash, 241 using Photoshop, 511–512 muzzle, animating, 378–379 MXML application O FXG as subset of, 587 object level mode, ISM, 332, 338–341 programming in Flash vs. Flex, 43 objects

709 objects (continued)

objects (continued) creating sliding movie clip, 495–498 adding in JiglibFlash, 349–351 creating weather widget interface, 576–577 defined, 35, 40 laying out in Photoshop, 500–501 dragging, 436–437 placing textarea on 3D panel in Flash CS4, interactivity of. See ISM (Interactive Scene 493–494 Manager) positioning on stage with XML, 470–472 optimizing for Web, 392 toggling, 472–473 overview of, 36 pano objects in world coordinates, 436 adding skybox with motion, 225–226 octahedron, mapping single image onto, creating, 192–193 617–620 panoramas OnFrame( ) method, StatsView, 176 bringing into PV3D, 193–194 onRenderTick method, 70–72, 547–548 creating, 163 OOP (object oriented) class, building, 43–50 creating cubic, 191–192 adding custom properties, 49–50 creating pano object, 192–193 creating methods, 47–48 putting bump map inside, 163–168 defining scope, 48–49 Paperplane primitive, 76 demonstrating inheritance, 45–47 paraboloid, puckered cubes, 101–102 extending with inheritance, 44–45 parameter command, Pixel Bender filter, 210 overview of, 43–44 parameterized splines, racing tracks, 438–441 open source community, benefits of hacking in, parameters 102–103 adding to Pixel Bender, 210 Open Source Flash, 456–457 Perlin noise, 298 optimizing for Web parametric equations avoiding black hat techniques, 483–484 creating CS4 super prim, 120–125 building 3D games, 390–392 image ball generated from, 26–28 orbiting ship, building, 599–606 mapping single image onto octahedron, org folder, 34 619–620 oscbright, 161–162 overview of, 15–16 oscillators, and Foucault pendulum, 331–332 for torus, 17–19, 103–105 outCoord property, Pixel Bender, 209 for tubes, 105–107 output command, Pixel Bender, 209 parametric particle system (Torus worm), 17–20 overlays parsers, 179–201 creating pool balls with bullet imprint, 380–381 3DSMax, 204 or Flex or Flash components, 345 Blender, 202–203 override statement Collada. See Collada parsers ChineseGoodbye( ) method, 46–47 KMZ, 187 using BasicView, 67–69 MD2. See MD2 parser Molfile, 610–612 overview of, 201–202 P SketchUp, 200–201 Package Explorer panel, Flash Builder, 503 types of, 179–180 paddle methods, 546 using binary data vs. XML, 201 Painter’s Algorithm, 37 particle systems, 221–252 paletteMap building interactive particles (object level), adding color to terrain, 289–293 338–341 and CellMaterial, 156–160 building modeling program, 286–287 panels, 3D site defined, 221 building nav system, 474–476 Flint particles, 249–251 collapsing in Flash Catalyst, 557–559 key concepts, 232–233

710 physics Index Plug-in media, 244–248 perspective concept, physics, 321–324 properties of, 221–222 perspective scaling in PV3D, 222–227 building 3D engine. See Flash 9, building 3D treating multiple models like, 184–185 engine in 18 lines of code using Alchemy for more bang, 251 creating illusion of depth, 6 particle systems, creating generic CS4 particle derived from Thales Theorem, 3 class, 227–244 rendering to screen, 8–9 ActionScript main code for, 230–231 Phong shading building Particle3D class, 228–229 creating bump map, 162–163 connecting movie clip to, 229–230 defined, 149 exploding objects, 233–237 overview of, 156–160 handling trash, 240–242 PhongMaterial, 156, 161 integrating video, 240 Photoshop key concepts, 232–233 adding 3D to Flickr menu, 565–566 lifetime or bounding box, 233 building Jiglib bounding box, 374 making 2D surfaces look 3D (billboarding), creating layout for tweeting, 554 242–244 CS4 engine, 511–512 mipmapping, 244 laying out 3D site, 500–501 overview of, 227 learning for 3Dsites, 461 slicing image, 237–240 making collage in, 465–466 Particle3D class producing FXG graphics, 587 billboarding surfaces to look 3D, 242–244 PHP building, 228–229 creating map maker database, 277–278 connecting movie clip to, 229–230 defined, 275 creating, 228–229 installing test server, 276–277 handling particle trash, 240–242 PHP, automatically generating, 278–284 key particle concepts, 232–233 creating custom client side application, making things explode (Flash CS4), 233–237 280–284 ParticleBlowUp class, 233–237 defined, 276 particleClick snippet, 340 overview of, 278 ParticleField class, 222, 269–273 PHP side, 280 particleLife variable, 233 using Flex data wizard, 278–280 ParticleMaterial class phpMyAdmin, map maker database, 277–278 building planetarium in PV3D, 269–273 physics, 3D, 321–364 creating particles in PV3D, 222 3rd party engines, 346–348. See also putting stars in skybox, 223–227 JiglibFlash Particles class, 222–223 adding Jiglib to FLAR, 533–541 paths, creating with splines, 36 adding to camera, 322–323 pendulums, building physics classes, 328–332 adding to DisplayObject3D class, 326–328 performance AS3Dmod, 360–362 Flash Media Server, 422–423 building own classes, 328–329 gauging PV3D, 175–176 creating 3D wall course, 341–346 making FLAR run faster, 549 creating camera perspectives, 323–324 optimizing PV3D for Flash 10, 590–591 cruising stars using cameras, 324–326 Perlin noise double pendulum, 330–331 creating terrain editor, 298–299 Foucault pendulum, 331–332 defined, 298 interacting with objects in PV3D. See ISM using drawTriangles to build shaded TIE (Interactive Scene Manager) fighter, 594 overview of, 321 perlin plane, 67–68 simple pendulum, 329–330

711 physics, 3D (continued)

physics, 3D (continued) Pixel Bender simple, 213 torsion pendulum, 331 purpose of, 151 understanding guts of, 321–322 point processes, Pixel Bender, 210–215 Pinched Sphere, 96–97 point sprites, saving CPU usage, 460 pinning, creating double pendulums, 330–331 polar octahedron derivation, 617–618 pipe command, cubes, 86–87 polygons Pixel Bender, 206–218 bringing down count when modeling, 173–174 adding parameters, 210 defined, 35 adding to PV3D, 215–217 making 2D surfaces look 3D, 242–244 animating light source, 218 modeling for PV3D, 173 bringing into Flash, 215 reducing by removing hidden surfaces, 37 creating multiple light source, 217–218 rendering using Painter’s Algorithm, 37–38 getting started, 207–208 polymorphism Pixelero blog resource for, 606 creating particles in PV3D, 222 problem optimizing PV3D for Flash 10, 591 slicing image using, 238–239 rendering large particle sets, 251 PONG, adding to FLAR, 542–548 typical point processes, 210–215 adding ball collision method, 546 understanding, 206–207 adding paddle methods, 546 writing filter, 208–210 adding Pong properties, 542–543 Pixelero blog, 606 adding PONG to FLAR, 543–545 pixels, modifying source, 210–215 adding textboxes to keep score, 545–546 plane prim inserting code into onRenderTick, 547–548 constructing, 78–79 renaming file, 542 CS4 native, 114 transferring Embed assets, 542 curved, 100–101 transferring Lively folder, 542 custom double-sided, 110 Pong game, with Jiglib, 404–425 rezzing to stage, 81–83 adding AI, 408 Plane3D method, slicing sphere, 89–91 adding Jiglib, 406–407 planes adding mouse-controlled paddle motion, 3D Wall Carousel Course, 341–344 414–415 Jiglib Hello World, 351–356 adding reflectivity and skybox, 411 JiglibFlash, 350 building bounding box, 406 making clouds, 467–468 building navigation system, 411–413 making trees, 468–470 creating textboxes, 410–411 planetarium making game levels and scoring system, building in Flash CS4, 273–275 408–410 building in PV3D, 268–273 moving at constant velocity, 408 playState, Flash Catalyst, 588 overview of, 404–405 playState panel, 370–371 pool shooting game Plug-in media adding background image, 368 Big and Small TV show, 460–461 adding Wii to, 402–404 making snow for Christmas, 245–247 building custom skybox, 373–375 overview of, 244–248 building Jiglib bounding box, 375–376 scanning, 247–248 creating animated muzzle, 378–379 PNG encoder, 307–309 creating pool balls with bullet imprint, 380–381 png files, Second Life tree prim, 109–110, creating ray gun, 377–378 111–112 creating tracer, 379 point light exploding pool balls, 382–383 constructing, 147–148 following game flow, 387–390 creating shaders. See shaders game sight, 386

712 PV3D (Papervision3D) Index game states, 367–370 overview of, 96 optimizing for web, 390–392 Pinched Sphere, 96–97 overview, 366–367 puckered cube, 101–102 programming panel containers, 370–373 pyramid, 97–99 racking pool balls, 381–382 Second Life Tree, 108–110 scoring balls, 383–386 torus, 102–105 pool shooting game, rebuilding in Flash Builder, tube, 105–107 586–591 primitives (prims), rezzing to stage, 79–96 adding code, 589–590 cone, 93–95 adding game states in Flash Catalyst, 588–589 cube, 83–87 laying out framework, 586–587 cylinder, 91–93 overview of, 586 making ellipsoids, 95–96 Popup Manager, generating game states, 367 overview of, 79 popups, feeding XML data into, 267–268 plane, 81–83 portabilty, Second Life Tree prim in CS4, 112 prim template code ( wireframe material), porting, 102 80–81 position concept, physics, 321–322 slicing sphere, 89–91 preloaders sphere, 87–89 converting site from 2D to 3D, 476 wireframe sphere. See spheres, wireframe creating 3D movies in Flash CS4, 499 primitives folder, 76 implementing BitmapAssetsMaterial in, private access modifier, 48–49 136–137 processing images. See Pixel Bender simple JiglibFlash, 360 projection plane (viewport), Thales Theorem, 5–6 Presenter Chat, 418 projectVectors method, drawing in 3D using primitives (prims) CS4, 114 adding materials to, 130–134 properties defined, 56 adding PONG to FLAR, 542–543 exploring guts of, 76–79 creating Particle3D class, 228 putting Google map on PV3D, 254–255 custom, 49–50 sculpting using face3d, 312–315 defined, 43 understanding, 75–76 defining scope, 48–49 using Matrix3D, 125–126 particle, 227 using VideoStreamMaterial to play video particle systems, 232–233 on, 143 putting stars in skybox, 223–227 video on CS4, 145–147 simple materials, 129 video on Papervision3D, 144–145 Wireframe material, 129 primitives (prims), creating in CS4, 110–125 protected access modifier, 48 animate cube, 116–118 prototypes, military virtual reality, 522 create cube, 116 proximity based billboarding, Massive3D, 551 cube, 114–116 PSD files, 554–559 overview of, 110–111 public access modifier, 48–49 plane, 114 PV3D (Papervision3D) pyramid, 118–119 adding Pixel Bender to, 215–217 Second Life Tree, 111–114 building modeling program, 285–293 super prim, 120–125 building planetarium, 268–273 primitives (prims), customizing, 96–110 building studio, 518–519 curved plane, 100–101 creating Second Life tree, 112 double-sided plane, 110 CS4 vs., 493 geodesic, 99–100 CS4 vs. version 2 of, 259 hourglass, 107–108 defined, 3

713 PV3D (Papervision3D) (continued)

PV3D (Papervision3D) (continued) ray gun, modeling, 377–378 depth sorting, 20–21 ray tracing, 151 gauging performance, 175–176 realism, with physics. See physics, 3D optimizing for Flash 10, 590–591 recording 3D animations, 512–518 particle systems, 222–227 animating Spark, 516–518 using Collada Files, 31–32 creating animation recorder in PV3D, 512–515 using Flint, 249–251 overview of, 512 PV3D (Papervision3D), formulating class in, 50–57 storing results using DOM, 515–516 building custom class. See OOP (object Red 5, 456–457 oriented) class, building reflection camera, 53 3D site, 464 creating pyramid prim, 98 collage for 3D site, 465 final base code, 54–57 Pong game, 411 overview of, 50–52 sliding movie clip, 496–498 renderer, 54 ReflectionView, 464 scene, 53–54 regenerator property, particle systems, 232 viewport, 52–53 Relativistic Mechanics (special relativity), 322 PV3D (Papervision3D), getting started, 33–73 Remote Shared Objects (RSO), 423 adding motion, 60–66 removeChild method application elements, 39–41 handling particle trash, 241 building OOP custom class, 43–50 removing models from stage, 184 building Papervision3D class, 50–57 renderers hidden surface drawing, 37 defined, 40–41 history of, 33 formulating PV3D class, 54 incorporating CS4, 72–73 using BasicView , 67–72 obtaining, 33–34 using Flint in PV3D, 249–251 Painter’s Algorithm, 37–38 rendering. See also Flash 10, rendering 3D running applications, 57–60 models running four different ways (Hello World), 41–43 adding motion, 61–62 shapes vs. objects, 35–36 defined, 36 simulating 3D objects in ActionScript, 34–35 hidden surface drawing for, 37 using BasicView (Hello Plane), 67–72 overview of, 7–8 view frustum (culling and clipping), 38–39 using Painter’s Algorithm, 37 PV3D plug-in, Jiglibflash, 348–349, 373–375 RenderStatistics class, StatsView, 176 pyramids, creating, 97–98, 118–119 restitution, Pong game, 406–407 Python, 202–203 resultHandler method, 281–284 rezzing prims. See primitives (prims), rezzing to stage Q rooms, building 3D, 460 quadrant render engine, 378 Rotate3D Spark effect, Flash Builder, 506–510 Quake II engine. See MD2 parser rotation, 3D, 21–28 quality, stage, 66 adding motion using, 60–62 Quantum Mechanics, 322 adding to skybox, 226–227 building interactive particles (object level), R 338–339 creating carousel, 23–25 race track. See slot car racing game creating image ball, 25–28 racking pool balls, 381–382 creating sliding movie clip, 496–498 radio buttons, creating, 295 Flash 10, 22–23 rawChildren tag, using MXML with, 43 with Flash Builder, 506–510

714 shader materials Index Flash CS4 tool, 492 search Matrix3D class and, 4, 125–126 adding to twitter, 557 overview of, 21 grabbing data from twitter site, 563–565 planes, 81–83 search engine result pages (SERPs), 483 spheres, 87–88 Second Life tree primitives rotationX command, 22–23 creating, 75–76 rotationY command, 22–23 creating in CS4, 111–114 rotationZ command, 22–23 customizing, 108–110 round tripping, Flash Catalyst/Flash Builder, 503 2nd person camera perspective, 323–324 RSO (Remote Shared Objects), 423 security, and Flash sandbox, 255–259 RSS Feed, checking local weather, 571 segments, spline, 35–36 RTMP, 417, 419, 421–422 self-signed certificates, deploying AIR, 312 Rubik’s cube, 285–286 sensor bar, adding Wii to game, 401–402 Runge-Kitta, 331 SEO (Search Engine Optimization) running code, 11–13 ever-changing art of, 488 getting pages higher, 483 overview of, 484 S using HTML, 484 Sandbox, and security, 255–259 using SWFObject, 485–488 saving projects SERPs (search engine result pages), 483 in 3DSMax, 178 server side, FMS development, 419 in Blender, 177 services, adding Web, 553–583 before upgrading, 34 adding 3D to Flickr menu, 565–570 scale modes, stage, 66 CNN news feed. See CNN news reader, building scaling, 28–32 overview of, 553 adding motion with, 60, 63–65 tweeting. See twitter search application defined, 28 Yahoo. See Yahoo Web services, mining Matrix3D class handling, 4, 125–126 setPixel method, terrain editor, 293–295 mipmapping reducing, 242 setProperty, instantiating RSO, 424–425 with Thales Theorem. See Thales Theorem setSpringCamera( ) method, Jiglib Hello turning timeline animation into ActionScript, World, 351, 353 29–31 setStage method, 70–72 using Collada files, 31–32 setUpCamera( ) method, game flow, 388–389 Walt Disney’s Rules of animation, 28–29 setUpListeners( ) method, game flow, 389 scanning, 247–248 Seven Revolutions project scenes building. See CSIS (Center for Strategic and defined, 39–41 International Studies) website formulating PV3D class, 53–54 overview of, 462–463 optimizing programmatically, 176 rebuilding in CS4. See CSIS (Center for Strategic putting materials together in, 138–139 and International Studies) website, rebuilding using BasicView, 67–72 in Flash CS4 scope (access modifiers), 48–49 shader materials, 148–160 scoring systems CellMaterial, 156–160 adding PONG to FLAR, 545 code template, 150–151 game scoreboard, 383–386 coding, 149 Pong game, 408–410 EnvMapMaterial, 155–156 screen (x, y) coordinates, 436–437 FlatShadeMaterial, 152–154 Script Tag application, and MXML, 43 GouraudMaterial, 154–155 scroll bar, twitter search application, 555–556 overview of, 148–149 scroll policies, Web optimization, 391, 462 PhongMaterial, 156

715 shaders

shaders turning code into game, 456 bump map, 162–163 Smooth property, MaterialObject3D class, 129 low polygon model, 174 snow, particle effect, 247–248 Pixel Bender, 208–210 snow systems, 244–246 as processor intensive, 168–169 sorting, depth, 20–21 updating, 161–162 Sothink SWF Decompiler, 421–422 using drawTriangles to build shaded TIE space bar, Blender, 177 fighter, 594–599 Spark shapes, 35 animating, 516–518 sharpen filter, Pixel Bender, 213 overview of, 504–505 shift command, particle trash, 241 using FXG graphics in, 587 short cuts, Blender, 177 special relativity (Relativistic Mechanics), 322 Show Gun checkbox, scoring balls, 384–385 specularLevel variable, flat shading, 595 showTime method, games, 384, 389 speed parameter, Wii virtual driving, 401 sight, game, 386 sphereMorphOver method, sculpting prim, silly string program, Wii, 397–399 312–315 Simple Cube class, FLARToolKit, 524 spheres simple materials (or just materials), 129 building interactive particles (object level), simple pendulums, 329–330 338–339 Simple Pixelate, Pixel Bender, 212 instantiating for Jiglib Hello World, 351–356 simple point light, Pixel Bender, 213 for JiglibFlash, 351 simulators, military VR, 522 spheres, wireframe singularity (blow-up point), scaling equation, 7–8 adding motion, 60–66 site maps, 3D sites, 461–462 adding to scene, 57 SketchUp adding to stage, 54 Google 3D Warehouse and, 511–512 building, 51–54 as modeling application, 178–179 customizing by pinching, 96–97 overview of, 200–201 formulating PV3D class, 50–51 parser, 180 geodesic, 99–100 SketchUp Pro, 201 rezzing to stage, 87–89 SketchUpCollada class, 180 running applications, 57–60 skyboxes running final base code, 54–57 adding reflectivity to, 411 slicing, 89–91 building custom, 373–375 using drawTriangles to create burning, 606 dropping for Web optimization, 391 spherical (blimp) ellipsoids, 95–96 putting stars in, 223–227 spin property, ParticleBlowUp class, 233 slicing spinning out, slot car racing game, 442–444 images into particles, 237–240 splines spheres, 89–91 defined, 35 slide layer, clouds for 3D site, 468 overview of, 35–36 sliders parameterized, 438 adding to terrain editor, 296 Spring Powered Camera creating torsion pendulums with, 331 defined, 322 sliding display object, 3D movies, 495–498 dropping for Web optimization, 391 slot car racing game, 437–456 instantiating for Jiglib Hello World, 351–356 adding FMS to, 444–456 star cruiser using, 324–326 constructing virtual highway, 441–442 sprites creating track, 438–441 creating pool balls with bullet imprint, 380 having wreck, 442–444 saving CPU usage with point, 460 overview of, 437–438 viewports extending, 51

716 Thales Theorem Index squash and stretch, animation principle, building viewer in Jiglib, 356–359 28–39, 62 insecurity of, 421–422 src folder, 34 SWFObject search engine optimizer, 484 stage Swift3D modeling application, 179 adding viewport to, 52 switch statement, Flex, 280 depth sorting objects on, 20 Sync event, instantiating RSO, 425 getting program from Flash library to, 113 synchronous file operations, AIR, 307 optimizing for web, 392 positioning panels using XML, 470–472 removing and adding models to, 184 T rezzing primitives to. See primitives (prims), tags, XML, 263–265 rezzing to stage template code setting, 65–66 adding materials to prim, 130–131 stars prim, using wireframe material, 80–81 building planetarium in Flash CS4, shaders, 150–151 273–275 terminology, for 3D in this book, 35–37 building planetarium in PV3D, 269–273 terrain editor, creating in AIR, 293–306 cruising using cameras, 324–326 adding key listeners, 299–306 getting online, 268 adding sliders, 296 starter code, Wii, 393–397 drawing code, 294–295 state engines, recording 3D animations, incorporating Adobe AIR, 306–312 514–515 overview of, 293–294 states, game using buttons, 296–297 adding in Flash Catalyst, 588–589 using radio buttons, 295 building 3D games for Wii, 367 working with Perlin noise, 298–299 filling, 370 terrains, modeling, 287–293 graphics layout in Flash Catalyst, 586–587 adding color, 289–293 programming panel containers, 370–373 displacing vertices, 288–289 using modal control, 367–368 heightmaps, 287–288 view states, 368–369 overview of, 287 Static Properties and Methods, 239–240 tessellation, and cubes, 193 StatsView, 175–176 test server, installing MySQL and PHP, 276–277 steer parameter, Wii virtual driving, 401 testing, with military virtual reality, 522 steering mechanism, 195–196 text, website, 462 String Theory, 322 text nodes, XML, 264 studio, PV3D, 518–519 TextArea component, styling CNN news feed, styling, CNN news feed, 579–583 579–580 sub-objects, 35, 36 textboxes SubsetDataconfig.as file, Flex data adding PONG to FLAR, 545 wizard, 280 Pong, 410–411 SubsetData.php file, Flex data wizard, 280 texture SubsetDataScript.as file, Flex data applying, 127–128 wizard, 280 low polygon models, 174 Super Prim, CS4 ray gun, 377 creating, 120–125 texture baking, 217 video on, 145–147 texture mapping, 134–135 SWF Encrypt, 422 Thales Theorem swf files applying, 5–6 adding animation to muzzle, 379 creating illusion of depth, 3 adding simple preloader, 360 deriving scaling equation, 6–8

717 3rd person camera perspective

3rd person camera perspective, 323–324 Triangle Culling, 121 3D. See 3D triangle-face normal vector, point lights, 148 3DSMax. See 3DSMax TriangleMesh3D class, vertices, 77 throttling frame animation, 52, 118 triangles Tiled property, MaterialObject3D class, 129 bringing down count when modeling, 173–174 time concept, physics, 321–322 deriving scaling equation from, 6–8 timeline animation drawing in Flash, 128 adding to muzzle, 378–379 drawing objects using, 127–128 converting into ActionScript, 29–31 face indices and, 77–78 timeline scripting application, PV3D in Flash as, modeling for PV3D, 173 41–43 putting textures on, 134 timer rendering models in 3D. See drawTriangles creating CS4 super prim, 121–125 class creating game scoreboard, 383–384 tube prim, 105–107 creating loop function, 52 tube radius, parametric equations, 16 timestamps, recording 3D animations, 513–515 Tunnel class, Google map wormhole, 607–609 toggling panels, 3D sites, 472–473 TVStation, 418 torsion pendulum, 331 tweeting. See twitter search application Torus prim twitter search application, 553–565 creating parametric particle system orbiting adding interactivity in Flash Catalyst, 554–559 around, 17–19 building connectivity in Flash Builder, 559–565 creating tube from, 105–107 creating layout in Photoshop, 554 parametric equations for, 15–16 overview of, 553 porting into PV3D from Away3D, 102–105 2D. See 2D Torus worm, 17–20, 103–105 tracers creating bullet, 379 U optimizing for Web, 391–392 UAVs (unmanned aerial vehicles), 522 track racing. See slot car racing game unprojecting coordinates, Hello World, 436 trackUpdate method updates creating virtual highway, 441–442 DisplayObject3D class, 160–161 having a wreck, 442–444 shaders, 161–162 training, skill set, 461 updateStage() function, parametric particle transformations, affine, 134–135 system, 17 translation, 3D, 15–21 upgrades, PV3D classes, 34 adding motion with, 60–62 URLLoader class, 266 creating parametric particle system (Torus Utils3D class, CS4, 114 worm), 17–20 UV coordinates, 78–79 creating sliding movie clip, 496–498 depth sorting, 20–21 Flash CS4 tool, 492 V Matrix3D class handling, 4, 125–126 validation, XML, 264 overview of, 15–16 vanishing point (asymptotic zero) transparency removal, Web optimization, adjusting to Center Stage, 12–13 391–392 deriving scaling equation, 7–8 transposition, in AS3, 20–21 Flash 10 automatically setting, 14 trash collection, 240–242 in Thales Theorem, 5–6 trees variable declarations, materials, 137–138 converting site from 2D to 3D, 468–470 Vector3D method, container-sorting, 21 customizing prim, 108–110 velocity

718 Web references Index adding to DisplayObject3D, 326–328 building 3D worlds using Massive3d, 550–551 creating constant, 408 creating augmented reality. See augmented particle systems and, 232 reality vertex visual_scenes library, Collada, 195 customizing tree prim, 109 VizualPV3D, 518–519 defined, 35 Vnext, animation in MD2, 186–187 as element in object, 36 vertex normal, Gouraud Material, 154–155 W Vertex3D class, 77 vertices (verts) Wall Carousel Course, 3D, 341–346 animation in MD2, 186–187 the big problem, 344–345 building modeling program, 286–287 building, 341–344 constructing plane primitive, 78–79 rapid large-scale development, 345–346 creating cube in CS4, 114–116 Walt Disney’s Rules of animation, 28–29 defined, 35 WAMP test server, installing, 276–277 drawing objects using materials, 127–128 weaponContainer gauging performance of, 176 adding animation to muzzle, 378–379 geodesic spheres and, 99–100 building ray gun, 377–378 modeling terrains by displacing, 288–289 game play, 390 as primitive element, 77 weather widget, creating reducing size for Web, 391–392 checking local weather, 571 splines composed of, 35–36 creating code, 573–576 video, integrating particles and, 240 examining weather XML, 572–573 VideoBlogger, 418 making widget interface, 576–577 VideoStreamMaterial, 143, 145 Web, optimizing for, 390–392 view frustum, 38–39 Web references view states, 368–369 Adobe Flash Collaboration Service, 458 viewable volume, view frustum, 39 Alchemy, 201 viewer, building JiglibFlash, 356–359 AS3Dmod, 360–361 viewport layer, 378 augmented reality, 523 Viewport3D, 54 Blender, 177 viewports CNN news feeds, 577 building interactive cube (face level), 334–337 coding Flash and Flex, 461 building nav system for 3D website, 474–475 creating action tokens, 282–283 creating CS4 super prim, 126 CS4 Flash, 120 creating multiple, 53 Daily Papervision, 462–463 defined, 39–40 DOM, 516 formulating PV3D class, 51–52 FLARToolKit, 523 mapping coordinates to, 39 Flash & Math, 591 size reduction for Web, 391–392 Flash Catalyst and Flash Builder basics, 553 using BasicView, 67–72 Flash Kit, 445 views, FMS Administration Console, 423 Flint particles, 249 virtual driving, Wii, 400–401 FMS alternatives, 456 virtual highway, constructing, 441–442 generating own markers, 532 virtual mouse getting star data from NOMAD, 268 adding paddle motion, 414–415 Google SketchUp, 178–179 adding to game, 403–404 hooking up Web Services to Flash Builder, 583 integrating environments, 404 Influxis account for FMS, 417 virtual tours, 521–551 IPO curves, 199 applying to military development, 521–523 JiglibFlash, 348

719 Web references (continued)

Web references (continued) optimizing for Web, 390–392 learning Photoshop, 461 overview of, 393 MD2 model packs, 181 starter code, 393–397 motion editor, 29 virtual driving, 400–401 Papervision3D classes, 33–34 wildcards, when to avoid, 115 Papervision3D import statements, 50 winState, 370, 588 parameterized splines, 438 wireframe material parametric curves, 15 adding to prim, 132 parametric equations, 26 BitmapWireframeMaterial vs., 141–142 particle creation, 19–20 prim template code using, 80–81 Pixel Bender, 207 properties, 129 Pixelero blog, 606 racking pool balls using, 381–382 prims, 76 rezzing primitives to stage using. See primitives Red 5, 457 (prims), rezzing to stage Runge-Kitta, 331 silly string using, 397–399 Search Engine Optimization, 484 spheres. See spheres, wireframe Seb Lee-Delisle’s snow systems, 244–246 WireSphere class, 87–89 setting up Google maps, 254 world (x, y, z perspective projection) coordinates, Seven Revolutions project, 462 436–437 Spring Powered Camera, 322 wormhole, Google Map, 606–609 SWFObject, 485 Wowza, 457 VizualPV3D, 518–519 wrecks, in slot car racing game, 442–444 WiiFlash, 393 WOW, 347 Web services. See services, adding Web X Web spiders, 483–488 x-axis overview of, 483–484 3D rotation of, 22 using HTML, 484 building interactive particles (object level), using SWFObject, 485–488 340–341 Webcams constructing plane prims, 78–79 adding video to CS4 prim from, 146–147 Flash 10 clockwise rotation of, 22–23 for slot car racing game, 456 PV3D coordinate system and, 4 Websites, developing 3D, 459–488 rendering to screen and, 8–9 converting 2D websites. See CSIS (Center for XML, 262–275 Strategic and International Studies) website advantages of, 202 creating site map, 461–462 binary data vs., 201 in Flash, 460–461 building planetarium in Flash CS4, 273–275 learning Photoshop, 461 building planetarium in PV3D, 268–273 reasons for, 459–460 Collada files using, 31–32 Web spiders and, 483–488 converting site from 2D to 3D, 470–473 weight, animation principle, 29 feeding data into popup, 267–268 well-formed XML documents, 263–264 importing into Flex and Flash, 266 widgets. See weather widget, creating overview of, 262 Wii, creating 3D games for, 365–416 positioning panels on stage with, 470–472 adding Wii to a game, 402–404 recording 3D animations, 514 building car for slot car racing game, 445 understanding, 263–264 creating pool shooting game. See pool shooting using E4X, 264–265 game XML Exporter drawing silly string, 397–400 3DSMax, 204 Jiglib Pong. See Pong game, with Jiglib Blender, 202–203, 599–606

720 z-sorting (depth sorting) Index Y Z Yahoo Web services, mining, 570–577 z component, Flash CS4, 492 checking local weather, 571 z-axis creating code, 573–576 3D rotation of, 22 examining weather XML, 572–573 building interactive particles (object level), making widget interface, 576–577 340–341 overview of, 570 deriving scaling equation, 6–7 yaw command Flash 10 clockwise rotation of, rolling wheels and moving car, 196 22–23 rotating plane using, 81–83 getting 3D with, 5 rotating sphere using, 87–88 rendering to screen and, 8–9 y-axis in Thales Theorem, 5–6 3D rotation of, 22 Z-buffer techniques, 38 building interactive particles (object level), zoom, camera property, 53 340–341 z-sorter property, particle systems, 232 constructing plane prim, 78–79 z-sorting (depth sorting), 20–21, Flash 10 clockwise rotation of, 22–23 23–24 PV3D coordinate system and, 4–5 rendering to screen and, 8–9

721