cocos3d Programming Guide

The cocos3d framework is a sophisticated 3D application development framework for the iOS platform. This document describes the framework components and provides guidelines and best practices for building cocos3d iOS applications.

cocos3d is a significant extension to cocos2d, a popular, well-designed framework for building iOS games and applications that play out in 2D (or 2.5D isometric projection). Although on the one hand it is possible to start with the cocos3d Application template and develop a 3D application without knowing too much about the workings of cocos2d, to get the most out of this document, you should familiarize yourself with cocos2d. You can learn more about cocos2d at the cocos2d Wiki.

CC3Layer

CC3Layer is one of two key classes that each application will subclass and customize. The other class you will subclass and customize in your application is CC3World, described below.

Although cocos3d is a full 3D modeling and rendering engine, all 3D rendering occurs within CC3Layer, a special cocos2d CCLayer subclass. Since it is a type of CCLayer, instances of CC3Layer fit seamlessly into the cocos2d CCNode hierarchy, allowing 2D nodes such as controls, labels, and health bars to be drawn under, over, or beside 3D model objects. With this design, 2D objects, 3D objects, and sound can interact with each other to create a rich, synchronized audio-visual experience. CC3Layer acts as the bridge between the 2D and 3D worlds.

You can add a CC3Layer instance anywhere in the visual node hierarchy of a cocos2d application. Although for the purposes of most games, the CC3Layer instance will usually be sized to cover the complete screen, a CC3Layer instance can be set to any size and added to a 2D parent visual CCNode. So, your application could have a 2D scene that contains a smaller 3D scene embedded into it via a small CC3Layer instance attached to a parent 2D CCLayer.

Conversely, since CC3Layer is a type of CCNode, you can add child CCNodes to it as well, mixing 2D nodes on top or below the 3D scene that is playing out within the CC3Layer. Generally, this is the way most applications will combine 2D and 3D components, with a main 3D scene overlaid with 2D controls such as joysticks, fire buttons, dashboards, etc. Any CCNode that is added to the CC3Layer using the standard addChild: method will appear overlaid on top of the 3D scene. You can also add 2D CCNodes behind the 3D action as well, by using the addChild:z: method, with a negative Z-order. Although this is generally not a common requirement, it can be used to provide a 2D skybox behind the 3D action.

When customizing your application’s subclass of CC3Layer, you will typically override the following two template methods:

  • initializeControls – this method is where you can add your 2D controls into the CC3Layer, and otherwise generally initialize your layer. This method is invoked automatically from any of the init methods of the layer.
  • update: – if you have scheduled regular updates using the standard CCNode scheduleUpdate method, this update: method will be called periodically. This is where you can update any of the 2D controls you’ve added, or pass data from dynamic controls such as sliders or joysticks to your CC3World instance. If you override this method, be sure to call invoke the superclass implementation so it can pass the update notice to the CC3World instance.

CC3World

Although CC3Layer forms the bridge between the 2D and 3D visual worlds, it remains primarily a cocos2D layer, and no 3D activity actually takes place within CC3Layer. All 3D activity, from model management to rendering, takes place within, and is the responsibility of, your application’s customized CC3World subclass.

As with CC3Layer, there are several key template methods that you will typically override in your application’s subclass of CC3World:

  • initializeWorld – This method is where you populate the 3D models of your world. This can be accomplished through a combination of instantiting model objects directly and loading them from model data files exported from a 3D editor. The CC3World instance forms the base of a structural tree of nodes. Model objects are added as nodes to this root node instance using the addChild: method.
  • updateBeforeTransform: and updateAfterTransform: – If you have scheduled regular updates through CCLayer, these CC3World methods will be invoked automatically as part of the scheduled update, respectively before and after the transformation matrices of the nodes of the world have been recalculated, respectively, and before and after the same methods are invoked on the descendants of your CC3World.
  • nodeSelected:byTouchEvent:at: – If your application is configured to support allowing the user to select 3D nodes using touch events, this template callback method will automatically be invoked when a touch event occurs. See the section on touch events for more information about handling the selection of 3D nodes by user touch events.

As the names imply, the difference between the two update methods is that updateBeforeTransform: is invoked before the transformation matrix of the node is recalculated, whereas the updateAfterTransform: is invoked after. Therefore, if you want to move, rotate or scale a node, you should do so in the udpateBeforeTransform: method, to have your changes automatically applied to the transformation matrix of the node.

However, the global transform properties of each node (globalLocation, globalRotation, and globalScale) are determined as part of the calculation of the transformation matrix of that node. Therefore, if you want to make use of the current global properties, you should do so in the updateAfterTransform: method. The global properties of a node can be used to test for collisions, or end conditions of movements, etc.

Sometimes, you may find the need to change the location, rotation, or scale properties of a node in the updateAfterTransform: method, for example as part of collision detection and reaction. If you do, you should invoke the updateTransformMatrices method on the top-most node that is affected, to have those changes immediately applied to the transformation matrix.

You do not need to do anything in either update methods for 3D objects that are acting predictably, such as those on trajectories, or those controlled by CCActions. Their behaviour is handled by the nodes and actions themselves.

In addition to these main template methods, you may find the following CC3World methods useful during operation, or simply useful to understand:

  • addChild: – Familiar from cocos2d, you can add child nodes (CC3Nodes) to your 3D world.
  • addContentFromPODResourceFile: & addContentFromPODFile: -  These are convenience methods for loading POD files directly into the CC3World, and are added by the PVRPOD category if your application uses the module for loading 3D models from POD formatted files.
  • getNodeNamed: – Retrieves the node with the specified name that was previously added to the 3D world. Useful for grabbing hold of a node that was added as part of a file load.
  • activeCamera – Property returns (or sets) the 3D camera that is viewing the objects in the 3D world. You do not need to set this property. It will be automatically set to the first camera node added via one of the add... methods (even if the camera is buried deep within a loaded node hierarchy). However, if you have multiple cameras, you can flip between them by setting this property.
  • ambientLight – This is the color of the ambient light of the 3D world. This is independent of any distinct lights that are added as child nodes.
  • createGLBuffers – This method causes all contained vertex data held by contained mesh nodes to be buffered to Vertex Buffer Objects (VBO’s) in the GL engine and usually into hardware buffers accessible to the GPU. This is an optional step, but highly recommended for improving performance. You can also invoke this method at any level in the node structural hierarchy if for some reason you want to load some, but not all, mesh data in the 3D world into VBO’s.
  • releaseRedundantData – After the createGLBuffer method has been invoked, this method can be used to release the data in main memory that is now redundant for meshes that have been buffered to the GL engine. You can also invoke this method at any level in the node structural hierarchy if for some reason you want to release some, but not all, mesh data from main memory. You can also exempt individual vertex arrays from releasing data by setting the shouldReleaseRedundantData property to NO on the individual vertex array.
  • updateTransformMatrices – If you make changes to the location, rotation and scale properties of a node within the updateAfterTransform: method, those changes will not automatically be applied to the transformation matrix of the node, since it has already been calculated when the udpateAfterTransform: method is invoked. However, you can use the udpateTransformMatrices to have those changes immediately applied to the transformation matrices of a node and all its descendants.
  • play & pause – these enable the operation of the update: method. When the CC3World is paused, the updateBeforeTransform: and updateAfterTransform: methods are skipped, along with updates of all other 3D nodes.
  • cleanCaches – automatically invoked during low memory conditions within the application. This gives you the chance to dump any unneeded resources, for example, 3D objects that are far away, not part of this scene, etc.

Many of the responsibilities handled by CC3World are divided by the implementation into private template methods that you can override in your subclass, to customize behaviour in a modular way.

CC3Node

All the objects in the 3D world, including models, cameras, lights, and the CC3World itself, are known as nodes. CC3Node is the base of the 3D node class hierarchy. Nodes can be assembled into structural assemblies using parent/child relationships. Moving, rotating, or hiding a node moves, rotates or hides all the children (and other ancestors) in concert.

This design will no doubt feel familiar to you as being analogous to CCNodes in cocos2d, and the two node hierarchies do follow the same design pattern. However, CCNodes and CC3Nodes cannot be mixed in the same structural assembly, primarily because CC3Nodes must keep track of location, rotation and scaling in three dimensions instead of two. Nevertheless, the structural concepts between the two node families are consistent.

You assemble 3D nodes using the addChild: method. All nodes have an identifying tag and can have a name. You can retrieve a specified node in an assembly with the getNodeNamed: and getNodeTagged: methods.

All nodes have location, rotation, and scale properties (plus a couple of others). You move, rotate, and scale CC3Nodes by setting these properties. And, again, as with cocos2d nodes, the values of these properties are measured relative to the node’s parent node.

You can override the updateBeforeTransform: and updateAfterTransform: methods of a node subclass that acts predictively, such as those following a trajectory, to update these transform properties. See the discussion on CC3World above regarding the difference between these two methods.

So, the wheels of a car can be child nodes on a car node. Each wheel node can rotate on its axis, and move up and down relative to the car as if on suspension via the location property of each wheel node, and all the while, the whole car assembly may be travelling and bouncing down a dirt road simply by manipulating the location property of the car node.

Any node can be the target of a CCAction, to simply and easily control the movement and behaviour of the node in sophisticated ways. In addition, any node may be configured to respond to finger touch events by the user. See the sections on actions and touch events for more information on this interactivity.

You can use the createGLBuffers method to cause all contained vertex data held by contained mesh nodes to be buffered to VBO’s in the GL engine. Generally, you should invoke this method at the highest level in the structure, CC3World, but you can also invoke this method at any level in the node structural hierarchy if for some reason you want to load some, but not all, mesh data in the 3D world into VBO’s. Once data is loaded into GL VBO’s, you can use the releaseRedundantData method to release the data from your main application memory.

CC3MeshNode

CC3MeshNode puts the ’3′ in 3D. An instance of CC3MeshNode contains the 3D mesh data for a 3D object, plus the material, texture, or solid color that covers the surface of the object.

Each CC3MeshNode instance can be covered in only a single material, texture, or color. However, like any node, mesh nodes can be assembled into a node structure. Moving the parent node will move all of the child mesh nodes in concert, along with their materials and textures. So, a multi-colored beach-ball could be one parent node and several child mesh nodes, each corresponding to a differently colored panel on the beach-ball. The parent ball node can be moved, rotated and scaled, and all component mesh nodes will be affected in unison.

Materials, Textures and Colors

The visible characteristics of the surface of a mesh node is determined by either the material it is covered with, or the pure, solid color it is painted with. As mentioned above, each mesh node can be covered with only one material, or one solid color.

The mesh node holds an instance of a CC3Material to describe the visual characteristics of the surface of the mesh. CC3Material includes properties to set the various coloring characteristics of the surface, including the ambient, diffuse and specular reflective colors, the emissive color, and the surface shininess. It also includes properties to determine how the colors should blend with the colors from objects behind the mesh node, permitting effects such as translucency.

In addition to basic coloring, each CC3Material instance can hold an instance of CC3Texture to cover the surface of the mesh with a texture image.

The combination of coloring properties, blending properties, and textures interact with lighting conditions to create complex and realistic surface visual characteristics for the mesh.

There are two mechanisms for changing the coloring and opacity of a material covering a mesh node.

  • To achieve the highest level of detail, accuracy and realism, you can individually set the explicit ambientColor, diffuseColor, specularColor, emissiveColor, shininess, sourceBlend, and destinationBlend properties. This suite of properties gives you the most complete control over the appearance of the material and its interaction with lighting conditions and the colors of the objects behind it, allowing you to generate rich visual effects.
  • At a simpler level, CC3Material also supports the cocos2d <CCRGBAProtocol> protocol. You can use the color and opacity properties of this protocol to set the most commonly used coloring and blending characteristics simply and easily. Setting the color property changes both the ambient and diffuse colors of the material in tandem. Setting the opacity property also automatically sets the source and destination blend functions to appropriate values for the opacity level. By using the color and opacity properties, you will not be able to achieve the complexity and realism that you can by using the more detailed properties, but you can achieve good effect with much less effort. And by supporting the <CCRGBAProtocol> protocol, the coloring and translucency of nodes with materials can be changed using standard cocos2d CCTint and CCFade actions, making it easier for you to add dynamic coloring effects to your nodes.

If the mesh node does not contain a material, it will be painted with the color defined in the pureColor property. This color is painted as is, pure and solid, and is not affected by the lighting conditions. In most cases, this looks artificial, and is not recommended for realistic scene coloring. But in some circumstances, such as cartoon effects, it may be useful.

CC3MeshModel

Underpinning CC3MeshNode is CC3MeshModel, which in turn holds the raw vertex data in several CC3VertexArray instances. The management of mesh data is spread across these three classes (CC3MeshNode, CC3MeshModel, and CC3VertexArray) to enable data reuse and reduce memory requirements. A single mesh model instance can be used in any number of individual mesh nodes, each covered with a different material, and placed in a different location. Whether you’re talking about hordes of zombies, or a twelve-place dinner setting laid out on a table, a single mesh model instance, with one copy of the raw vertex data can be reused in an number of similar nodes.

CC3VertexArrays

Each mesh model instance (typically an instance of the concrete subclass CC3VertexArrayMeshModel) holds several CC3VertexArray instances, one for each type of vertex data, such as vertex locations, normals, vertex colors, texture coordinate mapping, and vertex indices. And reuse can be applied at this level as well. A single vertex array instance can be attached to many mesh models. So, if you have the need for two teapot meshes, one textured, and one painted a solid color, you can use two separate instances of CC3VertexArrayMeshModel, each containing the same instances of the vertex locations and vertex normals arrays (CC3VertexLocations and CC3VertexNormals, respectively), but only the mesh model for the textured teapot would also contain a texture coordinate vertex array (CC3VertexTextureCoordinates) instance. With this arrangement, there is only ever one copy of the underlying vertex data.

CC3LineNode & CC3PlaneNode

CC3LineNode and CC3PlaneNode are specialized subclasses of CC3MeshNode that simplify the creation and drawing of lines and planes using vertex arrays, respectively.

Duplicating 3D Models

In order to allow you to create hordes of invading armies, the CC3Node class supports the <NSCopying> protocol. To duplicate a node is simply a matter of invoking the copy method on that node. In addition, there is a copyWithName: method that duplicates the node, and gives the new copy its own name.

Copying a CC3Node creates a deep copy. This means that not only is the node itself copied, but copies are created of most components of the node, including the material, and any descendant child nodes. This allows the properties and structure of the duplicate node to be changed separately from those of the original node. For instance, the new node can be positioned, rotated, scaled, colored, or assigned a different texture from that of the original. Similarly, the child nodes of the duplicate may be modified separately, without affecting the original, or any other copies. You can copy an automobile node, and remove one of the wheels of the duplicate, while retaining all four wheels on the original. If a deep copy were not performed, changing parameters in the duplicate node, or its children, would result in the same changes appearing in all other copies of the original node.

There is one big exception to this deep copying. Since mesh data is for the most part static, and is designed to be shared between instances of CC3MeshNode, mesh data is not copied, but is automatically shared between the original node and all its copies. Specifically, when an instance of CC3MeshNode is copied, a copy is made of the encapsulated CC3Material instance and is retained by the duplicate node, but the encapsulated CC3MeshModel is not duplicated. Instead, the single CC3MeshModel instance is retained by both the original node and the duplicate, and is henceforth shared by both nodes. This shallow-copying of the mesh data ensures that only one copy of the mesh data appears in the device memory.

The following additional rules apply to duplicating a CC3Node:

  • The tag property is not copied. The duplicate node is assigned a new unique value for its tag property. This is to ensure that the tag property is unique across all nodes, including duplicates, and allows you to identify a duplicate as distinct from the original, even if the name property is left the same.
  • The duplicate will initially have no parent. That will automatically be set when the duplicate node is added as a child to a parent node somewhere in the world. This applies to the specific node on which the copy or copyWithName: method was invoked. Any descendants of this node will be assigned to their parents after they are copied, so that the overall structure of the node assembly below the invoking node will be replicated in its entirety.
  • Like mesh data, underlying textures are not duplicated (although the CC3Texture instance itself is). Nor is node animation data duplicated.

If you create your own subclass of CC3Node, or any of its existing subclasses, and your new subclass adds state, you must implement the populateFrom: method, including invoking the same superclass method as part of it, to ensure that state is transferred correctly from the original node to the duplicate, whenever an instance of your node class is duplicated.

CC3Camera & CC3Light

These are special nodes that represent the camera and lights in the 3D world. Cameras and lights are directional, and are examples of CC3TargettingNode, which, in addition to being able to move and rotate like other nodes, can be assigned a target or targetLocation at which to point. A target can be any other CC3Node in the scene, and the camera or light can be told to track the target as it moves.

Controlling CC3Nodes with CCActions

If you’ve used cocos2d, you’re no doubt familiar with the family of CCActions, used to control the movement, coloring, visibility, and activities of CCNodes.

In cocos3d, CC3Nodes can similarly be manipulated and controlled by CCActions. Some features, such as tinting and fading can manipulated by standard cocos2d actions. However, because the 3D coordinate system is different than the 2D coordinate system, specialized 3D versions of movement, rotation, scaling, and animation actions are required. This family of 3D actions can be found as subclasses of the base CC3TransformTo interval action. In addition, there are several cocos3d action subclasses that handle material-tinting.

The 3D family of actions can, of course, be used with standard cocos2d combination actions such as the family of ease actions, sequence actions, etc.

