…man is it hard to select and copy things on the forums these days!
The last thing I think the editor needs is a nudge function.
I need a function to bump the entire Voxel model one block in any direction, with the wrinkle that if the model goes past a grid boundary it wraps around to the other side (demonstration image attached).
Can anyone (@dave1707 maybe? ) help me with a function that does this?
@UberGoober Just use different for loops depending on the direction you want to move. If a move exceeds the limit of the move, move it to the other side. You just need to know the beginning x,y,z positions of each block, add or subtract 1 from x or y or z to reposition the block in the direction you want.
@dave1707 I get the concepts involved, I just don’t know how to actually do it.
I know I could fumble and stumble and figure it out on my own, but the whole reason this thread’s title is a plea for help is so hopefully we can pool our expertise to more quickly get this thing in shape to replace the official Voxel Editor.
So if you wouldn’t mind helping out with some sample code on how to do this I’d really appreciate it.
@UberGoober Heres a quick example. Move the parameters to select a movement then tap move.
PS. I only added code to wrap the y value. Set the y parameter to +1 and keep tapping move. Eventually it will wrap.
viewer.mode=STANDARD
function setup()
parameter.integer("xm",-1,1,0)
parameter.integer("ym",-1,1,0)
parameter.integer("zm",-1,1,0)
parameter.action("move",move)
tab={}
fill(255)
assert(OrbitViewer, "Please include Cameras as a dependency")
scene = craft.scene()
skyMaterial=scene.sky.material
skyMaterial.sky=color(0, 62, 255, 255)
skyMaterial.horizon=color(99, 255, 0, 255)
scene.sun.rotation=quat.eulerAngles(20,45,-30)
v=scene.camera:add(OrbitViewer, vec3(0,0,0), 100, 0, 200)
v.rx,v.ry=30,30
map = readImage(asset.builtin.Surfaces.Basic_Bricks_AO)
-- create some random cubes to move
for x=1,10 do
for y=1,10 do
for z=1,10 do
if math.random(10)<3 then
createRect(x,y,z)
end
end
end
end
end
function draw()
update(DeltaTime)
scene:draw()
text("make choice then tap move parameter",WIDTH/2,HEIGHT-100)
end
function update(dt)
scene:update(dt)
end
function createRect(x,y,z) -- create cubes at random positions
local c1 = scene:entity()
c1.position=vec3(x,y,z)
c1.model = craft.model.cube(vec3(1,1,1))
c1.material = craft.material(asset.builtin.Materials.Specular)
c1.material.map = map
table.insert(tab,c1)
end
function move() -- move in selected direction
for a,b in pairs(tab) do
b.position=b.position+vec3(xm,ym,zm)
-- only code for the y value wraps the cubes
if b.position.y>20 then
p=b.position
p.y=1
b.position=p
end
end
end
@dave1707 the problem is that Voxel Editor isn’t creating entities, it’s creating Voxel blocks, and also that it doesn’t keep a separate table of every block.
This is my attempt to move just the y value upward for every block in the volume:
function move() -- move in selected direction
local sizeX, sizeY, sizeZ = G.volume:size()
for z = 0, sizeZ - 1 do
for y = 0, sizeY - 1 do
for x = 0, sizeX - 1 do
local id = G.volume:get(vec3(x, y, z), BLOCK_ID)
local newVec = vec3(x, y, z) + vec3(0, 1, 0)
if newVec.y > sizeY - 1 then
newVec.y = 1
end
G.volume:set(newVec, BLOCK_ID)
end
end
end
end
…and the result of this code is that all blocks disappear except the ones with y values of 0. So I’m doing something very wrong here.
So, this works, but it’s only for up and down, so to cover all directions would require repeating all this code two more times, and it seems like there has to be a better way:
function colorFromInt(int)
local r = (int>>24) & 255
local g = (int>>16) & 255
local b = (int>>8) & 255
return color(r, g, b)
end
function move(up)
local sizeX, sizeY, sizeZ = volume:size()
local volumeTable = {}
if not up then
for z = 0, sizeZ - 1 do
for y = 0, sizeY - 1 do
for x = 0, sizeX - 1 do
local blockTable
local id = volume:get(vec3(x, y, z), BLOCK_ID)
local newVec = vec3(x, y, z) - vec3(0, 1, 0)
if y == 0 then
newVec.y = sizeY - 1
end
local colorInt = volume:get(x, y, z, BLOCK_STATE)
if colorInt ~= 0 then
blockTable = {newVec, BLOCK_ID, id, "color", colorFromInt(colorInt)}
else
blockTable = {newVec, BLOCK_ID, id}
end
table.insert(volumeTable, blockTable)
end
end
end
else
for z = sizeZ - 1, 0, -1 do
for y = sizeY - 1, 0, -1 do
for x = sizeX - 1, 0, -1 do
local blockTable
local id = volume:get(vec3(x, y, z), BLOCK_ID)
local newVec = vec3(x, y, z) + vec3(0, 1, 0)
if newVec.y == sizeY then
newVec.y = 0
end
local colorInt = volume:get(x, y, z, BLOCK_STATE)
if colorInt then
blockTable = {newVec, BLOCK_ID, id, "color", colorFromInt(colorInt)}
else
blockTable = {newVec, BLOCK_ID, id}
end
table.insert(volumeTable, blockTable)
end
end
end
end
for i, blockTable in ipairs(volumeTable) do
volume:set(table.unpack(blockTable))
end
end
If you want to see it in action, a version of the editor that implements it is attached; just use the “nudge” and “nudge up” buttons at the top of the overlay panel.
@UberGoober Is this closer.
viewer.mode=STANDARD
function setup()
parameter.action("X+", function() move(1,0,0) end )
parameter.action("X-", function() move(-1,0,0) end )
parameter.action("Y+", function() move(0,1,0) end )
parameter.action("Y-", function() move(0,-1,0) end )
parameter.action("Z+", function() move(0,0,1) end )
parameter.action("Z-", function() move(0,0,-1) end )
assert(OrbitViewer, "Please include Cameras as a dependency")
scene = craft.scene()
v=scene.camera:add(OrbitViewer,vec3(5,5,0), 100, 0, 2000)
v.rx=30
v.ry=30
scene.voxels.blocks:addAssetPack("Blocks")
snow = scene.voxels.blocks:new("Snow")
snow.setTexture(ALL, "Blocks:Snow")
scene.voxels:resize(vec3(10,1,10))
scene.voxels.coordinates = vec3(0,0,0)
-- create a random voxel group
scene.voxels:fill("Snow")
for x=3,10 do
for y=3,10 do
for z=3,10 do
if math.random(10)>9 then
scene.voxels:block(x,y,z)
end
end
end
end
end
function update(dt)
scene:update(dt)
end
function draw()
update(DeltaTime)
scene:draw()
end
function xx()
-- read the voxel area and save any snow-ID x,y,z positions
-- clear the blocks with EMPTY
tab={}
for x=1,15 do
for y=1,15 do
for z=1,15 do
-- get the name of the voxel at x,y,z
name=scene.voxels:get(vec3(x,y,z),BLOCK_NAME)
if name=="Snow" then
-- putx,y,z values in a table for later
table.insert(tab,vec3(x,y,z))
-- set voxel block to EMPTY
scene.voxels:fill(EMPTY)
scene.voxels:block(x,y,z)
end
end
end
end
end
function move(x,y,z)
xx()
for a,b in pairs(tab) do
scene.voxels:fill("Snow")
if b.x+x>15 then
scene.voxels:block(1,b.y,b.z)
elseif b.y+y>15 then
scene.voxels:block(b.x+x,1,b.z)
elseif b.z+z>15 then
scene.voxels:block(b.x+x,b.y,1)
elseif b.x+x<1 then
scene.voxels:block(15,b.y,b.z)
elseif b.y+y<1 then
scene.voxels:block(b.x+x,15,b.z)
elseif b.z+z<1 then
scene.voxels:block(b.x+x,b.y,15)
else
scene.voxels:block(b.x+x,b.y+y,b.z+z)
end
end
end
@dave1707 that’s great!
I tweaked it to move any kind of block:
viewer.mode=STANDARD
function setup()
assert(OrbitViewer, "Please include Cameras as a dependency")
scene = craft.scene()
skyMaterial=scene.sky.material
skyMaterial.sky=color(163, 178, 223)
skyMaterial.horizon=color(195, 224, 177)
scene.sun.rotation=quat.eulerAngles(20,45,-30)
v=scene.camera:add(OrbitViewer, vec3(5, 8, 0), 10, 0, 200)
--v.rx,v.ry=30,30
map = readImage(asset.builtin.Blocks.Redstone_Emerald_Alt)
nudgeTimer = 0
parameter.integer("nudgeX", -1, 1, 0)
parameter.integer("nudgeY", -1, 1, 0)
parameter.integer("nudgeZ", -1, 1, 0)
scene.voxels.blocks:addAssetPack("Blocks")
snow = scene.voxels.blocks:new("Snow")
snow.setTexture(ALL, "Blocks:Stone Gold")
scene.voxels:resize(vec3(10,1,10))
scene.voxels.coordinates = vec3(0,0,0)
-- create a random voxel group
scene.voxels:fill("Snow")
for x=3,10 do
for y=3,10 do
for z=3,10 do
if math.random(10)>9 then
scene.voxels:block(x,y,z)
end
end
end
end
end
function nudgeCheck()
if nudgeTimer == 0 then
if nudgeX ~= 0 then
move(nudgeX, 0, 0)
nudgeTimer = ElapsedTime
end
if nudgeY ~= 0 then
move(0, nudgeY, 0)
nudgeTimer = ElapsedTime
end
if nudgeZ ~= 0 then
move(0, 0, nudgeZ)
nudgeTimer = ElapsedTime
end
elseif ElapsedTime - nudgeTimer > 0.25 then
nudgeX, nudgeY, nudgeZ = 0, 0, 0
nudgeTimer = 0
end
end
function update(dt)
scene:update(dt)
nudgeCheck()
end
function draw()
update(DeltaTime)
scene:draw()
end
function xx()
-- read the voxel area and save any snow-ID x,y,z positions
-- clear the blocks with EMPTY
tab={}
for x=1,15 do
for y=1,15 do
for z=1,15 do
-- get the name of the voxel at x,y,z
name=scene.voxels:get(vec3(x,y,z),BLOCK_NAME)
-- put x,y,z and name in a table for later
table.insert(tab,{pos=vec3(x,y,z), name = name})
-- set voxel block to EMPTY
scene.voxels:fill(EMPTY)
scene.voxels:block(x,y,z)
end
end
end
end
function move(x,y,z)
xx()
for a,b in pairs(tab) do
scene.voxels:fill(b.name)
b = b.pos
if b.x+x>15 then
scene.voxels:block(1,b.y,b.z)
elseif b.y+y>15 then
scene.voxels:block(b.x+x,1,b.z)
elseif b.z+z>15 then
scene.voxels:block(b.x+x,b.y,1)
elseif b.x+x<1 then
scene.voxels:block(15,b.y,b.z)
elseif b.y+y<1 then
scene.voxels:block(b.x+x,15,b.z)
elseif b.z+z<1 then
scene.voxels:block(b.x+x,b.y,15)
else
scene.voxels:block(b.x+x,b.y+y,b.z+z)
end
end
end
This version can move colored blocks, which will be the actual application of this code.
Apparently using get
with COLOR doesn’t work, which is why the colorFromInt
function is needed.
viewer.mode=OVERLAY
function setup()
assert(OrbitViewer, "Please include Cameras as a dependency")
scene = craft.scene()
skyMaterial=scene.sky.material
skyMaterial.sky=color(163, 178, 223)
skyMaterial.horizon=color(195, 224, 177)
scene.sun.rotation=quat.eulerAngles(20,45,-30)
v=scene.camera:add(OrbitViewer, vec3(5, 8, 0), 10, 0, 200)
--v.rx,v.ry=30,30
map = readImage(asset.builtin.Blocks.Redstone_Emerald_Alt)
nudgeTimer = 0
parameter.integer("nudgeX", -1, 1, 0)
parameter.integer("nudgeY", -1, 1, 0)
parameter.integer("nudgeZ", -1, 1, 0)
scene.voxels:resize(vec3(10,1,10))
scene.voxels.coordinates = vec3(0,0,0)
volume = scene:entity():add(craft.volume, 15, 15, 15)
-- create a random voxel group with random colors
for x=3,10 do
for y=3,10 do
for z=3,10 do
if math.random(10)>9 then
local randColor = color(math.random(255), math.random(255), math.random(255))
volume:set(x,y,z,COLOR,randColor,BLOCK_NAME,"Solid")
end
end
end
end
end
function nudgeCheck()
if nudgeTimer == 0 then
if nudgeX ~= 0 then
move(nudgeX, 0, 0)
nudgeTimer = ElapsedTime
end
if nudgeY ~= 0 then
move(0, nudgeY, 0)
nudgeTimer = ElapsedTime
end
if nudgeZ ~= 0 then
move(0, 0, nudgeZ)
nudgeTimer = ElapsedTime
end
elseif ElapsedTime - nudgeTimer > 0.25 then
nudgeX, nudgeY, nudgeZ = 0, 0, 0
nudgeTimer = 0
end
end
function update(dt)
scene:update(dt)
nudgeCheck()
end
function draw()
update(DeltaTime)
scene:draw()
end
function colorFromInt(int)
local r = (int>>24) & 255
local g = (int>>16) & 255
local b = (int>>8) & 255
return color(r, g, b)
end
function extractBlockData()
-- read the voxel area
-- save block data
-- clear blocks with EMPTY
tab={}
local vX, vY, vZ = volume:size()
vX, vY, vZ = vX - 1, vY - 1, vZ -1
for x=0, vX do
for y=0, vY do
for z=0, vZ do
-- get the name of the voxel at x,y,z
local name = volume:get(x,y,z,BLOCK_ID)
local pos = vec3(x, y, z)
local colorInt = volume:get(x, y, z, BLOCK_STATE)
local blockTable = {vec3(x,y,z), BLOCK_ID, name}
if colorInt ~= 0 then
table.insert(blockTable, COLOR)
table.insert(blockTable, colorFromInt(colorInt))
end
-- put x,y,z and name in a table for later
table.insert(tab,blockTable)
-- set voxel block to EMPTY
volume:set(x,y,z,BLOCK_NAME,"Empty")
end
end
end
end
function move(x,y,z)
extractBlockData()
local vX, vY, vZ = volume:size()
vX, vY, vZ = vX - 1, vY - 1, vZ -1
for a,b in pairs(tab) do
local pos = table.remove(b, 1)
if pos.x+x>vX then
pos = vec3(0,pos.y,pos.z)
elseif pos.y+y>vY then
pos = vec3(pos.x+x,0,pos.z)
elseif pos.z+z>vZ then
pos = vec3(pos.x+x,pos.y,0)
elseif pos.x+x<0 then
pos = vec3(vX, pos.y, pos.z)
elseif pos.y+y<0 then
pos = vec3(pos.x+x,vY,pos.z)
elseif pos.z+z<0 then
pos = vec3(pos.x+x,pos.y,vZ)
else
pos = vec3(pos.x+x,pos.y+y,pos.z+z)
end
volume:set(pos,table.unpack(b))
end
end
Update:
Nudge is in!
Code still messy.
Update: fixed bug where nudged models would reset when edited
Question:
Should I include some kind of user avatar for the movement mode?
I was thinking maybe a tiny helicopter, since the icon for movement mode is a helicopter?
It would disappear during editing mode so it wouldn’t block anything.
Yes/no?
A helicopter does sound cute!
Update
Mirroring got broken somewhere along the way. Fixed now.
Update
Mirroring was broken a whole different way and that’s now fixed too.
Update
RGB sliders are now HSV sliders, much easier to get an exact color with.
What do you think of this helicopter?
It’s supposed to look like the copter in the iOS emoji (as seen in the button in the upper right.)
Also what do you think of the new grid color?
Wow, looking great. Not sure about purple for the grid, was that to make it stand out more?
@John I just kind of did the purple accidentally and thought it looked good. But it’s such a strong choice that it might wear out its welcome…
@John I would love it if the grid didn’t fade out with distance but I can’t seem to find where that’s set. Can it be changed? It’s the same grid code as in your original Voxel Editor.