Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Created December 3, 2024 20:08
Show Gist options
  • Save amirrajan/f020517ae6dccf69106683806b1c4cb8 to your computer and use it in GitHub Desktop.
Save amirrajan/f020517ae6dccf69106683806b1c4cb8 to your computer and use it in GitHub Desktop.
DragonRuby Game Toolkit - FF8 Rinoa
def tick args
if Kernel.tick_count == 0
r = read_obj_raw 'models/rinoa.obj'
end
args.grid.origin_center!
args.state.obj ||= read_obj_raw 'models/rinoa.obj'
args.state.triangles ||= read_obj 'models/rinoa.obj'
args.state.target_triangles_start ||= 0
args.state.target_triangles_end ||= 952
args.state.target_triangles ||= (read_obj 'models/rinoa.obj')[args.state.target_triangles_start..args.state.target_triangles_end]
movement_multiplier = 1000
args.state.cam_y ||= 100.0
if args.inputs.keyboard.i
args.state.cam_y += 0.01
elsif args.inputs.keyboard.k
args.state.cam_y -= 0.01
end
args.state.cam_angle_y ||= 0
if args.inputs.keyboard.q
args.state.cam_angle_y += 0.25
elsif args.inputs.keyboard.e
args.state.cam_angle_y -= 0.25
end
args.state.cam_angle_x ||= 0
if args.inputs.keyboard.u
args.state.cam_angle_x += 0.1
elsif args.inputs.keyboard.o
args.state.cam_angle_x -= 0.1
end
args.state.cam_angle_z ||= 0
if args.inputs.keyboard.c
args.state.cam_angle_z += 0.1
elsif args.inputs.keyboard.z
args.state.cam_angle_z -= 0.1
end
if args.inputs.mouse.has_focus
y_change_rate = (args.inputs.mouse.x / 640) ** 2
if args.inputs.mouse.x < 0
args.state.cam_angle_y -= 1.8 * y_change_rate
else
args.state.cam_angle_y += 1.8 * y_change_rate
end
x_change_rate = (args.inputs.mouse.y / 360) ** 2
if args.inputs.mouse.y < 0
args.state.cam_angle_x += 1.8 * x_change_rate
else
args.state.cam_angle_x -= 1.8 * x_change_rate
end
end
args.state.cam_z ||= 1200
if args.inputs.keyboard.up
point_1 = { x: 0, y: 0.02 }
point_r = args.geometry.rotate_point point_1, args.state.cam_angle_y
args.state.cam_x -= point_r.x * movement_multiplier
args.state.cam_z -= point_r.y * movement_multiplier
elsif args.inputs.keyboard.down
point_1 = { x: 0, y: -0.02 }
point_r = args.geometry.rotate_point point_1, args.state.cam_angle_y
args.state.cam_x -= point_r.x * movement_multiplier
args.state.cam_z -= point_r.y * movement_multiplier
end
args.state.cam_x ||= -4
if args.inputs.keyboard.right
point_1 = { x: -0.02, y: 0 }
point_r = args.geometry.rotate_point point_1, args.state.cam_angle_y
args.state.cam_x -= point_r.x * movement_multiplier
args.state.cam_z -= point_r.y * movement_multiplier
elsif args.inputs.keyboard.left
point_1 = { x: 0.02, y: 0 }
point_r = args.geometry.rotate_point point_1, args.state.cam_angle_y
args.state.cam_x -= point_r.x * movement_multiplier
args.state.cam_z -= point_r.y * movement_multiplier
end
if args.inputs.keyboard.key_down.r || args.inputs.keyboard.key_down.zero
args.state.cam_x = 0.00
args.state.cam_y = 0.00
args.state.cam_z = 1.00
args.state.cam_angle_y = 0
args.state.cam_angle_x = 0
end
camera_matrix = Matrix.mul (translate -args.state.cam_x, -args.state.cam_y, -args.state.cam_z),
(rotate_y args.state.cam_angle_y),
(rotate_x args.state.cam_angle_x),
(rotate_z args.state.cam_angle_z)
args.state.perspective_target_triangles = args.state.target_triangles.map do |t|
per = { p1: perspective(Matrix.mul(t.v[0], camera_matrix)),
p2: perspective(Matrix.mul(t.v[1], camera_matrix)),
p3: perspective(Matrix.mul(t.v[2], camera_matrix)),
source: t }
per.z_avg = (per.p1.z + per.p2.z + per.p3.z) / 3
per
end
args.state.perspective_target_triangles.sort_by { |t| t.z_avg }.each do |triangle|
if triangle.p1 && triangle.p2 && triangle.p3
args.state.sprite_sizes ||= {}
p1 = triangle.p1
p2 = triangle.p2
p3 = triangle.p3
path = if triangle.source.vt[0].usemtl
args.state.obj.mtl[triangle.source.vt[0].usemtl].map_Kd
else
:solid
end
if path != :solid
args.state.sprite_sizes[path] ||= args.gtk.calcspritebox path
sprite_w, sprite_h = args.state.sprite_sizes[path]
source_x = triangle.source.vt[0].u * sprite_w
source_y = if triangle.source.vt[0].v < -1
(triangle.source.vt[0].v + 2) * sprite_h
elsif triangle.source.vt[0].v < 0
(triangle.source.vt[0].v + 1) * sprite_h
elsif triangle.source.vt[0].v > 1
(triangle.source.vt[0].v - 1) * sprite_h
else
triangle.source.vt[0].v * -sprite_h
end
source_x2 = triangle.source.vt[1].u * sprite_w
source_y2 = if triangle.source.vt[1].v < -1
(triangle.source.vt[1].v + 2) * sprite_h
elsif triangle.source.vt[1].v < 0
(triangle.source.vt[1].v + 1) * sprite_h
elsif triangle.source.vt[1].v > 1
(triangle.source.vt[1].v - 1) * sprite_h
else
triangle.source.vt[1].v * sprite_h
end
source_x3 = triangle.source.vt[2].u * sprite_w
source_y3 = if triangle.source.vt[2].v < -1
(triangle.source.vt[2].v + 2) * sprite_h
elsif triangle.source.vt[2].v < 0
(triangle.source.vt[2].v + 1) * sprite_h
elsif triangle.source.vt[2].v > 1
(triangle.source.vt[2].v - 1) * sprite_h
else
triangle.source.vt[2].v * sprite_h
end
r = 255
g = 255
b = 255
a = 255
else
source_x = 0
source_y = 0
source_x2 = 100
source_y2 = 0
source_x3 = 100
source_y3 = 100
r = 255
g = 0
b = 0
a = 80
end
args.outputs.sprites << {
x: p1.x,
y: p1.y,
x2: p2.x,
y2: p2.y,
x3: p3.x,
y3: p3.y,
source_x: source_x,
source_y: source_y,
source_x2: source_x2,
source_y2: source_y2,
source_x3: source_x3,
source_y3: source_y3,
path: path,
r: r, g: g, b: b, a: a
}
end
end
# args.outputs.watch "#{args.state.cam_x}"
# args.outputs.watch "#{args.state.cam_y}"
# args.outputs.watch "#{args.state.cam_z}"
end
def perspective vec
left = 100.0
right = -100.0
bottom = 100.0
top = -100.0
near = 3000.0
far = 8000.0
sx = 2 * near / (right - left)
sy = 2 * near / (top - bottom)
c2 = - (far + near) / (far - near)
c1 = 2 * near * far / (near - far)
tx = -near * (left + right) / (right - left)
ty = -near * (bottom + top) / (top - bottom)
p = Matrix.mat4 sx, 0, 0, tx,
0, sy, 0, ty,
0, 0, c2, c1,
0, 0, -1, 0
r = Matrix.mul vec, p
return nil if r.w < 0
r.x *= r.z / r.w / 100
r.y *= r.z / r.w / 100
Matrix.vec4(r.x, r.y, r.z, r.w)
end
def translate dx, dy, dz
Matrix.mat4 1, 0, 0, dx,
0, 1, 0, dy,
0, 0, 1, dz,
0, 0, 0, 1
end
def rotate_y angle_d
cos_t = Math.cos angle_d.to_radians
sin_t = Math.sin angle_d.to_radians
(Matrix.mat4 cos_t, 0, sin_t, 0,
0, 1, 0, 0,
-sin_t, 0, cos_t, 0,
0, 0, 0, 1)
end
def rotate_x angle_d
cos_t = Math.cos angle_d.to_radians
sin_t = Math.sin angle_d.to_radians
(Matrix.mat4 1, 0, 0, 0,
0, cos_t, -sin_t, 0,
0, sin_t, cos_t, 0,
0, 0, 0, 1)
end
def scale sx, sy, sz
(Matrix.mat4 sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, 1)
end
def rotate_z angle_d
cos_t = Math.cos angle_d.to_radians
sin_t = Math.sin angle_d.to_radians
(Matrix.mat4 cos_t, -sin_t, 0, 0,
sin_t, cos_t, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1)
end
def read_obj_raw path
contents = ($gtk.read_file path)
root = {
v: [],
f: [],
vt: [],
vn: [],
groups: { default: {} },
mtllib: [],
mtl: {}
}
current_group = root[:groups][:default]
contents.each_line do |l|
if l.strip.start_with? "v "
x, y, z = l.strip.split(' ')[1..-1].map { |t| t.to_f }
root.v << { x: x, y: y, z: z }
elsif l.strip.start_with? "usemtl "
name = l.strip.split(' ')[1]
current_group[:usemtl] = name
elsif l.strip.start_with? "f "
a, b, c = l.strip.split(' ')[1..-1]
as = a.split('/')
a_v = as[0] ? as[0].to_i - 1 : nil
a_vt = as[1] ? as[1].to_i - 1 : nil
a_vn = as[2] ? as[2].to_i - 1 : nil
bs = b.split('/')
b_v = bs[0] ? bs[0].to_i - 1 : nil
b_vt = bs[1] ? bs[1].to_i - 1 : nil
b_vn = bs[2] ? bs[2].to_i - 1 : nil
cs = c.split('/')
c_v = cs[0] ? cs[0].to_i - 1 : nil
c_vt = cs[1] ? cs[1].to_i - 1 : nil
c_vn = cs[2] ? cs[2].to_i - 1 : nil
root.f << { a: { v: a_v, vt: a_vt, vn: a_vn },
b: { v: b_v, vt: b_vt, vn: b_vn },
c: { v: c_v, vt: c_vt, vn: c_vn },
group: current_group }
elsif l.strip.start_with? "g "
name = l.strip.split(' ')[1] || ""
root[:groups][name] ||= {}
current_group = root[:groups][name]
elsif l.strip.start_with? "mtllib "
name = l.strip.split(' ')[1]
root[:mtllib] << name
elsif l.strip.start_with? "vt "
u, v, w = l.strip.split(' ')[1..-1].map { |t| t.to_f }
root[:vt] ||= []
root[:vt] << { u: u, v: v, w: w }
elsif l.strip.start_with? "vn "
x, y, z = l.strip.split(' ')[1..-1].map { |t| t.to_f }
root[:vn] ||= []
root[:vn] << { x: x, y: y, z: z }
end
end
root.mtllib.each do |v|
contents = ($gtk.read_file File.dirname(path) + "/" + v)
current_mtl = nil
contents.each_line do |l|
if l.strip.start_with? "#"
next
elsif l.strip.start_with? "newmtl "
name = l.strip.split(' ')[1]
root.mtl[name] = {}
current_mtl = root.mtl[name]
elsif l.strip.start_with? "Kd "
r, g, b = l.strip.split(' ')[1..-1].map { |t| t.to_f }
current_mtl[:Kd] = { r: r, g: g, b: b }
elsif l.strip.start_with? "Ks "
r, g, b = l.strip.split(' ')[1..-1].map { |t| t.to_f }
current_mtl[:Ks] = { r: r, g: g, b: b }
elsif l.strip.start_with? "Ns "
ns = l.strip.split(' ')[1].to_f
current_mtl[:Ns] = ns
elsif l.strip.start_with? "map_Kd "
map_kd = l.strip.split(' ')[1]
current_mtl[:map_Kd] = File.dirname(path) + "/" + map_kd
end
end
end
root
end
def read_obj path
obj = read_obj_raw path
triangles = obj.f.map do |f|
{
v: [
Matrix.mul(Matrix.mul(Matrix.vec4(obj.v[f.a.v].x, obj.v[f.a.v].y, obj.v[f.a.v].z, 1), (rotate_z -180)), (scale 0.01, 0.01, 0.01)),
Matrix.mul(Matrix.mul(Matrix.vec4(obj.v[f.b.v].x, obj.v[f.b.v].y, obj.v[f.b.v].z, 1), (rotate_z -180)), (scale 0.01, 0.01, 0.01)),
Matrix.mul(Matrix.mul(Matrix.vec4(obj.v[f.c.v].x, obj.v[f.c.v].y, obj.v[f.c.v].z, 1), (rotate_z -180)), (scale 0.01, 0.01, 0.01))
],
vt: [
f.a.vt ? obj.vt[f.a.vt].merge(f.group) : nil,
f.b.vt ? obj.vt[f.b.vt].merge(f.group) : nil,
f.c.vt ? obj.vt[f.c.vt].merge(f.group) : nil,
],
vn: [
f.a.vn ? obj.vn[f.a.vn] : nil,
f.b.vn ? obj.vn[f.b.vn] : nil,
f.c.vn ? obj.vn[f.c.vn] : nil
],
}
end
triangles
end
# $gtk.reset
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment