-
-
Notifications
You must be signed in to change notification settings - Fork 43
Creating a custom controller
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
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.
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.
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"
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);
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));
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);
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);
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.
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();
Any recommendations on additional information to add can be requested in the discussions tab. If you have additional questions, you can @ me in my Discord server or on direct message me on Twitter.