Created
February 2, 2016 14:28
-
-
Save vaiorabbit/7827370d413339c687b8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
require 'fiddle' | |
Dir.chdir(File.dirname(__FILE__)) | |
require_relative './../lib/dxsdl2r' | |
require_relative './mmd' | |
require_relative './motion' | |
$ss_name = "ss0000.tga" | |
$ss_id = 0 | |
def save_screenshot(w, h, name) | |
image = Fiddle::Pointer.malloc(Fiddle::SIZEOF_CHAR*w*h*4) | |
return if image == nil | |
glReadPixels(0, 0, w, h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, image) | |
File.open( name, 'wb' ) do |fout| | |
fout.write [0].pack('c') # identsize | |
fout.write [0].pack('c') # colourmaptype | |
fout.write [2].pack('c') # imagetype | |
fout.write [0].pack('s') # colourmapstart | |
fout.write [0].pack('s') # colourmaplength | |
fout.write [0].pack('c') # colourmapbits | |
fout.write [0].pack('s') # xstart | |
fout.write [0].pack('s') # ystart | |
fout.write [w].pack('s') # image_width | |
fout.write [h].pack('s') # image_height | |
fout.write [8 * 4].pack('c') # image_bits_per_pixel | |
fout.write [8].pack('c') # descriptor | |
fout.write image[0, Fiddle::SIZEOF_CHAR*w*h*4] | |
end | |
end | |
MAX_BONE_COUNT = 96 | |
src_vs = <<EOF | |
uniform vec3 boneOrigin[#{MAX_BONE_COUNT}]; | |
uniform vec4 boneRotation[#{MAX_BONE_COUNT}]; | |
uniform vec3 bonePosition[#{MAX_BONE_COUNT}]; | |
uniform bool isEdge; | |
attribute float boneWeight; | |
attribute vec3 normal; | |
attribute vec2 texcoord; | |
attribute vec3 position; | |
attribute float index1; | |
attribute float index2; | |
varying vec3 vPosition; | |
varying vec3 vNormal; | |
varying vec2 vTexCoord; | |
vec3 qtransform(vec4 q, vec3 v) { | |
return v + 2.0 * cross(cross(v, q.xyz) - q.w*v, q.xyz); | |
} | |
void main(void) | |
{ | |
int i1 = int(index1); | |
int i2 = int(index2); | |
vec3 t_localpos = position - boneOrigin[i1]; | |
vec3 t_position = qtransform(boneRotation[i1], t_localpos) + bonePosition[i1]; | |
vec3 t_normal = qtransform(boneRotation[i1], normal); | |
vPosition = (gl_ModelViewProjectionMatrix * vec4(t_position, 1.0)).xyz; | |
vNormal = gl_NormalMatrix * t_normal; | |
if (boneWeight < 0.99) { | |
vec3 t_localpos2 = position - boneOrigin[i2]; | |
vec3 p2 = qtransform(boneRotation[i2], t_localpos2) + bonePosition[i2]; | |
vec3 n2 = qtransform(boneRotation[i2], normal); | |
t_position = mix(p2, t_position, boneWeight); | |
t_normal = normalize(mix(n2, t_normal, boneWeight)); | |
} | |
if (isEdge) { | |
vec4 pos = gl_ModelViewProjectionMatrix * vec4(t_position, 1.0); | |
vec4 pos2 = gl_ModelViewProjectionMatrix * vec4(t_position + t_normal, 1.0); | |
vec4 norm = normalize(pos2 - pos); | |
gl_Position = pos + norm * 0.05; | |
return; | |
} | |
vTexCoord = texcoord; | |
gl_Position = gl_ModelViewProjectionMatrix * vec4(t_position, 1.0); | |
} | |
EOF | |
src_fs = <<EOF | |
uniform bool isEdge; | |
uniform bool useTexture; | |
uniform bool isSphereAdd; | |
uniform bool isSphereUse; | |
uniform float alpha; | |
uniform float shininess; | |
uniform sampler2D sampler; | |
uniform sampler2D toonSampler; | |
uniform sampler2D sphereSampler; | |
uniform vec3 ambient; | |
uniform vec3 diffuse; | |
uniform vec3 specularColor; | |
uniform vec3 lightDir; | |
uniform vec3 lightDiffuse; | |
varying vec3 vPosition; | |
varying vec3 vNormal; | |
varying vec2 vTexCoord; | |
vec4 edgeColor = vec4(0.0, 0.0, 0.0, 1.0); | |
void main (void) | |
{ | |
if(isEdge){ | |
gl_FragColor = edgeColor; | |
}else{ | |
vec3 cameraDir = normalize(-vPosition); | |
vec3 halfAngle = normalize(lightDir + cameraDir); | |
float specularWeight = pow(max(0.001, dot(halfAngle, normalize(vNormal))) , shininess); | |
vec3 specular = specularWeight * specularColor; | |
vec3 color = ambient + lightDiffuse * (diffuse + specular); | |
if(useTexture){ | |
color *= texture2D(sampler, vTexCoord).rgb; | |
} | |
if(isSphereUse){ | |
vec2 sphereCoord = 0.5 * (1.0 + vec2(1.0, -1.0) * normalize(vNormal).xy); | |
if(isSphereAdd){ | |
color += texture2D(sphereSampler, sphereCoord).rgb; | |
}else{ | |
color *= texture2D(sphereSampler, sphereCoord).rgb; | |
} | |
} | |
color = clamp(color, 0.0, 1.0); | |
float dotNL = max(0.0, dot(normalize(lightDir), normalize(vNormal))); | |
vec2 toonCoord = vec2(0.0, 0.5 * (1.0 - dotNL)); | |
vec3 toon = texture2D(toonSampler, toonCoord).rgb; | |
gl_FragColor = vec4(color * toon, alpha); | |
} | |
} | |
EOF | |
core = Shader::Core3D.new(src_vs, src_fs, isEdge: :int, | |
useTexture: :int, | |
isSphereAdd: :int, | |
isSphereUse: :int, | |
alpha: :float, | |
shininess: :float, | |
sampler: :texture, | |
toonSampler: :texture, | |
sphereSampler: :texture, | |
ambient: :float, | |
diffuse: :float, | |
specularColor: :float, | |
lightDir: :float, | |
lightDiffuse: :float, | |
boneOrigin: :fv, | |
boneRotation: :fv, | |
bonePosition: :fv | |
) | |
class BoneManager | |
attr_reader :bones, :count | |
def initialize(max_bone_count) | |
@m_b_map = {} | |
@max_bone_count = max_bone_count | |
end | |
def add_bone(material, bone) | |
if @m_b_map.has_key?(material) | |
if @m_b_map[material].has_key?(bone) | |
return @m_b_map[material][bone] | |
else | |
@m_b_map[material][bone] = @m_b_map[material].size | |
end | |
else | |
@m_b_map[material] = {bone=>0} | |
end | |
@m_b_map[material].size - 1 | |
end | |
def commit | |
@m_b_map.each do |material, bones| | |
material.shader.boneOrigin = bones.keys.map{|bone|bone.pos} + [[0,0,0]]*(@max_bone_count-bones.size) | |
material.shader.boneRotation = bones.keys.map{|bone|bone.arot} + [[0,0,0]]*(@max_bone_count-bones.size) | |
material.shader.bonePosition = bones.keys.map{|bone|bone.apos} + [[0,0,0]]*(@max_bone_count-bones.size) | |
end | |
end | |
end | |
class OneModel | |
attr_accessor :shader, :max_bone_count | |
attr_accessor :mmd, :bone_manager, :toons, :model, :face_current_index | |
attr_accessor :motion, :bones_hash | |
def initialize(pmd_path, vmd_path, | |
shader, max_bone_count) | |
@shader = nil | |
@max_bone_count = nil | |
@motion = nil | |
@bones_hash = nil | |
@mmd = MMDModel.load_file(pmd_path) | |
@bone_manager = BoneManager.new(max_bone_count) | |
# トゥーンテクスチャ読み込み | |
@toons = @mmd.toon_texture.names.map.with_index{|name, i| | |
tmp = name.force_encoding("Shift_JIS").encode("UTF-8",:invalid => :replace, :replace => "") | |
if tmp and tmp.end_with?(".bmp") | |
Image.load("./mmd/" + tmp) | |
else | |
Image.load("./mmd/toon" + (i + 1).to_s.rjust(2, "0") + ".bmp") | |
end | |
} + [Image.load("./mmd/toon00.bmp")] | |
# モデルデータ作成 | |
@model = Model.new | |
@face_current_index = 0 | |
@mmd.materials.each do |m| | |
material = Material.new | |
material.shader = Shader.new(shader) | |
# 頂点バッファ生成 | |
v = VertexBuffer.new([ | |
[:position, :float, 3], | |
[:normal, :float, 3], | |
[:texcoord, :float, 2], | |
[:boneWeight, :float, 1], | |
[:index1, :float, 1], | |
[:index2, :float, 1] | |
] | |
) | |
# 頂点情報設定 | |
m.vert_count.times do | |
pos = @mmd.vertices[@mmd.face.indices[@face_current_index]].pos | |
normal = @mmd.vertices[@mmd.face.indices[@face_current_index]].normal | |
uv = @mmd.vertices[@mmd.face.indices[@face_current_index]].uv | |
bones = @mmd.vertices[@mmd.face.indices[@face_current_index]].bone_nums | |
weight = @mmd.vertices[@mmd.face.indices[@face_current_index]].bone_weight | |
i1 = bone_manager.add_bone(material, @mmd.bones[bones[0]]) | |
i2 = bone_manager.add_bone(material, @mmd.bones[bones[1]]) | |
v << v.new_vertex(pos, normal, uv, [weight], [i1], [i2]) | |
@face_current_index += 1 | |
end | |
v.commit | |
# テクスチャ | |
if m.texture | |
material.shader.useTexture = 1 | |
material.shader.sampler = Image.load("./mmd/" + m.texture) | |
else | |
material.shader.useTexture = 0 | |
end | |
# スフィアマップ | |
if m.sphere | |
material.shader.isSphereUse = 1 | |
material.shader.sphereSampler = Image.load("./mmd/" + m.sphere) | |
if m.sphere.end_with?('.spa') | |
material.shader.isSphereAdd = 1 | |
else | |
material.shader.isSphereAdd = 0 | |
end | |
else | |
material.shader.isSphereUse = 0 | |
end | |
# トゥーン | |
toon_index = m.toon_index | |
toon_index = 10 if toon_index > 10 or toon_index < 0 | |
material.shader.toonSampler = toons[toon_index] | |
# シェーダのパラメータ設定 | |
material.shader.isEdge = 0 | |
material.shader.alpha = m.alpha | |
material.shader.diffuse = m.diffuse | |
material.shader.specularColor = m.specular | |
material.shader.lightDiffuse = [0.6, 0.6, 0.6] | |
material.shader.lightDir = [-0.5, -1.0, 0.5] | |
material.shader.ambient = m.ambient | |
material.shader.shininess = m.specularity | |
# 頂点セットと内部処理起動 | |
material.vertex_buffer = v | |
# material.commit # 頂点の値を変えたらこれが必要 | |
@model.materials << material | |
end | |
# モーション読み込み | |
@motion = MMDMotion.load_file(vmd_path) | |
# ボーンを名前で検索するためのハッシュ | |
@bones_hash = {} | |
@mmd.bones.each do |bone| | |
@bones_hash[bone.name] = bone | |
end | |
end | |
def update(frame) | |
@motion.motions.each do |m| | |
next if m.flame_no != frame | |
next if @bones_hash[m.bone_name] == nil | |
@bones_hash[m.bone_name].pos = Vector.new(*(@bones_hash[m.bone_name].pos)) | |
@bones_hash[m.bone_name].mpos = Vector.new(*m.location) | |
@bones_hash[m.bone_name].mrot = Quaternion.new(*m.rotation) | |
end | |
@mmd.bones.each do |bone| | |
if bone.parent_index == -1 # センターボーン | |
bone.apos = bone.pos + bone.mpos | |
bone.arot = bone.mrot | |
else | |
parent_bone = @mmd.bones[bone.parent_index] | |
# 位置計算 | |
v = (bone.pos - parent_bone.pos + bone.mpos) | |
bone.apos = v.rotate_by_quat(parent_bone.arot) + parent_bone.apos | |
# 回転計算 | |
bone.arot = parent_bone.arot * bone.mrot | |
end | |
end | |
@bone_manager.commit | |
end | |
def draw | |
@model.draw | |
end | |
end | |
mds = [ | |
OneModel.new("./mmd/Lat式ミクVer2.31_Normal.pmd", "./mmd/aachan.vmd", core, MAX_BONE_COUNT), | |
OneModel.new("./mmd/Lat式ミクVer2.31_Sailor夏服.pmd", "./mmd/nocchi.vmd", core, MAX_BONE_COUNT), | |
OneModel.new("./mmd/Lat式ミクVer2.31_White.pmd", "./mmd/kashiyuka.vmd", core, MAX_BONE_COUNT) | |
] | |
Window.width = 1280 | |
Window.height = 720 | |
rt3d = RenderTarget3D.new(Window.width, Window.height, bgcolor=[64, 64, 64, 255]) | |
rt3d.projection_matrix = Matrix.perspective(30.0, Window.width.to_f / Window.height, 3.0, 100000.0) | |
rt3d.view_matrix = Matrix.look_at([23*1.5, 20, 47*1.5], [12, 10, 29], [0, 1, 0]) | |
mds.each { |md| md.model.target = rt3d } | |
frame = 0 | |
Window.fps = 30 | |
Window.loop do | |
mds.each { |md| md.update(frame) } | |
=begin | |
mds[0].mmd.bones.each do |bone| | |
if bone.parent_index == -1 # センターボーン | |
p bone.apos | |
break | |
end | |
end | |
mds[1].mmd.bones.each do |bone| | |
if bone.parent_index == -1 # センターボーン | |
p bone.apos | |
break | |
end | |
end | |
mds[2].mmd.bones.each do |bone| | |
if bone.parent_index == -1 # センターボーン | |
p bone.apos | |
break | |
end | |
end | |
↓ | |
[24.998855590820312, 7.29958438873291, 50.33089065551758] | |
[-10.829872131347656, 7.107809066772461, 35.494022369384766] | |
[0.09120321273803711, 7.528717994689941, 2.59773588180542] | |
=end | |
mds.each { |md| md.draw } | |
Window.draw(0, 0, rt3d) | |
break if Input.key_push?(K_ESCAPE) | |
frame += 1 | |
$ss_name = sprintf("ss%05d.tga", $ss_id) | |
save_screenshot(Window.width, Window.height, $ss_name) | |
$ss_id += 1 | |
# puts $ss_id | |
exit if $ss_id == 2100 | |
end | |
=begin | |
class Matrix < Array | |
def self.look_at(eye, center, up) # RH fix | |
eye = Vector.new(*eye) | |
center = Vector.new(*center) | |
up = Vector.new(*up) | |
forward = (eye - center).normalize # axis_z | |
side = up.cross_product(forward).normalize # axis_x | |
up = forward.cross_product(side) # axis_y | |
Matrix.new( | |
[side.x, up.x, forward.x, 0], | |
[side.y, up.y, forward.y, 0], | |
[side.z, up.z, forward.z, 0], | |
[-(side.dot_product(eye)), -(up.dot_product(eye)), -(forward.dot_product(eye)), 1] | |
end | |
end | |
=end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment