Skip to content

Creating a custom controller

Coela Can't! edited this page Aug 19, 2021 · 4 revisions

Controller Overview

A controller defines the pixel locations, offsets, and rotations for the cameras i.e. displays in software and the method for writing this data to the hardware displays.

To create a controller there are a few steps you must follow:

  • Create a .CSV file containing the pixel data to create the pixel map
  • Run the ProtoTracerPixelCreator to process this .CSV file into a C++ file
  • Create a pixel group from the pixel map
  • Specify the transform for the camera i.e. rotation, position, and scale
  • Specify the camera layout to set the base orientation i.e. Z is the forward axis, Y is the up axis
  • Create the camera(s) from the transform, camera layout, and pixel group
  • Write the display function, which writes the rendered camera data to a hardware display

Controller Object

The controller parent object allows you to create a controller containing multiple cameras to be rendered polymorphically. This class will give you access to Render a scene object, calculate the render time in seconds, store your camera objects, and set requirements for a user-created Initialize function and Display function.

Creating the Pixel Map from a .CSV File

A pixel map is a .CSV file containing the list of the XY coordinates (in millimeters) of the pixels on a camera. The easiest way to generate a pixel list is to export a Pick and Place file from a PCB design tool. The formatting utilizes a CSV file, each line is a pixel containing the object name, X coordinate, and Y coordinate. Here is an example:

U1,18,4
U2,25,4
U3,32,4

The object name is for your reference only and can be a repeated value, changing this will not set the order of the pixels. The order is set based on the first to the last item within the list.

Converting the .CSV to a Pixel Map

To convert this to a pixel map we need to use a separate utility, the ProtoTracerPixelCreator. This converts the .CSV to a .h file that can be included in the C++ program. To run this program you need to edit the top three values to specify the arrays variable name (name), the input file location (origFileName), and the output file location (outputFileName).

name = "KaiborgV1Pixels"
origFileName = "Example Files\KaiborgV1.csv"
outputFileName = "Output\\" + name + ".h"

Importing the Pixel Map

The pixel map can now be imported and used to create a Pixel Group object. The pixel group constructor takes in the variable name you specified above, the exact number of pixels in your camera, and optionally if the group is ready from zero to max position or max position to zero.

#include "Flash/PixelGroups/KaiborgV1Pixels.h"

PixelGroup camRghtPixels = PixelGroup(KaiborgV1Pixels, 571, PixelGroup::ZEROTOMAX);

Setting the Camera Transform

The camera transform specifies the orientation, location, and scale respectively of the camera object. The orientation can be set in either XYZ Euler Angles as a Vector3D or directly with a Quaternion object, the location is set in millimeters from origin, and the scale is set by origin.

Transform camFronTopTransform = Transform(Vector3D(35.25f, -2.25f, 216.5f), Vector3D(68.25f, 210.75f, 31.0f), Vector3D(-1, 1, 1));
Transform camFronTopTransform = Transform(Quaternion(1.0f, 0.0f, 0.0f, 0.0f), Vector3D(68.25f, 210.75f, 31.0f), Vector3D(-1, 1, 1));

Setting the Camera Layout

The camera layout specifies the base orientation that the camera will work from. This typically is different per program i.e. Cinema 4D uses the positive Z-axis as forward and the Y-axis as the up axis.

CameraLayout cameraLayout = CameraLayout(CameraLayout::ZForward, CameraLayout::YUp);

Creating the Camera

The camera is constructed from the reference of these previous three objects, the Transform, the CameraLayout, and the PixelGroup.

Camera camFronTop = Camera(&camFronTopTransform, &cameraLayout, &camFronTopPixels);

Writing the Display Function

The display function writes the RGB color data within the Pixel Group to the display hardware. This function completely depends on the display hardware you are using. For instance, if you are using a Teensy microcontroller with the Kaiborg V1.1 controller, your display function would look as follows:

//Include necessary hardware library requirements
const int ledsPerStrip = 346;
DMAMEM int displayMemory[346 * 6];
int drawingMemory[346 * 6];
const int config = WS2811_GRB | WS2811_800kHz;

OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);

//Override display function
void Display() override {
        for (int i = 0; i < 571; i++){
            camLeftPixels.GetPixel(i)->Color = camLeftPixels.GetPixel(i)->Color.Scale(maxBrightness);
            camRghtPixels.GetPixel(i)->Color = camRghtPixels.GetPixel(i)->Color.Scale(maxBrightness);

        }

        for (int i = 0; i < 571; i++) {
            if (i < 346){
                leds.setPixel(i + 346 * 2, camLeftPixels.GetPixel(i + 225)->Color.R, camLeftPixels.GetPixel(i + 225)->Color.G, camLeftPixels.GetPixel(i + 225)->Color.B);//Pin 7
                leds.setPixel(i + 346 * 7, camRghtPixels.GetPixel(i)->Color.R, camRghtPixels.GetPixel(i)->Color.G, camRghtPixels.GetPixel(i)->Color.B);//Pin 8
            }
            else{
                leds.setPixel(i + 346 * 2, camLeftPixels.GetPixel(i - 346)->Color.R, camLeftPixels.GetPixel(i - 346)->Color.G, camLeftPixels.GetPixel(i - 346)->Color.B);//Pin 8
                leds.setPixel(i + 346 * 5, camRghtPixels.GetPixel(i)->Color.R, camRghtPixels.GetPixel(i)->Color.G, camRghtPixels.GetPixel(i)->Color.B);//Pin 8
            }
        }

        leds.show();
    }

As you can see, this function iterates through each RGB color provided by the camera and writes it to the hardware that accepts RGB data in a specific ordering. This will depend on how your hardware is set up as far as communication and data structuring.

Conclusion

This controller will allow you to render and display your cameras passing in a Scene object with the following code:

Controller* controller = new KaiborgV1Controller(maxBrightness);

controller->Render(animation.GetScene());

controller->Display();
Clone this wiki locally