Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New dev branch #30

Merged
merged 43 commits into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4933ba7
Use empties instead of an armature for glTF nodes
scurest Mar 19, 2018
057e3df
configure the camera from the gltf data
dvd0101 Mar 22, 2018
a314aac
camera/create_camera: intentionally return an uninitialized camera fo…
dvd0101 Mar 26, 2018
9fbe168
camera/create_camera: handle a missing zfar in the perspective data
dvd0101 Mar 26, 2018
d0d955b
camera/create_camera: fallback behavior to handle a missing apesctRatio
dvd0101 Mar 26, 2018
00c50f6
create_camera: the glTF yfov must clearly go to the property `angle_y`
giorgiomarcias Apr 24, 2018
a1984ef
removed auto smoothing
zignig Jun 14, 2018
486e439
Tidy up __init__ and unify the caches...
scurest Jul 4, 2018
109dc4b
Tidy up the mesh creator and enable RGBA colors
scurest Jul 4, 2018
b0db3fd
Begin implementing a new node creator
scurest Jul 4, 2018
283257a
Creates skins and attach them to armatures
scurest Jul 4, 2018
11cb513
WIP animation loading
scurest Jul 5, 2018
50656d5
Put animation handling in order
scurest Jul 5, 2018
e66f0ca
Use memoryviews for buffers and buffer views.
scurest Jul 5, 2018
9f8f073
Cleanup and explain how the vforest works
scurest Jul 5, 2018
00c7eb9
Note that Blender 2.79 is needed
scurest Jul 5, 2018
654310c
Minor tweaks
scurest Jul 5, 2018
86531b0
Put cameras in the scene
scurest Jul 5, 2018
f3bad07
Handle creating scenes again
scurest Jul 6, 2018
294600e
Add more import options
scurest Jul 10, 2018
94af3a1
Cache images used for texturing
scurest Jul 11, 2018
863001f
Autogenerate node group data
scurest Jul 11, 2018
395d3ea
Small work-around to enable vertex color influence
scurest Jul 11, 2018
2d56423
Tighten up scene graph creation
scurest Jul 11, 2018
128a4b3
Fix some typos in animation.py
scurest Jul 12, 2018
1ea94f2
Avoid bone->nonbone->bone child relations
scurest Jul 12, 2018
bf112fd
Rename node.py -> scene.py
scurest Jul 12, 2018
f216c93
Large push towards full material implementation
scurest Jul 15, 2018
32478f8
Fill in sampler properties
scurest Jul 15, 2018
37a39dd
Support KHR_materials_unlit extension
scurest Jul 15, 2018
1ba2263
Merge with upstream master
scurest Jul 15, 2018
6eef98b
Merge branch 'zignig/master' into dev
scurest Jul 15, 2018
2c27553
Merge camera support
scurest Jul 15, 2018
891e829
Gate animation importing behind a flag...
scurest Jul 15, 2018
6147a17
Allow for >4 vertex weights
scurest Jul 15, 2018
19462a2
Bugfix: do not modify vertex color accessor arrays
scurest Jul 15, 2018
9d3f6c8
Bugfix: don't set vertex weights of 0.0
scurest Jul 15, 2018
064c3b9
Support MSFT_texture_dds extension.
scurest Jul 15, 2018
26b062d
Implement KHR_texture_transform
scurest Jul 15, 2018
e534294
Greatly simplify animation importing
scurest Jul 16, 2018
bbf469c
Add COLOR_X, etc. layers in order of X
scurest Jul 16, 2018
fbd47dc
Slightly better bones
scurest Jul 17, 2018
fe0e1af
Work-around re vertex colors for Blender 2.79.0
scurest Jul 17, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Blender importer for glTF 2.0.

## Installation
Blender ≥2.78 is recommended.
Blender =2.79 is recommended.

Find the latest archive here:

Expand Down
243 changes: 135 additions & 108 deletions addons/io_scene_gltf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import json
import os
import struct
import json, os, struct

import bpy
from bpy.props import StringProperty
from bpy.props import StringProperty, BoolProperty, FloatProperty
from bpy_extras.io_utils import ImportHelper

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

