Examples

Rotating Cubes

In this example we integrate DirectX11 with CellSpeak. We create a number of cubes - each cube is a cell - and we send it messages to change its behaviour every few seconds or so. The result is a series of cubes that move, rotate or change color as requested.

The DirectX11 library cannot be used in a parallel program just like that - precautions have to be taken. As this is an example and as the next implementation will use DirectX12 - which is much better at handling parallelism - just make sure that the example is run in a non-parallel mode, by limiting the nr of threads or by simply running in debug mode.

09 Rotating Cubes.celsrc
-.-
	Rotating cubes
	
	This program creates a number of cubes that do some rotations etc.
	
	Each cube is implemented as a seperate cell that reacts to the different messages 
	it receives (Rotate, Draw, etc).	
-.-

use Math, Strings, Editor, Windows
use D3D11, Camera, Light, Color, Material, Shape

-- The cubes that are used in this program are derived from the shape with a shared mesh.
-- As all cubes are the same (cubes - they can have a different size though) this is the most
-- efficient solution in terms of space. As an alternative every cube can be given its own mesh data. 
design ShapeWithAction(MeshClass cppMesh) like SharedMesh( cppMesh ) is
			
	-- we create a function variable that will be used to calculate a new position for the cube at each frame
	var function FrameAction (word Interval) to do
	
	-- when a new frame has to be calculated, we simply execute the FrameAction
	on NewFrame(word Interval) do
		
		-- just execute the frame action for that cube
		FrameAction( Interval )
	end
	
	-. Below are a number of messages that redefine the action to be executed for each frame.-
		
	-- A message to start rotating - 
	on RandomRotation do
	
		-- Redefine the body of FrameAction
		body FrameAction with
			-- a random angular speed (in the range 0 to 0.05 rads/sec)
			float AngularSpeed = randf() * 0.05		

			-- A random normalised rotation axis			
			xyz Axis = norm( [randf(),randf(),randf()] )	

			-- calculate the rotation matrix					
			matrix4 RotationMatrix =  Rotate.Axis( Axis , AngularSpeed)	
		end 
		is
			WorldMatrix = RotationMatrix * WorldMatrix 
		end

	end
	
	-- Another action - to rotate  - all cubes will have the same speed and axis
	on Rotate( xyz Axis, float Speed) do
						
		-- we redefine frame action
		body FrameAction with
			matrix4 RotationMatrix = Rotate.Axis( Axis, Speed )
		end
		is
			WorldMatrix = RotationMatrix * WorldMatrix 
		end
	end
	
	-- Local rotation
	on Spin( xyz Axis, float Speed ) do	

		-- redefine frame action
		body FrameAction with 
			matrix4 Spin = Rotate.Axis( Axis, Speed )
		end
		is 
			WorldMatrix = WorldMatrix * Spin 
		end 	
	end
	
	-- Move radially
	on RadialMove( float Factor) do
	
		body FrameAction
			keep Factor
		is
			WorldMatrix.c14 = WorldMatrix.c14 * Factor
			WorldMatrix.c24 = WorldMatrix.c24 * Factor
			WorldMatrix.c34 = WorldMatrix.c34 * Factor
		end	
	end
	
	-- Move radially with random speed
	on RandomRadialMove do
	
		body FrameAction with
			float Factor = 1 + randf()*0.01
		end
		is
			WorldMatrix.c14 = WorldMatrix.c14 * Factor
			WorldMatrix.c24 = WorldMatrix.c24 * Factor
			WorldMatrix.c34 = WorldMatrix.c34 * Factor
		end	
		
	end
	
	-- Change the material in a random way
	on NewRandomMaterial do	
		MatSpec = Material.random()
	end
	
end

-- This design groups the cubes in a 'cube of cubes' with NxNxN cubes in total
design CubeOfCubes(int NrOfCubes ) is 

	float Rib = 3 				-- The rib size of each individual cube
	float Sep = 7				-- The seperation between two cubes 
	float CubeSpace = Rib + Sep	-- The total space taken by a single cube
	Mesh.Cube CubeMesh			-- the cube mesh
	
	-- Build one (1) instance of the cube mesh
	CubeMesh.Build()
	
	-- The initial worldmatrix for each cube
	matrix4 WorldMatrix = [
			Rib,	0,		0,		0,
			0,		Rib,	0,		0,
			0,		0,		Rib,	0,
			0,		0,		0,		1
		]
	
	-- In CellSpeak we can collapse the three loops to create the structure into one statement
	for i = 0 to NrOfCubes-1, j = 0 to NrOfCubes-1, k = 0 to NrOfCubes-1 do
	
		-- Create a cube and send a message to move to a new location - note that we do not save the references to all the cubes.
		create ShapeWithAction(CubeMesh.cppMesh) <- SetWorldMatrix( WorldMatrix ) , Move([i*CubeSpace, j*CubeSpace, k*CubeSpace]),SetMaterial( Material.random() )
	end
	
	-- All messages are passed on to the cubes 
	on ? => all <- (same)
	
end

-- This is the design that we will instatiate to get the prgram going
design World is

	cell Camera								-- We need a camera
	cell Light								-- We need a light
	cell Window								-- A text output window 
	cell Timer 								-- we need a timer 
	CanvasWindowClass CameraWindow			-- The camera needs a window to display into
	Hwnd CameraWindowHandle					-- The window handle
	RectangleType Rectangle					-- The window rectangle
	float zz
	cell Log
	word FrameCount=0	
	cell Scene
	
	constructor is
		
		-- for the camera position and direction
		xyz From = [100,150,250]
		xyz To = [0,0,0]
		
		-- We create the output cell.
		Window = create MenuWindow("Rotating Cubes")
		
		-- set stdout 
		system <- stdout.Set( Window )
	
		-- Create a camera that looks at the origin 
		Camera 	= create FromTo(From,To)
		
		-- The camera needs a window to display in - the rectangle defines the size of the window
		Rectangle = [900,0,2500,1200]		
		
		-- Create the window class (calls a C++ method)
		CameraWindow = CameraWindow.Create("Camera",&Rectangle, 0, 0,null )
		
		-- Tell the window about the camera - the window will send keyboard and mouse events to the camera
		-- CameraWindow is a C++ object, so SetCamera is a C++ object method call.
		CameraWindow.SetCamera(Camera)
	
		-- Create a light for the scene
		Light = create Directional( Color.White*0.7, Color.White*0.7, Color.White*0.7, [1,1,-1] )
			
		-- and build the scene of NxNxN cubes
		Scene = create CubeOfCubes( 10 )
		
		-- Tell the camera about the window, the light and the scene (which starts at the world = self)
		Camera <- SetWindow(CameraWindow), SetLight(Light), SetScene( Scene )
	
		-- Request a timer service from the system
		system <- Service.Get("Timer")
		
		-- We start the camera - we also request a delivery notification
		Camera <+- Roll
		
		-- and we print some stuff ...
		Window <- print("\nScene was created - Camera = [Camera] and Light = [Light] ") 

	end

	-- The delivery notification from the camera
	on Roll.DN do
		Window <- print("\nReceived Delivery Notification from Camera [sender]")
	end
	
	-- The timer
	on Service.Provider( ansi Name, cell Provider) do
	
		-- check that we received a timer service ..
		Name is "Timer" ? Timer = Provider	: yield
		
		-- we start some action
		Scene <- Rotate([1,1,1],0.005)

		-- every two and a half seconds we will change the action
		Timer <- Subscribe.Interval(2500w,"ChangeAction")
	end
	
	int Count = 0
	on ChangeAction( word Time) do
	
		system <- println("[Time/1000] seconds: change to action [Count%7]")

		-- we cycle through the seven actions we have defined..
		switch Count%7				
			case 0: Scene <- RandomRadialMove
			
			case 1: Scene <- Rotate( [1,0,0], 0.02 )
		
			case 2: Scene <- RandomRotation
			
			case 3: Scene <- Spin([1,1,1], 0.25)
			
			-- Set four timers to fire after 250 msec to change the color
			case 4: Timer <- Subscribe.Relative(250w,"Color.Red")
					Timer <- Subscribe.Relative(500w,"Color.Green")
					Timer <- Subscribe.Relative(750w,"Color.Blue")
					Timer <- Subscribe.Relative(1000w,"Color.Random")
					
			case 5: Scene <- RadialMove(0.995)
			
			case 6: Scene <- Rotate( [0,1,0], 0.1 )
		end
		Count += 1
	end
	
	-- We create an interface to handle the color messages
	interface Color is

		on Red(word t) 		=> Scene <- SetMaterial( Red )		
		on Blue(word t) 	=> Scene <- SetMaterial( Blue )
		on Green(word t) 	=> Scene <- SetMaterial( Green )
		on Random(word t) 	=> Scene <- NewRandomMaterial
	
	interface end 
	

end -- world