Skip to content

Instantly share code, notes, and snippets.

@wanabe
Last active May 8, 2016 01:50
Show Gist options
  • Select an option

  • Save wanabe/8564e6b9fa804e27b250f1c785906792 to your computer and use it in GitHub Desktop.

Select an option

Save wanabe/8564e6b9fa804e27b250f1c785906792 to your computer and use it in GitHub Desktop.
Three.js on Opal test with sepated files
#three-canvas {
height: 100%;
width: 100%;
}
html {
height: 100%;
}
body {
height: 100%;
margin: 0px;
}
class MyVertexShader < VertexShader
def main
gl_Position = vec4( position, 1.0 )
end
end
class MyFragmentShader < FragmentShader
def main(resolution = vec2, time = float)
p = vec2 -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy
a = float time * 40.0
g = float 1.0 / 40.0
d = e = f = h = i = r = q = float
e = 400.0 * (p.x * 0.5 + 0.5)
f = 400.0 * (p.y * 0.5 + 0.5)
i = 200.0 + sin(e * g + a / 150.0) * 20.0
d = 200.0 + cos(f * g / 2.0) * 18.0 + cos(e * g) * 7.0
r = sqrt(pow(abs(i - e), 2.0) + pow(abs(d - f), 2.0))
q = f / r
e = (r * cos(q)) - a / 2.0
f = (r * sin(q)) - a / 2.0
d = sin(e * g) * 176.0 + sin(e * g) * 164.0 + r
h = ((f + d) + a / 2.0) * g
i = cos(h + r * p.x / 1.3) * (e + e + a) + cos(q * g * 6.0) * (r + h / 3.0)
h = sin(f * g) * 144.0 - sin(e * g) * 212.0 * p.x
h = (h + (f - e) * q + sin(r - (a + h) / 7.0) * 10.0 + i / 4.0) * g
i += cos(h * 2.3 * sin(a / 350.0 - q)) * 184.0 * sin(q - (r * 4.3 + a / 12.0) * g) + tan(r * g + h) * 184.0 * cos(r * g + h)
i = mod(i / 5.6, 256.0) / 64.0
i += 4.0 if i < 0.0
i = 4.0 - i if i >= 2.0
d = r / 350.0
d += sin(d * d * 8.0) * 0.52
f = (sin(a * g) + 1.0) / 2.0
gl_FragColor = vec4(vec3(f * i / 1.6, i / 2.0 + d / 13.0, i) * d * p.x + vec3(i / 1.3 + d / 8.0, i / 2.0 + d / 18.0, i) * d * (1.0 - p.x), 1.0)
end
end
class MyApp < App
def initialize
@margin = 10
super
@container.on :click do |event|
@running = !@running
animate if @running
end
@camera.position.z = 1
geometry = THREE::PlaneGeometry.new(2, 2)
vertex_shader = MyVertexShader.new
fragment_shader = MyFragmentShader.new(time: 1.0, resolution: THREE::Vector2.new)
@uniforms = fragment_shader.uniforms
material_arg = {
uniforms: @uniforms,
vertexShader: vertex_shader.to_s,
fragmentShader: fragment_shader.to_s
}
material = THREE::ShaderMaterial.new material_arg
mesh = THREE::Mesh.new geometry, material
@scene.add mesh
@sensor = ResizeSensor.new(@container, method("on_resize"))
end
def render
@uniforms.time.value += 0.05
end
def on_resize
@renderer.set_size @container.width - @margin, @container.height - @margin
@uniforms.resolution.value.x = @renderer.domElement.width
@uniforms.resolution.value.y = @renderer.domElement.height
end
end
MyApp.run
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="app.css">
<script src="//cdn.opalrb.org/opal/0.9.2/opal.min.js"></script>
<script src="//cdn.opalrb.org/opal/0.9.2/opal-parser.min.js"></script>
<script src="//cdn.opalrb.org/opal/0.9.2/external/opal-browser-0.2.0.js"></script>
<script src="https://raw.githubusercontent.com/mrdoob/three.js/r76/build/three.min.js"></script>
<script src="https://raw.githubusercontent.com/marcj/css-element-queries/master/src/ResizeSensor.js"></script>
<script>Opal.load('opal-parser')</script>
<script type="text/ruby" src="native_class.rb"></script>
<script type="text/ruby" src="three.rb"></script>
<script type="text/ruby" src="resize_sensor.rb"></script>
<script type="text/ruby" src="three_ext.rb"></script>
<script type="text/ruby" src="app.rb"></script>
</head>
<body>
<div id="three-canvas"></div>
</body>
</html>
class NativeClass
include Native
def initialize(*args)
@native = native(*args)
end
end
class ResizeSensor < NativeClass
def native(element, proc)
element = element.to_n
proc = proc.to_proc unless proc.is_a? Proc
`new ResizeSensor(element, proc)`
end
end
module THREE
class Base < NativeClass
end
class Camera < Base
def native
`new THREE.Camera()`
end
alias_native :position
end
class Scene < Base
def native
`new THREE.Scene()`
end
alias_native :add
end
class WebGLRenderer < Base
def native
`new THREE.WebGLRenderer()`
end
alias_native :pixel_ratio=, :setPixelRatio
alias_native :domElement
alias_native :set_size, :setSize
alias_native :render
end
class PlaneGeometry < Base
def native(width, height, width_segments = nil, height_segments = nil)
`new THREE.PlaneGeometry(width, height, width_segments, height_segments)`
end
end
class ShaderMaterial < Base
def native(parameters)
parameters = parameters.to_n
`new THREE.ShaderMaterial(parameters)`
end
end
class Mesh < Base
def native(geometry, material)
geometry, material = geometry.to_n, material.to_n
`new THREE.Mesh(geometry, material)`
end
end
class Vector2 < Base
def native
`new THREE.Vector2()`
end
alias_native :x
alias_native :y
end
end
class App
class << self
def run
new.animate
end
end
def initialize
@camera = THREE::Camera.new
@scene = THREE::Scene.new
@renderer = THREE::WebGLRenderer.new
@renderer.pixel_ratio = $$.devicePixelRatio
@container = $document['three-canvas']
@container << @renderer.domElement.to_n
@renderer.set_size @container.width, @container.height
@animate = method(:animate).to_proc
@running = true
end
def animate
return unless @running
$$.requestAnimationFrame @animate
render
@renderer.render(@scene, @camera)
end
end
class Shader
BRACE_TABLE = { "(" => [1, 0], ")" => [-1, 1], "," => [0, 0] }
OP_TABLE = {
"plus" => "+", "minus" => "-", "times" => "*", "divide" => "/",
"lt" => "<", "gt" => "<", "le" => "<=", "ge" => ">="
}
TYPE_TABLE = { "float" => "f", "vec2" => "v2" }
def initialize(**initial_values)
@initial_values = initial_values
parse
end
def to_s
@shader
end
def uniforms
uniforms = Native(@uniforms.to_n)
@initial_values.each do |name, value|
uniforms[name].value = value
end
uniforms
end
private
def parse
meth = method(:main)
c = 0
str = `String(meth.method)`
.sub(/function */, 'void main')
.sub(/ *var( .*)? self = .*\n *\n/, '')
.gsub(/ return /, ' ')
.gsub(/(gl_Position|gl_FragColor)\$/) { $1 }
.gsub(/[(),]/) {
d, o = BRACE_TABLE[$&]
c += d
"#{$&}<#{c + o}>"
}
str = str.sub(/void main\(<1>(.+?)\)<1>/, "void main(<1>)<1>")
if $1
uniforms = $1.split(/,<1> /)
@uniforms = {}
uniforms.each_with_index do |uniform, i|
str = str.sub(/ *if \(<1>#{uniform} == null\)<1> {\n *#{uniform} = self\.\$([^()]+)\(<1>\)<1>\n *}\n/, "")
raise "shader parse error" unless $1
str = "uniform #{$1} #{uniform};\n" + str
@uniforms[uniform] = { type: TYPE_TABLE[$1] }
end
end
str = str
.gsub(/( *)(.*) = self.\$(float)\(<\d+>\)<\d+>;$/) {
"#{$1}#{$3} #{$2.gsub(/ += +/, ', ')};"
}
.gsub(/^( *)(\w+) = self.\$(float|vec[2])\(<1>(.*?)\)<1>;$/) {
"#{$1}#{$3} #{$2} = #{$4};"
}
.gsub(/(?:self\.|(\.))?\$([^()]*)\(<\d+>\)<\d+>/) {
"#{$1}#{$2}"
}
.gsub(/if \(<1>\(<2>\(<3>\$a = (.*?)\)<3> !== nil && \(<3>!\$a.\$\$is_boolean \|\| \$a == true\)<3>\)<2>\)<1> {/) {
"if (#{$1}) {"
}
.gsub(/ ( *)(.*)};/) {
" #{$1}#{$2};\n#{$1}}"
}
.gsub(/([>\- ])(\d+)([), ])/) {
"#{$1}#{$2}.0#{$3}"
}
str2 = nil
while str != str2
str = str2 || str
str2 = str
.gsub(/\$rb_(plus|minus|times|divide|lt|le|gt|ge)\(<(\d+)>(.*?),<\2> *(.*?)\)<\2>/) {
"#{$3} #{OP_TABLE[$1]} #{$4}"
}
.gsub(/self\.\$([^()]+)\(<(\d+)>(.*?)\)<\2>/) {
"#{$1}(#{$3})"
}
end
@shader = str.gsub(/([(),])<\d+>/) { $1 }
end
end
class VertexShader < Shader
end
class FragmentShader < Shader
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment