Skip to content

Instantly share code, notes, and snippets.

@vaiorabbit
Created February 1, 2016 15:41
Show Gist options
  • Save vaiorabbit/e3633bf900cd9be3f00b to your computer and use it in GitHub Desktop.
Save vaiorabbit/e3633bf900cd9be3f00b to your computer and use it in GitHub Desktop.
# coding: utf-8
Dir.chdir(File.dirname(__FILE__))
require_relative './../lib/dxsdl2r'
require_relative './mmd'
require_relative './motion'
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
@m_b_map = {}
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
#mmd = MMDModel.load_file("./mmd/Lat式ミクVer2.31_Normal.pmd")
#mmd = MMDModel.load_file("./mmd/Lat式ミクVer2.31_Sailor夏服.pmd")
mmd = MMDModel.load_file("./mmd/Lat式ミクVer2.31_Whiteエッジ無し専用.pmd")
bone_manager = BoneManager.new
# トゥーンテクスチャ読み込み
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
i = 0
hash = {}
mmd.materials.each do |m|
material = Material.new
material.shader = Shader.new(core)
# 頂点バッファ生成
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[i]].pos
normal = mmd.vertices[mmd.face.indices[i]].normal
uv = mmd.vertices[mmd.face.indices[i]].uv
bones = mmd.vertices[mmd.face.indices[i]].bone_nums
weight = mmd.vertices[mmd.face.indices[i]].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])
i += 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("./mmd/kashiyuka.vmd")
#motion = MMDMotion.load_file("./mmd/kishimen.vmd")
# ボーンを名前で検索するためのハッシュ
bones_hash = {}
mmd.bones.each do |bone|
bones_hash[bone.name] = bone
end
Window.width = 640
Window.height = 360
rt3d = RenderTarget3D.new(Window.width, Window.height)
rt3d.projection_matrix = Matrix.perspective(30.0, Window.width.to_f / Window.height, 3.0, 1000.0)
rt3d.view_matrix = Matrix.look_at([0, 10, 50], [0, 10, 0], [0, 1, 0])
model.target = rt3d
flame = 0
Window.fps = 30
Window.loop do
motion.motions.each do |m|
next if m.flame_no != flame
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
model.draw
Window.draw(0, 0, rt3d)
break if Input.key_push?(K_ESCAPE)
flame += 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment