[Bf-extensions-cvs] [44cc56c] master: X3D import: expanded support to a larger subset of the standard.
Seva Alekseyev
noreply at git.blender.org
Thu Oct 15 12:45:59 CEST 2015
Commit: 44cc56c92796cecb93506a33afff1c48f1ac39ee
Author: Seva Alekseyev
Date: Thu Oct 15 12:39:11 2015 +0200
Branches: master
https://developer.blender.org/rBA44cc56c92796cecb93506a33afff1c48f1ac39ee
X3D import: expanded support to a larger subset of the standard.
It supports:
- all geometry nodes from Rendering
- all geometry nodes from Geometry3D
- ImageTexture (including primitives)
- TextureTransform (needs careful testing)
- all lamp nodes from Lighting
- Viewpoint
I've been going by the standard as outlined here:
http://www.web3d.org/documents/specifications/19775-1/V3.3/index.html
When not sure, I'd compare to a reference implementation, X3DOM ( http://www.x3dom.org/ ).
The UI was left intact from the existing implementation.
Reviewed by campbellbarton and mont29
===================================================================
M io_scene_x3d/__init__.py
M io_scene_x3d/import_x3d.py
===================================================================
diff --git a/io_scene_x3d/__init__.py b/io_scene_x3d/__init__.py
index 78851a6..4d2a401 100644
--- a/io_scene_x3d/__init__.py
+++ b/io_scene_x3d/__init__.py
@@ -20,16 +20,16 @@
bl_info = {
"name": "Web3D X3D/VRML2 format",
- "author": "Campbell Barton, Bart, Bastien Montagne",
- "version": (1, 1, 0),
- "blender": (2, 74, 0),
+ "author": "Campbell Barton, Bart, Bastien Montagne, Seva Alekseyev",
+ "version": (1, 2, 0),
+ "blender": (2, 76, 0),
"location": "File > Import-Export",
"description": "Import-Export X3D, Import VRML2",
"warning": "",
- "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Import-Export/Web3D",
+ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Import-Export/Web3D",
"support": 'OFFICIAL',
- "category": "Import-Export"}
+ "category": "Import-Export",
+}
if "bpy" in locals():
import importlib
diff --git a/io_scene_x3d/import_x3d.py b/io_scene_x3d/import_x3d.py
index aec4f89..82a27d3 100644
--- a/io_scene_x3d/import_x3d.py
+++ b/io_scene_x3d/import_x3d.py
@@ -23,6 +23,13 @@ DEBUG = False
# This should work without a blender at all
import os
import shlex
+import math
+from math import sin, cos, pi
+
+texture_cache = {}
+material_cache = {}
+
+EPSILON = 0.0000001 # Very crude.
def imageConvertCompat(path):
@@ -374,7 +381,8 @@ class vrmlNode(object):
'DEF_NAMESPACE',
'ROUTE_IPO_NAMESPACE',
'PROTO_NAMESPACE',
- 'x3dNode')
+ 'x3dNode',
+ 'parsed')
def __init__(self, parent, node_type, lineno):
self.id = None
@@ -383,6 +391,7 @@ class vrmlNode(object):
self.blendObject = None
self.blendData = None
self.x3dNode = None # for x3d import only
+ self.parsed = None # We try to reuse objects in a smart way
if parent:
parent.children.append(self)
@@ -517,6 +526,11 @@ class vrmlNode(object):
# Check inside a list of optional types
return [child for child in self_real.children if child.getSpec() in node_spec]
+ def getChildrenBySpecCondition(self, cond): # spec could be Transform, Shape, Appearance
+ self_real = self.getRealNode()
+ # using getSpec functions allows us to use the spec of USE children that dont have their spec in their ID
+ return [child for child in self_real.children if cond(child.getSpec())]
+
def getChildBySpec(self, node_spec): # spec could be Transform, Shape, Appearance
# Use in cases where there is only ever 1 child of this type
ls = self.getChildrenBySpec(node_spec)
@@ -525,6 +539,14 @@ class vrmlNode(object):
else:
return None
+ def getChildBySpecCondition(self, cond): # spec could be Transform, Shape, Appearance
+ # Use in cases where there is only ever 1 child of this type
+ ls = self.getChildrenBySpecCondition(cond)
+ if ls:
+ return ls[0]
+ else:
+ return None
+
def getChildrenByName(self, node_name): # type could be geometry, children, appearance
self_real = self.getRealNode()
return [child for child in self_real.children if child.id if child.id[0] == node_name]
@@ -773,7 +795,7 @@ class vrmlNode(object):
def array_as_number(array_string):
array_data = []
try:
- array_data = [int(val) for val in array_string]
+ array_data = [int(val, 0) for val in array_string]
except:
try:
array_data = [float(val) for val in array_string]
@@ -1224,6 +1246,14 @@ class vrmlNode(object):
self.fields.append(value)
i += 1
+ # This is a prerequisite for DEF/USE-based material caching
+ def canHaveReferences(self):
+ return self.node_type == NODE_NORMAL and self.getDefName()
+
+ # This is a prerequisite for raw XML-based material caching. For now, only for X3D
+ def desc(self):
+ return None
+
def gzipOpen(path):
import gzip
@@ -1363,6 +1393,12 @@ class x3dNode(vrmlNode):
else:
return None
+ def canHaveReferences(self):
+ return self.x3dNode.getAttributeNode('DEF')
+
+ def desc(self):
+ return self.getRealNode().x3dNode.toxml()
+
def x3d_parse(path):
"""
@@ -1423,7 +1459,7 @@ for i, f in enumerate(files):
# -----------------------------------------------------------------------------------
import bpy
from bpy_extras import image_utils
-from mathutils import Vector, Matrix
+from mathutils import Vector, Matrix, Quaternion
GLOBALS = {'CIRCLE_DETAIL': 16}
@@ -1524,12 +1560,6 @@ def translateTexTransform(node, ancestry):
return new_mat
-
-# 90d X rotation
-import math
-MATRIX_Z_TO_Y = Matrix.Rotation(math.pi / 2.0, 4, 'X')
-
-
def getFinalMatrix(node, mtx, ancestry, global_matrix):
transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() == 'Transform']
@@ -1550,307 +1580,772 @@ def getFinalMatrix(node, mtx, ancestry, global_matrix):
return mtx
-def importMesh_IndexedFaceSet(geom, bpyima, ancestry):
- # print(geom.lineno, geom.id, vrmlNode.DEF_NAMESPACE.keys())
+# -----------------------------------------------------------------------------------
+# Mesh import utilities
+
+# Assumes that the mesh has tessfaces - doesn't support polygons.
+# Also assumes that tessfaces are all triangles.
+# Assumes that the sequence of the mesh vertices array matches
+# the source file. For indexed meshes, that's almost a given;
+# for nonindexed ones, this is a consideration.
+
+
+def importMesh_ApplyColors(bpymesh, geom, ancestry):
+ colors = geom.getChildBySpec(['ColorRGBA', 'Color'])
+ if colors:
+ if colors.getSpec() == 'ColorRGBA':
+ # Array of arrays; no need to flatten
+ rgb = [c[:3] for c
+ in colors.getFieldAsArray('color', 4, ancestry)]
+ else:
+ rgb = colors.getFieldAsArray('color', 3, ancestry)
+ tc = bpymesh.tessface_vertex_colors.new()
+ tc.data.foreach_set("color1", [i for face
+ in bpymesh.tessfaces
+ for i in rgb[face.vertices[0]]])
+ tc.data.foreach_set("color2", [i for face
+ in bpymesh.tessfaces
+ for i in rgb[face.vertices[1]]])
+ tc.data.foreach_set("color3", [i for face
+ in bpymesh.tessfaces
+ for i in rgb[face.vertices[2]]])
+
+
+# Assumes that the vertices have not been rearranged compared to the
+# source file order # or in the order assumed by the spec (e. g. in
+# Elevation, in rows by x).
+# Assumes tessfaces have been set, doesn't support polygons.
+def importMesh_ApplyNormals(bpymesh, geom, ancestry):
+ normals = geom.getChildBySpec('Normal')
+ if not normals:
+ return
- ccw = geom.getFieldAsBool('ccw', True, ancestry)
- ifs_colorPerVertex = geom.getFieldAsBool('colorPerVertex', True, ancestry) # per vertex or per face
- ifs_normalPerVertex = geom.getFieldAsBool('normalPerVertex', True, ancestry)
+ per_vertex = geom.getFieldAsBool('normalPerVertex', True, ancestry)
+ vectors = normals.getFieldAsArray('vector', 0, ancestry)
+ if per_vertex:
+ bpymesh.vertices.foreach_set("normal", vectors)
+ else:
+ bpymesh.tessfaces.foreach_set("normal", vectors)
- # This is odd how point is inside Coordinate
- # VRML not x3d
- #coord = geom.getChildByName('coord') # 'Coordinate'
+# Reads the standard Coordinate object - common for all mesh elements
+# Feeds the vertices in the mesh.
+# Rearranging the vertex order is a bad idea - other elements
+# in X3D might rely on it, if you need to rearrange, please play with
+# vertex indices in the tessfaces/polygons instead.
+#
+# Vertex culling that we have in IndexedFaceSet is an unfortunate exception,
+# brought forth by a very specific issue.
+def importMesh_ReadVertices(bpymesh, geom, ancestry):
+ # We want points here as a flat array, but the caching logic in
+ # IndexedFaceSet presumes a 2D one.
+ # The case for caching is stronger over there.
+ coord = geom.getChildBySpec('Coordinate')
+ points = coord.getFieldAsArray('point', 0, ancestry)
+ bpymesh.vertices.add(len(points) // 3)
+ bpymesh.vertices.foreach_set("co", points)
+
+
+# Assumes the mesh only contains triangular tessfaces, and the order
+# of vertices matches the source file.
+# Relies upon texture coordinates in the X3D node; if a coordinate generation
+# algorithm for a geometry is in the spec (e. g. for ElevationGrid), it needs
+# to be implemeted by the geometry handler.
+#
+# Texture transform is applied in ProcessObject.
+def importMesh_ApplyTextureToTessfaces(bpymesh, geom, ancestry, bpyima):
+ if not bpyima:
+ return
- coord = geom.getChildBySpec('Coordinate') # works for x3d and vrml
+ tex_coord = geom.getChildBySpec('TextureCoordinate')
+ if not tex_coord:
+ return
- if coord:
- ifs_points = coord.getFieldAsArray('point', 3, ancestry)
- else:
- coord = []
+ coord_points = tex_coord.getFieldAsArray('point', 2, ancestry)
+ if not coord_points:
+ return
- if not coord:
- print('\tWarnint: IndexedFaceSet has no points')
- return None, ccw
+ d = bpymesh.tessface_uv_textures.new().data
+ for face in d: # No foreach_set for nonscalars
+ face.image = bpyima
+ uv = [i for face in bpymesh.tessfaces
+ for vno in range(3) for i in coord_points[face.vertices[vno]]]
+ d.foreach_set('uv', uv)
- ifs_faces = geom.getFieldAsArray('coordIndex', 0, ancestry)
- coords_tex = None
- if ifs_faces: # In rare cases this causes problems - no faces but UVs???
+# Common steps for all triangle meshes once the geometry has been set:
+# normals, vertex colors, and texture.
+def importMesh_FinalizeTriangleMesh(bpymesh, geom, ancestry, bpyim
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list