[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