class CyberarmEngine::Transform

Basic 4x4 matrix operations

Attributes

elements[R]

Public Class Methods

concat(left, right) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 88
def self.concat(left, right)
  matrix = Array.new(left.elements.size)
  rows = 4

  matrix.size.times do |i|
    matrix[i] = 0

    rows.times do |j|
      matrix[i] += left.elements[i / rows * rows + j] * right.elements[i % rows + j * rows]
    end
  end

  Transform.new(matrix)
end
identity() click to toggle source
# File lib/cyberarm_engine/transform.rb, line 13
def self.identity
  Transform.new(
    [
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1
    ]
  )
end
new(matrix) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 6
def initialize(matrix)
  @elements = matrix

  raise "Transform is wrong size! Got #{@elements.size}, expected 16" if 16 != @elements.size
  raise "Invalid value for matrix, must all be numeric!" if @elements.any? { |e| e.nil? || !e.is_a?(Numeric) }
end
orthographic(left, right, bottom, top, near, far) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 200
def self.orthographic(left, right, bottom, top, near, far)
  s = Vector.new(
    2 / (right - left.to_f),
    2 / (top - bottom.to_f),
    -2 / (far - near.to_f)
  )

  t = Vector.new(
    (right + left.to_f)   / (right - left.to_f),
    (top   + bottom.to_f) / (top   - bottom.to_f),
    (far   + near.to_f)   / (far   - near.to_f)
  )

  Transform.new(
    [
      s.x, 0.0, 0.0, t.x,
      0.0, s.y, 0.0, t.y,
      0.0, 0.0, s.z, t.z,
      0.0, 0.0, 0.0, 1.0
    ]
  )
end
perspective(fov_y, aspect_ratio, near, far) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 185
def self.perspective(fov_y, aspect_ratio, near, far)
  f = 1.0 / Math.tan(fov_y.degrees_to_radians / 2.0) # cotangent
  zn = (far + near.to_f) / (near - far.to_f)
  zf = (2.0 * far * near.to_f) / (near - far.to_f)

  Transform.new(
    [
      f / aspect_ratio, 0.0,    0.0, 0.0,
      0.0,                f,    0.0, 0.0,
      0.0,                0.0, zn, zf,
      0.0,                0.0, -1.0, 0.0
    ]
  )
end
rotate(angle, rotate_around = nil) click to toggle source

2d rotate operation, replicates Gosu's Gosu.rotate function

# File lib/cyberarm_engine/transform.rb, line 27
def self.rotate(angle, rotate_around = nil)
  double c = Math.cos(angle).degrees_to_radians
  double s = Math.sin(angle).degrees_to_radians
  matrix = [
    +c, +s, 0, 0,
    -s, +c, 0, 0,
    0,  0,  1, 0,
    0,  0,  0, 1
  ]

  rotate_matrix = Transform.new(matrix, rows: 4, columns: 4)

  if rotate_around && (rotate_around.x != 0.0 || rotate_around.y != 0.0)
    negative_rotate_around = Vector.new(-rotate_around.x, -rotate_around.y, -rotate_around.z)

    rotate_matrix = concat(
      concat(translate(negative_rotate_around), rotate_matrix),
      translate(rotate_around)
    )
  end

  rotate_matrix
end
rotate_3d(vector, _order = "zyx") click to toggle source
# File lib/cyberarm_engine/transform.rb, line 117
def self.rotate_3d(vector, _order = "zyx")
  x, y, z = vector.to_a[0..2].map { |axis| axis * Math::PI / 180.0 }

  rotation_x = Transform.new(
    [
      1, 0,           0,            0,
      0, Math.cos(x), -Math.sin(x), 0,
      0, Math.sin(x), Math.cos(x),  0,
      0, 0,           0,            1
    ]
  )

  rotation_y = Transform.new(
    [
      Math.cos(y),  0, Math.sin(y), 0,
      0,            1, 0,           0,
      -Math.sin(y), 0, Math.cos(y), 0,
      0,            0, 0,           1
    ]
  )

  rotation_z = Transform.new(
    [
      Math.cos(z), -Math.sin(z), 0, 0,
      Math.sin(z), Math.cos(z),  0, 0,
      0,           0,            1, 0,
      0,           0,            0, 1
    ]
  )

  rotation_z * rotation_y * rotation_x
end
rotate_gl(angle, axis) click to toggle source

Implements glRotatef www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml

# File lib/cyberarm_engine/transform.rb, line 152
def self.rotate_gl(angle, axis)
  radians = angle * Math::PI / 180.0
  s = Math.sin(radians)
  c = Math.cos(radians)

  axis = axis.normalized
  x, y, z = axis.to_a[0..2]

  n = (1.0 - c)

  Transform.new(
    [
      x * x * n + c,     x * y * n - z * s, x * z * n + y * s, 0,
      y * x * n + z * s, y * y * n + c,     y * z * n - x * s, 0,
      x * z * n - y * s, y * z * n + x * s, z * z * n + c,     0,
      0,                 0,                 0, 1.0
    ]
  )
end
scale(vector, center_around = nil) click to toggle source

2d scale operation, replicates Gosu's Gosu.rotate function

# File lib/cyberarm_engine/transform.rb, line 65
def self.scale(vector, center_around = nil)
  scale_x, scale_y, scale_z = vector.to_a[0..2]
  matrix = [
    scale_x, 0,       0,       0,
    0,       scale_y, 0,       0,
    0,       0,       scale_z, 0,
    0,       0,       0,       1
  ]

  scale_matrix = Transform.new(matrix)

  if center_around && (center_around.x != 0.0 || center_around.y != 0.0)
    negative_center_around = Vector.new(-center_around.x, -center_around.y, -center_around.z)

    scale_matrix = concat(
      concat(translate(negative_center_around), scale_matrix),
      translate(center_around)
    )
  end

  scale_matrix
end
scale_3d(vector) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 172
def self.scale_3d(vector)
  x, y, z = vector.to_a[0..2]

  Transform.new(
    [
      x, 0, 0, 0,
      0, y, 0, 0,
      0, 0, z, 0,
      0, 0, 0, 1
    ]
  )
end
translate(vector) click to toggle source

2d translate operation, replicates Gosu's Gosu.translate function

# File lib/cyberarm_engine/transform.rb, line 52
def self.translate(vector)
  x, y, z = vector.to_a[0..2]
  matrix = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    x, y, z, 1
  ]

  Transform.new(matrix)
end
translate_3d(vector) click to toggle source

3D Operations meant for OpenGL ###

# File lib/cyberarm_engine/transform.rb, line 105
def self.translate_3d(vector)
  x, y, z = vector.to_a[0..2]
  matrix = [
    1, 0, 0, x,
    0, 1, 0, y,
    0, 0, 1, z,
    0, 0, 0, 1
  ]

  Transform.new(matrix)
end
view(eye, orientation) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 223
def self.view(eye, orientation)
  # https://www.3dgep.com/understanding-the-view-matrix/#The_View_Matrix
  cosPitch = Math.cos(orientation.z * Math::PI / 180.0)
  sinPitch = Math.sin(orientation.z * Math::PI / 180.0)
  cosYaw = Math.cos(orientation.y * Math::PI / 180.0)
  sinYaw = Math.sin(orientation.y * Math::PI / 180.0)

  x_axis = Vector.new(cosYaw, 0, -sinYaw)
  y_axis = Vector.new(sinYaw * sinPitch, cosPitch, cosYaw * sinPitch)
  z_axis = Vector.new(sinYaw * cosPitch, -sinPitch, cosPitch * cosYaw)

  Transform.new(
    [
      x_axis.x, y_axis.y, z_axis.z, 0,
      x_axis.x,         y_axis.y,         z_axis.z, 0,
      x_axis.x,         y_axis.y,         z_axis.z, 0,
      -x_axis.dot(eye), -y_axis.dot(eye), -z_axis.dot(eye), 1
    ]
  )
end

Public Instance Methods

*(other) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 244
def *(other)
  case other
  when CyberarmEngine::Vector
    matrix = @elements.clone
    list = other.to_a

    @elements.each_with_index do |e, i|
      matrix[i] = e + list[i % 4]
    end

    Transform.new(matrix)

  when CyberarmEngine::Transform
    multiply_matrices(other)
  else
    p other.class
    raise TypeError, "Expected CyberarmEngine::Vector or CyberarmEngine::Transform got #{other.class}"
  end
end
get(x, y) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 264
def get(x, y)
  width = 4

  # puts "Transform|#{self.object_id} -> #{@elements[width * y + x].inspect} (index: #{width * y + x})"
  @elements[width * y + x]
end
multiply_matrices(other) click to toggle source
# File lib/cyberarm_engine/transform.rb, line 271
def multiply_matrices(other)
  matrix = Array.new(16, 0)

  4.times do |x|
    4.times do |y|
      4.times do |k|
        matrix[4 * y + x] += get(x, k) * other.get(k, y)
      end
    end
  end

  Transform.new(matrix)
end
to_gl() click to toggle source

arranges Matrix in column major form

# File lib/cyberarm_engine/transform.rb, line 286
def to_gl
  e = @elements
  [
    e[0], e[4], e[8],  e[12],
    e[1], e[5], e[9],  e[13],
    e[2], e[6], e[10], e[14],
    e[3], e[7], e[11], e[15]
  ]
end