Skip to content

Instantly share code, notes, and snippets.

@vaiorabbit
Created February 2, 2016 14:28
Show Gist options
  • Save vaiorabbit/7827370d413339c687b8 to your computer and use it in GitHub Desktop.
Save vaiorabbit/7827370d413339c687b8 to your computer and use it in GitHub Desktop.
# 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