CC3Node Animation

Any CC3Node can be animated with key-frame animation data held in a CC3NodeAnimation instance within the CC3Node instance. Typically, this animation data is loaded from the 3D model files exported from your 3D editor, but you can also assemble this data programmatically using the simple CC3ArrayNodeAnimation subclass of CC3NodeAnimation. Like mesh data, the CC3NodeAnimation instances are stateless, and each may be shared by multiple CC3Node instances, so that animation data need not be duplicated redundantly.

Any or all of the transformation properties: location, rotation, and scale, may be animated using CC3NodeAnimation or CC3ArrayNodeAnimation. If you need to animate other characteristics of your nodes, you can subclass these classes to add additional animation capabilities.

Once animation data is associated with a CC3Node, the node can be animated with repeated invocations of the establishAnimationFrameAt: method on the node itself, passing in a frame time, which is a floating point value between zero and one, with zero representing the first animation frame, and one representing the last animation frame.

In order to perform smooth animation, by default, the CC3NodeAnimation will interpolate animation data between frames if the timing value passed in through the establishAnimationFrameAt: method is between actual frame times. This is controlled by the shouldInterpolate property on the CC3NodeAnimation instance. By default, this property is set to YES, but may be set to NO if heavy interpolation interferes with frame rates.

For node assemblies, you will typically want to animate the whole assembly in concert. To support this, the establishAnimationFrameAt: method automatically invokes the same method on each child node, passing the same animation frame time to each child node. Thus, animating a character, by invoking that method on the character node itself, will automatically forward the same invocation to each component child node of the character, such as the character’s limbs, or any weapon the character might be holding. As a result, all components of the character will move in sync with the character, as it is animated.

However, you can turn animation of any node in the assembly off, including selectively turning off animation of specific child nodes, via the disableAnimation method on that child node. You can turn the animation of whole sub-assemblies of nodes off via the disableAllAnimation method.

Fractional animation of individual movements is possible by simply limiting the range of frame times that are sent with the establishAnimationFrameAt: method. For example, if your character animation includes a run frame sequence and a jump frame sequence, you can invoke one or the other by simply starting and ending your animation at the frame times associated with the specific movement you want to invoke.

Controlling Animation with Actions

Although you can arrange to invoke the establishAnimationFrameAt: method of your animated CC3Node directly, in most cases it is much easier to use an instance of CC3Animate to control the animation of a node.

CC3Animate is a type of CCActionInterval, and will run through the animation frames automatically over a configurable time duration. Moreover, when instantiating the CC3Animate, you can restrict the frames to a particular range, to allow fractional animation of specific movements.

Selecting CC3Nodes with User Touch Events

To allow the user to interact with the objects in your 3D world, you can enable the selection of 3D objects using standard iOS finger touch events.

The first step in enabling touch activity is to set the isTouchEnabled property of the CC3Layer to YES. Typically you will do this in the initializeControls method of your customized CC3Layer class. This causes the CC3Layer to register itself to receive touch events from iOS.

As such, your CC3Layer will receive and process kCCTouchBegan, kCCTouchEnded, and kCCTouchCancelled events. By default, your CC3Layer will not receive or process kCCTouchMoved events, because these are both quite voluminous and seldom used. However, you can configure your customized CC3Layer subclass to receive and handle kCCTouchMoved events by copying the commented-out ccTouchMoved:withEvent: method from the CC3Layer implementation and paste it into the implementation of your customized CC3Layer subclass, with the commenting removed.

The second step is to set the isTouchEnabled property to YES, in any CC3Nodes that you want the user to be able to select with a touch event. So, for example, let’s say your 3D world had a bowl of fruit on a table, you might want the user to be able to select a piece of fruit, but not the bowl or the table objects. In this case, you would set the isTouchEnabled property to YES on the CC3Node instances representing each piece of fruit, but not on the CC3Node instances that represent the bowl or table. Please also note that, to be touchable, the node must have its visible property set to YES.

Once these two steps have been completed, the nodeSelected:byTouchEvent:at: callback method of your customized CC3World subclass will automatically be invoked on each touch event. This callback includes the CC3Node instance that was touched, the type of touch, and the 2D location of the touch point, in the local coordinates of the CC3Layer.

If the touch occurred at a point under which there is no touchable CC3Node, the callback nodeSelected:byTouchEvent:at: method will still be invoked, but the node will be nil, to indicate that a touch event occurred, but not on a touchable node.

For node assemblies, the node passed to the nodeSelected:byTouchEvent:at: method will not necessarily be the individual component or leaf node that was touched. Instead, it will be the closest structural ancestor of the leaf node that has its isTouchEnabled property set to YES.

For example, if the node representing a wheel of a car is touched, it may be more desireable to identify the car as being the object of interest to be selected, instead of the wheel. In this case, setting the isTouchEnabled property to YES on the car, but leaving it as NO on each wheel, will allow a wheel to be touched, but the node received by the nodeSelected:byTouchEvent:at: callback will be the node that represents the car as a whole.

Selection Artifacts

When using translucent nodes, you might notice that when a translucent node is displayed over the raw background color of the CC3Layer, it will appear to flicker slightly when a touch event occurs. This is a side effect of the mechanism used to identify the 3D node that is under the 2D touch point.

The selection mechanism uses a color-picking algorithm, which momentarily paints each node with a unique color and then reads the color at the touch point to identify the node. The scene is then immediately drawn a second time with proper coloring. Since this second scene rendering occurs within the same rendering frame as the first, the second rendering completely overwrites the first, and the user is completely unaware than the first pass occurred.

The only exception to this is when a translucent node has no opaque nodes behind it. In this case, because the underlying layer background color is not redrawn on the second rendering pass, some of the solid coloring used to paint the node during the selection rendering pass will “leak through” the transparency of the second rendering pass. In effect, for one frame, the translucent node does not have the layer background color behind it, causing a momentary flicker.

It is important to realize that this effect does not occur when the translucent node has opaque nodes behind it in the 3D scene. This is because the opaque background nodes will be redrawn as well, and it will be these background nodes that will be seen through the translucent node, as they should be.

Therefore, if you have translucent nodes and are using touch selection, to avoid this slight flicker be sure to include an opaque skybox node at the back of your 3D scene, over which the other nodes of your scene will be drawn.

Projecting 3D Locations to 2D

The projectedLocation property of CC3Node holds the location of the 3D node in the 2D coordinate system of the window. It acts as a bridge between the 3D coordinate system and the 2D coordinate system. Knowing the projectedLocation of a 3D node allows you to relate it to a 2D controls such as a targetting reticle, or to a touch event.

The projectedLocation is a 3D vector. As you would expect, the X- and Y-components provide the location of the 3D node on the screen, in the 2D coordinate system of the CC3Layer. If the 3D node is located somewhere out of the view of the camera, and therefore not displayable within the layer, these values will be outside the range given by the contentSize of the CC3Layer. Following from this, either coordinate value may be negative, indicating that the node is located to the left, or below, the view of the camera.

The Z-component of the projectedLocation contains the straight-line distance between the 3D node and the camera, as measured in the coordinates of your 3D world. This value may be negative, indicating that the 3D node is actually behind the camera. In most cases, of course, you will be interested in nodes that are in front of the camera, and the Z-component of the projectedLocation can help you identify that.

CC3Node also supports the related projectedPosition property. It is derived from the projectedLocation property and, for the most part, contains the same X- and Y- coordinates as projectedLocation, but as a 2D CGPoint, which is immediately usable by the cocos2d framework.

However, as a 2D point, the projectedPosition lacks the needed ability to distinguish whether the node is in front of, or behind, the camera. Since this information is almost always needed, the point is encoded so that, if the 3D node is actually behind the plane of the camera, both the X- and Y-components of the projectedPosition will contain the large negative value -CGFLOAT_MAX. If you use projectedPosition, you can test for this value to determine whether the 3D node is in front of, or behind, the camera.

For most nodes, these properties are not calculated automatically. In the update: method of your CC3World you can have them calculated for a node of interest by passing the node to the projectNode: method of the activeCamera.

However, for CC3Billboard nodes, the projectedLocation and projectedPosition properties are calculated automatically on each update. A CC3Billboard is a 3D node that can hold an instance of a 2D cocos2d CCNode, and display that 2D node at the projectedPosition of the CC3Billboard node. Since the 2D node is drawn in 2D, it always appears to face the camera, and is always drawn over all 3D content. The contained CCNode can be any cocos2d node, and it can be configured to automatically scale to the correct perspective sizing, shrinking as the CC3Billboard moves away from the camera, and growing as the CC3Billboard approaches the camera. A common use of CC3Billboard is to display information about a 3D node, such as a textual name label or a health-bar for a game character.

Pluggable 3D Model Loading

Sophisticated 3D games are dependent on loading 3D models from resources created in 3D editors such as Blender, Cheetah3D, Maya, 3ds Max, and Cinema 4D. Since the number of possible file formats is significant, and can evolve, the loading of model data is handled by pluggable loaders and frameworks. When building your application with cocos3d, you only need to include the loaders that you use.

The pluggable loading framework is based around two template classes:

  • The resource class, which takes care of loading and parsing the 3D data file, creating instances of nodes, mesh models, vertex arrays, materials, textures, cameras, lights, etc., and assembles them into a node hierarchy. This will be a subclass of CC3Resource, and is tailored to parsing and assembling a specific data file format.
  • The resource node class. This will be a subclass of CC3ResourceNode, and as such, is actually a type of CC3Node. It wraps the CC3Resource instance, extracts populated nodes from it, and adds them as child nodes to itself. This node can be used like any other CC3Node instance, and is usually simply added as a child to your CC3World instance. You can move, rotate, scale, or hide all the components loaded from a file simply by manipulating the properties of the CC3ResourceNode instance.

In addition to these two template classes, a pluggable loading package will usually contain specialized subclasses of  CC3MeshNode, CC3Material, CC3Camera, etc., in order to set the properties easily from the data loaded from the file. But once created, these objects behave like any other node of their type.

A pluggable loading package may also add categories to base classes as a convenience. Thus, the PowerVR POD file loading package adds a category to CC3World that adds the method addContentFromPODResourceFile: as a convenience for loading POD files directly into your CC3World.

The initial loading package available with cocos3d is for PowerVR POD files.

Automatic Frustum Culling and Bounding Volumes

In a 3D world, the camera is always pointing in some direction, and generally, is only seeing a fraction of the objects in the 3D world. It is therefore a waste of precious time and resources to try to draw all of these objects that will not be seen.

The task of determining which objects don’t need to be drawn, and then removing them from the rendering pipeline is known as culling. There are two types of culling: frustum culling, which is the removal of objects that are not within the field of view of the camera, and occlusion culling, which is the removal of objects that are within the field of view of the camera, but can’t be seen because they are being visually blocked by other objects. For example, they might be located in another room in your the map, or might be hiding behind a large rock. At this time, cocos3d does not perform any kind of occlusion culling.

cocos3d performs frustum culling automatically. Objects that are outside the view of the camera will not be drawn. This is accomplished by specifying a bounding volume for each mesh node. Each node holds onto an instance of a CC3NodeBoundingVolume, which it delegates to when determining whether it should draw itself to the GL engine. All mesh nodes in cocos3d have a default bounding volume that is reasonably accurate, and for the most part, you won’t even need to think about bounding volumes. However, we’ll describe the operation of bounding volumes here, for those situations where the application may want to improve on the default behaviour.

The trick with bounding volumes is to strike a balance between the time it takes to determine whether a node should be rendered, and the time it would take to simply blindly render the object. To that end, different kinds of bounding volumes can be specified, some of which are very fast, but perhaps less accurate, and others that are very accurate, but time consuming.

Accuracy is important because acn inaccurate bounding volume can either result in the effort of rendering an object being performed when the objects won’t be seen, or worse, an object might be culled, when in fact at least part of it would really be seen if it were drawn. This second type of inaccuracy, over-zealous culling results in objects strangely popping in and out of existence, particularly around the edges of the camera’s field of view. Since this behaviour is generally undesirable, you should stay away from creating bounding volumes that are overly-zealous. The bounding volumes included by default in cocos3d always ensure that all vertices are included within the bounding volume, so that objects are never culled when they are actually partially visible.

The family of cocos3d bounding volumes includes a number of different subclasses, each performing a different type of boundary test. The two most commonly used are a spherical bounding volume, represented by subclasses of CC3NodeSphericalBoundingVolume, and axially-aligned-bounding-box (AABB) bounding volumes, represented by subclasses of CC3NodeBoundingBoxVolume.

Checking spherical boundaries against the camera frustum is very fast. However, for most non-spherical real-world 3D objects, such as a human character, or automobile, a sphere that completely envelopes the object is much larger than the object. The result is that near the periphery of the camera’s view, the sphere may be partially inside the frustum, but the object is not. The result is that the object will be rendered (because the sphere can be “seen” by the camera), even though the object itself will not be seen.

Bounding boxes are often more accurate for many real-world objects, but are more expensive to test against the camera frustum, because all eight points that define the volume must be tested against the frustum.

At the extreme end of the scale, the most accurate bounding volume would be to test every single vertex in the mesh against the camera’s frustum. By definition, this will result in almost perfect accuracy, but at the large cost of testing all the vertices. In many cases, since the GPU can generally do this faster than the CPU, there is no cost savings in testing all vertices. For this reason, cocos3d does not yet include a default all-vertex bounding volume test. However, such a bounding volume could easily be added and used by the application.

Bounding volumes, whether they be point, spherical, boxes, or custom, are calculated and defined automatically by the vertices in the mesh node. In addition, they scale automatically as the node is scaled.

Finally, to make use of both fast boundary tests to exclude objects that are clearly far from the camera’s field of view, and also more accurate boundary tests for objects that are at the edges of the camera’s field of view, cocos3d also includes the CC3NodeTighteningBoundingVolumeSequence bounding volume. Instances of this class hold an array of other bounding volumes, and tests the node against them in sequential order. As soon as one contained bounding volume indicates that the node is definitely outside the camera’s field of view, the node is rejected and not drawn.

The trick is to order the contained bounding volumes so that fast, but broad, tests are performed early in the sequence, so they can reject the node if it is clearly nowhere near the camera’s field of view. Subsequent bounding volumes in the sequence should be less broad, but will not be tested unless the earlier bounding volumes have accepted the node as being visible.

By default, mesh nodes in cocos3d make use of a tightening sequence that contains first a spherical bounding volume and then an AABB bounding volume. For the most part, this will likely be sufficient, and you won’t even have to think about bounding volumes. However, some applications may benefit from custom bounding volumes.

Rendering Order

The underlying OpenGL ES engine uses a pipeline of commands for manipulating the state of the rendering engine. The switching of some states can be a costly exercise. Consequently, it is often useful for the application to consider the order in which objects are rendered to the engine. For example, if several objects make use of the same texture, it is usually beneficial to draw all of these objects one after the other, before drawing an object that is covered with a different texture.

To that end, the drawing order of the objects within a CC3World can be configured by the application for optimal performance. The CC3World instance holds onto an instance of a CC3NodeSequencer, and delegates the ordering of nodes for drawing to that sequencer. If no node sequencer is supplied to the CC3World, the world will draw nodes hierarchically, following the structural hierarchy of its children. In most cases, this will not be the optimal ordering.

As with bounding volumes discussed above, there are a number of different types of node sequencers, and establishing a different ordering is simply a matter of plugging and configuring a different sequencer into your CC3World.

There are two key types of sequencers, CC3NodeArraySequencers, which hold a collection of nodes, grouped in some defined order, and CC3BTreeNodeSequencers, which hold a B-tree of other sequencers, allowing groupings of groupings of nodes, ad infinitum. You can assemble a complex sequence definition, by assembling different types of sequencers into a structural B-tree hierarchy.

Every sequencer contains in instance of a CC3NodeEvaluator subclass. A node evaluator tests a node against some criteria, and returns a boolean result indicating whether the node is accepted or rejected, effectively giving each sequencer a chance to answer the question “Do you want this node?”. Using this mechanism, a B-tree sequencer can determine which of its child sequencers wants to take care of ordering any particular node.

A simple example might help here. Both Imagination Technologies (the supplier of the iOS GPU’s) and Apple recommend that all opaque objects should be drawn before translucent objects. In addition, the translucent objects themselves should be drawn in Z-order, which is the reverse order of distance from the camera, with the farther translucent objects being drawn first, and the nearer translucent objects drawn over them.

This drawing order can be accomplished with a B-tree sequencer containing two array sequencers: the first with an evaluator that tests the node for opacity, and the second with an evaluator that tests the node for translucency. The array sequencer holding the opaque nodes can simply hold them in the order they are added. However, the array sequencer holding the translucent nodes needs to order the nodes by their Z-order.

This structure looks as follows:

  • CC3BTreeNodeSequencer (CC3LocalContentNodeAcceptor)
    • CC3NodeArraySequencer (CC3OpaqueNodeAcceptor)
    • CC3NodeArrayZOrderSequencer (CC3TranslucentNodeAcceptor)

When testing a node, the B-tree sequencer will first ask the child sequencer with the opacity test if it is interested in the node, before asking the second sequencer. The result is that the first sequencer will contain opaque nodes in the order they were added, and the second will contain translucent nodes in Z-order.