bl_info = {
'name': 'glTF 2.0 Importer',
'author': 'Kristian Sons (ksons), scurest',
'blender': (2, 71, 0),
'blender': (2, 79, 0),
'version': (0, 3, 0),
'location': 'File > Import > glTF JSON (.gltf/.glb)',
'description': 'Importer for the glTF 2.0 file format.',
Expand All @@ -24,8 +22,14 @@

# Supported glTF version
GLTF_VERSION = (2, 0)

# Supported extensions
EXTENSIONS = set()
EXTENSIONS = set((
'KHR_materials_pbrSpecularGlossiness',
'KHR_materials_unlit',
'KHR_texture_transform',
'MSFT_texture_dds',
))


class ImportGLTF(bpy.types.Operator, ImportHelper):
Expand All @@ -38,97 +42,92 @@ class ImportGLTF(bpy.types.Operator, ImportHelper):
options={'HIDDEN'},
)

def get_buffer(self, idx):
if idx not in self.buffers:
self.buffers[idx] = buffer.create_buffer(self, idx)
return self.buffers[idx]

def get_buffer_view(self, idx):
if idx not in self.buffer_views:
self.buffer_views[idx] = buffer.create_buffer_view(self, idx)
return self.buffer_views[idx]

def get_accessor(self, idx):
if idx not in self.accessors:
self.accessors[idx] = buffer.create_accessor(self, idx)
return self.accessors[idx]

def get_material(self, idx):
if idx not in self.materials:
self.materials[idx] = material.create_material(self, idx)
return self.materials[idx]

def get_default_material(self):
if not self.default_material:
self.default_material = material.create_default_material(self)
return self.default_material

def get_mesh(self, idx):
if idx not in self.meshes:
self.meshes[idx] = mesh.create_mesh(self, idx)
return self.meshes[idx]

def get_camera(self, idx):
if idx not in self.cameras:
self.cameras[idx] = camera.create_camera(self, idx)
return self.cameras[idx]

def generate_actions(self):
if 'animations' in self.gltf:
for idx in range(0, len(self.gltf['animations'])):
animation.create_action(self, idx)
import_under_current_scene = BoolProperty(
name='Import contents under current scene',
description=
'When enabled, all the objects will be placed in the current '
'scene and no scenes will be created.\n'
'When disabled, scenes will be created to match the ones in the '
'glTF file. Any object not in a scene will not be visible.',
default=True,
)
smooth_polys = BoolProperty(
name='Enable polygon smoothing',
description=
'Enable smoothing for all polygons in imported meshes. Suggest '
'disabling for low-res models.',
default=True,
)
import_animations = BoolProperty(
name='Import Animations (EXPERIMENTAL)',
description='',
default=False,
)
framerate = FloatProperty(
name='Frames/second',
description=
'Used for animation. The Blender frame corresponding to the glTF '
'time t is computed as framerate * t.',
default=60.0,
)

def check_version(self):
def str_to_version(s):
try:
version = tuple(int(x) for x in s.split('.'))
if len(version) >= 2:
return version
except Exception:
pass
def execute(self, context):
self.caches = {}

raise Exception('unknown version format: %s' % s)
self.load_config()
self.load()
self.check_version()
self.check_required_extensions()

asset = self.gltf['asset']
material.compute_materials_using_color0(self)
scene.create_scenes(self)
if self.import_animations:
animation.add_animations(self)

if 'minVersion' in asset:
min_version = str_to_version(asset['minVersion'])
supported = GLTF_VERSION >= min_version
if not supported:
raise Exception('unsupported minimum version: %s' % min_version)
else:
version = str_to_version(asset['version'])
# Check only major version; we should be backwards- and forwards-compatible
supported = version[0] == GLTF_VERSION[0]
if not supported:
raise Exception('unsupported version: %s' % version)
return {'FINISHED'}

def check_required_extensions(self):
# TODO: the below works but it will make the tests fails.
# Can be uncommented when KhronosGroup/glTF-Sample-Models#144
# is closed OR we implement pbrSpecularGlossiness.
pass
def draw(self, context):
layout = self.layout

layout.prop(self, 'import_under_current_scene')

# for ext in self.gltf.get('extensionsRequired', []):
# if ext not in EXTENSIONS:
# raise Exception('unsupported extension was required: %s' % ext)
col = layout.box().column()
col.label('Mesh:', icon='MESH_DATA')
col.prop(self, 'smooth_polys')

