Hello Everyone!
Today I learned how to make 3D meshes and stuff thanks to Ignatz’s really cool ebook! I tested my skills by writing my own “Rectangular Prism” class, and it worked! I then wanted a ground, so I used Ignatz’s “Tile” class that tiles a 3D mesh with the image you give it. Unfortunately, tough, when I put Ground.mesh:draw()
in the draw function next to Block.mesh:draw()
, my block stopped showing! I assume it has something to do with the Tiles class since everytime I comment out Ground.mesh:draw()
, my block is visible again. I don’t know how shaders work (that’s for me to learn some other day), but I’m guessing Tile’s shader may be causing the problem?
Here is the code:
-- Use this function to perform your initial setup
function setup()
BlockPos = vec3(0,200,-50)
BlockSpeed = 7
BlockXRot = 2
BlockYRot = 0
Block = RectangularPrism({x = BlockPos.x, y = BlockPos.y, z = BlockPos.z, w = 100, h = 100, d = 100})
Ground = Tile(readImage("Cargo Bot:Game Area"),1)
Ground:AddSurface(vec3(-1200,0,-1200),vec3(1200,0,1200))
Block.mesh.texture = readImage("Platformer Art:Crate") -- Tap this blue bubble to change the texture.
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
Block.pos.x = Block.pos.x + BlockSpeed
Block.rotation.x = Block.rotation.x + BlockXRot
Block.rotation.y = Block.rotation.y + BlockYRot
if Block.pos.x < -1000 or Block.pos.x > 1000 then
BlockSpeed = -BlockSpeed
BlockXRot = -BlockXRot
BlockYRot = -BlockYRot
end
perspective()
camera(0,300,-300,Block.pos.x,Block.pos.y,Block.pos.z)
Ground.mesh:draw() -- If I comment this out, it shows the cube?!
Block:draw()
end
RectangularPrism = class()
function RectangularPrism:init(data)
self.pos = vec3()
self.pos.x = data.x
self.pos.y = data.y
self.pos.z = data.z
self.w = data.w
self.h = data.h
self.d = data.d
self.rotation = vec3()
self.mesh = mesh()
do -- A do loop because we won't be needing these local variables later on in this function.
-- Below, we are setting up a few variables that will be needed a lot.
local hlfW = self.w*0.5 -- the width will be along the x axis,
local hlfH = self.h*0.5 -- the height will be along the y axis,
local hlfD = self.d*0.5 -- and the depth will be along the z axis.
-- We'll assume North is positive X, East is positive Z, South is negative X and West is negative Z,
-- to make things easier to understand.
-- Below, we are setting variables that define the corners in a rectangular prism.
local topNorthWest = vec3(hlfW, hlfH, -hlfD)
local topNorthEast = vec3(hlfW, hlfH, hlfD)
local bottomNorthWest = vec3(hlfW, -hlfH, -hlfD)
local bottomNorthEast = vec3(hlfW, -hlfH, hlfD)
-- The southern side of the rectangular prism will have the same corners except their x axis will be negative.
local topSouthWest = vec3(-hlfW, hlfH, -hlfD)
local topSouthEast = vec3(-hlfW, hlfH, hlfD)
local bottomSouthWest = vec3(-hlfW, -hlfH, -hlfD)
local bottomSouthEast = vec3(-hlfW, -hlfH, hlfD)
-- Now that we know all 8 corners in our prism, it should be easy to set the mesh vertices.
-- Below, we are setting the vertices for the mesh.
self.mesh.vertices = {
topNorthWest,topNorthEast,topSouthWest,-- triangle 1 on top
topNorthEast,topSouthWest,topSouthEast,-- triangle 2 on top
bottomNorthWest,bottomNorthEast,bottomSouthWest,-- triangle 1 on bottom
bottomNorthEast,bottomSouthWest,bottomSouthEast,-- triangle 2 on bottom
bottomNorthWest,bottomNorthEast,topNorthWest,-- bottom triangle to North
bottomNorthEast,topNorthWest,topNorthEast,-- top triangle to North
bottomNorthEast,bottomSouthEast,topNorthEast,-- bottom triangle to East
bottomSouthEast,topNorthEast,topSouthEast,-- top triangle to East
bottomSouthWest,bottomSouthEast,topSouthWest,-- bottom triangle to South
bottomSouthEast,topSouthWest,topSouthEast,-- top triangle to South
bottomNorthWest,bottomSouthWest,topNorthWest,-- bottom triangle to West
bottomSouthWest,topNorthWest,topSouthWest,-- top triangle to West
}
end
-- Below we set some variables for the texture coordinates.
local topLeft = vec2(0,1)
local topRight = vec2(1,1)
local bottomLeft = vec2(0,0)
local bottomRight = vec2(1,0)
-- Below, we are setting the texture coordinates.
self.mesh.texCoords = {
topLeft,topRight,bottomLeft, -- triangle 1 on top
topRight,bottomLeft,bottomRight, -- triangle 2 on top
topLeft,topRight,bottomLeft, -- triangle 1 on bottom
topRight,bottomLeft,bottomRight, -- triangle 2 on bottom
bottomLeft,bottomRight,topLeft, -- bottom triangle to North
bottomRight,topLeft,topRight, -- top triangle to North
bottomRight,bottomLeft,topRight, -- bottom triangle to East
bottomLeft,topRight,topLeft, -- top triangle to East
bottomRight,bottomLeft,topRight, -- bottom triangle to South
bottomLeft,topRight,topLeft, -- top triangle to South
bottomLeft,bottomRight,topLeft, -- bottom triangle to West
bottomRight,topLeft,topRight -- top triangle to West
}
end
function RectangularPrism:draw()
pushMatrix()
translate(self.pos.x,self.pos.y,self.pos.z)
rotate(self.rotation.x, 1, 0, 0)
rotate(self.rotation.y, 0, 1, 0)
rotate(self.rotation.z, 0, 0, 1)
self.mesh:draw()
popMatrix()
end
-- Thank you so much Ignatz for the fantastic library below!
--This class tiles an image across a rectangle of any size and provides a mesh
--You can add as many rectangles as you like to the same mesh
--Each rectangle must be vertical or horizontal, not at an angle, ie floor, roof or walls
Tile=class()
--img = image name or the image itself
--s = scale of image (0.5 reduces by half, 2 doubles its size,
function Tile:init(img,s)
if type(img)=="text" then img=readImage(img) end
self.img=img
self.iw,self.ih=img.width,img.height
self.mesh=mesh()
self.mesh.texture=self.img
self.mesh.shader=shader(Tile.Shader.vertexShader,Tile.Shader.fragmentShader)
self.scale=s
self.v,self.t={},{}
end
--The parameters are as follows
--p1 = vec3(x,y,z) = a corner position
--p2 = vec3(x,y,z) = the diagonally opposite corner position
--s = scale of image (0.5 reduces by half, 2 doubles its size, default is what was provided in the init function)
function Tile:AddSurface(p1,p2,s)
s=s or self.scale
local w,h=self.img.width*s,self.img.height*s
local d=p2-p1
local v,t
if d.x==0 then
v={vec3(p1.x,p1.y,p1.z),vec3(p1.x,p1.y,p2.z),vec3(p1.x,p2.y,p2.z),vec3(p1.x,p2.y,p1.z)}
t={vec2(0,0),vec2(d.z/w,0),vec2(d.z/w,d.y/h),vec2(0,d.y/h)}
elseif d.y==0 then
v={vec3(p1.x,p1.y,p1.z),vec3(p2.x,p1.y,p1.z),vec3(p2.x,p1.y,p2.z),vec3(p1.x,p1.y,p2.z)}
t={vec2(0,0),vec2(d.x/w,0),vec2(d.x/w,d.z/h),vec2(0,d.z/h)}
elseif d.z==0 then
v={vec3(p1.x,p1.y,p1.z),vec3(p2.x,p1.y,p1.z),vec3(p2.x,p2.y,p1.z),vec3(p1.x,p2.y,p1.z)}
t={vec2(0,0),vec2(d.x/w,0),vec2(d.x/w,d.y/h),vec2(0,d.y/h)}
else return nil
end
local seq={1,2,3,3,4,1}
for i=1,6 do
table.insert(self.v,v[seq[i]])
table.insert(self.t,t[seq[i]])
end
self.mesh.vertices=self.v
self.mesh.texCoords=self.t
end
Tile.Shader = {
vertexShader = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
vColor = color;
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
lowp vec4 col = texture2D( texture, vec2(mod(vTexCoord.x,1.0), mod(vTexCoord.y,1.0)));
gl_FragColor = col;
}
]]}
BTW, for anyone that wants an easy way to make a 3D Rectangle, you may use my class “RectangularPrism” found in the code above.
And also, @Ignatz , thank you so much for writing that tutorial!