Because nodes may be moving around from frame to frame, be aware that Z-ordering requires that the nodes in the CC3NodeArrayZOrderSequencer be sorted on each and every frame update. This can have significant performance implications if there are a large number of translucent nodes. To improve performance, ensure that translucent nodes really are visibly translucent (don’t waste time sorting translucent nodes that can’t be seen as translucent by the user), and keep the number of translucent nodes to a minimum.

The requirement that this example illustrates is so common that CC3BTreeNodeSequencer includes the class method sequencerLocalContentOpaqueFirst, to create just such a sequencer assembly. There are also the class methods sequencerLocalContentOpaqueFirstGroupTextures and sequencerLocalContentOpaqueFirstGroupMeshes, which take the concept a bit further and group the opaque nodes so that opaque nodes with the same texture or mesh are drawn together, one after the other, before other opaque nodes are drawn. As above, in each of these sequencers, the translucent nodes are sorted by Z-order.

By default, CC3World uses a sequencer created by the sequencerLocalContentOpaqueFirst method, thereby drawing opaque nodes first, in the order they were added, then translucent nodes in Z-order.

Updating vs Drawing

By design, both cocos2d and cocos3d support a clear separation between updating and drawing activities, and each is invoked on a separate loop. In addition to helping to keep your application code organized, there are good performance reasons for separating updating from drawing. OpenGL ES is designed as a state engine pipeline, designed for a steady stream of rendering commands, with ideally no data traveling back upstream to the application. This allows the GPU to keep its own time. Keeping the application rendering loop lean and mean, helps the GPU operate at the full frame rate, and leaves open options for scaling back the update rate separately if needed, or breaking the update loop into several passes, or possibly even multi-threading the updates, in extreme cases.

With all this in mind, you should keep the code you write in the updateBeforeTransform: and updateAfterTransform: methods of any CC3Node subclass free of drawing or rendering operations. Similarly, should you override the drawWithVisitor: or draw methods in any CC3Node subclass, you should only perform rendering operations, and keep those methods free of model updates.

OpenGL ES State Management

As mentioned above in the discussion of rendering order, the performance of the OpenGL ES state machine pipeline is slowed down by frequent state changes. And the OpenGL ES engine incurs costs when performing state machine changes that really didn’t need to happen because the engine already was in that state. To that end, the application should do its best to ensure that GL engine state changes are only made if state really is changing.

To ensure that, cocos3d shadows the state of the GL engine outside of the GL engine itself. When an attempt is made to change state, cocos3d first checks to see if the GL engine is already in that state, by checking the value of the shadow state. Only if the state is changing from that of the shadow state is it propagated to the GL engine.

This state shadow tracking is automatic, and most application developers can safely ignore it. However, some sophisticated applications may need to interact with this framework, so I’ll give an overview of it here.

In cocos3d, this shadow state is managed by a singleton instance of CC3OpenGLES11Engine. All calls to the GL engine are made through this singleton instance.

The CC3OpenGLES11Engine singleton instance contains a number of instances of subclasses of CC3OpenGLES11StateTrackerManager, each geared towards tracking a particular grouping of GL state, such as capabilities, or lighting, materials, etc. Each of these tracker managers is accessed through a property in the CC3OpenGLES11Engine singleton instance. Each tracker manager exposes individual elements of state through properties.

For shadow tracking to work, it is absolutely critical that all GL state changes are handled by the shadow tracker. So, when adding functionality to cocos3d for your application, if you need to make calls to the GL engine, be sure to do so through the proper shadow tracker! A few examples of how to make a GL call through a shadow tracker, and the GL call that it replaces are:

  • [[CC3OpenGLES11Engine engine].serverCapabilities.texture2D enable] <-> glEnable(GL_TEXTURE_2D)
  • [CC3OpenGLES11Engine engine].materials.ambientColor.value = kCCC4FBlue <-> glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat*)&kCCC4FBlue)
  • [CC3OpenGLES11Engine engine].state.scissor.value = self.activeCamera.viewport <-> glScissor(vp.x, vp.y, vp.w, vp.h), where vp = self.activeCameraViewport

Since it is critical that all GL calls be processed through the state shadow trackers, but cocos2d does not use state shadow tracking, you may be wondering how the shadow trackers know what the value a GL state has at the time that rendering control is passed to the cocos3d framework from cocos2d.

Each primitive state tracker has an originalValueHandling property, which tells that tracker how to determine the original value. The enumerated value of this property can tell the tracker to do one of the following:

  • simply ignore the current state at the beginning of each rendering frame
  • read the current state at the beginning of each rendering frame
  • read the current state at the beginning of the first rendering frame and assume that value always

In addition, for the last two options, the enumeration can be modified to tell the tracker to restore that original state back to the GL engine, because cocos2d is assuming it to have stayed the same.

The original value state is set to the optimal value for combining cocos2d and cocos3d, generally either ignoring the value, or reading it once on the first frame. However, if your application varies from frame to frame and you find that the initial value can change from frame to frame at the point that cocos3d takes over, you can modify the originalValueHandling property of the associated state tracker to read the value on every frame.

Keep in mind that this reading interrupts the the flow of the GL pipeline significantly, and should generally be avoided at all costs. A better solution is to ensure that your cocos2d code leaves GL state with consistent values from frame to frame at the time cocos3d takes over.