col = layout.box().column()
col.label('Animation:', icon='OUTLINER_DATA_POSE')
col.prop(self, 'import_animations')
col.prop(self, 'framerate')


def get(self, type, id):
cache = self.caches.setdefault(type, {})
if id not in cache:
cache[id] = CREATE_FNS[type](self, id)
return cache[id]

def load(self):
filename = self.filepath

# Remember this for resolving relative paths
self.base_path = os.path.dirname(filename)

with open(filename, 'rb') as f:
contents = f.read()

# Use magic number to detect GLB files.
is_glb = contents[:4] == b'glTF'

if is_glb:
self.parse_glb(contents)
else:
self.gltf = json.loads(contents.decode('utf-8'))
self.glb_buffer = None

def parse_glb(self, contents):
contents = memoryview(contents)

# Parse the header
header = struct.unpack_from('<4sII', contents)
glb_version = header[1]
if glb_version != 2:
Expand All @@ -140,17 +139,26 @@ def parse_chunk(offset):
ty = header[1]
data = contents[offset + 8: offset + 8 + data_len]
next_offset = offset + 8 + data_len
return {'type': ty, 'data': data, 'next_offset': next_offset}
return {
'type': ty,
'data': data,
'next_offset': next_offset,
}

offset = 12 # end of header

# The first chunk must be JSON
json_chunk = parse_chunk(offset)
if json_chunk['type'] != b'JSON':
raise Exception('GLB: JSON chunk must be first')
self.gltf = json.loads(json_chunk['data'].decode('utf-8'))
self.gltf = json.loads(
json_chunk['data'].tobytes(),
encoding='utf-8'
)

offset = json_chunk['next_offset']
self.glb_buffer = None

offset = json_chunk['next_offset']
while offset < len(contents):
chunk = parse_chunk(offset)

Expand All @@ -162,44 +170,63 @@ def parse_chunk(offset):
if chunk['type'] == b'JSON':
raise Exception('GLB: Too many JSON chunks, should be 1')

if self.glb_buffer:
if self.glb_buffer != None:
raise Exception('GLB: Too many BIN chunks, should be 0 or 1')

self.glb_buffer = chunk['data']

offset = chunk['next_offset']

def execute(self, context):
self.glb_buffer = None
self.buffers = {}
self.buffer_views = {}
self.accessors = {}
self.cameras = {}
self.default_material = None
self.pbr_group = None
self.materials = {}
self.meshes = {}
self.scenes = {}
# Indices of the root nodes
self.root_idxs = []
# Maps the index of a root node to the objects in that tree
self.root_to_objects = {}
# Maps a node index to the corresponding bone's name
self.node_to_bone_name = {}

self.load()

self.check_version()
self.check_required_extensions()

node.create_hierarchy(self)
self.generate_actions()
def check_version(self):
def parse_version(s):
"""Parse a string like '1.1' to a tuple (1,1)."""
try:
version = tuple(int(x) for x in s.split('.'))
if len(version) >= 2: return version
except Exception:
pass
raise Exception('unknown version format: %s' % s)

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

return {'FINISHED'}
if 'minVersion' in asset:
min_version = parse_version(asset['minVersion'])
supported = GLTF_VERSION >= min_version
if not supported:
raise Exception('unsupported minimum version: %s' % min_version)
else:
version = parse_version(asset['version'])
# Check only major version; we should be backwards- and forwards-compatible
supported = version[0] == GLTF_VERSION[0]
if not supported:
raise Exception('unsupported version: %s' % version)

def check_required_extensions(self):
for ext in self.gltf.get('extensionsRequired', []):
if ext not in EXTENSIONS:
raise Exception('unsupported extension was required: %s' % ext)


def load_config(self):
"""Load user-supplied options into instance vars."""
keywords = self.as_keywords()
self.import_under_current_scene = keywords['import_under_current_scene']
self.smooth_polys = keywords['smooth_polys']
self.import_animations = keywords['import_animations']
self.framerate = keywords['framerate']



CREATE_FNS = {
'buffer': buffer.create_buffer,
'buffer_view': buffer.create_buffer_view,
'accessor': buffer.create_accessor,
'image': material.create_image,
'material': material.create_material,
'node_group': node_groups.create_group,
'mesh': mesh.create_mesh,
'camera': camera.create_camera,
}

# Add to a menu
def menu_func_import(self, context):
Expand Down
Loading