The 16x16x16 cube is done....ish Now time to map. Photos, video, and mapping questions

OK - I really did a lot of mental war with the diagram and I think I have it. Can you tell me if these annotations are correct?




Understanding Firestorm's role

Firestorm doesn’t know anything about:

  1. How many LEDs are driven by each pixelblaze
  2. The map loaded on each
  3. The code that makes up any given patterns on any given Pixelblaze

Firestorm just knows:

  1. What Pixelblazes it can see on a WiFi network
  2. The list of patterns (by name, not code) on each of those Pixelblazes

And it can:

  1. Launch all patterns of a given name at once, across all the Pixelblazes it discovers
  2. Arrange those pattern names into a playlist and launch them automatically in sequence
  3. Make sure that whatever code is running in each Pixelblaze, the output of time(K) will be synchronized between them all.

So with that as background, to answer your questions:

(Apr '23 edit: Now you should use the new Sync feature instead of Firestorm.)

Firestorm will synchronize which pattern is selected as active on all of the Pixelblazes, as well as the value that time() returns in those patterns. The maps can be different, and probably should be different, as each Pixelblaze will have different coordinates for the phantom pixels that nudge it’s towers into the 4 corners (as viewed from above).

Firestorm will only select one pattern for all four to run.

For most volumetric patterns, the code will be identical on each of the 4 Pixelblazes, but because the map specifies the LEDs for each Pixelblaze as having different coordinates, they will be crunching different pixel positions with the same code. By “volumetric” I mean patterns which use 3D renderers (they have a render3D function).

Depending on what you’re trying to achieve, the pattern code might need to be slightly modified on each of the four Pixelblaze. For patterns that only have a 1D renderer (render(index) in the examples), you’ll see four identical copies running. For something like Blinkfade, maybe that symmetry is distracting so you might modify each copy of the pattern code to use a slightly different random seed.

Given my refined understanding of your layout / routing, it’s more like you’ll need to add one phantom pixels to each Pixelblaze’s map.

As I understand it, each of the four Pixelblaze will be driving four towers. The map on one Pixelblaze will generate the pixel positions for all 512 LEDs in the four towers it’s driving.

I’m assuming you’ve read the mapping page and understand the global 0-to-1 normalized space that comes from a map and is fed into patterns’ renderers.

You’ll add one phantom pixels to a map that essentially squeezes and shifts that set of four towers into one quarter of the global (x, y, z) / (0-1, 0-1, 0-1) space.

Using the coordinates I defined in the annotations above, consider the map for PB1. You’ll add a phantom pixel around (1, 0, 1) (in normalized 0-1 global space), and that will shove the entire resulting co-ordinates for all PB1’s LEDs into the top-left quadrant as viewed from above. I know you might need to read that a few times. This shifted/squeezed map will result in normalized LED coordinates crunched by that Pixelblaze that only fall within (0-.5, 0-1. 0-.5). PB2 will only process it’s LED coordinates where (x, y, z) is within (.5-1, 0-1, 0-.5).

Phew, that’s a lot. I hope this helps!

2 Likes

Thank you! This helps a TON. I have read the mapping page and between that, this thread, and also the using the completed code from my 8x8x8, I’m pretty sure I got it handled now or at least eventually figure it out! The relationship between the PBs and the Firestorm was definitely what was giving me the most trouble. I had read the page which helped but in regards to mapping it was just a jumble in my brain.

Thank you for all the help Jeff! Will post some videos hopefully next post!

@jeff Hello! I’m working with Shakes on the mapping and programming side of this project IRL. I’m a bit new to mapping but not to programming, and I could use some guidance at some point in the relatively near future. I know the holidays are a rotten time for that, if/when you’ve got the bandwidth I would love to pick your brain somewhat. I’m testing parts of the 4-column system this week and weekend so that I can get my brain wrapped around what’s already been done.

Sure! I prefer to help in public if possible so that the other forum members can learn something and we build this up as a searchable support resource, so after you’ve read the stuff above and mapping docs, as well as tried it out a bit in the mapper tab, post here when you get stuck! I’ll try my best (holiday schedule allowing).

1 Like

Copy that, thank you! Wasn’t sure if maintaining the thread with new questions was appropriate, appreciate the prompt response :smiley:

@jeff SO! Back again! And was able to find someone to get this successfully mapped individually! So now I have the 4 of them working but now comes the part of getting them working together. So the person who mapped it used this code

function (pixelCount) {
  var mapx1=[0,1,2,3,3,2,1,0,0,1,2,3,3,2,1,0]
  var mapx2=[0,4,0,4,8,12,8,12,0,4,0,4,8,12,8,12]
  var mapy1=[3,3,3,3,2,2,2,2,1,1,1,1,0,0,0,0]
  var mapy2=[0,0,4,4,0,0,4,4,8,8,12,12,8,8,12,12]
  var mapz=[]
  for(var z=0;z<16;z++)  mapz.push(z/15)
  for(var z=15;z>=0;z--)  mapz.push(z/15)
  var map = []
  for (i = 0; i < pixelCount; i++) {
    n=Math.floor(i/16)+0  // pb1:0 pb2:64 pb3:128 pb4:192
    t1=n%16
    t2=Math.floor(n/16)
    z=mapz[i%32]
    y=mapz[mapy1[t1]+mapy2[t2]];
    x=mapz[mapx1[t1]+mapx2[t2]];
    map.push([x, y, z])
  }
  return map
} '

So the comment added in there was in theory supposed to offset them for that effect but it’s not producing any results (I did apply it to each individual PB with it’s respective number). So looking at his particular mapping, how would I go about changing this? I know Phantom PIxels are a thing but I’m not particularly sure how I would apply it under this setup.

Gettin closer!

Nevermind! And it’s done! This thread is officially finished and I can quit bothering everyone! Here’s the completed code with how the phantom pixel was applied!

function (pixelCount) {
  var mapx1=[0,1,2,3,3,2,1,0,0,1,2,3,3,2,1,0]
  var mapy1=[3,3,3,3,2,2,2,2,1,1,1,1,0,0,0,0]
  var mapx2=[0,4,0,4,8,12,8,12,0,4,0,4,8,12,8,12]
  var mapy2=[0,0,4,4,0,0,4,4,8,8,12,12,8,8,12,12]
  var mapz=[]
  for(var z=0;z<16;z++)  mapz.push(z/15)
  for(var z=15;z>=0;z--)  mapz.push(z/15)
  var map = []
  for (i = 0; i < (pixelCount-2); i++) {
    n=Math.floor(i/16)+0  // pb1:0 pb2:64 pb3:128 pb4:192
    t1=n%16
    t2=Math.floor(n/16)
    x=mapz[mapx1[t1]+mapx2[t2]];
    y=mapz[mapy1[t1]+mapy2[t2]];
    z=mapz[i%32]
    map.push([x, y, z])
  }
  map.push([0,0,0])
  map.push([1,1,1])
  return map
}
5 Likes

Wow! Congratulations! It’s totally gorgeous. I can’t imagine how you feel after SO many hours invested of soldering and construction.

1 Like

@Shakes999, amazing. Simply amazing. So much work into that, and what gorgeous results! I want to experiment with all kinds of 3D patterns on your cube now.

1 Like

You and me both!!! The 3D patterns are incredible on it! Infact, the only patterns I’ve found that don’t work are Coronal Mass inJection and Perlin fire which is weird but everything else looks great (They both work on the 1st section of the cube but crashes the other 3. It’s weird but I’ll figure it out after the burn)

I plan on finding a better video solution so I can get some decent videos of it. Everything just looks like a blob of light on camera phones. Example

1 Like

@Shakes999, if you get time, give this one a try and see if it works with all the mapping. It draws a spinning cube on your cube - just what you need, right? I’m really curious about how this sort of thing performs on your setup. (Which is completely epic, btw!)

Spinning Wireframe Cube Pattern
// Volumetric 3D wire-frame box!
//
// For volumetric displays only. Will probably not
// do the right thing on other display types!
//
// MIT License
// ZRanger1 1/2023


export var boxSize = 0.3;
export var edgeWidth = 0.0375;


// UI
export function sliderSize(v) {
  boxSize = 0.1+(v*v/4);
} 

export function sliderEdgeWidth(v) {
  edgeWidth = mix(0.025,0.08,v*v); 
}


// vector utilities
function max3(x,y,z) {
  return max(max(x,y),z);
}

function min3(x,y,z) {
  return min(min(x,y),z); 
}

// from https://iquilezles.org/articles/distfunctions/
// Returns signed distance to the edges of a box of size sz,
// with edge width e.
function wireframeBox( x,y,z,sz,e){
       px = abs(x)-sz;
       py = abs(y)-sz;
       pz = abs(z)-sz;
       
       qx = abs(px+e) - e;
       qy = abs(py+e) - e;
       qz = abs(pz+e) - e;   
       
       k1 = max3(px,qy,qz);  
       k2 = max3(qx,py,qz);
       k3 = max3(qx,qy,pz);
            
  return min(min(
      hypot(k1,0.0)+min(k1,0.0), 
      hypot(k2,0.0)+min(k2,0.0)),
      hypot(k3,0.0)+min(k3,0.0));
}

// tumble the box around a little
timebase = 0;
export function beforeRender(delta) {
  timebase = (timebase + delta /1000) % 3600;
  
  resetTransform();
  translate3D(-0.5,-0.5,-0.5);  
  rotateZ(timebase * 2);
  rotateX(timebase / 2); 
}

// I'm slightly cheesing the edge calculation here to gain a tiny
// bit of performance.Better math would have better anti-aliasing, but
// the resolution is so low that I think it looked better. YMMV.
export function render3D(index,x,y,z) {
  h = timebase + index/pixelCount
  hsv(h, 1, 1-wireframeBox(x,y,z,boxSize,edgeWidth)/edgeWidth)  
}

3D is easier to think about on a volumetric display like this than it is on a 2D screen - no ray projection to find the geometry, no perspective transform, no clipping planes, just, “If this pixel is part of the thing I’m drawing, light it up!”

Anyway, here’s a video of what it ought to look like, in this case using a single PB to drive a 10x10x10 cube.

3 Likes

Oh hell I’m gonna load that one up on land! We at least got hot spot out there. I got around 5-6 3D patterns but more are always welcome! I’ll shoot a video over tomorrow night when I get it all setup!

1 Like

I’m gonna think about how this could be antialiased!

1 Like

So i finally got the cube repaired after 2, 3 hour car rides, a well meaning friend “booping” it and frying 3 planes, and other assorted repairs.

And now im trouble shooting patterns! So thats zrangers pattern! Works fine and looks great!

However im running in to issues with 2 patterns and only these 2. Perlin fire, Coronal Mass Ejection. They work only on the first PB and it freezes PB 2-4. 2-4 are giving me a undefined symbol error despite it being the exact same code for PB 1 where it works.


Any thoughts?

Also will post some more videos!

Sinpulse 3D, Spiral twirls 3D, and Xorcery 3D!



2 Likes

setPerlinWrap wasn’t added to the language until version 3.30, so PBs 2 through 4 probably need to be updated to the latest firmware. Look for the “Updates” section on the Settings tab:

3 Likes

Omg DUH!!! Thank you!!!

@Shakes999 - Did you see the new Sync feature? I think you’ll definitely want to look into it, since you can run your entire cube without a separate computer running Firestorm, and you won’t need to clone your patterns to each of the four Pixelblazes. Plus, you can now run sound-reactive patterns across all four without hacks.

1 Like

And live code new patterns!

1 Like

Omg perfect timing! I’ve been having to lug around a laptop and a router to make a closed network. I’ll give it a shot!

2 Likes