179 comments to cocos3d Programming Guide

  • This sounds amazing! I’ve been creating our art assets for our Cocos2D game using Maya 3D and using 2D png snapshots. Can I export a mesh created in Maya as an FBX object? Will my shaders import too? Or do I have to download the Collada plug-in to use my Maya objects with the Cocos3D library? Thanks again for the library, awesome work! ;-) Elizabeth

    • Hi Elizabeth…

      Unfortunately, with this initial release, cocos3d supports importing via the POD file format only. We plan to add other file formats as we move forward.

      The PowerVR SDK (link in article above) does include an exporter from Maya to POD files, but not for Mac. If you’re using Mac, It looks like your best bet might be to export your meshes from Maya to Collada, and then use the Collada2POD converter in the PowerVR SDK.

      I’ll be posting an article on this site in the next few days, with instructions on configuring the Collada2POD tool for exporting to cocos3d. Keep an eye out for it.

      Regarding shaders…no. cocos3d, like cocos2d, uses OpenGLES 1.1, so it doesn’t currently support shaders. Unfortunately, you’ll have to export as meshes, materials and textures for the time being.

      Thanks…

      …Bill

    • ashok

      thanks great job!

      some example not available. put some examples. so we can use more!.

      • Hi Ashok…

        After the next release, we will be working on an new website, which will include better explanations, examples, and hopefully tutorials.

        In the meantime, your best source of examples is the CC3DemoMashUp demo app that comes with cocos3d. It contains examples of using most of the features.

        …Bill

  • iostriz

    Great!
    I’ve been missing this for a while now. I have a question: are other devices supported too? Like iPad, Mac…

    Igor

    • All iOS devices are supported. cocos3d has been tested on iPad.

      Mac support has not been tested. Based on the experience of cocos2d, we expect that there will be a few small issues around OpenGL on Mac…but mostly just about changing calls, etc. It’s something we will address at some point if Mac is a popular platform requirement.

      …Bill

  • wave_1102

    Very great job! Thanks, Bill.

  • Jesus

    Does this engine supports things like:

    - gabbing a 3D object?
    - collision detection between objects?

    It looks very cool from the demo app.

    • Hi Jesus…

      Can you clarify what you mean by ‘gabbing’? If you meant ‘grabbing’…as in manipulating or moving an object…then yes. If not, please clarify.

      Collision detection is up to the app for now (as with the simple bouncing ball in the CC3DemoMashUp). Each node contains a bounding volume that can help with that.

      We will be integrating a proper physics engine into cocos3d in the future. Please refer to the development roadmap for more info.

      …Bill

      • Jesus

        Yes I meant grabbing, such as detecting when an object was touched.

        How is this achieved? I didn’t find a class that provides this functionality.

        As for collision detection. I think the bounding volume will do for now, thanks. Looking forward to the physics engine :)

        • Hi Jesus…

          Ahh…you mean grabbing via a user touch…not programatically.

          There’s nothing automatic yet. However…you can do some crude detection as follows:

          You can use the projectedPosition property of a CC3Node to determine where on the screen it is. Then you can use some kind of 2D radius measurement from your touch point to determine whether you are close enough to consider it ‘grabbed’.

          The projectedPosition property is not set for all nodes automatically. You can either set it for the nodes you are interested in as part of your custom CC3World update: method (via [self.activeCamera projectNode: myNode]). Or you can add a CC3Billboard as a child to your CC3Node. The projectedPosition property of all CC3Billboards are updated automatically.

          If your node stays more or less in the same orientation (not rotating about), like, say, a character running around in a game, you could attach a 2D CCNode to the CC3Billboard, and size it so that it visually covers the 3D model. You could then use the boundingBox of the 2D CCNode as a visual 2D bounding area of your 3D object. By default, the CCNode will grow and shrink proportionally as the 3D object moves toward and away from the camera, so it should stay in sync. If you use a CCLayerColor as the 2D node, then you could color it to show how it covers as you lay it out, and then make it invisible during game play.

          Have a look at the text label attached to the robot arm in the CC3DemoMashUp example project for how CC3Billboards can be used.

          Unfortunately, if you have 3D objects that overlap visually, and you want to be able to touch either, this technique will indicate that both have been touched. You’ll have to have some kind of manual ordering to keep track of which object is of more interest to you.

          Admittedly, this is a crude technique. You’ve given us some things to think about. This is certainly a feature that needs proper support in a touch device. I’ll make sure it makes it into an upcoming release on the roadmap.

          Thanks…

          …Bill

          • salman

            > and size it so that it visually covers the 3D model

            Hi Bill,

            Could you give me a hint how to do that?

            • Hi Salman…

              You would just do it visually. You could used a CCLayerColor, configured with a background color so you can see it, as the cocos2d node being held by the billboard.

              Then play with a combination of sizing that layer and the unityScaleDistance property of CC3Billboard to line it up with 3D object. Once things are working, turn off the color of the CCLayerColor node.

              However…before you run off and do that, I should let you know that we are releasing an update to cocos3d next week that includes proper 3D object picking. Keep an eye out for it.

              …Bill

              • salman

                Hi Bill,

                Thanks for the touch event support in the current version.

                I still have a question regarding the API
                - (void) nodeSelected: (CC3Node *) aNode byTouchEvent: (uint) touchTypeat:(CGPoint) touchPoint

                The touchPoint is the location in 2D coordinate system of the CC3Layer where the touch occurred.

                In case I will move the aNode selected by the touch event, how I can set the aNode.location with the correct CC3Vector?

                Basically my question is how to calculate the 3d location of the object based on known desired 2d viewport location?

                • Hi Salman…

                  Sorry…there’s no true unproject capabilities yet.

                  Unproject gets messy fast because ultimately, one is dealing with individual mesh faces, all of which can be oriented in any way. And in keeping with the cocos2d/3d philosophy, we want to make sure we do something that’s easy and straightforward for developers to use.

                  I’ve added it to the development roadmap, tentatively as part of the 0.7 release (mid year).

                  In the meantime, if you have something like a floor that you want to move your object to, you can always overlay a series of transparent (don’t make them invisible, otherwise they won’t be detected) smaller rectangular meshes as tiles over your floor. Add the tile nodes as child nodes to the floor. You’ll then get notification of which tile has been touched, and you can move your object to that location.

                  …Bill

  • Rasmus

    Great stuff! This is a huge step for cocos2d. Big thanks for the good work.

    Is it possible to do any kind of character animation at this point, knowing that rigging and skinning isn’t yet available? I know how to do it in blender, but i’m not sure how to go about dividing and exporting the animations in a way that would be controllable through code.

    Thanks

    • Hi Ramus…

      Thanks for your encouraging comments.

      Unfortunately, we have not yet even begun to address bone rigging, so I can’t give you much help there…yet.

      It might help you to look at the way the CC3DemoMashUp example in the cococ3d distribution animates the robot arm, which is an assembly of CC3Nodes rigged together. It’s not true bone rigging, but it does illustrate how something like an arm can be managed in cococ3d. If you are able to get Blender to export your character models to COLLADA and converted to POD in a similar manner, it should work.

      I also suggest that you post this question as a new topic in the cocos3d forum, where you might find developers with more real-world bone rigging experience that myself.

      Thanks…

      …Bill

  • Raul

    Hi Bill. Congrats! Great guide.

    I would like to ask if it is possible to do this animation (http://www.box.net/shared/y1qeo8pn2u) with 3 individual models using cocos3d. The animation must start when the piece on the border is touched. The rotation could be either on the rows or columns and always initiated by the touch or click.

    Thanks in advance for your help.

    Raul.

    • Raul

      Sorry, the correct link of the demo animation: http://www.box.net/shared/a96ih83b6u

    • Hi Raul…

      Yes…it’s possible. The rotation is simply a matter of applying a CC3RotateTo action to the CC3Node that is the middle row (or whatever row).

      The real trick is to handle the touches. At present, cocos3d has no true automatic way to determine which object has been touched. That functionality will be coming in a future release.

      In the meantime, if you need to know that the middle row has been touched, you can map it yourself. One technique I can suggest is to attach a CC3Billboard, containing a 2D cocos2d CCNode to the 3D row, and visually size it to be the same size as the row. It will grow and shrink in tandem with the 3D node it is attached to. You can use the boundingBox of the 2D object as a way of determining whether the row has been touched.

      …Bill

      • Raul

        Hi again Bill.

        Could an object be part of a cc3node at one moment and part of another one later? I mean, one object may need to be rotated with the entire row, but later, the same object may rotate with the column.

        Thanks in advance.

        Raul

        • Hi Raul…

          Yes…you can use removeChild: and addChild: to move nodes around in your structural hierarchy. You can do this as part of the update: processing.

          At any one time, though, a node may be added to only one parent…because transformations are processed hierarchically.

          …Bill

          • Raul

            Hi Again Bill.

            Please help! Maybe I’m a little confused with this apparently very simple task.

            I’ve created a new Cocos3d project and just change the pod file and get the node called “Monkey” from the new pod file. Just rotated a bit and it’s working just as expected.

            This is the changed code:

            CC3MeshNode* Monkey = (CC3MeshNode*)[self getNodeNamed: @"Monkey"];

            Monkey.rotation = cc3v(90.0, 0.0, 0.0);

            CCActionInterval* partialRot = [CC3RotateBy actionWithDuration: 1.0
            rotateBy: cc3v(0.0, 30.0, 0.0)];
            [Monkey runAction: [CCRepeatForever actionWithAction: partialRot]];

            Now I want to add a second “Monkey” as a child of the first, a little bit to the left to make it act as the satellite teapot do in the CC3DemoMashUp project, but I’m totally lost.

            Thanks in advance for your help

            Raul

            • Hi Raul…

              Have a look at this post in the cocos3d forum.

              You might also think about using the forum for future questions of this sort…to get assistance from a larger community.

              Also…later this week, we will be releasing an update to cocos3d that greatly simplifies the ability to create copies of nodes. Keep an eye out for it!

              …Bill

  • Kai

    Hi Bill,
    can you help me with a little problem?
    I have an 3d object an it rotates on the simulator, but how can I add a texture to this model? where i’ve to put the images etc?
    thanks for your help kai

    • Hi Kai…

      The texture images go in the Resources folder of your application, as do any POD files that you are using.

      If you haven’t done so already, have a look at the CC3DemoMashUp example in the cocos3d distribution. It includes several textured components.

      …Bill

  • Naimed

    Hello Bill, I have a little issue. I want to include a CCNode (a CCSprite actually) attached to a 3d figure, but i dont want the sprite to face the camera automatically. CC3Billboards have an automatic orientation towards the cam, and i want to disable this, or go somehow around.
    Thanks a lot for your help and congratulations for this awesome features!

  • [...] can download the Cocos3D beta here: Cocos3D Beta You can find the cocos3D programming guide here: Cocos3D Programming Guide It looks like Cocos3D has a ways to go, but judging by the impressive start it appears to be in the [...]

  • [...] You can find the cocos3D programming guide here: Cocos3D Programming Guide [...]

  • Syler Cider

    I’ve made a few games for the iPhone already, but they were all static (i.e. no moving images). I want to now start creating games with movement.

    Being new to games with movement, should I start out with cocos*2*d or jump right into cocos*3*d?

    • Hi Syler…

      Well…that all depends on what you want to do!

      Full 3D games are very different than 2D games.

      3D games provide a much richer user experience. Think flight simulators or first-person shooters. But on smaller devices, 2D games can often be more playable. Think Angry Birds…or Run! (the latter is a cocos2d game).

      3D games are more complex to create, both in terms of content creation, and in terms of managing the objects…simply because you have to think in terms of 3 dimensions.

      If you’re brand new to dynamic games…start out with cocos2d. That will give you a taste of thinking and working in the dynamic world. Then move to cocos3d when you want to explore a richer user experience.

      …Bill

  • Vincenzo

    Fantastic guide! :)
    I hope I’ll be able to do something with that..
    Any news about Xcode 4 templates?
    thanks

    Vincenzo

    P.S: I traslated this page in italian…I hope that isn’t a problem..:)

  • ris

    Hi Sir,
    I am a new guy to 3d world, and cocos 3d is a great framework,i have a question that how can i render a image in the cocos 3d,does the cocos3d framework always need the pod file? (i just need a static background image).

    Thanks in advance.

    • Hi ris…

      Have a look at family of populateAs... methods on CC3MeshNode. Plus the subclasses CC3PlaneNode and CC3BoxNode.

      You can find examples of how to use these in the demos CC3DemoMashUp and CC3Performance. The ground plane in CC3DemoMashUp is a static background rectangle like you are asking about.

      …Bill

      • ris

        Hi Bill,

        Thanks a lot for your reply,i get it, i am a new guy to the 3d world,and i am plan to use cocos3d to build a 3d book application. But i find that i can not control the camera very well. Now i have no idea about this, Would you like to give me some idea about this? Is it easy to be done by cocos3d? If i can get some reply from,that will be a great help. Thanks in advance.

        Ris

        • Hi ris…

          The best thing to do is to become familiar with the CC3DemoMashUp and CC3Performance demos. They include code examples of how to work with many of the features of cocos3d, including controlling the camera.

          In addition to the basic behaviour of these demos, you’ll also notice that there are a number of sections of code commented out in the demos. You can selectively uncomment these to see how they change the behaviour.

          …Bill

  • ris

    Hi Bill,
    That is great help,i look into the CC3DemoMashUp,and build some mesh node like
    CC3MeshNode* floater = [CC3PlaneNode nodeWithName: kFloaterName];
    [floater populateAsCenteredRectangleWithSize: CGSizeMake(500.0, 500.0)
    withTexture: [CC3Texture textureFromFile: kFloaterTextureFile]
    invertTexture: NO];
    But i find i have two problems
    1.the image render on the stage is black,when i click it seems appear the original color,how can i get the right color?
    2.If i remove the Robot , it seems that the location and direction control do not work,how can i keep the camera function and remove the robot on the stage.

    It should be easy problem,but seems to be confuse to me. I hope i can get an answer from you. If it is not correct to post such problem here.Would you like to tell me where i can post the problem.Thanks again for your help.

    Thanks,
    Ris

    • Hi ris…

      Sorry to see you’re having trouble.

      Please start posting your questions to the cocos3d forum. You’ll get answers much faster that way.

      Regarding the rendering issue, what is it that is rendering black? Is it the floating ring? Are you substituting your own texture file, or is it one of the textures in the demo?

      Regarding the camera and robot, have a look at this forum posting. It explains how to replace the camera when you remove the robot.

      …Bill

      • ris

        Hi Bill,
        That’s great to see your answer,yes,i have add my own texture as plain node in the project,and it seems render a black color,when i set acts.color = ccWHITE; it appears,but not the real color of my texture, but when i rotate it,it seems get the right color,how can i disappear it correct as render the “ground ” plain node?
        Again,thanks for your help,bill.

        Thanks,
        Ris

  • I have created project from template (using Xcode 4) and generated project has no target (“0 targets, missing base SDK”).
    I have no clue how to fix that – anyone had similar issue?

    cocos3d 0.5.4, cocos2d 1.0-rc

    cheers!

  • Hmm… actually I forgot to install cocos2d template in Xcode. Now it works great.
    Thanks for the great job!

  • Awesome ,is there any chance of having a top view game with a 2d physic engine like chipmonk or box2d settind the 3d positioning?

    • Hi Gil…

      It depends on what you want to control. If you just want the 3D for a backdrop, you can control the 2D cocos2d sprites on the top layer with box2d or chipmunk.

      But those physics engines wont control the nodes in the 3D world.

      …Bill

      • Do you have a sample code of it running with a Box2d physic engine? Do you think Is it simple to implement?
        I will try it, I think it might work good for a race game, I could add springs for curves letting the car rotate 5 degrees on it’s Z on the curves, it might give a nice felling.
        I’m thinking something like I did for http://www.formulanova.com but having a 3d Scene without road mountains, just a 2d physic engine top view.

  • Kareem Daker

    hello Bill,

    im using cocos3d 0.5.2, and im getting build warnings in xcode about having multiple position: getters and setters… one in cocos2d’s CALayer

    @property CGPoint position;

    and the other in CC3OpenGLES11Lighting:

    @property(nonatomic, retain) CC3OpenGLES11StateTrackerLightVector4* position;

    it is not creating any problems and everything is running fine but im just wondering what i need to change to remove the warning… or is this fixed in later versions of cocos3d?

    • Hi Kareem…

      It’s extremely unlikely that a CC3OpenGLES11Lighting class could be getting mixed up with the CALayer class. The two classes are completely unrelated.

      Have a look at how your code is structured. If you still have problems, can you copy the exact error message and paste it here, please?

      …Bill

      • Kareem Daker

        the warning is appearing on any line i use [lang] setPosition: [/lang] on subclasses of CCSprite or CCLayer (not related to cocos3d in anyway). the warning says: “Multiple methods named ‘position’ found”… and they direct me to the properties i mentioned earlier. note that the warning does not appear if i do [lang] mySpriteSub.position [/lang] but only if i do [lang] [mySpriteSub setPosition:aPosition] [/lang]…

        I did not start my project using cocos3d templates. It was an old cocos2d project and i simply added cocos3d source files to it. Also i am adding my only CC3Layer as a sub layer to a CClayer.

        • Kareem Daker

          sorry false alarm… turns out i was calling setPosition: on CCNodes created as id objects… the warning went away with casting. sorry about that.

  • Kareem Daker

    Hello again Bill,

    i am having a problem with the device orientation and cocos3d. When i started my project, i had my autorotation set to kGameAutorotationUIViewController from GameConfig.h… now i want to remove autorotation support and only view things in landscapeLeft… when i changed the cocos2d GAME_AUTOROTATION kGameAutorotationNone, everything was fine with cocos2d but all my 3d objects are now misplaced in Z. I tried playing around with several autorotation values from the RootViewController but nothing seems to work. any ideas?

    • Hi Kareem…

      cocos3d does not use RootController. Instead, it uses CCNodeController, which is compatible with both cocos3d and cocos2d.

      Please see the AppDelegate class in any of the hello, world template, the CC3DemoMashUp demo, or the CC3Performance demo for examples of how to control device orientation with the CCNodeController.

      You can fix it to a particular orientation, or you can configure it to autorotate.

      …Bill

  • Neogene

    Hi Bill,

    wonderful project (and really nice transparent integration witch cc2d), i’m facing a problem and i dunno if its my fault or something in the framework:

    i modified the default cc3d project: i created n CC3PlaneNode , each of them uses a texture, i place them on z axis using ground.location = cc3v(rect.origin.x, rect.origin.y, -990.0+(30*i)); where -990 is a nice distance from the camera and i is an incremental number (i=0…1…2…etc) so the distance from the camera decreases but i detected rotating the objects (with an action) that all of them are on the same plane! the z is not affecting them, the z are:

    -990
    -960
    -930 and so on…

    Note: the textures are transparent png.

    Have you any suggestion?

    CC3PlaneNode *ground = [CC3PlaneNode nodeWithName:[NSString stringWithFormat:@"n_%i",i]];
    [ground populateAsCenteredRectangleWithSize: rect.size
    withTexture: [CC3Texture textureFromFile:[content objectForKey:@"filename"]]
    invertTexture: YES];

    ground.isOpaque = NO;
    ground.location = cc3v(rect.origin.x, rect.origin.y, -990.0+(30*i));
    ground.rotation = cc3v(-180.0, 180, -180);

    • Hi Neogene…

      Please use the cocos3d forum for further questions. You’ll be able to make use of the community for help.

      In what direction are you rotating the planes? And where is your camera?

      If your camera is looking down the default negative Z-axis, and you are rotating all of the planes around the X or Y axis, they will indeed all appear inline at 90 degrees, because each is rotating along its own axis (eg- try holding out your hands with one covering the other with a bit of space between them, then rotate your wrists).

      If you are trying to rotate them all together and retain their spacing, you need to add them all to a parent node and rotate that parent node instead.

      Try modifying your code to create a CC3Node as a parent (call it say cardHolder), then add the planes to it at Z = i * 30. Add the cardHolder to the CC3World, and place it at Z = -990.

      When you now rotate the parent cardHolder node, you’ll see that it maintains the relative spacing of the planes it is holding.

      You can see something like this in the LandingCraft class of the CC3DemoMashUp demo, where many robot arms are added to a parent CC3Node.

      …Bill

  • Neogene

    Thank you bill, i managed changing the to nil;

    I signal probably a bug:

    1) create a CC3PlaneNode using a transparent png:

    [head populateAsCenteredRectangleWithSize:headRect
    withTexture:[CC3Texture textureFromFile:@"first.png"] invertTexture:YES
    ];

    2) set opaque property to NO;

    3) change after a touch event or other events the texture using:

    [head populateAsCenteredRectangleWithSize:headRect
    withTexture:[CC3Texture textureFromFile:@"second.png"] invertTexture:YES
    ];

    The second time i change the texture it shows black in transparent areas, so i have to set the node to opaque=NO again to have the transparent texture again.

    • Hi Neogene…

      The populateAs... methods are meant to be constructor methods, so they set a number of default property values.

      If you want to simply change the texture, do so via the texture property of CC3Material.

      …Bill

  • Neogene

    first row: I managed changing to nil the drawingSequencer;

  • George

    Nice work!
    I already stated using it and I have a problem:
    I have 3D object loaded (a thin tube) that’s supposed to be a wire between 2 points (loose, not stretched).
    I want to make it swing a bit left-right. How can I modify it to appear as it is swinging?

    Thank you very much!
    Excellent library!

    • Hi George…

      Please submit your questions to the cocos3d forum to get answers from the cocos3d developer community.

      In your case, I guess you could make the two end points the zero-points in your mesh and then just rotate the node back and forth by a few degrees using the rotate property.

      …Bill

  • Jp

    Thx! I learned a lot about 3d engines by reading this programing guide. It’s a long time I wish to make a step in the 3d world and I feel I could do it with your engine. Thx again.

  • cocoachina

    how to show game center leadboard in cocos3d?
    and how to handle the leadboard orientation?

  • cocoachina

    i have showed game center leadboard successfully,
    in AppDelegate.m add the line :
    window.rootViewController = viewController;

    but the leadboard won’t change orientation automaticlly while showing

  • Great work with cocos3d. Been studying the samples.
    I’m still confused as to how one can create a mesh with code.
    Basically I want to make a rectangular mesh and split it into triangles and then have access to all the points in order to change their positions during the game thus altering the mesh.

    • Hi Mihai…

      For simple examples of how to construct a mesh from vertices, have a look at the populateAs... methods of CC3MeshNode. For example, check out populateAsSolidBox:.

      Once you have constructed a mesh (either using a 3D editor, or programatically), you can manipulate the data on the vertices using accessor methods like setLocation:at:, setTexCoord2F:at:, etc. of the various CC3VertexArray subclasses that hold the mesh data.

      …Bill

  • Mark

    Hi there, is it possible to use this software/coding to embed a 3d model viewer into an iphone app? I’m a novice developer looking for a plugin that will allow my users to call an ACTION that will open a small 3D/MD2 model so cached within the app and allow them to rotate zoom and leave model and return to main page. My iphone app knowledge is basic but modelling is good. Appreciate your time and advice. :)

  • Thanks Bill, I was able to make a cube programmatically. Is there a way to control the mesh resolution?

  • Hi Mihai…

    If you’re referring to the number of triangles in your mesh, the factory methods for creating planes do allow you to define tessellation. For example, the populateAsRectangleWithSize:andPivot:andTessellation: method.

    Have a look at how that method is structured to develop your own mesh creation.

    Going further, you can scan the internet for code algorithms for creating basic shape meshes programmatically.

    Alternately, even for a simple cube or ball, you can use a 3D editor to create the mesh for you, in whatever level of mesh structure you want.

    …Bill

  • Thank you for the quick response Bill. Back to your answer on my previous question, all these inner vertices should be accessible through the CC3VertexArray class, am I correct?
    Also, am I correct in assuming that a rectangle mesh has only one instance of CC3VertexArray?

    • Hi Mihai…

      Please submit further questions to the cocos3d forum. Others can help answer your questions, and also benefit from seeing the collective answers.

      CC3VertexArray has several subclasses. Typically a mesh will have several arrays, one for vertex locations, another for normals, another for text coordinates…but only one of each type, unless you are using multi-texturing.

      The arrays can use interleaved data if you want.

      The underlying vertex data can be read and manipulated by accessor methods such as locationAt: and setLocationAt:. Be sure to familiarize yourself with the shouldAllowVertexBuffering and shouldReleaseRedundantData properties to help you retain the data in main memory so you can manipulate it.

      Again...have a look at the populateAs... methods to get an understanding of how they work together.

      ...Bill

  • James Allen

    Hi Bill, this looks great! This may have been asked before, so I apologize if it has – is there a way to loop through a 3d animation, writing each frame to a sprite-sheet? For instance, I want to make an isometric game, allowing for player customization, so using 3d parts and animations would be great, however for speed purposes I want to stick to animated sprite sheets for each character. Is this something that I can do with cocos3d? Thanks!

    • Hi James…

      Nothing automatic. But there are solid render-to-texture features in cocos2d, and since cocos3d runs inside a cocos2d layer, so you may be able to easily render the 3D scene to a texture through cocos2d capabilities.

      …Bill

  • James Allen

    That’s actually along the lines of what I was thinking Bill, thanks. I’ve been away on my honeymoon & haven’t yet had the chance to actually dig into cocos2d or 3d yet, but is there a way to set up a camera and your character and run through the 3d character’s animation without actually rendering to screen? I’m just assuming here, but I create the 3d character, set it up, but don’t addChild, right? Would I then have to manage the memory myself and dealoc it manually?

    • Hi James…

      No…you should be able to just set everything up as you normally would, 3D models, cameras, lights, animation, etc. You can let cocos3d do all the memory management for you.

      Just don’t add the CC3Layer to the cocos2d director. Render it to texture instead. I’m not an expert on cocos2d rendering to texture…so I can’t give you much more advice than that. It looks to be fairly straightforward. And if you get stuck, check out the cocos2d forum.

      …Bill

  • James Allen

    Yep, that’s the plan :) Even if renderToTexture won’t work there’s more than one way of getting the bitmap data & outputting to a cached file. Thanks for your responses Bill, I’ll try to keep people posted on my progress over the next couple months on your cocos3d forums.

  • James Allen

    I guess I couldn’t really do what I need without some sort of bones or BIP + skin support though – I know you mentioned it’s something you were working on for future iteration, do you know your proposed timeline for such a feature?

    Basically, and this is all theoretical, I’d need a way to export each armor & weapon for each character’s animation, then assemble the character based on current weapons, armor, & character’s customization, and then for each animation render to a spritesheet all of the frames of each animation. With each weapon change I’d have to re-render everything though, so I’m not sure if it would take too long. The other thought is to have every possible combination as single pngs, which can be assembled on the fly to 2d spritesheets. The problem with this is the size of the download would be huge I’d think, so that’s why I thought using iOS support for 3d and then baking on the fly would be best.

    Am I completely out of my mind??? :)

  • Chris Myers

    Hey Bill,

    Great job on cocos3d. I’ve found it very useful and easy to use.
    I have found an issue with vertex colors that I suspect has gone overlooked due to lack of use.

    For some reason PVRFoundation is missing a few PVRT data types including ARGB and RGBA. I have a fix that works that I will post to the forum. It would be nice if these changes could get merged in to the release so I don’t have to insert it every time I upgrade.

    Also, I’ve been working on Bone + Skin support for meshes using glMatrixPaletteOES which is nearly integrated with cocos3d. It works, but the transformations are still incorrect, due to my lack of understanding of the way cocos3d handles the matrix stack. Unlike the vertex color issue, bone skinning has been designed as a plugin so I can work on it while you release changes.

    Chris

    • Hi Chris…

      Thanks for working on your extensions.

      Please post the PVRT data types code to the forum, and I’d be happy to roll it into the next release.

      Also, when you get to a point that you’re happy with on the Bone Skinning, I’d be happy to have a look at it, and see if it would make sense to roll it into the main development (giving proper credit to you for your efforts).

      As you are working on that, if you have any questions about how the matrix transforms work in cocos3d, or if I can help point you in the right direction about anything, please contact me at support@brenwill.com.

      Thanks…

      …Bill

  • Hi again Bill
    I’d like to make sure I understand if I am using the setlocation at method properly.
    Is the index created by traversing the mesh in the x direction first, then moving to the next row, etc?
    Thanks!

    • Hi Mihai…

      The index of setLocation:at: is simply the index into the mesh vertex array, which is just one big long array of CC3Vectors.

      If you’ve created the mesh programmatically, the order of that array is whatever you set.

      If you’ve created the mesh using a 3D editor, then the order of that array is whatever order the 3D editor laid out for the mesh.

      It is not related to the X, Y & Z coordinate system, and is more typically aligned to the drawing order.

      …Bill

  • But the indexing is automatically created by the method “populateAsRectangleWithSize:andPivot:andTessellation:”
    , so I was wondering if that follows x/y (e.g. first and second coordinates in the default cc3v argument list)

    See sketch here:
    http://img710.imageshack.us/img710/9422/2dcoords2index.jpg

    Thanks

    • Hi Mihai…

      The best thing I can suggest is review the code in the method you mention to determine the order it lays out the vertices.

      If that order is not usable for you, you could consider writing a version of that method that lays out the vertices in a more useful order.

      …Bill

Add Your Comment

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <pre> <q cite=""> <strike> <strong>

You can enter source code snippets this way

[lang] your source code [/lang]

where lang is a source language format (eg. text, objc, java). You can find the full list of language formats and additional configuration options here. The source code will be colored according to the language syntax. Don't forget the [] square brackets.