Hello.
For this part, just use something like:
saveFrame(coolname-####.jpg);
to get files like
coolname-0001.jpg, coolname-0002.jpg
Saving is time consuming. I’ve dealt with this kind of problems in different ways.
- try
.tif
or .targa
. They are faster.
if that’s not enough
- Save images to a list and at exiting the app, traverse the list saving the files.
if that’s not eough
- save all the parameters (probably a class to store the data together) and store them in a list. When ready retrive each frame’s parameters and save them. It works like a “render”
just did that in some code, but its a working uncommented code, if you wanna have a look…
here:
There is a lot of controls.
1 - toogle rec
2 - toogle replay
3 - toogle save (you can replay without saving)
Any movement is recorded and replayed/saved.
press 1 → move stuff → press 1 to stop recording → press 3 (or not) → press 2(replay/save)
Again a messy wip code… with dependencies. But you don’t need to run it, just to get the idea. look at draw and the extra classes. But if you want, it’s in test mode so it won’t try to load anything you don’t have : ) .
the main tab:
import nub.primitives.*;
import nub.core.*;
import nub.processing.*;
// Scene object for managing the 3D environment
Scene scene;
// Node objects for scene hierarchy
Node master, caixa;
// Canvas dimensions
int canvas_width = 1200;
int canvas_height = 810;
// Zoom factor for mouse wheel interaction
float zoom_factor = 2;
// Rotation offsets and speeds
float x_offset;
float y_offset;
float z_offset;
// Rotation of master
float x_speed;
float y_speed;
float z_speed;
// Orbit speeds around axes
float x_orbit_speed;
float y_orbit_speed;
float z_orbit_speed;
float offset_inc = 0.05;
float inc = 0.001;
// Speed variable for forward movement
float trav_speed;
// Translation variables
float trans_x, trans_y, trans_z;
// Terrain shape
PShape terrain_shape;
// Flags
boolean record = false, play = false, show_grid = true, test = true;
// Background color
color background_color = color(0);
boolean just_initialized = true;
boolean printout = false;
boolean first_run = true;
String name = "";
// Player object for managing scene interaction
Player player;
Player initial;
ArrayList<ArrayList<State>> states;
void settings() {
// Set canvas size with P3D renderer
size(canvas_width, canvas_height, P3D);
}
void setup() {
// Instantiate nub objects
scene = new Scene(this, 1000);
master = new Node();
caixa = new Node(master);
// Load terrain shape
if (test) {
// Create a test box shape
terrain_shape = createShape(BOX, 100, 50, 300);
} else {
// Load 3D model
terrain_shape = loadShape("Mariana_simplified_3d_mesh.obj");
// Flip the model
terrain_shape.scale(1.0, -1, 1);
}
// Set the shape for the caixa node
caixa.setShape(terrain_shape);
caixa.align(master);
// Initialize player object
player = new Player(scene.eye(), master, caixa);
initial = new Player(scene.eye(), master, caixa);
initial.rec();
println(scene.eye());
}
void draw() {
smooth();
// Set background color
// background_color = printout? color(0): color(255);
background(background_color);
// Rotate master node
master.rotate(1, 0, 0, x_speed);
master.rotate(0, 1, 0, y_speed);
master.rotate(0, 0, 1, z_speed);
// Orbit around axes
scene.eye().orbit(Vector.plusK, x_orbit_speed, 0.5);
scene.eye().orbit(Vector.plusI, y_orbit_speed, 0.5);
scene.eye().orbit(Vector.plusJ, z_orbit_speed, 0.5);
// Translate eye
scene.eye().translate(trans_x, trans_y, trans_z, 0.5);
// Move scene forward
scene.moveForward(trav_speed);
stroke(120);
// Toggle grid and axes visibility
if (show_grid) {
scene.drawGrid();
scene.drawAxes();
caixa.enableHint(Node.AXES);
} else {
caixa.disableHint(Node.AXES);
}
// Render the scene
scene.render();
if (record) {
name =
String.valueOf(year()) +
String.valueOf(month())+
String.valueOf(day()) +
String.valueOf(hour()) +
String.valueOf(minute());
play = false;
player.rec();
}
if (play) {
if (just_initialized) {
reset();
}
just_initialized = false;
record = false;
if (player.hasNext()) {
player.play();
if (printout) {
saveFrame(name+"/pr########.tif");
}
} else {
player.clear();
just_initialized = true;
play = false;
printout = false;
exit();
}
}
if(first_run){
initial.rec();
first_run =false;
}
}
void mouseDragged() {
if (mouseButton == LEFT)
scene.spin();
else if (mouseButton == RIGHT)
scene.shift();
}
void mouseWheel(MouseEvent event) {
scene.moveForward(event.getCount() * zoom_factor);
}
// Handle key presses
void keyPressed() {
// Switch statement to handle different keys
switch(key) {
// Start/stop recording
case '1':
case '!':
record = !record;
println("Recording: " + record);
break;
// Play recorded actions
case '2':
case '@':
play = !play;
println("Playing: " + play);
break;
// Decrease Z-axis rotation speed
case 'a':
z_speed -= inc;
println("Z speed: " + z_speed);
break;
// Decrease Z-axis rotation speed by half
case 'A':
z_speed -= inc * 2;
println("Z speed: " + z_speed);
break;
// Increase Z-axis rotation speed
case 'z':
z_speed += inc;
println("Z speed: " + z_speed);
break;
// Increase Z-axis rotation speed by half
case 'Z':
z_speed += inc * 2;
println("Z speed: " + z_speed);
break;
// Decrease X-axis rotation speed
case 's':
x_speed -= inc;
println("X speed: " + x_speed);
break;
// Decrease X-axis rotation speed by half
case 'S':
x_speed -= inc * 2;
println("X speed: " + x_speed);
break;
// Increase X-axis rotation speed
case 'x':
x_speed += inc;
println("X speed: " + x_speed);
break;
// Increase X-axis rotation speed by half
case 'X':
x_speed += inc * 2;
println("X speed: " + x_speed);
break;
// Decrease Y-axis rotation speed
case 'c':
y_speed -= inc;
println("Y speed: " + y_speed);
break;
// Decrease Y-axis rotation speed by half
case 'C':
y_speed -= inc * 2;
println("Y speed: " + y_speed);
break;
// Increase Y-axis rotation speed
case 'd':
y_speed += inc;
println("Y speed: " + y_speed);
break;
// Increase Y-axis rotation speed by half
case 'D':
y_speed += inc * 2;
println("Y speed: " + y_speed);
break;
// Reset Z-axis rotation speed
case 'q':
case 'Q':
z_speed = 0;
println("Z speed: " + z_speed);
break;
// Reset X-axis rotation speed
case 'w':
case 'W':
x_speed = 0;
println("X speed: " + x_speed);
break;
// Reset Y-axis rotation speed
case 'e':
case 'E':
y_speed = 0;
println("Y speed: " + y_speed);
break;
// Reset forward movement speed
case 'p':
trav_speed = 0;
println("Traversal speed: " + trav_speed);
break;
// Reset forward movement speed by half
case 'P':
trav_speed = 0;
println("Traversal speed: " + trav_speed);
break;
// Increase forward movement speed
case 'i':
trav_speed += inc;
println("Traversal speed: " + trav_speed);
break;
// Increase forward movement speed by half
case 'I':
trav_speed += inc * 2;
println("Traversal speed: " + trav_speed);
break;
// Decrease forward movement speed
case 'o':
trav_speed -= inc;
println("Traversal speed: " + trav_speed);
break;
// Decrease forward movement speed by half
case 'O':
trav_speed -= inc * 2;
println("Traversal speed: " + trav_speed);
break;
// Translate caixa along X-axis (right)
case '.':
case '>':
caixa.translate(new Vector(5, 0, 0));
println(caixa.worldPosition());
break;
// Translate caixa along X-axis (left)
case ',':
case '<':
caixa.translate(new Vector(-5, 0, 0));
println(caixa.worldPosition());
break;
// Translate caixa along Y-axis (up)
case '/':
caixa.translate(new Vector(0, 5, 0));
println(caixa.worldPosition());
break;
case '?':
trans_x = trans_y = trans_z = 0;
println(caixa.worldPosition());
break;
// Translate caixa along Y-axis (down)
case '\'':
case '\"':
caixa.translate(new Vector(0, -5, 0));
println(caixa.worldPosition());
break;
// Toggle grid visibility
case '9':
case '(':
show_grid = !show_grid;
println("Grid visibility: " + show_grid);
break;
// Translate caixa along Z-axis (forward)
case '[':
case '{':
caixa.translate(new Vector(0, 0, 5));
println(caixa.worldPosition());
break;
// Translate caixa along Z-axis (backward)
case ']':
case '}':
caixa.translate(new Vector(0, 0, -5));
println(caixa.worldPosition());
break;
// Reset all speeds and positions
case '\\':
case '|':
reset();
break;
case 'j':
caixa.rotate(0, 0, 1, -1 * offset_inc);
break;
case 'J':
caixa.rotate(0, 1, 0, offset_inc);
break;
case 'l':
caixa.rotate(0, 0, 1, offset_inc);
break;
case 'L':
caixa.rotate(0, 1, 0, -offset_inc);
break;
case 'k':
caixa.rotate(1, 0, 0, offset_inc);
break;
case 'm':
caixa.rotate(1, 0, 0, -offset_inc);
break;
// Handle arrow key presses
case CODED:
switch(keyCode) {
// Arrow key RIGHT to increase trans_x
case RIGHT:
trans_x += inc * 2 ;
println("Translate X: " + trans_x);
break;
// Arrow key LEFT to decrease trans_x
case LEFT:
trans_x -= inc * 2;
println("Translate X: " + trans_x);
break;
// Arrow key UP to increase trans_y
case UP:
trans_y += inc * 2;
println("Translate Y: " + trans_y);
break;
// Arrow key DOWN to decrease trans_y
case DOWN:
trans_y -= inc * 2;
println("Translate Y: " + trans_y);
break;
}
break;
// Additional key bindings for fine-tuning speed
// 'v' - Increase Z-axis
// rotation speed by 0.001 units
case 'v':
z_speed += inc;
println("Z speed: " + z_speed);
break;
// 'b' - Increase X-axis rotation speed by 0.001 units
case 'b':
x_speed += inc;
println("X speed: " + x_speed);
break;
// 'n' - Increase Y-axis rotation speed by 0.001 units
case 'n':
y_speed += inc;
println("Y speed: " + y_speed);
break;
// 'f' - Decrease Z-axis rotation speed by 0.001 units
case 'f':
z_speed -= inc;
println("Z speed: " + z_speed);
break;
// 'g' - Decrease X-axis rotation speed by 0.001 units
case 'g':
x_speed -= inc;
println("X speed: " + x_speed);
break;
// 'h' - Decrease Y-axis rotation speed by 0.001 units
case 'h':
y_speed -= inc;
println("Y speed: " + y_speed);
break;
// 'r' - Reset Z-axis rotation speed to 0
case 'r':
z_speed = 0;
println("Z speed: " + z_speed);
break;
// 't' - Reset X-axis rotation speed to 0
case 't':
x_speed = 0;
println("X speed: " + x_speed);
break;
// 'y' - Reset Y-axis rotation speed to 0
case 'y':
y_speed = 0;
println("Y speed: " + y_speed);
break;
case '3':
printout = !printout;
println("Printing is: " + printout);
default:
break;
}
}
void reset() {
// Reset rotation speeds
z_speed = 0;
x_speed = 0;
y_speed = 0;
// Reset orbit speeds
z_orbit_speed = 0;
x_orbit_speed = 0;
y_orbit_speed = 0;
// Reset traversal speed
trav_speed = 0;
// Reset translation variables
trans_x = 0;
trans_y = 0;
trans_z = 0;
// Reset scene eye position
//scene.eye().reset();
//scene.eye().setMagnitude(0.57735026);
//scene.eye().setPosition(new Vector(0, 0, 2000));
initial.play();
println(scene.eye());
}
State is a data holder
class State {
Vector worldPosition;
Quaternion worldOrientation;
float worldMagnitude;
Vector xAxis;
Vector yAxis;
Vector zAxis;
State(Vector worldPosition, Quaternion worldOrientation, float worldMagnitude,
Vector xAxis, Vector yAxis, Vector zAxis) {
this.worldPosition = worldPosition;
this.worldOrientation = worldOrientation;
this.worldMagnitude = worldMagnitude;
this.xAxis = xAxis;
this.yAxis = yAxis;
this.zAxis = zAxis;
}
State(Node node) {
this.worldPosition = node.worldPosition();
this.worldOrientation = node.worldOrientation();
this.worldMagnitude = node.worldMagnitude();
this.xAxis = node.xAxis();
this.yAxis = node.yAxis();
this.zAxis = node.zAxis();
}
// Set Node properties with State values
public void setNodeState(Node node) {
node.setWorldPosition(worldPosition);
node.setWorldOrientation(worldOrientation);
node.setWorldMagnitude(worldMagnitude);
// Assuming there are methods in Node class to set xAxis, yAxis, and zAxis
node.setXAxis(xAxis);
node.setYAxis(yAxis);
node.setZAxis(zAxis);
}
}
and Player is the recplay
class Player {
ArrayList<Node> nodes;
ArrayList<ArrayList<State>> states;
// Constructor taking undefined number of nodes
Player(Node... nodes) {
this.nodes = new ArrayList<Node>();
this.states = new ArrayList<ArrayList<State>>();
for (Node node : nodes) {
addNode(node);
}
}
// Default constructor
Player() {
this.nodes = new ArrayList<Node>();
this.states = new ArrayList<ArrayList<State>>();
}
void addNode(Node node) {
nodes.add(node);
states.add(new ArrayList<State>());
}
void rec() {
for (int i = 0; i < nodes.size(); i++) {
states.get(i).add(new State(nodes.get(i)));
}
//println("State recorded for all nodes");
}
void play() {
if (states.isEmpty()) {
println("No nodes added for state management");
return;
}
for (int i = 0; i < nodes.size(); i++) {
if (states.get(i).isEmpty()) {
println("No recorded states available for node " + (i + 1));
} else {
State state = states.get(i).remove(0);
state.setNodeState(nodes.get(i));
//println("Playbacked state for node " + (i + 1));
}
}
}
void clear() {
nodes.clear();
states.clear();
println("All lists cleared");
}
boolean hasNext() {
return this.states.size() > 0;
}
}
The whole thing is a bit overcomplicated, can be done in a simpler way depending of your data and etc.
The player class specially makes things a bit convoluted. Dealing with the lists direct in draw is more straightforward.
cheers