Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
ksons committed Mar 22, 2018
2 parents 0a2e25e + 3e73fda commit 68ba265
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 227 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ python:
install:
- sudo add-apt-repository -y ppa:thomas-schiex/blender
- sudo apt-get update
- sudo apt-get install -y blender
- sudo apt-get install -y --allow-unauthenticated blender
- blender --version

script:
Expand Down
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@

[![Build Status](https://travis-ci.org/ksons/gltf-blender-importer.svg?branch=master)](https://travis-ci.org/ksons/gltf-blender-importer)

Blender importer for glTF 2.0 (alpha)

Until now, only designed to load the glTF 2.0 [sample models](https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0). Expect updates soon; feel free to contribute!
Blender importer for glTF 2.0.

## Installation
Blender ≥2.78 is recommended. See [INSTALL.md](INSTALL.md) for installation instructions.
Blender ≥2.78 is recommended.

Find the latest archive here:

<a href="https://github.com/ksons/gltf-blender-importer/releases/download/0.5.0/io_scene_gltf-0.5.0.zip"><img src="./doc/archive.png"/></a>

You can install the archive using the ``Install from File...`` button in ``File->User preferences...->Add-ons``.
After installing you have to find the add-on and activate it.
<p align="center"><img width="50%" src="./doc/addon-install.png"/></p>

After this procedure, the exporter is available from ``File->Import->glTF JSON (.gltf/.glb)``.



See [INSTALL.md](INSTALL.md) for further installation instructions.

## Samples Renderings
![BoomBox](https://github.com/ksons/gltf-blender-importer/blob/master/doc/boom-box.png)
Expand Down
21 changes: 10 additions & 11 deletions addons/io_scene_gltf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper

from io_scene_gltf import animation, buffer, material, mesh, node
from io_scene_gltf import animation, buffer, material, mesh, node, camera

bl_info = {
'name': 'glTF 2.0 Importer',
'author': 'Kristian Sons',
'author': 'Kristian Sons (ksons), scurest',
'blender': (2, 71, 0),
'location': 'File > Import',
'description': '',
'version': (0, 0, 0),
'location': 'File > Import > glTF JSON (.gltf/.glb)',
'description': 'Importer for the glTF 2.0 file format.',
'warning': '',
'wiki_url': '',
'wiki_url': 'https://github.com/ksons/gltf-blender-importer/blob/master/README.md',
'tracker_url': 'https://github.com/ksons/gltf-blender-importer/issues',
'category': 'Import-Export'
}

Expand Down Expand Up @@ -68,10 +70,7 @@ def get_mesh(self, idx):

def get_camera(self, idx):
if idx not in self.cameras:
# TODO: actually handle cameras
camera = self.gltf['cameras'][idx]
name = camera.get('name', 'cameras[%d]' % idx)
self.cameras[idx] = bpy.data.cameras.new(name)
self.cameras[idx] = camera.create_camera(self, idx)
return self.cameras[idx]

def generate_actions(self):
Expand Down Expand Up @@ -193,10 +192,10 @@ def execute(self, context):
self.check_version()
self.check_required_extensions()

node.generate_scenes(self)
node.create_hierarchy(self)
self.generate_actions()

if 'scene' in self.gltf:
if 'scene' in self.gltf and bpy.context.screen:
bpy.context.screen.scene = self.scenes[self.gltf['scene']]

return {'FINISHED'}
Expand Down
8 changes: 8 additions & 0 deletions addons/io_scene_gltf/camera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import bpy


def create_camera(op, idx):
camera = op.gltf['cameras'][idx]
name = camera.get('name', 'cameras[%d]' % idx)
data = bpy.data.cameras.new(name)
return data
106 changes: 70 additions & 36 deletions addons/io_scene_gltf/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import bpy
from bpy_extras.image_utils import load_image

# This is a hack as it is not possible to access a "normal" slot via name or
# store it in a temporary variable
NORMAL = 6


def do_with_temp_file(contents, func):
"""Call func with the path to a temp file containing contents.
Expand Down Expand Up @@ -107,54 +111,60 @@ def create_pbr_group():
multColorNode1 = tree.nodes.new('ShaderNodeMixRGB')
multColorNode1.location = -680, 466
multColorNode1.blend_type = 'MULTIPLY'
multColorNode1.inputs[0].default_value = 1
links.new(inputNode.outputs[0], multColorNode1.inputs[1])
links.new(inputNode.outputs[1], multColorNode1.inputs[2])
multColorNode1.inputs['Fac'].default_value = 1
links.new(inputNode.outputs['baseColorFactor'],
multColorNode1.inputs['Color1'])
links.new(inputNode.outputs['baseColorTexture'],
multColorNode1.inputs['Color2'])

multColorNode2 = tree.nodes.new('ShaderNodeMixRGB')
multColorNode2.location = -496, 466
multColorNode2.blend_type = 'MULTIPLY'
multColorNode2.inputs[0].default_value = 1
links.new(inputNode.outputs[5], multColorNode2.inputs[1])
links.new(multColorNode1.outputs[0], multColorNode2.inputs[2])
colorOutputLink = multColorNode2.outputs[0]
multColorNode2.inputs['Fac'].default_value = 1
links.new(inputNode.outputs['Vertex Color'],
multColorNode2.inputs['Color1'])
links.new(multColorNode1.outputs['Color'], multColorNode2.inputs['Color2'])
colorOutputLink = multColorNode2.outputs['Color']

# Calculate roughness and metalness
separator = tree.nodes.new('ShaderNodeSeparateRGB')
separator.location = -749, -130
links.new(inputNode.outputs[4], separator.inputs[0])
links.new(
inputNode.outputs['metallicRoughnessTexture'], separator.inputs['Image'])

multRoughnessNode = tree.nodes.new('ShaderNodeMath')
multRoughnessNode.location = -476, -50
multRoughnessNode.operation = 'MULTIPLY'
links.new(separator.outputs[1], multRoughnessNode.inputs[0])
links.new(inputNode.outputs[3], multRoughnessNode.inputs[1])
roughnessOutputLink = multRoughnessNode.outputs[0]
links.new(separator.outputs['G'], multRoughnessNode.inputs[0])
links.new(inputNode.outputs['metallicFactor'], multRoughnessNode.inputs[1])
roughnessOutputLink = multRoughnessNode.outputs['Value']

multMetalnessNode = tree.nodes.new('ShaderNodeMath')
multMetalnessNode.location = -476, -227
multMetalnessNode.operation = 'MULTIPLY'
links.new(separator.outputs[2], multMetalnessNode.inputs[0])
links.new(inputNode.outputs[2], multMetalnessNode.inputs[1])
metalnessOutputLink = multMetalnessNode.outputs[0]
links.new(separator.outputs['B'], multMetalnessNode.inputs[0])
links.new(inputNode.outputs['roughnessFactor'],
multMetalnessNode.inputs[1])
metalnessOutputLink = multMetalnessNode.outputs['Value']

# First mix
mixNode1 = tree.nodes.new('ShaderNodeMixShader')
mixNode1.location = 226, 429

fresnelNode = tree.nodes.new('ShaderNodeFresnel')
fresnelNode.location = 14, 553
links.new(inputNode.outputs[6], fresnelNode.inputs[1])
links.new(inputNode.outputs[NORMAL], fresnelNode.inputs[1])

diffuseNode = tree.nodes.new('ShaderNodeBsdfDiffuse')
diffuseNode.location = 14, 427
links.new(colorOutputLink, diffuseNode.inputs[0])
links.new(roughnessOutputLink, diffuseNode.inputs[1])
links.new(inputNode.outputs[6], diffuseNode.inputs[2])
links.new(colorOutputLink, diffuseNode.inputs['Color'])
links.new(roughnessOutputLink, diffuseNode.inputs['Roughness'])
links.new(inputNode.outputs[NORMAL], diffuseNode.inputs['Normal'])

glossyNode = tree.nodes.new('ShaderNodeBsdfGlossy')
glossyNode.location = 14, 289
links.new(roughnessOutputLink, glossyNode.inputs[1])
links.new(inputNode.outputs[6], glossyNode.inputs[2])
links.new(roughnessOutputLink, glossyNode.inputs['Roughness'])
links.new(inputNode.outputs[NORMAL], glossyNode.inputs['Normal'])

links.new(fresnelNode.outputs[0], mixNode1.inputs[0])
links.new(diffuseNode.outputs[0], mixNode1.inputs[1])
Expand All @@ -166,9 +176,9 @@ def create_pbr_group():

glossyNode2 = tree.nodes.new('ShaderNodeBsdfGlossy')
glossyNode2.location = 66, -114
links.new(colorOutputLink, glossyNode2.inputs[0])
links.new(roughnessOutputLink, glossyNode2.inputs[1])
links.new(inputNode.outputs[6], glossyNode2.inputs[2])
links.new(colorOutputLink, glossyNode2.inputs['Color'])
links.new(roughnessOutputLink, glossyNode2.inputs['Roughness'])
links.new(inputNode.outputs[NORMAL], glossyNode2.inputs['Normal'])

links.new(metalnessOutputLink, mixNode2.inputs[0])
links.new(mixNode1.outputs[0], mixNode2.inputs[1])
Expand Down Expand Up @@ -209,38 +219,45 @@ def create_material_from_properties(op, material, material_name):
group_node.node_tree = group

mo = tree.nodes.new('ShaderNodeOutputMaterial')
mo.location = 365, -25
links.new(group_node.outputs[0], mo.inputs[0])
mo.location = 420, -25
final_output = group_node.outputs[0]

metalness = pbr_metallic_roughness.get('metallicFactor', 1)
roughness = pbr_metallic_roughness.get('roughnessFactor', 1)
base_color = pbr_metallic_roughness.get('baseColorFactor', [1, 1, 1, 1])

group_node.inputs[0].default_value = base_color
group_node.inputs[2].default_value = metalness
group_node.inputs[3].default_value = roughness
group_node.inputs['baseColorFactor'].default_value = base_color
group_node.inputs['metallicFactor'].default_value = metalness
group_node.inputs['roughnessFactor'].default_value = roughness

base_color_texture = None
# TODO texCoord property
if 'baseColorTexture' in pbr_metallic_roughness:
image_idx = pbr_metallic_roughness['baseColorTexture']['index']
tex = create_texture(op, image_idx, 'baseColorTexture', tree)
tex.location = -580, 200
links.new(tex.outputs[0], group_node.inputs[1])
base_color_texture = create_texture(
op, image_idx, 'baseColorTexture', tree)
base_color_texture.location = -580, 200
links.new(
base_color_texture.outputs['Color'], group_node.inputs['baseColorTexture'])

if 'metallicRoughnessTexture' in pbr_metallic_roughness:
image_idx = pbr_metallic_roughness['metallicRoughnessTexture']['index']
tex = create_texture(op, image_idx, 'metallicRoughnessTexture', tree)
tex.location = -580, -150
links.new(tex.outputs[0], group_node.inputs[4])
links.new(tex.outputs[0],
group_node.inputs['metallicRoughnessTexture'])

if 'normalTexture' in material:
image_idx = material['normalTexture']['index']
tex = create_texture(op, image_idx, 'normalTexture', tree)
tex.location = -342, -366
tex.color_space = 'NONE'
normal_map_node = tree.nodes.new('ShaderNodeNormalMap')
normal_map_node.location = -150, -170
links.new(tex.outputs[0], normal_map_node.inputs[1])
links.new(normal_map_node.outputs[0], group_node.inputs[6])
links.new(tex.outputs['Color'], normal_map_node.inputs['Color'])
links.new(normal_map_node.outputs[0], group_node.inputs[NORMAL])
# TODO scale

if 'emissiveTexture' in material:
image_idx = material['emissiveTexture']['index']
tex = create_texture(op, image_idx, 'emissiveTexture', tree)
Expand All @@ -250,12 +267,29 @@ def create_material_from_properties(op, material, material_name):
add_node = tree.nodes.new('ShaderNodeAddShader')
add_node.location = 357, -89
links.new(tex.outputs[0], emission_node.inputs[0])
links.new(group_node.outputs[0], add_node.inputs[0])
links.new(final_output, add_node.inputs[0])
links.new(emission_node.outputs[0], add_node.inputs[1])
links.new(add_node.outputs[0], mo.inputs[0])
final_output = add_node.outputs[0]
mo.location = 547, -84
# TODO occlusion texture

alpha_mode = material.get("alphaMode", "OPAQUE")
if alpha_mode == "BLEND" and base_color_texture:

transparent_node = tree.nodes.new('ShaderNodeBsdfTransparent')
transparent_node.location = 43, -240

mix_node = tree.nodes.new('ShaderNodeMixShader')
mix_node.location = 250, -151

links.new(base_color_texture.outputs['Alpha'], mix_node.inputs['Fac'])
links.new(transparent_node.outputs[0], mix_node.inputs[1])
links.new(final_output, mix_node.inputs[2])

final_output = mix_node.outputs[0]

links.new(final_output, mo.inputs[0])

return mat


Expand Down
17 changes: 1 addition & 16 deletions addons/io_scene_gltf/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,22 +120,7 @@ def assign_texcoords(uvs, uv_layer):
if 'TEXCOORD_1' in attributes:
assign_texcoords(op.get_accessor(attributes['TEXCOORD_1']), me.uv_layers[1].data)

# Assign joints by generating vertex groups
if 'JOINTS_0' in attributes and 'WEIGHTS_0' in attributes:
# Don't seem to need to deal with all_attributes here.
# The only way I could find to set vertex groups was by
# round-tripping through a bmesh.
# TODO: find a better way?
joints = op.get_accessor(attributes['JOINTS_0'])
weights = op.get_accessor(attributes['WEIGHTS_0'])
bme = bmesh.new()
bme.from_mesh(me)
layer = bme.verts.layers.deform.new('JOINTS_0')
for vert, joint_vec, weight_vec in zip(bme.verts, joints, weights):
for joint, weight in zip(joint_vec, weight_vec):
vert[layer][joint] = weight
bme.to_mesh(me)
bme.free()
# TODO: handle joints and weights

me.update()

Expand Down
Loading

0 comments on commit 68ba265

Please sign in to comment.