diff options
author | sanine <sanine.not@pm.me> | 2023-02-12 23:53:22 -0600 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2023-02-12 23:53:22 -0600 |
commit | f1fe73d1909a2448a004a88362a1a532d0d4f7c3 (patch) | |
tree | ab37ae3837e2f858de2932bcee9f26e69fab3db1 /libs/assimp/port/PyAssimp | |
parent | f567ea1e2798fd3156a416e61f083ea3e6b95719 (diff) |
switch to tinyobj and nanovg from assimp and cairo
Diffstat (limited to 'libs/assimp/port/PyAssimp')
21 files changed, 0 insertions, 8103 deletions
diff --git a/libs/assimp/port/PyAssimp/3d_viewer_screenshot.png b/libs/assimp/port/PyAssimp/3d_viewer_screenshot.png Binary files differdeleted file mode 100644 index 2031faf..0000000 --- a/libs/assimp/port/PyAssimp/3d_viewer_screenshot.png +++ /dev/null diff --git a/libs/assimp/port/PyAssimp/README.md b/libs/assimp/port/PyAssimp/README.md deleted file mode 100644 index c9944f7..0000000 --- a/libs/assimp/port/PyAssimp/README.md +++ /dev/null @@ -1,86 +0,0 @@ -PyAssimp Readme -=============== - -A simple Python wrapper for Assimp using `ctypes` to access the library. -Requires Python >= 2.6. - -Python 3 support is mostly here, but not well tested. - -Note that pyassimp is not complete. Many ASSIMP features are missing. - -USAGE ------ - -### Complete example: 3D viewer - -`pyassimp` comes with a simple 3D viewer that shows how to load and display a 3D -model using a shader-based OpenGL pipeline. - -![Screenshot](3d_viewer_screenshot.png) - -To use it, from within `/port/PyAssimp`: - -```console -$ cd scripts -$ python ./3D-viewer <path to your model> -``` - -You can use this code as starting point in your applications. - -### Writing your own code - -To get started with `pyassimp`, examine the simpler `sample.py` script in `scripts/`, -which illustrates the basic usage. All Assimp data structures are wrapped using -`ctypes`. All the data+length fields in Assimp's data structures (such as -`aiMesh::mNumVertices`, `aiMesh::mVertices`) are replaced by simple python -lists, so you can call `len()` on them to get their respective size and access -members using `[]`. - -For example, to load a file named `hello.3ds` and print the first -vertex of the first mesh, you would do (proper error handling -substituted by assertions ...): - -```python - -from pyassimp import load -with load('hello.3ds') as scene: - - assert len(scene.meshes) - mesh = scene.meshes[0] - - assert len(mesh.vertices) - print(mesh.vertices[0]) - -``` - -Another example to list the 'top nodes' in a -scene: - -```python - -from pyassimp import load -with load('hello.3ds') as scene: - - for c in scene.rootnode.children: - print(str(c)) - -``` - -INSTALL -------- - -Install `pyassimp` by running: - -```console -$ python setup.py install -``` - -PyAssimp requires a assimp dynamic library (`DLL` on windows, -`.so` on linux, `.dynlib` on macOS) in order to work. The default search directories are: - - the current directory - - on linux additionally: `/usr/lib`, `/usr/local/lib`, - `/usr/lib/x86_64-linux-gnu` - -To build that library, refer to the Assimp master `INSTALL` -instructions. To look in more places, edit `./pyassimp/helper.py`. -There's an `additional_dirs` list waiting for your entries. diff --git a/libs/assimp/port/PyAssimp/README.rst b/libs/assimp/port/PyAssimp/README.rst deleted file mode 100644 index 03b7968..0000000 --- a/libs/assimp/port/PyAssimp/README.rst +++ /dev/null @@ -1,93 +0,0 @@ -PyAssimp: Python bindings for libassimp -======================================= - -A simple Python wrapper for Assimp using ``ctypes`` to access the -library. Requires Python >= 2.6. - -Python 3 support is mostly here, but not well tested. - -Note that pyassimp is not complete. Many ASSIMP features are missing. - -USAGE ------ - -Complete example: 3D viewer -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``pyassimp`` comes with a simple 3D viewer that shows how to load and -display a 3D model using a shader-based OpenGL pipeline. - -.. figure:: 3d_viewer_screenshot.png - :alt: Screenshot - - Screenshot - -To use it, from within ``/port/PyAssimp``: - -:: - - $ cd scripts - $ python ./3D-viewer <path to your model> - -You can use this code as starting point in your applications. - -Writing your own code -~~~~~~~~~~~~~~~~~~~~~ - -To get started with ``pyassimp``, examine the simpler ``sample.py`` -script in ``scripts/``, which illustrates the basic usage. All Assimp -data structures are wrapped using ``ctypes``. All the data+length fields -in Assimp's data structures (such as ``aiMesh::mNumVertices``, -``aiMesh::mVertices``) are replaced by simple python lists, so you can -call ``len()`` on them to get their respective size and access members -using ``[]``. - -For example, to load a file named ``hello.3ds`` and print the first -vertex of the first mesh, you would do (proper error handling -substituted by assertions ...): - -.. code:: python - - - from pyassimp import load - with load('hello.3ds') as scene: - - assert len(scene.meshes) - mesh = scene.meshes[0] - - assert len(mesh.vertices) - print(mesh.vertices[0]) - - -Another example to list the 'top nodes' in a scene: - -.. code:: python - - - from pyassimp import load - with load('hello.3ds') as scene: - - for c in scene.rootnode.children: - print(str(c)) - - -INSTALL -------- - -Install ``pyassimp`` by running: - -:: - - $ python setup.py install - -PyAssimp requires a assimp dynamic library (``DLL`` on windows, ``.so`` -on linux, ``.dynlib`` on macOS) in order to work. The default search -directories are: - -- the current directory -- on linux additionally: ``/usr/lib``, ``/usr/local/lib``, - ``/usr/lib/x86_64-linux-gnu`` - -To build that library, refer to the Assimp master ``INSTALL`` -instructions. To look in more places, edit ``./pyassimp/helper.py``. -There's an ``additional_dirs`` list waiting for your entries. diff --git a/libs/assimp/port/PyAssimp/gen/materialgen.py b/libs/assimp/port/PyAssimp/gen/materialgen.py deleted file mode 100644 index ef32d8e..0000000 --- a/libs/assimp/port/PyAssimp/gen/materialgen.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python -# -*- Coding: UTF-8 -*- - -# --------------------------------------------------------------------------- -# Open Asset Import Library (ASSIMP) -# --------------------------------------------------------------------------- -# -# Copyright (c) 2006-2020, ASSIMP Development Team -# -# All rights reserved. -# -# Redistribution and use of this software in source and binary forms, -# with or without modification, are permitted provided that the following -# conditions are met: -# -# * Redistributions of source code must retain the above -# copyright notice, this list of conditions and the -# following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the -# following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# * Neither the name of the ASSIMP team, nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior -# written permission of the ASSIMP Development Team. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# --------------------------------------------------------------------------- - -"""Update PyAssimp's texture type constants C/C++ headers. - -This script is meant to be executed in the source tree, directly from -port/PyAssimp/gen -""" - -import os -import re - -REenumTextureType = re.compile(r'' - r'enum\saiTextureType' # enum aiTextureType - r'[^{]*?\{' # { - r'(?P<code>.*?)' # code - r'\};' # }; - , re.IGNORECASE + re.DOTALL + re.MULTILINE) - -# Replace comments -RErpcom = re.compile(r'' - r'\s*(/\*+\s|\*+/|\B\*\s?|///?!?)' # /** - r'(?P<line>.*?)' # * line - , re.IGNORECASE + re.DOTALL) - -# Remove trailing commas -RErmtrailcom = re.compile(r',$', re.IGNORECASE + re.DOTALL) - -# Remove #ifdef __cplusplus -RErmifdef = re.compile(r'' - r'#ifndef SWIG' # #ifndef SWIG - r'(?P<code>.*)' # code - r'#endif(\s*//\s*!?\s*SWIG)*' # #endif - , re.IGNORECASE + re.DOTALL) - -path = '../../../include/assimp' - -files = os.listdir (path) -enumText = '' -for fileName in files: - if fileName.endswith('.h'): - text = open(os.path.join(path, fileName)).read() - for enum in REenumTextureType.findall(text): - enumText = enum - -text = '' -for line in enumText.split('\n'): - line = line.lstrip().rstrip() - line = RErmtrailcom.sub('', line) - text += RErpcom.sub('# \g<line>', line) + '\n' -text = RErmifdef.sub('', text) - -file = open('material.py', 'w') -file.write(text) -file.close() - -print("Generation done. You can now review the file 'material.py' and merge it.") diff --git a/libs/assimp/port/PyAssimp/gen/structsgen.py b/libs/assimp/port/PyAssimp/gen/structsgen.py deleted file mode 100644 index f34ec19..0000000 --- a/libs/assimp/port/PyAssimp/gen/structsgen.py +++ /dev/null @@ -1,290 +0,0 @@ -#!/usr/bin/env python -# -*- Coding: UTF-8 -*- - -# --------------------------------------------------------------------------- -# Open Asset Import Library (ASSIMP) -# --------------------------------------------------------------------------- -# -# Copyright (c) 2006-2020, ASSIMP Development Team -# -# All rights reserved. -# -# Redistribution and use of this software in source and binary forms, -# with or without modification, are permitted provided that the following -# conditions are met: -# -# * Redistributions of source code must retain the above -# copyright notice, this list of conditions and the -# following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the -# following disclaimer in the documentation and/or other -# materials provided with the distribution. -# -# * Neither the name of the ASSIMP team, nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior -# written permission of the ASSIMP Development Team. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# --------------------------------------------------------------------------- - -"""Update PyAssimp's data structures to keep up with the -C/C++ headers. - -This script is meant to be executed in the source tree, directly from -port/PyAssimp/gen -""" - -import os -import re - -#==[regexps]================================================= - -# Clean desc -REdefine = re.compile(r'' - r'(?P<desc>)' # /** *desc */ - r'#\s*define\s(?P<name>[^(\n]+?)\s(?P<code>.+)$' # #define name value - , re.MULTILINE) - -# Get structs -REstructs = re.compile(r'' - #r'//\s?[\-]*\s(?P<desc>.*?)\*/\s' # /** *desc */ - #r'//\s?[\-]*(?P<desc>.*?)\*/(?:.*?)' # garbage - r'//\s?[\-]*\s(?P<desc>.*?)\*/\W*?' # /** *desc */ - r'struct\s(?:ASSIMP_API\s)?(?P<name>[a-z][a-z0-9_]\w+\b)' # struct name - r'[^{]*?\{' # { - r'(?P<code>.*?)' # code - r'\}\s*(PACK_STRUCT)?;' # }; - , re.IGNORECASE + re.DOTALL + re.MULTILINE) - -# Clean desc -REdesc = re.compile(r'' - r'^\s*?([*]|/\*\*)(?P<line>.*?)' # * line - , re.IGNORECASE + re.DOTALL + re.MULTILINE) - -# Remove #ifdef __cplusplus -RErmifdef = re.compile(r'' - r'#ifdef __cplusplus' # #ifdef __cplusplus - r'(?P<code>.*)' # code - r'#endif(\s*//\s*!?\s*__cplusplus)*' # #endif - , re.IGNORECASE + re.DOTALL) - -# Replace comments -RErpcom = re.compile(r'' - r'\s*(/\*+\s|\*+/|\B\*\s|///?!?)' # /** - r'(?P<line>.*?)' # * line - , re.IGNORECASE + re.DOTALL) - -# Restructure -def GetType(type, prefix='c_'): - t = type - while t.endswith('*'): - t = t[:-1] - if t[:5] == 'const': - t = t[5:] - - # skip some types - if t in skiplist: - return None - - t = t.strip() - types = {'unsigned int':'uint', 'unsigned char':'ubyte',} - if t in types: - t = types[t] - t = prefix + t - while type.endswith('*'): - t = "POINTER(" + t + ")" - type = type[:-1] - return t - -def restructure( match ): - type = match.group("type") - if match.group("struct") == "": - type = GetType(type) - elif match.group("struct") == "C_ENUM ": - type = "c_uint" - else: - type = GetType(type[2:], '') - if type is None: - return '' - if match.group("index"): - type = type + "*" + match.group("index") - - result = "" - for name in match.group("name").split(','): - result += "(\"" + name.strip() + "\", "+ type + ")," - - return result - -RErestruc = re.compile(r'' - r'(?P<struct>C_STRUCT\s|C_ENUM\s|)' # [C_STRUCT] - r'(?P<type>\w+\s?\w+?[*]*)\s' # type - #r'(?P<name>\w+)' # name - r'(?P<name>\w+|[a-z0-9_, ]+)' # name - r'(:?\[(?P<index>\w+)\])?;' # []; (optional) - , re.DOTALL) -#==[template]================================================ -template = """ -class $NAME$(Structure): - \"\"\" -$DESCRIPTION$ - \"\"\" -$DEFINES$ - _fields_ = [ - $FIELDS$ - ] -""" - -templateSR = """ -class $NAME$(Structure): - \"\"\" -$DESCRIPTION$ - \"\"\" -$DEFINES$ - -$NAME$._fields_ = [ - $FIELDS$ - ] -""" - -skiplist = ("FileIO", "File", "locateFromAssimpHeap",'LogStream','MeshAnim','AnimMesh') - -#============================================================ -def Structify(fileName): - file = open(fileName, 'r') - text = file.read() - result = [] - - # Get defines. - defs = REdefine.findall(text) - # Create defines - defines = "\n" - for define in defs: - # Clean desc - desc = REdesc.sub('', define[0]) - # Replace comments - desc = RErpcom.sub('#\g<line>', desc) - defines += desc - if len(define[2].strip()): - # skip non-integral defines, we can support them right now - try: - int(define[2],0) - except: - continue - defines += " "*4 + define[1] + " = " + define[2] + "\n" - - - # Get structs - rs = REstructs.finditer(text) - - fileName = os.path.basename(fileName) - print fileName - for r in rs: - name = r.group('name')[2:] - desc = r.group('desc') - - # Skip some structs - if name in skiplist: - continue - - text = r.group('code') - - # Clean desc - desc = REdesc.sub('', desc) - - desc = "See '"+ fileName +"' for details." #TODO - - # Remove #ifdef __cplusplus - text = RErmifdef.sub('', text) - - # Whether the struct contains more than just POD - primitive = text.find('C_STRUCT') == -1 - - # Restructure - text = RErestruc.sub(restructure, text) - # Replace comments - text = RErpcom.sub('# \g<line>', text) - text = text.replace("),#", "),\n#") - text = text.replace("#", "\n#") - text = "".join([l for l in text.splitlines(True) if not l.strip().endswith("#")]) # remove empty comment lines - - # Whether it's selfreferencing: ex. struct Node { Node* parent; }; - selfreferencing = text.find('POINTER('+name+')') != -1 - - complex = name == "Scene" - - # Create description - description = "" - for line in desc.split('\n'): - description += " "*4 + line.strip() + "\n" - description = description.rstrip() - - # Create fields - fields = "" - for line in text.split('\n'): - fields += " "*12 + line.strip() + "\n" - fields = fields.strip() - - if selfreferencing: - templ = templateSR - else: - templ = template - - # Put it all together - text = templ.replace('$NAME$', name) - text = text.replace('$DESCRIPTION$', description) - text = text.replace('$FIELDS$', fields) - - if ((name.lower() == fileName.split('.')[0][2:].lower()) and (name != 'Material')) or name == "String": - text = text.replace('$DEFINES$', defines) - else: - text = text.replace('$DEFINES$', '') - - - result.append((primitive, selfreferencing, complex, text)) - - return result - -text = "#-*- coding: UTF-8 -*-\n\n" -text += "from ctypes import POINTER, c_int, c_uint, c_size_t, c_char, c_float, Structure, c_char_p, c_double, c_ubyte\n\n" - -structs1 = "" -structs2 = "" -structs3 = "" -structs4 = "" - -path = '../../../include/assimp' -files = os.listdir (path) -#files = ["aiScene.h", "aiTypes.h"] -for fileName in files: - if fileName.endswith('.h'): - for struct in Structify(os.path.join(path, fileName)): - primitive, sr, complex, struct = struct - if primitive: - structs1 += struct - elif sr: - structs2 += struct - elif complex: - structs4 += struct - else: - structs3 += struct - -text += structs1 + structs2 + structs3 + structs4 - -file = open('structs.py', 'w') -file.write(text) -file.close() - -print("Generation done. You can now review the file 'structs.py' and merge it.") diff --git a/libs/assimp/port/PyAssimp/pyassimp/__init__.py b/libs/assimp/port/PyAssimp/pyassimp/__init__.py deleted file mode 100644 index bb67a43..0000000 --- a/libs/assimp/port/PyAssimp/pyassimp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .core import * diff --git a/libs/assimp/port/PyAssimp/pyassimp/core.py b/libs/assimp/port/PyAssimp/pyassimp/core.py deleted file mode 100644 index 35ad882..0000000 --- a/libs/assimp/port/PyAssimp/pyassimp/core.py +++ /dev/null @@ -1,556 +0,0 @@ -""" -PyAssimp - -This is the main-module of PyAssimp. -""" - -import sys -if sys.version_info < (2,6): - raise RuntimeError('pyassimp: need python 2.6 or newer') - -# xrange was renamed range in Python 3 and the original range from Python 2 was removed. -# To keep compatibility with both Python 2 and 3, xrange is set to range for version 3.0 and up. -if sys.version_info >= (3,0): - xrange = range - - -try: - import numpy -except ImportError: - numpy = None -import logging -import ctypes -from contextlib import contextmanager -logger = logging.getLogger("pyassimp") -# attach default null handler to logger so it doesn't complain -# even if you don't attach another handler to logger -logger.addHandler(logging.NullHandler()) - -from . import structs -from . import helper -from . import postprocess -from .errors import AssimpError - -class AssimpLib(object): - """ - Assimp-Singleton - """ - load, load_mem, export, export_blob, release, dll = helper.search_library() -_assimp_lib = AssimpLib() - -def make_tuple(ai_obj, type = None): - res = None - - #notes: - # ai_obj._fields_ = [ ("attr", c_type), ... ] - # getattr(ai_obj, e[0]).__class__ == float - - if isinstance(ai_obj, structs.Matrix4x4): - if numpy: - res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((4,4)) - #import pdb;pdb.set_trace() - else: - res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_] - res = [res[i:i+4] for i in xrange(0,16,4)] - elif isinstance(ai_obj, structs.Matrix3x3): - if numpy: - res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((3,3)) - else: - res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_] - res = [res[i:i+3] for i in xrange(0,9,3)] - else: - if numpy: - res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]) - else: - res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_] - - return res - -# Returns unicode object for Python 2, and str object for Python 3. -def _convert_assimp_string(assimp_string): - if sys.version_info >= (3, 0): - return str(assimp_string.data, errors='ignore') - else: - return unicode(assimp_string.data, errors='ignore') - -# It is faster and more correct to have an init function for each assimp class -def _init_face(aiFace): - aiFace.indices = [aiFace.mIndices[i] for i in range(aiFace.mNumIndices)] -assimp_struct_inits = { structs.Face : _init_face } - -def call_init(obj, caller = None): - if helper.hasattr_silent(obj,'contents'): #pointer - _init(obj.contents, obj, caller) - else: - _init(obj,parent=caller) - -def _is_init_type(obj): - - if obj and helper.hasattr_silent(obj,'contents'): #pointer - return _is_init_type(obj[0]) - # null-pointer case that arises when we reach a mesh attribute - # like mBitangents which use mNumVertices rather than mNumBitangents - # so it breaks the 'is iterable' check. - # Basically: - # FIXME! - elif not bool(obj): - return False - tname = obj.__class__.__name__ - return not (tname[:2] == 'c_' or tname == 'Structure' \ - or tname == 'POINTER') and not isinstance(obj, (int, str, bytes)) - -def _init(self, target = None, parent = None): - """ - Custom initialize() for C structs, adds safely accessible member functionality. - - :param target: set the object which receive the added methods. Useful when manipulating - pointers, to skip the intermediate 'contents' deferencing. - """ - if not target: - target = self - - dirself = dir(self) - for m in dirself: - - if m.startswith("_"): - continue - - if m.startswith('mNum'): - if 'm' + m[4:] in dirself: - continue # will be processed later on - else: - name = m[1:].lower() - - obj = getattr(self, m) - setattr(target, name, obj) - continue - - if m == 'mName': - target.name = str(_convert_assimp_string(self.mName)) - target.__class__.__repr__ = lambda x: str(x.__class__) + "(" + getattr(x, 'name','') + ")" - target.__class__.__str__ = lambda x: getattr(x, 'name', '') - continue - - name = m[1:].lower() - - obj = getattr(self, m) - - # Create tuples - if isinstance(obj, structs.assimp_structs_as_tuple): - setattr(target, name, make_tuple(obj)) - logger.debug(str(self) + ": Added array " + str(getattr(target, name)) + " as self." + name.lower()) - continue - - if m.startswith('m') and len(m) > 1 and m[1].upper() == m[1]: - - if name == "parent": - setattr(target, name, parent) - logger.debug("Added a parent as self." + name) - continue - - if helper.hasattr_silent(self, 'mNum' + m[1:]): - - length = getattr(self, 'mNum' + m[1:]) - - # -> special case: properties are - # stored as a dict. - if m == 'mProperties': - setattr(target, name, _get_properties(obj, length)) - continue - - - if not length: # empty! - setattr(target, name, []) - logger.debug(str(self) + ": " + name + " is an empty list.") - continue - - - try: - if obj._type_ in structs.assimp_structs_as_tuple: - if numpy: - setattr(target, name, numpy.array([make_tuple(obj[i]) for i in range(length)], dtype=numpy.float32)) - - logger.debug(str(self) + ": Added an array of numpy arrays (type "+ str(type(obj)) + ") as self." + name) - else: - setattr(target, name, [make_tuple(obj[i]) for i in range(length)]) - - logger.debug(str(self) + ": Added a list of lists (type "+ str(type(obj)) + ") as self." + name) - - else: - setattr(target, name, [obj[i] for i in range(length)]) #TODO: maybe not necessary to recreate an array? - - logger.debug(str(self) + ": Added list of " + str(obj) + " " + name + " as self." + name + " (type: " + str(type(obj)) + ")") - - # initialize array elements - try: - init = assimp_struct_inits[type(obj[0])] - except KeyError: - if _is_init_type(obj[0]): - for e in getattr(target, name): - call_init(e, target) - else: - for e in getattr(target, name): - init(e) - - - except IndexError: - logger.error("in " + str(self) +" : mismatch between mNum" + name + " and the actual amount of data in m" + name + ". This may be due to version mismatch between libassimp and pyassimp. Quitting now.") - sys.exit(1) - - except ValueError as e: - - logger.error("In " + str(self) + "->" + name + ": " + str(e) + ". Quitting now.") - if "setting an array element with a sequence" in str(e): - logger.error("Note that pyassimp does not currently " - "support meshes with mixed triangles " - "and quads. Try to load your mesh with" - " a post-processing to triangulate your" - " faces.") - raise e - - - - else: # starts with 'm' but not iterable - setattr(target, m, obj) - logger.debug("Added " + name + " as self." + name + " (type: " + str(type(obj)) + ")") - - if _is_init_type(obj): - call_init(obj, target) - - if isinstance(self, structs.Mesh): - _finalize_mesh(self, target) - - if isinstance(self, structs.Texture): - _finalize_texture(self, target) - - if isinstance(self, structs.Metadata): - _finalize_metadata(self, target) - - - return self - - -def pythonize_assimp(type, obj, scene): - """ This method modify the Assimp data structures - to make them easier to work with in Python. - - Supported operations: - - MESH: replace a list of mesh IDs by reference to these meshes - - ADDTRANSFORMATION: add a reference to an object's transformation taken from their associated node. - - :param type: the type of modification to operate (cf above) - :param obj: the input object to modify - :param scene: a reference to the whole scene - """ - - if type == "MESH": - meshes = [] - for i in obj: - meshes.append(scene.meshes[i]) - return meshes - - if type == "ADDTRANSFORMATION": - def getnode(node, name): - if node.name == name: return node - for child in node.children: - n = getnode(child, name) - if n: return n - - node = getnode(scene.rootnode, obj.name) - if not node: - raise AssimpError("Object " + str(obj) + " has no associated node!") - setattr(obj, "transformation", node.transformation) - -def recur_pythonize(node, scene): - ''' - Recursively call pythonize_assimp on - nodes tree to apply several post-processing to - pythonize the assimp datastructures. - ''' - node.meshes = pythonize_assimp("MESH", node.meshes, scene) - for mesh in node.meshes: - mesh.material = scene.materials[mesh.materialindex] - for cam in scene.cameras: - pythonize_assimp("ADDTRANSFORMATION", cam, scene) - for c in node.children: - recur_pythonize(c, scene) - -def release(scene): - ''' - Release resources of a loaded scene. - ''' - _assimp_lib.release(ctypes.pointer(scene)) - -@contextmanager -def load(filename, - file_type = None, - processing = postprocess.aiProcess_Triangulate): - ''' - Load a model into a scene. On failure throws AssimpError. - - Arguments - --------- - filename: Either a filename or a file object to load model from. - If a file object is passed, file_type MUST be specified - Otherwise Assimp has no idea which importer to use. - This is named 'filename' so as to not break legacy code. - processing: assimp postprocessing parameters. Verbose keywords are imported - from postprocessing, and the parameters can be combined bitwise to - generate the final processing value. Note that the default value will - triangulate quad faces. Example of generating other possible values: - processing = (pyassimp.postprocess.aiProcess_Triangulate | - pyassimp.postprocess.aiProcess_OptimizeMeshes) - file_type: string of file extension, such as 'stl' - - Returns - --------- - Scene object with model data - ''' - - if hasattr(filename, 'read'): - # This is the case where a file object has been passed to load. - # It is calling the following function: - # const aiScene* aiImportFileFromMemory(const char* pBuffer, - # unsigned int pLength, - # unsigned int pFlags, - # const char* pHint) - if file_type is None: - raise AssimpError('File type must be specified when passing file objects!') - data = filename.read() - model = _assimp_lib.load_mem(data, - len(data), - processing, - file_type) - else: - # a filename string has been passed - model = _assimp_lib.load(filename.encode(sys.getfilesystemencoding()), processing) - - if not model: - raise AssimpError('Could not import file!') - scene = _init(model.contents) - recur_pythonize(scene.rootnode, scene) - try: - yield scene - finally: - release(scene) - -def export(scene, - filename, - file_type = None, - processing = postprocess.aiProcess_Triangulate): - ''' - Export a scene. On failure throws AssimpError. - - Arguments - --------- - scene: scene to export. - filename: Filename that the scene should be exported to. - file_type: string of file exporter to use. For example "collada". - processing: assimp postprocessing parameters. Verbose keywords are imported - from postprocessing, and the parameters can be combined bitwise to - generate the final processing value. Note that the default value will - triangulate quad faces. Example of generating other possible values: - processing = (pyassimp.postprocess.aiProcess_Triangulate | - pyassimp.postprocess.aiProcess_OptimizeMeshes) - - ''' - - exportStatus = _assimp_lib.export(ctypes.pointer(scene), file_type.encode("ascii"), filename.encode(sys.getfilesystemencoding()), processing) - - if exportStatus != 0: - raise AssimpError('Could not export scene!') - -def export_blob(scene, - file_type = None, - processing = postprocess.aiProcess_Triangulate): - ''' - Export a scene and return a blob in the correct format. On failure throws AssimpError. - - Arguments - --------- - scene: scene to export. - file_type: string of file exporter to use. For example "collada". - processing: assimp postprocessing parameters. Verbose keywords are imported - from postprocessing, and the parameters can be combined bitwise to - generate the final processing value. Note that the default value will - triangulate quad faces. Example of generating other possible values: - processing = (pyassimp.postprocess.aiProcess_Triangulate | - pyassimp.postprocess.aiProcess_OptimizeMeshes) - Returns - --------- - Pointer to structs.ExportDataBlob - ''' - exportBlobPtr = _assimp_lib.export_blob(ctypes.pointer(scene), file_type.encode("ascii"), processing) - - if exportBlobPtr == 0: - raise AssimpError('Could not export scene to blob!') - return exportBlobPtr - -def _finalize_texture(tex, target): - setattr(target, "achformathint", tex.achFormatHint) - if numpy: - data = numpy.array([make_tuple(getattr(tex, "pcData")[i]) for i in range(tex.mWidth * tex.mHeight)]) - else: - data = [make_tuple(getattr(tex, "pcData")[i]) for i in range(tex.mWidth * tex.mHeight)] - setattr(target, "data", data) - -def _finalize_mesh(mesh, target): - """ Building of meshes is a bit specific. - - We override here the various datasets that can - not be process as regular fields. - - For instance, the length of the normals array is - mNumVertices (no mNumNormals is available) - """ - nb_vertices = getattr(mesh, "mNumVertices") - - def fill(name): - mAttr = getattr(mesh, name) - if numpy: - if mAttr: - data = numpy.array([make_tuple(getattr(mesh, name)[i]) for i in range(nb_vertices)], dtype=numpy.float32) - setattr(target, name[1:].lower(), data) - else: - setattr(target, name[1:].lower(), numpy.array([], dtype="float32")) - else: - if mAttr: - data = [make_tuple(getattr(mesh, name)[i]) for i in range(nb_vertices)] - setattr(target, name[1:].lower(), data) - else: - setattr(target, name[1:].lower(), []) - - def fillarray(name): - mAttr = getattr(mesh, name) - - data = [] - for index, mSubAttr in enumerate(mAttr): - if mSubAttr: - data.append([make_tuple(getattr(mesh, name)[index][i]) for i in range(nb_vertices)]) - - if numpy: - setattr(target, name[1:].lower(), numpy.array(data, dtype=numpy.float32)) - else: - setattr(target, name[1:].lower(), data) - - fill("mNormals") - fill("mTangents") - fill("mBitangents") - - fillarray("mColors") - fillarray("mTextureCoords") - - # prepare faces - if numpy: - faces = numpy.array([f.indices for f in target.faces], dtype=numpy.int32) - else: - faces = [f.indices for f in target.faces] - setattr(target, 'faces', faces) - -def _init_metadata_entry(entry): - entry.type = entry.mType - if entry.type == structs.MetadataEntry.AI_BOOL: - entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_bool)).contents.value - elif entry.type == structs.MetadataEntry.AI_INT32: - entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_int32)).contents.value - elif entry.type == structs.MetadataEntry.AI_UINT64: - entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_uint64)).contents.value - elif entry.type == structs.MetadataEntry.AI_FLOAT: - entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_float)).contents.value - elif entry.type == structs.MetadataEntry.AI_DOUBLE: - entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_double)).contents.value - elif entry.type == structs.MetadataEntry.AI_AISTRING: - assimp_string = ctypes.cast(entry.mData, ctypes.POINTER(structs.String)).contents - entry.data = _convert_assimp_string(assimp_string) - elif entry.type == structs.MetadataEntry.AI_AIVECTOR3D: - assimp_vector = ctypes.cast(entry.mData, ctypes.POINTER(structs.Vector3D)).contents - entry.data = make_tuple(assimp_vector) - - return entry - -def _finalize_metadata(metadata, target): - """ Building the metadata object is a bit specific. - - Firstly, there are two separate arrays: one with metadata keys and one - with metadata values, and there are no corresponding mNum* attributes, - so the C arrays are not converted to Python arrays using the generic - code in the _init function. - - Secondly, a metadata entry value has to be cast according to declared - metadata entry type. - """ - length = metadata.mNumProperties - setattr(target, 'keys', [str(_convert_assimp_string(metadata.mKeys[i])) for i in range(length)]) - setattr(target, 'values', [_init_metadata_entry(metadata.mValues[i]) for i in range(length)]) - -class PropertyGetter(dict): - def __getitem__(self, key): - semantic = 0 - if isinstance(key, tuple): - key, semantic = key - - return dict.__getitem__(self, (key, semantic)) - - def keys(self): - for k in dict.keys(self): - yield k[0] - - def __iter__(self): - return self.keys() - - def items(self): - for k, v in dict.items(self): - yield k[0], v - - -def _get_properties(properties, length): - """ - Convenience Function to get the material properties as a dict - and values in a python format. - """ - result = {} - #read all properties - for p in [properties[i] for i in range(length)]: - #the name - p = p.contents - key = str(_convert_assimp_string(p.mKey)) - key = (key.split('.')[1], p.mSemantic) - - #the data - if p.mType == 1: - arr = ctypes.cast(p.mData, - ctypes.POINTER(ctypes.c_float * int(p.mDataLength/ctypes.sizeof(ctypes.c_float))) - ).contents - value = [x for x in arr] - elif p.mType == 3: #string can't be an array - value = _convert_assimp_string(ctypes.cast(p.mData, ctypes.POINTER(structs.MaterialPropertyString)).contents) - - elif p.mType == 4: - arr = ctypes.cast(p.mData, - ctypes.POINTER(ctypes.c_int * int(p.mDataLength/ctypes.sizeof(ctypes.c_int))) - ).contents - value = [x for x in arr] - else: - value = p.mData[:p.mDataLength] - - if len(value) == 1: - [value] = value - - result[key] = value - - return PropertyGetter(result) - -def decompose_matrix(matrix): - if not isinstance(matrix, structs.Matrix4x4): - raise AssimpError("pyassimp.decompose_matrix failed: Not a Matrix4x4!") - - scaling = structs.Vector3D() - rotation = structs.Quaternion() - position = structs.Vector3D() - - _assimp_lib.dll.aiDecomposeMatrix(ctypes.pointer(matrix), - ctypes.byref(scaling), - ctypes.byref(rotation), - ctypes.byref(position)) - return scaling._init(), rotation._init(), position._init() - diff --git a/libs/assimp/port/PyAssimp/pyassimp/errors.py b/libs/assimp/port/PyAssimp/pyassimp/errors.py deleted file mode 100644 index e017b51..0000000 --- a/libs/assimp/port/PyAssimp/pyassimp/errors.py +++ /dev/null @@ -1,11 +0,0 @@ -#-*- coding: UTF-8 -*- - -""" -All possible errors. -""" - -class AssimpError(BaseException): - """ - If an internal error occurs. - """ - pass diff --git a/libs/assimp/port/PyAssimp/pyassimp/formats.py b/libs/assimp/port/PyAssimp/pyassimp/formats.py deleted file mode 100644 index 5d454e5..0000000 --- a/libs/assimp/port/PyAssimp/pyassimp/formats.py +++ /dev/null @@ -1,41 +0,0 @@ -FORMATS = ["CSM", - "LWS", - "B3D", - "COB", - "PLY", - "IFC", - "OFF", - "SMD", - "IRRMESH", - "3D", - "DAE", - "MDL", - "HMP", - "TER", - "WRL", - "XML", - "NFF", - "AC", - "OBJ", - "3DS", - "STL", - "IRR", - "Q3O", - "Q3D", - "MS3D", - "Q3S", - "ZGL", - "MD2", - "X", - "BLEND", - "XGL", - "MD5MESH", - "MAX", - "LXO", - "DXF", - "BVH", - "LWO", - "NDO"] - -def available_formats(): - return FORMATS diff --git a/libs/assimp/port/PyAssimp/pyassimp/helper.py b/libs/assimp/port/PyAssimp/pyassimp/helper.py deleted file mode 100644 index 7c14f60..0000000 --- a/libs/assimp/port/PyAssimp/pyassimp/helper.py +++ /dev/null @@ -1,283 +0,0 @@ -#-*- coding: UTF-8 -*- - -""" -Some fancy helper functions. -""" - -import os -import ctypes -import operator - -from distutils.sysconfig import get_python_lib -import re -import sys - -try: import numpy -except ImportError: numpy = None - -import logging;logger = logging.getLogger("pyassimp") - -from .errors import AssimpError - -additional_dirs, ext_whitelist = [],[] - -# populate search directories and lists of allowed file extensions -# depending on the platform we're running on. -if os.name=='posix': - additional_dirs.append('./') - additional_dirs.append('/usr/lib/') - additional_dirs.append('/usr/lib/x86_64-linux-gnu/') - additional_dirs.append('/usr/lib/aarch64-linux-gnu/') - additional_dirs.append('/usr/local/lib/') - - if 'LD_LIBRARY_PATH' in os.environ: - additional_dirs.extend([item for item in os.environ['LD_LIBRARY_PATH'].split(':') if item]) - - # check if running from anaconda. - anaconda_keywords = ("conda", "continuum") - if any(k in sys.version.lower() for k in anaconda_keywords): - cur_path = get_python_lib() - pattern = re.compile('.*\/lib\/') - conda_lib = pattern.match(cur_path).group() - logger.info("Adding Anaconda lib path:"+ conda_lib) - additional_dirs.append(conda_lib) - - # note - this won't catch libassimp.so.N.n, but - # currently there's always a symlink called - # libassimp.so in /usr/local/lib. - ext_whitelist.append('.so') - # libassimp.dylib in /usr/local/lib - ext_whitelist.append('.dylib') - -elif os.name=='nt': - ext_whitelist.append('.dll') - path_dirs = os.environ['PATH'].split(';') - additional_dirs.extend(path_dirs) - -def vec2tuple(x): - """ Converts a VECTOR3D to a Tuple """ - return (x.x, x.y, x.z) - -def transform(vector3, matrix4x4): - """ Apply a transformation matrix on a 3D vector. - - :param vector3: array with 3 elements - :param matrix4x4: 4x4 matrix - """ - if numpy: - return numpy.dot(matrix4x4, numpy.append(vector3, 1.)) - else: - m0,m1,m2,m3 = matrix4x4; x,y,z = vector3 - return [ - m0[0]*x + m0[1]*y + m0[2]*z + m0[3], - m1[0]*x + m1[1]*y + m1[2]*z + m1[3], - m2[0]*x + m2[1]*y + m2[2]*z + m2[3], - m3[0]*x + m3[1]*y + m3[2]*z + m3[3] - ] - -def _inv(matrix4x4): - m0,m1,m2,m3 = matrix4x4 - - det = m0[3]*m1[2]*m2[1]*m3[0] - m0[2]*m1[3]*m2[1]*m3[0] - \ - m0[3]*m1[1]*m2[2]*m3[0] + m0[1]*m1[3]*m2[2]*m3[0] + \ - m0[2]*m1[1]*m2[3]*m3[0] - m0[1]*m1[2]*m2[3]*m3[0] - \ - m0[3]*m1[2]*m2[0]*m3[1] + m0[2]*m1[3]*m2[0]*m3[1] + \ - m0[3]*m1[0]*m2[2]*m3[1] - m0[0]*m1[3]*m2[2]*m3[1] - \ - m0[2]*m1[0]*m2[3]*m3[1] + m0[0]*m1[2]*m2[3]*m3[1] + \ - m0[3]*m1[1]*m2[0]*m3[2] - m0[1]*m1[3]*m2[0]*m3[2] - \ - m0[3]*m1[0]*m2[1]*m3[2] + m0[0]*m1[3]*m2[1]*m3[2] + \ - m0[1]*m1[0]*m2[3]*m3[2] - m0[0]*m1[1]*m2[3]*m3[2] - \ - m0[2]*m1[1]*m2[0]*m3[3] + m0[1]*m1[2]*m2[0]*m3[3] + \ - m0[2]*m1[0]*m2[1]*m3[3] - m0[0]*m1[2]*m2[1]*m3[3] - \ - m0[1]*m1[0]*m2[2]*m3[3] + m0[0]*m1[1]*m2[2]*m3[3] - - return[[( m1[2]*m2[3]*m3[1] - m1[3]*m2[2]*m3[1] + m1[3]*m2[1]*m3[2] - m1[1]*m2[3]*m3[2] - m1[2]*m2[1]*m3[3] + m1[1]*m2[2]*m3[3]) /det, - ( m0[3]*m2[2]*m3[1] - m0[2]*m2[3]*m3[1] - m0[3]*m2[1]*m3[2] + m0[1]*m2[3]*m3[2] + m0[2]*m2[1]*m3[3] - m0[1]*m2[2]*m3[3]) /det, - ( m0[2]*m1[3]*m3[1] - m0[3]*m1[2]*m3[1] + m0[3]*m1[1]*m3[2] - m0[1]*m1[3]*m3[2] - m0[2]*m1[1]*m3[3] + m0[1]*m1[2]*m3[3]) /det, - ( m0[3]*m1[2]*m2[1] - m0[2]*m1[3]*m2[1] - m0[3]*m1[1]*m2[2] + m0[1]*m1[3]*m2[2] + m0[2]*m1[1]*m2[3] - m0[1]*m1[2]*m2[3]) /det], - [( m1[3]*m2[2]*m3[0] - m1[2]*m2[3]*m3[0] - m1[3]*m2[0]*m3[2] + m1[0]*m2[3]*m3[2] + m1[2]*m2[0]*m3[3] - m1[0]*m2[2]*m3[3]) /det, - ( m0[2]*m2[3]*m3[0] - m0[3]*m2[2]*m3[0] + m0[3]*m2[0]*m3[2] - m0[0]*m2[3]*m3[2] - m0[2]*m2[0]*m3[3] + m0[0]*m2[2]*m3[3]) /det, - ( m0[3]*m1[2]*m3[0] - m0[2]*m1[3]*m3[0] - m0[3]*m1[0]*m3[2] + m0[0]*m1[3]*m3[2] + m0[2]*m1[0]*m3[3] - m0[0]*m1[2]*m3[3]) /det, - ( m0[2]*m1[3]*m2[0] - m0[3]*m1[2]*m2[0] + m0[3]*m1[0]*m2[2] - m0[0]*m1[3]*m2[2] - m0[2]*m1[0]*m2[3] + m0[0]*m1[2]*m2[3]) /det], - [( m1[1]*m2[3]*m3[0] - m1[3]*m2[1]*m3[0] + m1[3]*m2[0]*m3[1] - m1[0]*m2[3]*m3[1] - m1[1]*m2[0]*m3[3] + m1[0]*m2[1]*m3[3]) /det, - ( m0[3]*m2[1]*m3[0] - m0[1]*m2[3]*m3[0] - m0[3]*m2[0]*m3[1] + m0[0]*m2[3]*m3[1] + m0[1]*m2[0]*m3[3] - m0[0]*m2[1]*m3[3]) /det, - ( m0[1]*m1[3]*m3[0] - m0[3]*m1[1]*m3[0] + m0[3]*m1[0]*m3[1] - m0[0]*m1[3]*m3[1] - m0[1]*m1[0]*m3[3] + m0[0]*m1[1]*m3[3]) /det, - ( m0[3]*m1[1]*m2[0] - m0[1]*m1[3]*m2[0] - m0[3]*m1[0]*m2[1] + m0[0]*m1[3]*m2[1] + m0[1]*m1[0]*m2[3] - m0[0]*m1[1]*m2[3]) /det], - [( m1[2]*m2[1]*m3[0] - m1[1]*m2[2]*m3[0] - m1[2]*m2[0]*m3[1] + m1[0]*m2[2]*m3[1] + m1[1]*m2[0]*m3[2] - m1[0]*m2[1]*m3[2]) /det, - ( m0[1]*m2[2]*m3[0] - m0[2]*m2[1]*m3[0] + m0[2]*m2[0]*m3[1] - m0[0]*m2[2]*m3[1] - m0[1]*m2[0]*m3[2] + m0[0]*m2[1]*m3[2]) /det, - ( m0[2]*m1[1]*m3[0] - m0[1]*m1[2]*m3[0] - m0[2]*m1[0]*m3[1] + m0[0]*m1[2]*m3[1] + m0[1]*m1[0]*m3[2] - m0[0]*m1[1]*m3[2]) /det, - ( m0[1]*m1[2]*m2[0] - m0[2]*m1[1]*m2[0] + m0[2]*m1[0]*m2[1] - m0[0]*m1[2]*m2[1] - m0[1]*m1[0]*m2[2] + m0[0]*m1[1]*m2[2]) /det]] - -def get_bounding_box(scene): - bb_min = [1e10, 1e10, 1e10] # x,y,z - bb_max = [-1e10, -1e10, -1e10] # x,y,z - inv = numpy.linalg.inv if numpy else _inv - return get_bounding_box_for_node(scene.rootnode, bb_min, bb_max, inv(scene.rootnode.transformation)) - -def get_bounding_box_for_node(node, bb_min, bb_max, transformation): - - if numpy: - transformation = numpy.dot(transformation, node.transformation) - else: - t0,t1,t2,t3 = transformation - T0,T1,T2,T3 = node.transformation - transformation = [ [ - t0[0]*T0[0] + t0[1]*T1[0] + t0[2]*T2[0] + t0[3]*T3[0], - t0[0]*T0[1] + t0[1]*T1[1] + t0[2]*T2[1] + t0[3]*T3[1], - t0[0]*T0[2] + t0[1]*T1[2] + t0[2]*T2[2] + t0[3]*T3[2], - t0[0]*T0[3] + t0[1]*T1[3] + t0[2]*T2[3] + t0[3]*T3[3] - ],[ - t1[0]*T0[0] + t1[1]*T1[0] + t1[2]*T2[0] + t1[3]*T3[0], - t1[0]*T0[1] + t1[1]*T1[1] + t1[2]*T2[1] + t1[3]*T3[1], - t1[0]*T0[2] + t1[1]*T1[2] + t1[2]*T2[2] + t1[3]*T3[2], - t1[0]*T0[3] + t1[1]*T1[3] + t1[2]*T2[3] + t1[3]*T3[3] - ],[ - t2[0]*T0[0] + t2[1]*T1[0] + t2[2]*T2[0] + t2[3]*T3[0], - t2[0]*T0[1] + t2[1]*T1[1] + t2[2]*T2[1] + t2[3]*T3[1], - t2[0]*T0[2] + t2[1]*T1[2] + t2[2]*T2[2] + t2[3]*T3[2], - t2[0]*T0[3] + t2[1]*T1[3] + t2[2]*T2[3] + t2[3]*T3[3] - ],[ - t3[0]*T0[0] + t3[1]*T1[0] + t3[2]*T2[0] + t3[3]*T3[0], - t3[0]*T0[1] + t3[1]*T1[1] + t3[2]*T2[1] + t3[3]*T3[1], - t3[0]*T0[2] + t3[1]*T1[2] + t3[2]*T2[2] + t3[3]*T3[2], - t3[0]*T0[3] + t3[1]*T1[3] + t3[2]*T2[3] + t3[3]*T3[3] - ] ] - - for mesh in node.meshes: - for v in mesh.vertices: - v = transform(v, transformation) - bb_min[0] = min(bb_min[0], v[0]) - bb_min[1] = min(bb_min[1], v[1]) - bb_min[2] = min(bb_min[2], v[2]) - bb_max[0] = max(bb_max[0], v[0]) - bb_max[1] = max(bb_max[1], v[1]) - bb_max[2] = max(bb_max[2], v[2]) - - - for child in node.children: - bb_min, bb_max = get_bounding_box_for_node(child, bb_min, bb_max, transformation) - - return bb_min, bb_max - -def try_load_functions(library_path, dll): - ''' - Try to bind to aiImportFile and aiReleaseImport - - Arguments - --------- - library_path: path to current lib - dll: ctypes handle to library - - Returns - --------- - If unsuccessful: - None - If successful: - Tuple containing (library_path, - load from filename function, - load from memory function, - export to filename function, - export to blob function, - release function, - ctypes handle to assimp library) - ''' - - try: - load = dll.aiImportFile - release = dll.aiReleaseImport - load_mem = dll.aiImportFileFromMemory - export = dll.aiExportScene - export2blob = dll.aiExportSceneToBlob - except AttributeError: - #OK, this is a library, but it doesn't have the functions we need - return None - - # library found! - from .structs import Scene, ExportDataBlob - load.restype = ctypes.POINTER(Scene) - load_mem.restype = ctypes.POINTER(Scene) - export2blob.restype = ctypes.POINTER(ExportDataBlob) - return (library_path, load, load_mem, export, export2blob, release, dll) - -def search_library(): - ''' - Loads the assimp library. - Throws exception AssimpError if no library_path is found - - Returns: tuple, (load from filename function, - load from memory function, - export to filename function, - export to blob function, - release function, - dll) - ''' - #this path - folder = os.path.dirname(__file__) - - # silence 'DLL not found' message boxes on win - try: - ctypes.windll.kernel32.SetErrorMode(0x8007) - except AttributeError: - pass - - candidates = [] - # test every file - for curfolder in [folder]+additional_dirs: - if os.path.isdir(curfolder): - for filename in os.listdir(curfolder): - # our minimum requirement for candidates is that - # they should contain 'assimp' somewhere in - # their name - if filename.lower().find('assimp')==-1 : - continue - is_out=1 - for et in ext_whitelist: - if et in filename.lower(): - is_out=0 - break - if is_out: - continue - - library_path = os.path.join(curfolder, filename) - logger.debug('Try ' + library_path) - try: - dll = ctypes.cdll.LoadLibrary(library_path) - except Exception as e: - logger.warning(str(e)) - # OK, this except is evil. But different OSs will throw different - # errors. So just ignore any errors. - continue - # see if the functions we need are in the dll - loaded = try_load_functions(library_path, dll) - if loaded: candidates.append(loaded) - - if not candidates: - # no library found - raise AssimpError("assimp library not found") - else: - # get the newest library_path - candidates = map(lambda x: (os.lstat(x[0])[-2], x), candidates) - res = max(candidates, key=operator.itemgetter(0))[1] - logger.debug('Using assimp library located at ' + res[0]) - - # XXX: if there are 1000 dll/so files containing 'assimp' - # in their name, do we have all of them in our address - # space now until gc kicks in? - - # XXX: take version postfix of the .so on linux? - return res[1:] - -def hasattr_silent(object, name): - """ - Calls hasttr() with the given parameters and preserves the legacy (pre-Python 3.2) - functionality of silently catching exceptions. - - Returns the result of hasatter() or False if an exception was raised. - """ - - try: - if not object: - return False - return hasattr(object, name) - except AttributeError: - return False diff --git a/libs/assimp/port/PyAssimp/pyassimp/material.py b/libs/assimp/port/PyAssimp/pyassimp/material.py deleted file mode 100644 index a36e50a..0000000 --- a/libs/assimp/port/PyAssimp/pyassimp/material.py +++ /dev/null @@ -1,89 +0,0 @@ -# Dummy value. -# -# No texture, but the value to be used as 'texture semantic' -# (#aiMaterialProperty::mSemantic) for all material properties -# # not* related to textures. -# -aiTextureType_NONE = 0x0 - -# The texture is combined with the result of the diffuse -# lighting equation. -# -aiTextureType_DIFFUSE = 0x1 - -# The texture is combined with the result of the specular -# lighting equation. -# -aiTextureType_SPECULAR = 0x2 - -# The texture is combined with the result of the ambient -# lighting equation. -# -aiTextureType_AMBIENT = 0x3 - -# The texture is added to the result of the lighting -# calculation. It isn't influenced by incoming light. -# -aiTextureType_EMISSIVE = 0x4 - -# The texture is a height map. -# -# By convention, higher gray-scale values stand for -# higher elevations from the base height. -# -aiTextureType_HEIGHT = 0x5 - -# The texture is a (tangent space) normal-map. -# -# Again, there are several conventions for tangent-space -# normal maps. Assimp does (intentionally) not -# distinguish here. -# -aiTextureType_NORMALS = 0x6 - -# The texture defines the glossiness of the material. -# -# The glossiness is in fact the exponent of the specular -# (phong) lighting equation. Usually there is a conversion -# function defined to map the linear color values in the -# texture to a suitable exponent. Have fun. -# -aiTextureType_SHININESS = 0x7 - -# The texture defines per-pixel opacity. -# -# Usually 'white' means opaque and 'black' means -# 'transparency'. Or quite the opposite. Have fun. -# -aiTextureType_OPACITY = 0x8 - -# Displacement texture -# -# The exact purpose and format is application-dependent. -# Higher color values stand for higher vertex displacements. -# -aiTextureType_DISPLACEMENT = 0x9 - -# Lightmap texture (aka Ambient Occlusion) -# -# Both 'Lightmaps' and dedicated 'ambient occlusion maps' are -# covered by this material property. The texture contains a -# scaling value for the final color value of a pixel. Its -# intensity is not affected by incoming light. -# -aiTextureType_LIGHTMAP = 0xA - -# Reflection texture -# -# Contains the color of a perfect mirror reflection. -# Rarely used, almost never for real-time applications. -# -aiTextureType_REFLECTION = 0xB - -# Unknown texture -# -# A texture reference that does not match any of the definitions -# above is considered to be 'unknown'. It is still imported -# but is excluded from any further postprocessing. -# -aiTextureType_UNKNOWN = 0xC diff --git a/libs/assimp/port/PyAssimp/pyassimp/postprocess.py b/libs/assimp/port/PyAssimp/pyassimp/postprocess.py deleted file mode 100644 index 0c55d67..0000000 --- a/libs/assimp/port/PyAssimp/pyassimp/postprocess.py +++ /dev/null @@ -1,530 +0,0 @@ -# <hr>Calculates the tangents and bitangents for the imported meshes. -# -# Does nothing if a mesh does not have normals. You might want this post -# processing step to be executed if you plan to use tangent space calculations -# such as normal mapping applied to the meshes. There's a config setting, -# <tt>#AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE<tt>, which allows you to specify -# a maximum smoothing angle for the algorithm. However, usually you'll -# want to leave it at the default value. -# -aiProcess_CalcTangentSpace = 0x1 - -## <hr>Identifies and joins identical vertex data sets within all -# imported meshes. -# -# After this step is run, each mesh contains unique vertices, -# so a vertex may be used by multiple faces. You usually want -# to use this post processing step. If your application deals with -# indexed geometry, this step is compulsory or you'll just waste rendering -# time. <b>If this flag is not specified<b>, no vertices are referenced by -# more than one face and <b>no index buffer is required<b> for rendering. -# -aiProcess_JoinIdenticalVertices = 0x2 - -## <hr>Converts all the imported data to a left-handed coordinate space. -# -# By default the data is returned in a right-handed coordinate space (which -# OpenGL prefers). In this space, +X points to the right, -# +Z points towards the viewer, and +Y points upwards. In the DirectX -# coordinate space +X points to the right, +Y points upwards, and +Z points -# away from the viewer. -# -# You'll probably want to consider this flag if you use Direct3D for -# rendering. The #aiProcess_ConvertToLeftHanded flag supersedes this -# setting and bundles all conversions typically required for D3D-based -# applications. -# -aiProcess_MakeLeftHanded = 0x4 - -## <hr>Triangulates all faces of all meshes. -# -# By default the imported mesh data might contain faces with more than 3 -# indices. For rendering you'll usually want all faces to be triangles. -# This post processing step splits up faces with more than 3 indices into -# triangles. Line and point primitives are #not# modified! If you want -# 'triangles only' with no other kinds of primitives, try the following -# solution: -# <ul> -# <li>Specify both #aiProcess_Triangulate and #aiProcess_SortByPType <li> -# <li>Ignore all point and line meshes when you process assimp's output<li> -# <ul> -# -aiProcess_Triangulate = 0x8 - -## <hr>Removes some parts of the data structure (animations, materials, -# light sources, cameras, textures, vertex components). -# -# The components to be removed are specified in a separate -# configuration option, <tt>#AI_CONFIG_PP_RVC_FLAGS<tt>. This is quite useful -# if you don't need all parts of the output structure. Vertex colors -# are rarely used today for example... Calling this step to remove unneeded -# data from the pipeline as early as possible results in increased -# performance and a more optimized output data structure. -# This step is also useful if you want to force Assimp to recompute -# normals or tangents. The corresponding steps don't recompute them if -# they're already there (loaded from the source asset). By using this -# step you can make sure they are NOT there. -# -# This flag is a poor one, mainly because its purpose is usually -# misunderstood. Consider the following case: a 3D model has been exported -# from a CAD app, and it has per-face vertex colors. Vertex positions can't be -# shared, thus the #aiProcess_JoinIdenticalVertices step fails to -# optimize the data because of these nasty little vertex colors. -# Most apps don't even process them, so it's all for nothing. By using -# this step, unneeded components are excluded as early as possible -# thus opening more room for internal optimizations. -# -aiProcess_RemoveComponent = 0x10 - -## <hr>Generates normals for all faces of all meshes. -# -# This is ignored if normals are already there at the time this flag -# is evaluated. Model importers try to load them from the source file, so -# they're usually already there. Face normals are shared between all points -# of a single face, so a single point can have multiple normals, which -# forces the library to duplicate vertices in some cases. -# #aiProcess_JoinIdenticalVertices is #senseless# then. -# -# This flag may not be specified together with #aiProcess_GenSmoothNormals. -# -aiProcess_GenNormals = 0x20 - -## <hr>Generates smooth normals for all vertices in the mesh. -# -# This is ignored if normals are already there at the time this flag -# is evaluated. Model importers try to load them from the source file, so -# they're usually already there. -# -# This flag may not be specified together with -# #aiProcess_GenNormals. There's a configuration option, -# <tt>#AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE<tt> which allows you to specify -# an angle maximum for the normal smoothing algorithm. Normals exceeding -# this limit are not smoothed, resulting in a 'hard' seam between two faces. -# Using a decent angle here (e.g. 80 degrees) results in very good visual -# appearance. -# -aiProcess_GenSmoothNormals = 0x40 - -## <hr>Splits large meshes into smaller sub-meshes. -# -# This is quite useful for real-time rendering, where the number of triangles -# which can be maximally processed in a single draw-call is limited -# by the video driverhardware. The maximum vertex buffer is usually limited -# too. Both requirements can be met with this step: you may specify both a -# triangle and vertex limit for a single mesh. -# -# The split limits can (and should!) be set through the -# <tt>#AI_CONFIG_PP_SLM_VERTEX_LIMIT<tt> and <tt>#AI_CONFIG_PP_SLM_TRIANGLE_LIMIT<tt> -# settings. The default values are <tt>#AI_SLM_DEFAULT_MAX_VERTICES<tt> and -# <tt>#AI_SLM_DEFAULT_MAX_TRIANGLES<tt>. -# -# Note that splitting is generally a time-consuming task, but only if there's -# something to split. The use of this step is recommended for most users. -# -aiProcess_SplitLargeMeshes = 0x80 - -## <hr>Removes the node graph and pre-transforms all vertices with -# the local transformation matrices of their nodes. -# -# The output scene still contains nodes, however there is only a -# root node with children, each one referencing only one mesh, -# and each mesh referencing one material. For rendering, you can -# simply render all meshes in order - you don't need to pay -# attention to local transformations and the node hierarchy. -# Animations are removed during this step. -# This step is intended for applications without a scenegraph. -# The step CAN cause some problems: if e.g. a mesh of the asset -# contains normals and another, using the same material index, does not, -# they will be brought together, but the first meshes's part of -# the normal list is zeroed. However, these artifacts are rare. -# @note The <tt>#AI_CONFIG_PP_PTV_NORMALIZE<tt> configuration property -# can be set to normalize the scene's spatial dimension to the -1...1 -# range. -# -aiProcess_PreTransformVertices = 0x100 - -## <hr>Limits the number of bones simultaneously affecting a single vertex -# to a maximum value. -# -# If any vertex is affected by more than the maximum number of bones, the least -# important vertex weights are removed and the remaining vertex weights are -# renormalized so that the weights still sum up to 1. -# The default bone weight limit is 4 (defined as <tt>#AI_LMW_MAX_WEIGHTS<tt> in -# config.h), but you can use the <tt>#AI_CONFIG_PP_LBW_MAX_WEIGHTS<tt> setting to -# supply your own limit to the post processing step. -# -# If you intend to perform the skinning in hardware, this post processing -# step might be of interest to you. -# -aiProcess_LimitBoneWeights = 0x200 - -## <hr>Validates the imported scene data structure. -# This makes sure that all indices are valid, all animations and -# bones are linked correctly, all material references are correct .. etc. -# -# It is recommended that you capture Assimp's log output if you use this flag, -# so you can easily find out what's wrong if a file fails the -# validation. The validator is quite strict and will find #all# -# inconsistencies in the data structure... It is recommended that plugin -# developers use it to debug their loaders. There are two types of -# validation failures: -# <ul> -# <li>Error: There's something wrong with the imported data. Further -# postprocessing is not possible and the data is not usable at all. -# The import fails. #Importer::GetErrorString() or #aiGetErrorString() -# carry the error message around.<li> -# <li>Warning: There are some minor issues (e.g. 1000000 animation -# keyframes with the same time), but further postprocessing and use -# of the data structure is still safe. Warning details are written -# to the log file, <tt>#AI_SCENE_FLAGS_VALIDATION_WARNING<tt> is set -# in #aiScene::mFlags<li> -# <ul> -# -# This post-processing step is not time-consuming. Its use is not -# compulsory, but recommended. -# -aiProcess_ValidateDataStructure = 0x400 - -## <hr>Reorders triangles for better vertex cache locality. -# -# The step tries to improve the ACMR (average post-transform vertex cache -# miss ratio) for all meshes. The implementation runs in O(n) and is -# roughly based on the 'tipsify' algorithm (see <a href=" -# http:www.cs.princeton.edugfxpubsSander_2007_%3ETRtipsy.pdf">this -# paper<a>). -# -# If you intend to render huge models in hardware, this step might -# be of interest to you. The <tt>#AI_CONFIG_PP_ICL_PTCACHE_SIZE<tt>config -# setting can be used to fine-tune the cache optimization. -# -aiProcess_ImproveCacheLocality = 0x800 - -## <hr>Searches for redundantunreferenced materials and removes them. -# -# This is especially useful in combination with the -# #aiProcess_PretransformVertices and #aiProcess_OptimizeMeshes flags. -# Both join small meshes with equal characteristics, but they can't do -# their work if two meshes have different materials. Because several -# material settings are lost during Assimp's import filters, -# (and because many exporters don't check for redundant materials), huge -# models often have materials which are are defined several times with -# exactly the same settings. -# -# Several material settings not contributing to the final appearance of -# a surface are ignored in all comparisons (e.g. the material name). -# So, if you're passing additional information through the -# content pipeline (probably using #magic# material names), don't -# specify this flag. Alternatively take a look at the -# <tt>#AI_CONFIG_PP_RRM_EXCLUDE_LIST<tt> setting. -# -aiProcess_RemoveRedundantMaterials = 0x1000 - -## <hr>This step tries to determine which meshes have normal vectors -# that are facing inwards and inverts them. -# -# The algorithm is simple but effective: -# the bounding box of all vertices + their normals is compared against -# the volume of the bounding box of all vertices without their normals. -# This works well for most objects, problems might occur with planar -# surfaces. However, the step tries to filter such cases. -# The step inverts all in-facing normals. Generally it is recommended -# to enable this step, although the result is not always correct. -# -aiProcess_FixInfacingNormals = 0x2000 - -## <hr>This step splits meshes with more than one primitive type in -# homogeneous sub-meshes. -# -# The step is executed after the triangulation step. After the step -# returns, just one bit is set in aiMesh::mPrimitiveTypes. This is -# especially useful for real-time rendering where point and line -# primitives are often ignored or rendered separately. -# You can use the <tt>#AI_CONFIG_PP_SBP_REMOVE<tt> option to specify which -# primitive types you need. This can be used to easily exclude -# lines and points, which are rarely used, from the import. -# -aiProcess_SortByPType = 0x8000 - -## <hr>This step searches all meshes for degenerate primitives and -# converts them to proper lines or points. -# -# A face is 'degenerate' if one or more of its points are identical. -# To have the degenerate stuff not only detected and collapsed but -# removed, try one of the following procedures: -# <br><b>1.<b> (if you support lines and points for rendering but don't -# want the degenerates)<br> -# <ul> -# <li>Specify the #aiProcess_FindDegenerates flag. -# <li> -# <li>Set the <tt>AI_CONFIG_PP_FD_REMOVE<tt> option to 1. This will -# cause the step to remove degenerate triangles from the import -# as soon as they're detected. They won't pass any further -# pipeline steps. -# <li> -# <ul> -# <br><b>2.<b>(if you don't support lines and points at all)<br> -# <ul> -# <li>Specify the #aiProcess_FindDegenerates flag. -# <li> -# <li>Specify the #aiProcess_SortByPType flag. This moves line and -# point primitives to separate meshes. -# <li> -# <li>Set the <tt>AI_CONFIG_PP_SBP_REMOVE<tt> option to -# @code aiPrimitiveType_POINTS | aiPrimitiveType_LINES -# @endcode to cause SortByPType to reject point -# and line meshes from the scene. -# <li> -# <ul> -# @note Degenerate polygons are not necessarily evil and that's why -# they're not removed by default. There are several file formats which -# don't support lines or points, and some exporters bypass the -# format specification and write them as degenerate triangles instead. -# -aiProcess_FindDegenerates = 0x10000 - -## <hr>This step searches all meshes for invalid data, such as zeroed -# normal vectors or invalid UV coords and removesfixes them. This is -# intended to get rid of some common exporter errors. -# -# This is especially useful for normals. If they are invalid, and -# the step recognizes this, they will be removed and can later -# be recomputed, i.e. by the #aiProcess_GenSmoothNormals flag.<br> -# The step will also remove meshes that are infinitely small and reduce -# animation tracks consisting of hundreds if redundant keys to a single -# key. The <tt>AI_CONFIG_PP_FID_ANIM_ACCURACY<tt> config property decides -# the accuracy of the check for duplicate animation tracks. -# -aiProcess_FindInvalidData = 0x20000 - -## <hr>This step converts non-UV mappings (such as spherical or -# cylindrical mapping) to proper texture coordinate channels. -# -# Most applications will support UV mapping only, so you will -# probably want to specify this step in every case. Note that Assimp is not -# always able to match the original mapping implementation of the -# 3D app which produced a model perfectly. It's always better to let the -# modelling app compute the UV channels - 3ds max, Maya, Blender, -# LightWave, and Modo do this for example. -# -# @note If this step is not requested, you'll need to process the -# <tt>#AI_MATKEY_MAPPING<tt> material property in order to display all assets -# properly. -# -aiProcess_GenUVCoords = 0x40000 - -## <hr>This step applies per-texture UV transformations and bakes -# them into stand-alone vtexture coordinate channels. -# -# UV transformations are specified per-texture - see the -# <tt>#AI_MATKEY_UVTRANSFORM<tt> material key for more information. -# This step processes all textures with -# transformed input UV coordinates and generates a new (pre-transformed) UV channel -# which replaces the old channel. Most applications won't support UV -# transformations, so you will probably want to specify this step. -# -# @note UV transformations are usually implemented in real-time apps by -# transforming texture coordinates at vertex shader stage with a 3x3 -# (homogenous) transformation matrix. -# -aiProcess_TransformUVCoords = 0x80000 - -## <hr>This step searches for duplicate meshes and replaces them -# with references to the first mesh. -# -# This step takes a while, so don't use it if speed is a concern. -# Its main purpose is to workaround the fact that many export -# file formats don't support instanced meshes, so exporters need to -# duplicate meshes. This step removes the duplicates again. Please -# note that Assimp does not currently support per-node material -# assignment to meshes, which means that identical meshes with -# different materials are currently #not# joined, although this is -# planned for future versions. -# -aiProcess_FindInstances = 0x100000 - -## <hr>A postprocessing step to reduce the number of meshes. -# -# This will, in fact, reduce the number of draw calls. -# -# This is a very effective optimization and is recommended to be used -# together with #aiProcess_OptimizeGraph, if possible. The flag is fully -# compatible with both #aiProcess_SplitLargeMeshes and #aiProcess_SortByPType. -# -aiProcess_OptimizeMeshes = 0x200000 - - -## <hr>A postprocessing step to optimize the scene hierarchy. -# -# Nodes without animations, bones, lights or cameras assigned are -# collapsed and joined. -# -# Node names can be lost during this step. If you use special 'tag nodes' -# to pass additional information through your content pipeline, use the -# <tt>#AI_CONFIG_PP_OG_EXCLUDE_LIST<tt> setting to specify a list of node -# names you want to be kept. Nodes matching one of the names in this list won't -# be touched or modified. -# -# Use this flag with caution. Most simple files will be collapsed to a -# single node, so complex hierarchies are usually completely lost. This is not -# useful for editor environments, but probably a very effective -# optimization if you just want to get the model data, convert it to your -# own format, and render it as fast as possible. -# -# This flag is designed to be used with #aiProcess_OptimizeMeshes for best -# results. -# -# @note 'Crappy' scenes with thousands of extremely small meshes packed -# in deeply nested nodes exist for almost all file formats. -# #aiProcess_OptimizeMeshes in combination with #aiProcess_OptimizeGraph -# usually fixes them all and makes them renderable. -# -aiProcess_OptimizeGraph = 0x400000 - -## <hr>This step flips all UV coordinates along the y-axis and adjusts -# material settings and bitangents accordingly. -# -# <b>Output UV coordinate system:<b> -# @code -# 0y|0y ---------- 1x|0y -# | | -# | | -# | | -# 0x|1y ---------- 1x|1y -# @endcode -# -# You'll probably want to consider this flag if you use Direct3D for -# rendering. The #aiProcess_ConvertToLeftHanded flag supersedes this -# setting and bundles all conversions typically required for D3D-based -# applications. -# -aiProcess_FlipUVs = 0x800000 - -## <hr>This step adjusts the output face winding order to be CW. -# -# The default face winding order is counter clockwise (CCW). -# -# <b>Output face order:<b> -# @code -# x2 -# -# x0 -# x1 -# @endcode -# -aiProcess_FlipWindingOrder = 0x1000000 - -## <hr>This step splits meshes with many bones into sub-meshes so that each -# su-bmesh has fewer or as many bones as a given limit. -# -aiProcess_SplitByBoneCount = 0x2000000 - -## <hr>This step removes bones losslessly or according to some threshold. -# -# In some cases (i.e. formats that require it) exporters are forced to -# assign dummy bone weights to otherwise static meshes assigned to -# animated meshes. Full, weight-based skinning is expensive while -# animating nodes is extremely cheap, so this step is offered to clean up -# the data in that regard. -# -# Use <tt>#AI_CONFIG_PP_DB_THRESHOLD<tt> to control this. -# Use <tt>#AI_CONFIG_PP_DB_ALL_OR_NONE<tt> if you want bones removed if and -# only if all bones within the scene qualify for removal. -# -aiProcess_Debone = 0x4000000 - -aiProcess_GenEntityMeshes = 0x100000 -aiProcess_OptimizeAnimations = 0x200000 -aiProcess_FixTexturePaths = 0x200000 -aiProcess_EmbedTextures = 0x10000000, - -## @def aiProcess_ConvertToLeftHanded - # @brief Shortcut flag for Direct3D-based applications. - # - # Supersedes the #aiProcess_MakeLeftHanded and #aiProcess_FlipUVs and - # #aiProcess_FlipWindingOrder flags. - # The output data matches Direct3D's conventions: left-handed geometry, upper-left - # origin for UV coordinates and finally clockwise face order, suitable for CCW culling. - # - # @deprecated - # -aiProcess_ConvertToLeftHanded = ( \ - aiProcess_MakeLeftHanded | \ - aiProcess_FlipUVs | \ - aiProcess_FlipWindingOrder | \ - 0 ) - - -## @def aiProcessPreset_TargetRealtimeUse_Fast - # @brief Default postprocess configuration optimizing the data for real-time rendering. - # - # Applications would want to use this preset to load models on end-user PCs, - # maybe for direct use in game. - # - # If you're using DirectX, don't forget to combine this value with - # the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations - # in your application apply the #aiProcess_TransformUVCoords step, too. - # @note Please take the time to read the docs for the steps enabled by this preset. - # Some of them offer further configurable properties, while some of them might not be of - # use for you so it might be better to not specify them. - # -aiProcessPreset_TargetRealtime_Fast = ( \ - aiProcess_CalcTangentSpace | \ - aiProcess_GenNormals | \ - aiProcess_JoinIdenticalVertices | \ - aiProcess_Triangulate | \ - aiProcess_GenUVCoords | \ - aiProcess_SortByPType | \ - 0 ) - - ## @def aiProcessPreset_TargetRealtime_Quality - # @brief Default postprocess configuration optimizing the data for real-time rendering. - # - # Unlike #aiProcessPreset_TargetRealtime_Fast, this configuration - # performs some extra optimizations to improve rendering speed and - # to minimize memory usage. It could be a good choice for a level editor - # environment where import speed is not so important. - # - # If you're using DirectX, don't forget to combine this value with - # the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations - # in your application apply the #aiProcess_TransformUVCoords step, too. - # @note Please take the time to read the docs for the steps enabled by this preset. - # Some of them offer further configurable properties, while some of them might not be - # of use for you so it might be better to not specify them. - # -aiProcessPreset_TargetRealtime_Quality = ( \ - aiProcess_CalcTangentSpace | \ - aiProcess_GenSmoothNormals | \ - aiProcess_JoinIdenticalVertices | \ - aiProcess_ImproveCacheLocality | \ - aiProcess_LimitBoneWeights | \ - aiProcess_RemoveRedundantMaterials | \ - aiProcess_SplitLargeMeshes | \ - aiProcess_Triangulate | \ - aiProcess_GenUVCoords | \ - aiProcess_SortByPType | \ - aiProcess_FindDegenerates | \ - aiProcess_FindInvalidData | \ - 0 ) - - ## @def aiProcessPreset_TargetRealtime_MaxQuality - # @brief Default postprocess configuration optimizing the data for real-time rendering. - # - # This preset enables almost every optimization step to achieve perfectly - # optimized data. It's your choice for level editor environments where import speed - # is not important. - # - # If you're using DirectX, don't forget to combine this value with - # the #aiProcess_ConvertToLeftHanded step. If you don't support UV transformations - # in your application, apply the #aiProcess_TransformUVCoords step, too. - # @note Please take the time to read the docs for the steps enabled by this preset. - # Some of them offer further configurable properties, while some of them might not be - # of use for you so it might be better to not specify them. - # -aiProcessPreset_TargetRealtime_MaxQuality = ( \ - aiProcessPreset_TargetRealtime_Quality | \ - aiProcess_FindInstances | \ - aiProcess_ValidateDataStructure | \ - aiProcess_OptimizeMeshes | \ - 0 ) - - diff --git a/libs/assimp/port/PyAssimp/pyassimp/structs.py b/libs/assimp/port/PyAssimp/pyassimp/structs.py deleted file mode 100644 index e1fba19..0000000 --- a/libs/assimp/port/PyAssimp/pyassimp/structs.py +++ /dev/null @@ -1,1135 +0,0 @@ -#-*- coding: utf-8 -*- - -from ctypes import POINTER, c_void_p, c_uint, c_char, c_float, Structure, c_double, c_ubyte, c_size_t, c_uint32 - - -class Vector2D(Structure): - """ - See 'vector2.h' for details. - """ - - - _fields_ = [ - ("x", c_float),("y", c_float), - ] - -class Matrix3x3(Structure): - """ - See 'matrix3x3.h' for details. - """ - - - _fields_ = [ - ("a1", c_float),("a2", c_float),("a3", c_float), - ("b1", c_float),("b2", c_float),("b3", c_float), - ("c1", c_float),("c2", c_float),("c3", c_float), - ] - -class Texel(Structure): - """ - See 'texture.h' for details. - """ - - _fields_ = [ - ("b", c_ubyte),("g", c_ubyte),("r", c_ubyte),("a", c_ubyte), - ] - -class Color4D(Structure): - """ - See 'color4.h' for details. - """ - - - _fields_ = [ - # Red, green, blue and alpha color values - ("r", c_float),("g", c_float),("b", c_float),("a", c_float), - ] - -class Plane(Structure): - """ - See 'types.h' for details. - """ - - _fields_ = [ - # Plane equation - ("a", c_float),("b", c_float),("c", c_float),("d", c_float), - ] - -class Color3D(Structure): - """ - See 'types.h' for details. - """ - - _fields_ = [ - # Red, green and blue color values - ("r", c_float),("g", c_float),("b", c_float), - ] - -class String(Structure): - """ - See 'types.h' for details. - """ - - MAXLEN = 1024 - - _fields_ = [ - # Binary length of the string excluding the terminal 0. This is NOT the - # logical length of strings containing UTF-8 multibyte sequences! It's - # the number of bytes from the beginning of the string to its end. - ("length", c_uint32), - - # String buffer. Size limit is MAXLEN - ("data", c_char*MAXLEN), - ] - -class MaterialPropertyString(Structure): - """ - See 'MaterialSystem.cpp' for details. - - The size of length is truncated to 4 bytes on 64-bit platforms when used as a - material property (see MaterialSystem.cpp aiMaterial::AddProperty() for details). - """ - - MAXLEN = 1024 - - _fields_ = [ - # Binary length of the string excluding the terminal 0. This is NOT the - # logical length of strings containing UTF-8 multibyte sequences! It's - # the number of bytes from the beginning of the string to its end. - ("length", c_uint32), - - # String buffer. Size limit is MAXLEN - ("data", c_char*MAXLEN), - ] - -class MemoryInfo(Structure): - """ - See 'types.h' for details. - """ - - _fields_ = [ - # Storage allocated for texture data - ("textures", c_uint), - - # Storage allocated for material data - ("materials", c_uint), - - # Storage allocated for mesh data - ("meshes", c_uint), - - # Storage allocated for node data - ("nodes", c_uint), - - # Storage allocated for animation data - ("animations", c_uint), - - # Storage allocated for camera data - ("cameras", c_uint), - - # Storage allocated for light data - ("lights", c_uint), - - # Total storage allocated for the full import. - ("total", c_uint), - ] - -class Quaternion(Structure): - """ - See 'quaternion.h' for details. - """ - - - _fields_ = [ - # w,x,y,z components of the quaternion - ("w", c_float),("x", c_float),("y", c_float),("z", c_float), - ] - -class Face(Structure): - """ - See 'mesh.h' for details. - """ - - _fields_ = [ - # Number of indices defining this face. - # The maximum value for this member is - #AI_MAX_FACE_INDICES. - ("mNumIndices", c_uint), - - # Pointer to the indices array. Size of the array is given in numIndices. - ("mIndices", POINTER(c_uint)), - ] - -class VertexWeight(Structure): - """ - See 'mesh.h' for details. - """ - - _fields_ = [ - # Index of the vertex which is influenced by the bone. - ("mVertexId", c_uint), - - # The strength of the influence in the range (0...1). - # The influence from all bones at one vertex amounts to 1. - ("mWeight", c_float), - ] - -class Matrix4x4(Structure): - """ - See 'matrix4x4.h' for details. - """ - - - _fields_ = [ - ("a1", c_float),("a2", c_float),("a3", c_float),("a4", c_float), - ("b1", c_float),("b2", c_float),("b3", c_float),("b4", c_float), - ("c1", c_float),("c2", c_float),("c3", c_float),("c4", c_float), - ("d1", c_float),("d2", c_float),("d3", c_float),("d4", c_float), - ] - -class Vector3D(Structure): - """ - See 'vector3.h' for details. - """ - - - _fields_ = [ - ("x", c_float),("y", c_float),("z", c_float), - ] - -class MeshKey(Structure): - """ - See 'anim.h' for details. - """ - - _fields_ = [ - # The time of this key - ("mTime", c_double), - - # Index into the aiMesh::mAnimMeshes array of the - # mesh corresponding to the - #aiMeshAnim hosting this - # key frame. The referenced anim mesh is evaluated - # according to the rules defined in the docs for - #aiAnimMesh. - ("mValue", c_uint), - ] - -class MetadataEntry(Structure): - """ - See 'metadata.h' for details - """ - AI_BOOL = 0 - AI_INT32 = 1 - AI_UINT64 = 2 - AI_FLOAT = 3 - AI_DOUBLE = 4 - AI_AISTRING = 5 - AI_AIVECTOR3D = 6 - AI_META_MAX = 7 - _fields_ = [ - # The type field uniquely identifies the underlying type of the data field - ("mType", c_uint), - ("mData", c_void_p), - ] - -class Metadata(Structure): - """ - See 'metadata.h' for details - """ - _fields_ = [ - # Length of the mKeys and mValues arrays, respectively - ("mNumProperties", c_uint), - - # Arrays of keys, may not be NULL. Entries in this array may not be NULL - # as well. - ("mKeys", POINTER(String)), - - # Arrays of values, may not be NULL. Entries in this array may be NULL - # if the corresponding property key has no assigned value. - ("mValues", POINTER(MetadataEntry)), - ] - -class Node(Structure): - """ - See 'scene.h' for details. - """ - - -Node._fields_ = [ - # The name of the node. - # The name might be empty (length of zero) but all nodes which - # need to be accessed afterwards by bones or anims are usually named. - # Multiple nodes may have the same name, but nodes which are accessed - # by bones (see - #aiBone and - #aiMesh::mBones) *must* be unique. - # Cameras and lights are assigned to a specific node name - if there - # are multiple nodes with this name, they're assigned to each of them. - # <br> - # There are no limitations regarding the characters contained in - # this text. You should be able to handle stuff like whitespace, tabs, - # linefeeds, quotation marks, ampersands, ... . - ("mName", String), - - # The transformation relative to the node's parent. - ("mTransformation", Matrix4x4), - - # Parent node. NULL if this node is the root node. - ("mParent", POINTER(Node)), - - # The number of child nodes of this node. - ("mNumChildren", c_uint), - - # The child nodes of this node. NULL if mNumChildren is 0. - ("mChildren", POINTER(POINTER(Node))), - - # The number of meshes of this node. - ("mNumMeshes", c_uint), - - # The meshes of this node. Each entry is an index into the mesh - ("mMeshes", POINTER(c_uint)), - - # Metadata associated with this node or NULL if there is no metadata. - # Whether any metadata is generated depends on the source file format. - ("mMetadata", POINTER(Metadata)), - ] - -class Light(Structure): - """ - See 'light.h' for details. - """ - - - _fields_ = [ - # The name of the light source. - # There must be a node in the scenegraph with the same name. - # This node specifies the position of the light in the scene - # hierarchy and can be animated. - ("mName", String), - - # The type of the light source. - # aiLightSource_UNDEFINED is not a valid value for this member. - ("mType", c_uint), - - # Position of the light source in space. Relative to the - # transformation of the node corresponding to the light. - # The position is undefined for directional lights. - ("mPosition", Vector3D), - - # Direction of the light source in space. Relative to the - # transformation of the node corresponding to the light. - # The direction is undefined for point lights. The vector - # may be normalized, but it needn't. - ("mDirection", Vector3D), - - # Up direction of the light source in space. Relative to the - # transformation of the node corresponding to the light. - # - # The direction is undefined for point lights. The vector - # may be normalized, but it needn't. - ("mUp", Vector3D), - - # Constant light attenuation factor. - # The intensity of the light source at a given distance 'd' from - # the light's position is - # @code - # Atten = 1/( att0 + att1 - # d + att2 - # d*d) - # @endcode - # This member corresponds to the att0 variable in the equation. - # Naturally undefined for directional lights. - ("mAttenuationConstant", c_float), - - # Linear light attenuation factor. - # The intensity of the light source at a given distance 'd' from - # the light's position is - # @code - # Atten = 1/( att0 + att1 - # d + att2 - # d*d) - # @endcode - # This member corresponds to the att1 variable in the equation. - # Naturally undefined for directional lights. - ("mAttenuationLinear", c_float), - - # Quadratic light attenuation factor. - # The intensity of the light source at a given distance 'd' from - # the light's position is - # @code - # Atten = 1/( att0 + att1 - # d + att2 - # d*d) - # @endcode - # This member corresponds to the att2 variable in the equation. - # Naturally undefined for directional lights. - ("mAttenuationQuadratic", c_float), - - # Diffuse color of the light source - # The diffuse light color is multiplied with the diffuse - # material color to obtain the final color that contributes - # to the diffuse shading term. - ("mColorDiffuse", Color3D), - - # Specular color of the light source - # The specular light color is multiplied with the specular - # material color to obtain the final color that contributes - # to the specular shading term. - ("mColorSpecular", Color3D), - - # Ambient color of the light source - # The ambient light color is multiplied with the ambient - # material color to obtain the final color that contributes - # to the ambient shading term. Most renderers will ignore - # this value it, is just a remaining of the fixed-function pipeline - # that is still supported by quite many file formats. - ("mColorAmbient", Color3D), - - # Inner angle of a spot light's light cone. - # The spot light has maximum influence on objects inside this - # angle. The angle is given in radians. It is 2PI for point - # lights and undefined for directional lights. - ("mAngleInnerCone", c_float), - - # Outer angle of a spot light's light cone. - # The spot light does not affect objects outside this angle. - # The angle is given in radians. It is 2PI for point lights and - # undefined for directional lights. The outer angle must be - # greater than or equal to the inner angle. - # It is assumed that the application uses a smooth - # interpolation between the inner and the outer cone of the - # spot light. - ("mAngleOuterCone", c_float), - - # Size of area light source. - ("mSize", Vector2D), - ] - -class Texture(Structure): - """ - See 'texture.h' for details. - """ - - - _fields_ = [ - # Width of the texture, in pixels - # If mHeight is zero the texture is compressed in a format - # like JPEG. In this case mWidth specifies the size of the - # memory area pcData is pointing to, in bytes. - ("mWidth", c_uint), - - # Height of the texture, in pixels - # If this value is zero, pcData points to an compressed texture - # in any format (e.g. JPEG). - ("mHeight", c_uint), - - # A hint from the loader to make it easier for applications - # to determine the type of embedded textures. - # - # If mHeight != 0 this member is show how data is packed. Hint will consist of - # two parts: channel order and channel bitness (count of the bits for every - # color channel). For simple parsing by the viewer it's better to not omit - # absent color channel and just use 0 for bitness. For example: - # 1. Image contain RGBA and 8 bit per channel, achFormatHint == "rgba8888"; - # 2. Image contain ARGB and 8 bit per channel, achFormatHint == "argb8888"; - # 3. Image contain RGB and 5 bit for R and B channels and 6 bit for G channel, - # achFormatHint == "rgba5650"; - # 4. One color image with B channel and 1 bit for it, achFormatHint == "rgba0010"; - # If mHeight == 0 then achFormatHint is set set to '\\0\\0\\0\\0' if the loader has no additional - # information about the texture file format used OR the - # file extension of the format without a trailing dot. If there - # are multiple file extensions for a format, the shortest - # extension is chosen (JPEG maps to 'jpg', not to 'jpeg'). - # E.g. 'dds\\0', 'pcx\\0', 'jpg\\0'. All characters are lower-case. - # The fourth character will always be '\\0'. - ("achFormatHint", c_char*9), - - # Data of the texture. - # Points to an array of mWidth - # mHeight aiTexel's. - # The format of the texture data is always ARGB8888 to - # make the implementation for user of the library as easy - # as possible. If mHeight = 0 this is a pointer to a memory - # buffer of size mWidth containing the compressed texture - # data. Good luck, have fun! - ("pcData", POINTER(Texel)), - - # Texture original filename - # Used to get the texture reference - ("mFilename", String), - ] - -class Ray(Structure): - """ - See 'types.h' for details. - """ - - _fields_ = [ - # Position and direction of the ray - ("pos", Vector3D),("dir", Vector3D), - ] - -class UVTransform(Structure): - """ - See 'material.h' for details. - """ - - _fields_ = [ - # Translation on the u and v axes. - # The default value is (0|0). - ("mTranslation", Vector2D), - - # Scaling on the u and v axes. - # The default value is (1|1). - ("mScaling", Vector2D), - - # Rotation - in counter-clockwise direction. - # The rotation angle is specified in radians. The - # rotation center is 0.5f|0.5f. The default value - # 0.f. - ("mRotation", c_float), - ] - -class MaterialProperty(Structure): - """ - See 'material.h' for details. - """ - - _fields_ = [ - # Specifies the name of the property (key) - # Keys are generally case insensitive. - ("mKey", String), - - # Textures: Specifies their exact usage semantic. - # For non-texture properties, this member is always 0 - # (or, better-said, - #aiTextureType_NONE). - ("mSemantic", c_uint), - - # Textures: Specifies the index of the texture. - # For non-texture properties, this member is always 0. - ("mIndex", c_uint), - - # Size of the buffer mData is pointing to, in bytes. - # This value may not be 0. - ("mDataLength", c_uint), - - # Type information for the property. - # Defines the data layout inside the data buffer. This is used - # by the library internally to perform debug checks and to - # utilize proper type conversions. - # (It's probably a hacky solution, but it works.) - ("mType", c_uint), - - # Binary buffer to hold the property's value. - # The size of the buffer is always mDataLength. - ("mData", POINTER(c_char)), - ] - -class Material(Structure): - """ - See 'material.h' for details. - """ - - _fields_ = [ - # List of all material properties loaded. - ("mProperties", POINTER(POINTER(MaterialProperty))), - - # Number of properties in the data base - ("mNumProperties", c_uint), - - # Storage allocated - ("mNumAllocated", c_uint), - ] - -class Bone(Structure): - """ - See 'mesh.h' for details. - """ - - _fields_ = [ - # The name of the bone. - ("mName", String), - - # The number of vertices affected by this bone - # The maximum value for this member is - #AI_MAX_BONE_WEIGHTS. - ("mNumWeights", c_uint), - - # The vertices affected by this bone - ("mWeights", POINTER(VertexWeight)), - - # Matrix that transforms from mesh space to bone space in bind pose - ("mOffsetMatrix", Matrix4x4), - ] - - -class AnimMesh(Structure): - """ - See 'mesh.h' for details. - """ - - AI_MAX_NUMBER_OF_TEXTURECOORDS = 0x8 - AI_MAX_NUMBER_OF_COLOR_SETS = 0x8 - - _fields_ = [ - # Anim Mesh name - ("mName", String), - - # Replacement for aiMesh::mVertices. If this array is non-NULL, - # it *must* contain mNumVertices entries. The corresponding - # array in the host mesh must be non-NULL as well - animation - # meshes may neither add or nor remove vertex components (if - # a replacement array is NULL and the corresponding source - # array is not, the source data is taken instead) - ("mVertices", POINTER(Vector3D)), - - # Replacement for aiMesh::mNormals. - ("mNormals", POINTER(Vector3D)), - - # Replacement for aiMesh::mTangents. - ("mTangents", POINTER(Vector3D)), - - # Replacement for aiMesh::mBitangents. - ("mBitangents", POINTER(Vector3D)), - - # Replacement for aiMesh::mColors - ("mColors", POINTER(Color4D) * AI_MAX_NUMBER_OF_COLOR_SETS), - - # Replacement for aiMesh::mTextureCoords - ("mTextureCoords", POINTER(Vector3D) * AI_MAX_NUMBER_OF_TEXTURECOORDS), - - # The number of vertices in the aiAnimMesh, and thus the length of all - # the member arrays. - # - # This has always the same value as the mNumVertices property in the - # corresponding aiMesh. It is duplicated here merely to make the length - # of the member arrays accessible even if the aiMesh is not known, e.g. - # from language bindings. - ("mNumVertices", c_uint), - - # Weight of the AnimMesh. - ("mWeight", c_float), - ] - - -class Mesh(Structure): - """ - See 'mesh.h' for details. - """ - - AI_MAX_FACE_INDICES = 0x7fff - AI_MAX_BONE_WEIGHTS = 0x7fffffff - AI_MAX_VERTICES = 0x7fffffff - AI_MAX_FACES = 0x7fffffff - AI_MAX_NUMBER_OF_COLOR_SETS = 0x8 - AI_MAX_NUMBER_OF_TEXTURECOORDS = 0x8 - - _fields_ = [ # Bitwise combination of the members of the - #aiPrimitiveType enum. - # This specifies which types of primitives are present in the mesh. - # The "SortByPrimitiveType"-Step can be used to make sure the - # output meshes consist of one primitive type each. - ("mPrimitiveTypes", c_uint), - - # The number of vertices in this mesh. - # This is also the size of all of the per-vertex data arrays. - # The maximum value for this member is - #AI_MAX_VERTICES. - ("mNumVertices", c_uint), - - # The number of primitives (triangles, polygons, lines) in this mesh. - # This is also the size of the mFaces array. - # The maximum value for this member is - #AI_MAX_FACES. - ("mNumFaces", c_uint), - - # Vertex positions. - # This array is always present in a mesh. The array is - # mNumVertices in size. - ("mVertices", POINTER(Vector3D)), - - # Vertex normals. - # The array contains normalized vectors, NULL if not present. - # The array is mNumVertices in size. Normals are undefined for - # point and line primitives. A mesh consisting of points and - # lines only may not have normal vectors. Meshes with mixed - # primitive types (i.e. lines and triangles) may have normals, - # but the normals for vertices that are only referenced by - # point or line primitives are undefined and set to QNaN (WARN: - # qNaN compares to inequal to *everything*, even to qNaN itself. - # Using code like this to check whether a field is qnan is: - # @code - #define IS_QNAN(f) (f != f) - # @endcode - # still dangerous because even 1.f == 1.f could evaluate to false! ( - # remember the subtleties of IEEE754 artithmetics). Use stuff like - # @c fpclassify instead. - # @note Normal vectors computed by Assimp are always unit-length. - # However, this needn't apply for normals that have been taken - # directly from the model file. - ("mNormals", POINTER(Vector3D)), - - # Vertex tangents. - # The tangent of a vertex points in the direction of the positive - # X texture axis. The array contains normalized vectors, NULL if - # not present. The array is mNumVertices in size. A mesh consisting - # of points and lines only may not have normal vectors. Meshes with - # mixed primitive types (i.e. lines and triangles) may have - # normals, but the normals for vertices that are only referenced by - # point or line primitives are undefined and set to qNaN. See - # the - #mNormals member for a detailed discussion of qNaNs. - # @note If the mesh contains tangents, it automatically also - # contains bitangents (the bitangent is just the cross product of - # tangent and normal vectors). - ("mTangents", POINTER(Vector3D)), - - # Vertex bitangents. - # The bitangent of a vertex points in the direction of the positive - # Y texture axis. The array contains normalized vectors, NULL if not - # present. The array is mNumVertices in size. - # @note If the mesh contains tangents, it automatically also contains - # bitangents. - ("mBitangents", POINTER(Vector3D)), - - # Vertex color sets. - # A mesh may contain 0 to - #AI_MAX_NUMBER_OF_COLOR_SETS vertex - # colors per vertex. NULL if not present. Each array is - # mNumVertices in size if present. - ("mColors", POINTER(Color4D)*AI_MAX_NUMBER_OF_COLOR_SETS), - - # Vertex texture coords, also known as UV channels. - # A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per - # vertex. NULL if not present. The array is mNumVertices in size. - ("mTextureCoords", POINTER(Vector3D)*AI_MAX_NUMBER_OF_TEXTURECOORDS), - - # Specifies the number of components for a given UV channel. - # Up to three channels are supported (UVW, for accessing volume - # or cube maps). If the value is 2 for a given channel n, the - # component p.z of mTextureCoords[n][p] is set to 0.0f. - # If the value is 1 for a given channel, p.y is set to 0.0f, too. - # @note 4D coords are not supported - ("mNumUVComponents", c_uint*AI_MAX_NUMBER_OF_TEXTURECOORDS), - - # The faces the mesh is constructed from. - # Each face refers to a number of vertices by their indices. - # This array is always present in a mesh, its size is given - # in mNumFaces. If the - #AI_SCENE_FLAGS_NON_VERBOSE_FORMAT - # is NOT set each face references an unique set of vertices. - ("mFaces", POINTER(Face)), - - # The number of bones this mesh contains. - # Can be 0, in which case the mBones array is NULL. - ("mNumBones", c_uint), - - # The bones of this mesh. - # A bone consists of a name by which it can be found in the - # frame hierarchy and a set of vertex weights. - ("mBones", POINTER(POINTER(Bone))), - - # The material used by this mesh. - # A mesh does use only a single material. If an imported model uses - # multiple materials, the import splits up the mesh. Use this value - # as index into the scene's material list. - ("mMaterialIndex", c_uint), - - # Name of the mesh. Meshes can be named, but this is not a - # requirement and leaving this field empty is totally fine. - # There are mainly three uses for mesh names: - # - some formats name nodes and meshes independently. - # - importers tend to split meshes up to meet the - # one-material-per-mesh requirement. Assigning - # the same (dummy) name to each of the result meshes - # aids the caller at recovering the original mesh - # partitioning. - # - Vertex animations refer to meshes by their names. - ("mName", String), - - # The number of attachment meshes. Note! Currently only works with Collada loader. - ("mNumAnimMeshes", c_uint), - - # Attachment meshes for this mesh, for vertex-based animation. - # Attachment meshes carry replacement data for some of the - # mesh'es vertex components (usually positions, normals). - # Note! Currently only works with Collada loader. - ("mAnimMeshes", POINTER(POINTER(AnimMesh))), - - # Method of morphing when animeshes are specified. - ("mMethod", c_uint), - - ] - -class Camera(Structure): - """ - See 'camera.h' for details. - """ - - - _fields_ = [ - # The name of the camera. - # There must be a node in the scenegraph with the same name. - # This node specifies the position of the camera in the scene - # hierarchy and can be animated. - ("mName", String), - - # Position of the camera relative to the coordinate space - # defined by the corresponding node. - # The default value is 0|0|0. - ("mPosition", Vector3D), - - # 'Up' - vector of the camera coordinate system relative to - # the coordinate space defined by the corresponding node. - # The 'right' vector of the camera coordinate system is - # the cross product of the up and lookAt vectors. - # The default value is 0|1|0. The vector - # may be normalized, but it needn't. - ("mUp", Vector3D), - - # 'LookAt' - vector of the camera coordinate system relative to - # the coordinate space defined by the corresponding node. - # This is the viewing direction of the user. - # The default value is 0|0|1. The vector - # may be normalized, but it needn't. - ("mLookAt", Vector3D), - - # Half horizontal field of view angle, in radians. - # The field of view angle is the angle between the center - # line of the screen and the left or right border. - # The default value is 1/4PI. - ("mHorizontalFOV", c_float), - - # Distance of the near clipping plane from the camera. - # The value may not be 0.f (for arithmetic reasons to prevent - # a division through zero). The default value is 0.1f. - ("mClipPlaneNear", c_float), - - # Distance of the far clipping plane from the camera. - # The far clipping plane must, of course, be further away than the - # near clipping plane. The default value is 1000.f. The ratio - # between the near and the far plane should not be too - # large (between 1000-10000 should be ok) to avoid floating-point - # inaccuracies which could lead to z-fighting. - ("mClipPlaneFar", c_float), - - # Screen aspect ratio. - # This is the ration between the width and the height of the - # screen. Typical values are 4/3, 1/2 or 1/1. This value is - # 0 if the aspect ratio is not defined in the source file. - # 0 is also the default value. - ("mAspect", c_float), - ] - -class VectorKey(Structure): - """ - See 'anim.h' for details. - """ - - _fields_ = [ - # The time of this key - ("mTime", c_double), - - # The value of this key - ("mValue", Vector3D), - ] - -class QuatKey(Structure): - """ - See 'anim.h' for details. - """ - - _fields_ = [ - # The time of this key - ("mTime", c_double), - - # The value of this key - ("mValue", Quaternion), - ] - -class MeshMorphKey(Structure): - """ - See 'anim.h' for details. - """ - - _fields_ = [ - # The time of this key - ("mTime", c_double), - - # The values and weights at the time of this key - ("mValues", POINTER(c_uint)), - ("mWeights", POINTER(c_double)), - - # The number of values and weights - ("mNumValuesAndWeights", c_uint), - - ] - -class NodeAnim(Structure): - """ - See 'anim.h' for details. - """ - - _fields_ = [ - # The name of the node affected by this animation. The node - # must exist and it must be unique. - ("mNodeName", String), - - # The number of position keys - ("mNumPositionKeys", c_uint), - - # The position keys of this animation channel. Positions are - # specified as 3D vector. The array is mNumPositionKeys in size. - # If there are position keys, there will also be at least one - # scaling and one rotation key. - ("mPositionKeys", POINTER(VectorKey)), - - # The number of rotation keys - ("mNumRotationKeys", c_uint), - - # The rotation keys of this animation channel. Rotations are - # given as quaternions, which are 4D vectors. The array is - # mNumRotationKeys in size. - # If there are rotation keys, there will also be at least one - # scaling and one position key. - ("mRotationKeys", POINTER(QuatKey)), - - # The number of scaling keys - ("mNumScalingKeys", c_uint), - - # The scaling keys of this animation channel. Scalings are - # specified as 3D vector. The array is mNumScalingKeys in size. - # If there are scaling keys, there will also be at least one - # position and one rotation key. - ("mScalingKeys", POINTER(VectorKey)), - - # Defines how the animation behaves before the first - # key is encountered. - # The default value is aiAnimBehaviour_DEFAULT (the original - # transformation matrix of the affected node is used). - ("mPreState", c_uint), - - # Defines how the animation behaves after the last - # key was processed. - # The default value is aiAnimBehaviour_DEFAULT (the original - # transformation matrix of the affected node is taken). - ("mPostState", c_uint), - ] - -class MeshAnim(Structure): - """ - See 'anim.h' for details. - """ - - _fields_ = [ - # Name of the mesh to be animated. An empty string is not allowed, - # animated meshes need to be named (not necessarily uniquely, - # the name can basically serve as wild-card to select a group - # of meshes with similar animation setup) - ("mName", String), - - # Size of the #mKeys array. Must be 1, at least. - ("mNumKeys", c_uint), - - # Key frames of the animation. May not be NULL. - ("mKeys", POINTER(MeshKey)), - ] - -class MeshMorphAnim(Structure): - """ - See 'anim.h' for details. - """ - - _fields_ = [ - # Name of the mesh to be animated. An empty string is not allowed, - # animated meshes need to be named (not necessarily uniquely, - # the name can basically serve as wildcard to select a group - # of meshes with similar animation setup) - ("mName", String), - - # Size of the #mKeys array. Must be 1, at least. - ("mNumKeys", c_uint), - - # Key frames of the animation. May not be NULL. - ("mKeys", POINTER(MeshMorphKey)), - ] - - -class Animation(Structure): - """ - See 'anim.h' for details. - """ - - _fields_ = [ - # The name of the animation. If the modeling package this data was - # exported from does support only a single animation channel, this - # name is usually empty (length is zero). - ("mName", String), - - # Duration of the animation in ticks. - ("mDuration", c_double), - - # Ticks per second. 0 if not specified in the imported file - ("mTicksPerSecond", c_double), - - # The number of bone animation channels. Each channel affects - # a single node. - ("mNumChannels", c_uint), - - # The node animation channels. Each channel affects a single node. - # The array is mNumChannels in size. - ("mChannels", POINTER(POINTER(NodeAnim))), - - # The number of mesh animation channels. Each channel affects - # a single mesh and defines vertex-based animation. - ("mNumMeshChannels", c_uint), - - # The mesh animation channels. Each channel affects a single mesh. - # The array is mNumMeshChannels in size. - ("mMeshChannels", POINTER(POINTER(MeshAnim))), - - # The number of mesh animation channels. Each channel affects - # a single mesh and defines morphing animation. - ("mNumMorphMeshChannels", c_uint), - - # The morph mesh animation channels. Each channel affects a single mesh. - # The array is mNumMorphMeshChannels in size. - ("mMorphMeshChannels", POINTER(POINTER(MeshMorphAnim))), - - ] - -class ExportDataBlob(Structure): - """ - See 'cexport.h' for details. - - Note that the '_fields_' definition is outside the class to allow the 'next' field to be recursive - """ - pass - -ExportDataBlob._fields_ = [ - # Size of the data in bytes - ("size", c_size_t), - - # The data. - ("data", c_void_p), - - # Name of the blob. An empty string always - # indicates the first (and primary) blob, - # which contains the actual file data. - # Any other blobs are auxiliary files produced - # by exporters (i.e. material files). Existence - # of such files depends on the file format. Most - # formats don't split assets across multiple files. - # - # If used, blob names usually contain the file - # extension that should be used when writing - # the data to disc. - ("name", String), - - # Pointer to the next blob in the chain or NULL if there is none. - ("next", POINTER(ExportDataBlob)), - ] - - -class Scene(Structure): - """ - See 'aiScene.h' for details. - """ - - AI_SCENE_FLAGS_INCOMPLETE = 0x1 - AI_SCENE_FLAGS_VALIDATED = 0x2 - AI_SCENE_FLAGS_VALIDATION_WARNING = 0x4 - AI_SCENE_FLAGS_NON_VERBOSE_FORMAT = 0x8 - AI_SCENE_FLAGS_TERRAIN = 0x10 - AI_SCENE_FLAGS_ALLOW_SHARED = 0x20 - - _fields_ = [ - # Any combination of the AI_SCENE_FLAGS_XXX flags. By default - # this value is 0, no flags are set. Most applications will - # want to reject all scenes with the AI_SCENE_FLAGS_INCOMPLETE - # bit set. - ("mFlags", c_uint), - - # The root node of the hierarchy. - # There will always be at least the root node if the import - # was successful (and no special flags have been set). - # Presence of further nodes depends on the format and content - # of the imported file. - ("mRootNode", POINTER(Node)), - - # The number of meshes in the scene. - ("mNumMeshes", c_uint), - - # The array of meshes. - # Use the indices given in the aiNode structure to access - # this array. The array is mNumMeshes in size. If the - # AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always - # be at least ONE material. - ("mMeshes", POINTER(POINTER(Mesh))), - - # The number of materials in the scene. - ("mNumMaterials", c_uint), - - # The array of materials. - # Use the index given in each aiMesh structure to access this - # array. The array is mNumMaterials in size. If the - # AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always - # be at least ONE material. - ("mMaterials", POINTER(POINTER(Material))), - - # The number of animations in the scene. - ("mNumAnimations", c_uint), - - # The array of animations. - # All animations imported from the given file are listed here. - # The array is mNumAnimations in size. - ("mAnimations", POINTER(POINTER(Animation))), - - # The number of textures embedded into the file - ("mNumTextures", c_uint), - - # The array of embedded textures. - # Not many file formats embed their textures into the file. - # An example is Quake's MDL format (which is also used by - # some GameStudio versions) - ("mTextures", POINTER(POINTER(Texture))), - - # The number of light sources in the scene. Light sources - # are fully optional, in most cases this attribute will be 0 - ("mNumLights", c_uint), - - # The array of light sources. - # All light sources imported from the given file are - # listed here. The array is mNumLights in size. - ("mLights", POINTER(POINTER(Light))), - - # The number of cameras in the scene. Cameras - # are fully optional, in most cases this attribute will be 0 - ("mNumCameras", c_uint), - - # The array of cameras. - # All cameras imported from the given file are listed here. - # The array is mNumCameras in size. The first camera in the - # array (if existing) is the default camera view into - # the scene. - ("mCameras", POINTER(POINTER(Camera))), - - # This data contains global metadata which belongs to the scene like - # unit-conversions, versions, vendors or other model-specific data. This - # can be used to store format-specific metadata as well. - ("mMetadata", POINTER(Metadata)), - - # Internal data, do not touch - ("mPrivate", POINTER(c_char)), - ] - -assimp_structs_as_tuple = (Matrix4x4, - Matrix3x3, - Vector2D, - Vector3D, - Color3D, - Color4D, - Quaternion, - Plane, - Texel) diff --git a/libs/assimp/port/PyAssimp/scripts/3d_viewer.py b/libs/assimp/port/PyAssimp/scripts/3d_viewer.py deleted file mode 100755 index 08a6266..0000000 --- a/libs/assimp/port/PyAssimp/scripts/3d_viewer.py +++ /dev/null @@ -1,1318 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- - -""" This program loads a model with PyASSIMP, and display it. - -Based on: -- pygame code from http://3dengine.org/Spectator_%28PyOpenGL%29 -- http://www.lighthouse3d.com/tutorials -- http://www.songho.ca/opengl/gl_transform.html -- http://code.activestate.com/recipes/325391/ -- ASSIMP's C++ SimpleOpenGL viewer - -Authors: Séverin Lemaignan, 2012-2016 -""" -import sys -import logging - -logger = logging.getLogger("pyassimp") -gllogger = logging.getLogger("OpenGL") -gllogger.setLevel(logging.WARNING) -logging.basicConfig(level=logging.INFO) - -import OpenGL - -OpenGL.ERROR_CHECKING = False -OpenGL.ERROR_LOGGING = False -# OpenGL.ERROR_ON_COPY = True -# OpenGL.FULL_LOGGING = True -from OpenGL.GL import * -from OpenGL.arrays import vbo -from OpenGL.GL import shaders - -import pygame -import pygame.font -import pygame.image - -import math, random -from numpy import linalg - -import pyassimp -from pyassimp.postprocess import * -from pyassimp.helper import * -import transformations - -ROTATION_180_X = numpy.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], dtype=numpy.float32) - -# rendering mode -BASE = "BASE" -COLORS = "COLORS" -SILHOUETTE = "SILHOUETTE" -HELPERS = "HELPERS" - -# Entities type -ENTITY = "entity" -CAMERA = "camera" -MESH = "mesh" - -FLAT_VERTEX_SHADER_120 = """ -#version 120 - -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelMatrix; - -uniform vec4 u_materialDiffuse; - -attribute vec3 a_vertex; - -varying vec4 v_color; - -void main(void) -{ - v_color = u_materialDiffuse; - gl_Position = u_viewProjectionMatrix * u_modelMatrix * vec4(a_vertex, 1.0); -} -""" - -FLAT_VERTEX_SHADER_130 = """ -#version 130 - -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelMatrix; - -uniform vec4 u_materialDiffuse; - -in vec3 a_vertex; - -out vec4 v_color; - -void main(void) -{ - v_color = u_materialDiffuse; - gl_Position = u_viewProjectionMatrix * u_modelMatrix * vec4(a_vertex, 1.0); -} -""" - -BASIC_VERTEX_SHADER_120 = """ -#version 120 - -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelMatrix; -uniform mat3 u_normalMatrix; -uniform vec3 u_lightPos; - -uniform vec4 u_materialDiffuse; - -attribute vec3 a_vertex; -attribute vec3 a_normal; - -varying vec4 v_color; - -void main(void) -{ - // Now the normal is in world space, as we pass the light in world space. - vec3 normal = u_normalMatrix * a_normal; - - float dist = distance(a_vertex, u_lightPos); - - // go to https://www.desmos.com/calculator/nmnaud1hrw to play with the parameters - // att is not used for now - float att=1.0/(1.0+0.8*dist*dist); - - vec3 surf2light = normalize(u_lightPos - a_vertex); - vec3 norm = normalize(normal); - float dcont=max(0.0,dot(norm,surf2light)); - - float ambient = 0.3; - float intensity = dcont + 0.3 + ambient; - - v_color = u_materialDiffuse * intensity; - - gl_Position = u_viewProjectionMatrix * u_modelMatrix * vec4(a_vertex, 1.0); -} -""" - -BASIC_VERTEX_SHADER_130 = """ -#version 130 - -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelMatrix; -uniform mat3 u_normalMatrix; -uniform vec3 u_lightPos; - -uniform vec4 u_materialDiffuse; - -in vec3 a_vertex; -in vec3 a_normal; - -out vec4 v_color; - -void main(void) -{ - // Now the normal is in world space, as we pass the light in world space. - vec3 normal = u_normalMatrix * a_normal; - - float dist = distance(a_vertex, u_lightPos); - - // go to https://www.desmos.com/calculator/nmnaud1hrw to play with the parameters - // att is not used for now - float att=1.0/(1.0+0.8*dist*dist); - - vec3 surf2light = normalize(u_lightPos - a_vertex); - vec3 norm = normalize(normal); - float dcont=max(0.0,dot(norm,surf2light)); - - float ambient = 0.3; - float intensity = dcont + 0.3 + ambient; - - v_color = u_materialDiffuse * intensity; - - gl_Position = u_viewProjectionMatrix * u_modelMatrix * vec4(a_vertex, 1.0); -} -""" - -BASIC_FRAGMENT_SHADER_120 = """ -#version 120 - -varying vec4 v_color; - -void main() { - gl_FragColor = v_color; -} -""" - -BASIC_FRAGMENT_SHADER_130 = """ -#version 130 - -in vec4 v_color; - -void main() { - gl_FragColor = v_color; -} -""" - -GOOCH_VERTEX_SHADER_120 = """ -#version 120 - -// attributes -attribute vec3 a_vertex; // xyz - position -attribute vec3 a_normal; // xyz - normal - -// uniforms -uniform mat4 u_modelMatrix; -uniform mat4 u_viewProjectionMatrix; -uniform mat3 u_normalMatrix; -uniform vec3 u_lightPos; -uniform vec3 u_camPos; - -// output data from vertex to fragment shader -varying vec3 o_normal; -varying vec3 o_lightVector; - -/////////////////////////////////////////////////////////////////// - -void main(void) -{ - // transform position and normal to world space - vec4 positionWorld = u_modelMatrix * vec4(a_vertex, 1.0); - vec3 normalWorld = u_normalMatrix * a_normal; - - // calculate and pass vectors required for lighting - o_lightVector = u_lightPos - positionWorld.xyz; - o_normal = normalWorld; - - // project world space position to the screen and output it - gl_Position = u_viewProjectionMatrix * positionWorld; -} -""" - -GOOCH_VERTEX_SHADER_130 = """ -#version 130 - -// attributes -in vec3 a_vertex; // xyz - position -in vec3 a_normal; // xyz - normal - -// uniforms -uniform mat4 u_modelMatrix; -uniform mat4 u_viewProjectionMatrix; -uniform mat3 u_normalMatrix; -uniform vec3 u_lightPos; -uniform vec3 u_camPos; - -// output data from vertex to fragment shader -out vec3 o_normal; -out vec3 o_lightVector; - -/////////////////////////////////////////////////////////////////// - -void main(void) -{ - // transform position and normal to world space - vec4 positionWorld = u_modelMatrix * vec4(a_vertex, 1.0); - vec3 normalWorld = u_normalMatrix * a_normal; - - // calculate and pass vectors required for lighting - o_lightVector = u_lightPos - positionWorld.xyz; - o_normal = normalWorld; - - // project world space position to the screen and output it - gl_Position = u_viewProjectionMatrix * positionWorld; -} -""" - -GOOCH_FRAGMENT_SHADER_120 = """ -#version 120 - -// data from vertex shader -varying vec3 o_normal; -varying vec3 o_lightVector; - -// diffuse color of the object -uniform vec4 u_materialDiffuse; -// cool color of gooch shading -uniform vec3 u_coolColor; -// warm color of gooch shading -uniform vec3 u_warmColor; -// how much to take from object color in final cool color -uniform float u_alpha; -// how much to take from object color in final warm color -uniform float u_beta; - -/////////////////////////////////////////////////////////// - -void main(void) -{ - // normlize vectors for lighting - vec3 normalVector = normalize(o_normal); - vec3 lightVector = normalize(o_lightVector); - // intensity of diffuse lighting [-1, 1] - float diffuseLighting = dot(lightVector, normalVector); - // map intensity of lighting from range [-1; 1] to [0, 1] - float interpolationValue = (1.0 + diffuseLighting)/2; - - ////////////////////////////////////////////////////////////////// - - // cool color mixed with color of the object - vec3 coolColorMod = u_coolColor + vec3(u_materialDiffuse) * u_alpha; - // warm color mixed with color of the object - vec3 warmColorMod = u_warmColor + vec3(u_materialDiffuse) * u_beta; - // interpolation of cool and warm colors according - // to lighting intensity. The lower the light intensity, - // the larger part of the cool color is used - vec3 colorOut = mix(coolColorMod, warmColorMod, interpolationValue); - - ////////////////////////////////////////////////////////////////// - - // save color - gl_FragColor.rgb = colorOut; - gl_FragColor.a = 1; -} -""" - -GOOCH_FRAGMENT_SHADER_130 = """ -#version 130 - -// data from vertex shader -in vec3 o_normal; -in vec3 o_lightVector; - -// diffuse color of the object -uniform vec4 u_materialDiffuse; -// cool color of gooch shading -uniform vec3 u_coolColor; -// warm color of gooch shading -uniform vec3 u_warmColor; -// how much to take from object color in final cool color -uniform float u_alpha; -// how much to take from object color in final warm color -uniform float u_beta; - -// output to framebuffer -out vec4 resultingColor; - -/////////////////////////////////////////////////////////// - -void main(void) -{ - // normlize vectors for lighting - vec3 normalVector = normalize(o_normal); - vec3 lightVector = normalize(o_lightVector); - // intensity of diffuse lighting [-1, 1] - float diffuseLighting = dot(lightVector, normalVector); - // map intensity of lighting from range [-1; 1] to [0, 1] - float interpolationValue = (1.0 + diffuseLighting)/2; - - ////////////////////////////////////////////////////////////////// - - // cool color mixed with color of the object - vec3 coolColorMod = u_coolColor + vec3(u_materialDiffuse) * u_alpha; - // warm color mixed with color of the object - vec3 warmColorMod = u_warmColor + vec3(u_materialDiffuse) * u_beta; - // interpolation of cool and warm colors according - // to lighting intensity. The lower the light intensity, - // the larger part of the cool color is used - vec3 colorOut = mix(coolColorMod, warmColorMod, interpolationValue); - - ////////////////////////////////////////////////////////////////// - - // save color - resultingColor.rgb = colorOut; - resultingColor.a = 1; -} -""" - -SILHOUETTE_VERTEX_SHADER_120 = """ -#version 120 - -attribute vec3 a_vertex; // xyz - position -attribute vec3 a_normal; // xyz - normal - -uniform mat4 u_modelMatrix; -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelViewMatrix; -uniform vec4 u_materialDiffuse; -uniform float u_bordersize; // width of the border - -varying vec4 v_color; - -void main(void){ - v_color = u_materialDiffuse; - float distToCamera = -(u_modelViewMatrix * vec4(a_vertex, 1.0)).z; - vec4 tPos = vec4(a_vertex + a_normal * u_bordersize * distToCamera, 1.0); - gl_Position = u_viewProjectionMatrix * u_modelMatrix * tPos; -} -""" - -SILHOUETTE_VERTEX_SHADER_130 = """ -#version 130 - -in vec3 a_vertex; // xyz - position -in vec3 a_normal; // xyz - normal - -uniform mat4 u_modelMatrix; -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelViewMatrix; -uniform vec4 u_materialDiffuse; -uniform float u_bordersize; // width of the border - -out vec4 v_color; - -void main(void){ - v_color = u_materialDiffuse; - float distToCamera = -(u_modelViewMatrix * vec4(a_vertex, 1.0)).z; - vec4 tPos = vec4(a_vertex + a_normal * u_bordersize * distToCamera, 1.0); - gl_Position = u_viewProjectionMatrix * u_modelMatrix * tPos; -} -""" -DEFAULT_CLIP_PLANE_NEAR = 0.001 -DEFAULT_CLIP_PLANE_FAR = 1000.0 - - -def get_world_transform(scene, node): - if node == scene.rootnode: - return numpy.identity(4, dtype=numpy.float32) - - parents = reversed(_get_parent_chain(scene, node, [])) - parent_transform = reduce(numpy.dot, [p.transformation for p in parents]) - return numpy.dot(parent_transform, node.transformation) - - -def _get_parent_chain(scene, node, parents): - parent = node.parent - - parents.append(parent) - - if parent == scene.rootnode: - return parents - - return _get_parent_chain(scene, parent, parents) - - -class DefaultCamera: - def __init__(self, w, h, fov): - self.name = "default camera" - self.type = CAMERA - self.clipplanenear = DEFAULT_CLIP_PLANE_NEAR - self.clipplanefar = DEFAULT_CLIP_PLANE_FAR - self.aspect = w / h - self.horizontalfov = fov * math.pi / 180 - self.transformation = numpy.array([[0.68, -0.32, 0.65, 7.48], - [0.73, 0.31, -0.61, -6.51], - [-0.01, 0.89, 0.44, 5.34], - [0., 0., 0., 1.]], dtype=numpy.float32) - - self.transformation = numpy.dot(self.transformation, ROTATION_180_X) - - def __str__(self): - return self.name - - -class PyAssimp3DViewer: - base_name = "PyASSIMP 3D viewer" - - def __init__(self, model, w=1024, h=768): - - self.w = w - self.h = h - - pygame.init() - pygame.display.set_caption(self.base_name) - pygame.display.set_mode((w, h), pygame.OPENGL | pygame.DOUBLEBUF) - - glClearColor(0.18, 0.18, 0.18, 1.0) - - shader_compilation_succeeded = False - try: - self.set_shaders_v130() - self.prepare_shaders() - except RuntimeError, message: - sys.stderr.write("%s\n" % message) - sys.stdout.write("Could not compile shaders in version 1.30, trying version 1.20\n") - - if not shader_compilation_succeeded: - self.set_shaders_v120() - self.prepare_shaders() - - self.scene = None - self.meshes = {} # stores the OpenGL vertex/faces/normals buffers pointers - - self.node2colorid = {} # stores a color ID for each node. Useful for mouse picking and visibility checking - self.colorid2node = {} # reverse dict of node2colorid - - self.currently_selected = None - self.moving = False - self.moving_situation = None - - self.default_camera = DefaultCamera(self.w, self.h, fov=70) - self.cameras = [self.default_camera] - - self.current_cam_index = 0 - self.current_cam = self.default_camera - self.set_camera_projection() - - self.load_model(model) - - # user interactions - self.focal_point = [0, 0, 0] - self.is_rotating = False - self.is_panning = False - self.is_zooming = False - - def set_shaders_v120(self): - self.BASIC_VERTEX_SHADER = BASIC_VERTEX_SHADER_120 - self.FLAT_VERTEX_SHADER = FLAT_VERTEX_SHADER_120 - self.SILHOUETTE_VERTEX_SHADER = SILHOUETTE_VERTEX_SHADER_120 - self.GOOCH_VERTEX_SHADER = GOOCH_VERTEX_SHADER_120 - - self.BASIC_FRAGMENT_SHADER = BASIC_FRAGMENT_SHADER_120 - self.GOOCH_FRAGMENT_SHADER = GOOCH_FRAGMENT_SHADER_120 - - def set_shaders_v130(self): - self.BASIC_VERTEX_SHADER = BASIC_VERTEX_SHADER_130 - self.FLAT_VERTEX_SHADER = FLAT_VERTEX_SHADER_130 - self.SILHOUETTE_VERTEX_SHADER = SILHOUETTE_VERTEX_SHADER_130 - self.GOOCH_VERTEX_SHADER = GOOCH_VERTEX_SHADER_130 - - self.BASIC_FRAGMENT_SHADER = BASIC_FRAGMENT_SHADER_130 - self.GOOCH_FRAGMENT_SHADER = GOOCH_FRAGMENT_SHADER_130 - - def prepare_shaders(self): - - ### Base shader - vertex = shaders.compileShader(self.BASIC_VERTEX_SHADER, GL_VERTEX_SHADER) - fragment = shaders.compileShader(self.BASIC_FRAGMENT_SHADER, GL_FRAGMENT_SHADER) - - self.shader = shaders.compileProgram(vertex, fragment) - - self.set_shader_accessors(('u_modelMatrix', - 'u_viewProjectionMatrix', - 'u_normalMatrix', - 'u_lightPos', - 'u_materialDiffuse'), - ('a_vertex', - 'a_normal'), self.shader) - - ### Flat shader - flatvertex = shaders.compileShader(self.FLAT_VERTEX_SHADER, GL_VERTEX_SHADER) - self.flatshader = shaders.compileProgram(flatvertex, fragment) - - self.set_shader_accessors(('u_modelMatrix', - 'u_viewProjectionMatrix', - 'u_materialDiffuse',), - ('a_vertex',), self.flatshader) - - ### Silhouette shader - silh_vertex = shaders.compileShader(self.SILHOUETTE_VERTEX_SHADER, GL_VERTEX_SHADER) - self.silhouette_shader = shaders.compileProgram(silh_vertex, fragment) - - self.set_shader_accessors(('u_modelMatrix', - 'u_viewProjectionMatrix', - 'u_modelViewMatrix', - 'u_materialDiffuse', - 'u_bordersize' # width of the silhouette - ), - ('a_vertex', - 'a_normal'), self.silhouette_shader) - - ### Gooch shader - gooch_vertex = shaders.compileShader(self.GOOCH_VERTEX_SHADER, GL_VERTEX_SHADER) - gooch_fragment = shaders.compileShader(self.GOOCH_FRAGMENT_SHADER, GL_FRAGMENT_SHADER) - self.gooch_shader = shaders.compileProgram(gooch_vertex, gooch_fragment) - - self.set_shader_accessors(('u_modelMatrix', - 'u_viewProjectionMatrix', - 'u_normalMatrix', - 'u_lightPos', - 'u_materialDiffuse', - 'u_coolColor', - 'u_warmColor', - 'u_alpha', - 'u_beta' - ), - ('a_vertex', - 'a_normal'), self.gooch_shader) - - @staticmethod - def set_shader_accessors(uniforms, attributes, shader): - # add accessors to the shaders uniforms and attributes - for uniform in uniforms: - location = glGetUniformLocation(shader, uniform) - if location in (None, -1): - raise RuntimeError('No uniform: %s (maybe it is not used ' - 'anymore and has been optimized out by' - ' the shader compiler)' % uniform) - setattr(shader, uniform, location) - - for attribute in attributes: - location = glGetAttribLocation(shader, attribute) - if location in (None, -1): - raise RuntimeError('No attribute: %s' % attribute) - setattr(shader, attribute, location) - - @staticmethod - def prepare_gl_buffers(mesh): - - mesh.gl = {} - - # Fill the buffer for vertex and normals positions - v = numpy.array(mesh.vertices, 'f') - n = numpy.array(mesh.normals, 'f') - - mesh.gl["vbo"] = vbo.VBO(numpy.hstack((v, n))) - - # Fill the buffer for vertex positions - mesh.gl["faces"] = glGenBuffers(1) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl["faces"]) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - numpy.array(mesh.faces, dtype=numpy.int32), - GL_STATIC_DRAW) - - mesh.gl["nbfaces"] = len(mesh.faces) - - # Unbind buffers - glBindBuffer(GL_ARRAY_BUFFER, 0) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) - - @staticmethod - def get_rgb_from_colorid(colorid): - r = (colorid >> 0) & 0xff - g = (colorid >> 8) & 0xff - b = (colorid >> 16) & 0xff - - return r, g, b - - def get_color_id(self): - id = random.randint(0, 256 * 256 * 256) - if id not in self.colorid2node: - return id - else: - return self.get_color_id() - - def glize(self, scene, node): - - logger.info("Loading node <%s>" % node) - node.selected = True if self.currently_selected and self.currently_selected == node else False - - node.transformation = node.transformation.astype(numpy.float32) - - if node.meshes: - node.type = MESH - colorid = self.get_color_id() - self.colorid2node[colorid] = node - self.node2colorid[node.name] = colorid - - elif node.name in [c.name for c in scene.cameras]: - - # retrieve the ASSIMP camera object - [cam] = [c for c in scene.cameras if c.name == node.name] - node.type = CAMERA - logger.info("Added camera <%s>" % node.name) - logger.info("Camera position: %.3f, %.3f, %.3f" % tuple(node.transformation[:, 3][:3].tolist())) - self.cameras.append(node) - node.clipplanenear = cam.clipplanenear - node.clipplanefar = cam.clipplanefar - - if numpy.allclose(cam.lookat, [0, 0, -1]) and numpy.allclose(cam.up, [0, 1, 0]): # Cameras in .blend files - - # Rotate by 180deg around X to have Z pointing forward - node.transformation = numpy.dot(node.transformation, ROTATION_180_X) - else: - raise RuntimeError( - "I do not know how to normalize this camera orientation: lookat=%s, up=%s" % (cam.lookat, cam.up)) - - if cam.aspect == 0.0: - logger.warning("Camera aspect not set. Setting to default 4:3") - node.aspect = 1.333 - else: - node.aspect = cam.aspect - - node.horizontalfov = cam.horizontalfov - - else: - node.type = ENTITY - - for child in node.children: - self.glize(scene, child) - - def load_model(self, path, postprocess=aiProcessPreset_TargetRealtime_MaxQuality): - logger.info("Loading model:" + path + "...") - - if postprocess: - self.scene = pyassimp.load(path, processing=postprocess) - else: - self.scene = pyassimp.load(path) - logger.info("Done.") - - scene = self.scene - # log some statistics - logger.info(" meshes: %d" % len(scene.meshes)) - logger.info(" total faces: %d" % sum([len(mesh.faces) for mesh in scene.meshes])) - logger.info(" materials: %d" % len(scene.materials)) - self.bb_min, self.bb_max = get_bounding_box(self.scene) - logger.info(" bounding box:" + str(self.bb_min) + " - " + str(self.bb_max)) - - self.scene_center = [(a + b) / 2. for a, b in zip(self.bb_min, self.bb_max)] - - for index, mesh in enumerate(scene.meshes): - self.prepare_gl_buffers(mesh) - - self.glize(scene, scene.rootnode) - - # Finally release the model - pyassimp.release(scene) - logger.info("Ready for 3D rendering!") - - def cycle_cameras(self): - - self.current_cam_index = (self.current_cam_index + 1) % len(self.cameras) - self.current_cam = self.cameras[self.current_cam_index] - self.set_camera_projection(self.current_cam) - logger.info("Switched to camera <%s>" % self.current_cam) - - def set_overlay_projection(self): - glViewport(0, 0, self.w, self.h) - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - glOrtho(0.0, self.w - 1.0, 0.0, self.h - 1.0, -1.0, 1.0) - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - - def set_camera_projection(self, camera=None): - - if not camera: - camera = self.current_cam - - znear = camera.clipplanenear or DEFAULT_CLIP_PLANE_NEAR - zfar = camera.clipplanefar or DEFAULT_CLIP_PLANE_FAR - aspect = camera.aspect - fov = camera.horizontalfov - - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - - # Compute gl frustrum - tangent = math.tan(fov / 2.) - h = znear * tangent - w = h * aspect - - # params: left, right, bottom, top, near, far - glFrustum(-w, w, -h, h, znear, zfar) - # equivalent to: - # gluPerspective(fov * 180/math.pi, aspect, znear, zfar) - - self.projection_matrix = glGetFloatv(GL_PROJECTION_MATRIX).transpose() - - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - - def render_colors(self): - - glEnable(GL_DEPTH_TEST) - glDepthFunc(GL_LEQUAL) - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) - glEnable(GL_CULL_FACE) - - glUseProgram(self.flatshader) - - glUniformMatrix4fv(self.flatshader.u_viewProjectionMatrix, 1, GL_TRUE, - numpy.dot(self.projection_matrix, self.view_matrix)) - - self.recursive_render(self.scene.rootnode, self.flatshader, mode=COLORS) - - glUseProgram(0) - - def get_hovered_node(self, mousex, mousey): - """ - Attention: The performances of this method relies heavily on the size of the display! - """ - - # mouse out of the window? - if mousex < 0 or mousex >= self.w or mousey < 0 or mousey >= self.h: - return None - - self.render_colors() - # Capture image from the OpenGL buffer - buf = (GLubyte * (3 * self.w * self.h))(0) - glReadPixels(0, 0, self.w, self.h, GL_RGB, GL_UNSIGNED_BYTE, buf) - - # Reinterpret the RGB pixel buffer as a 1-D array of 24bits colors - a = numpy.ndarray(len(buf), numpy.dtype('>u1'), buf) - colors = numpy.zeros(len(buf) / 3, numpy.dtype('<u4')) - for i in range(3): - colors.view(dtype='>u1')[i::4] = a.view(dtype='>u1')[i::3] - - colorid = colors[mousex + mousey * self.w] - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - - if colorid in self.colorid2node: - return self.colorid2node[colorid] - - def render(self, wireframe=False, twosided=False): - - glEnable(GL_DEPTH_TEST) - glDepthFunc(GL_LEQUAL) - - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE if wireframe else GL_FILL) - glDisable(GL_CULL_FACE) if twosided else glEnable(GL_CULL_FACE) - - self.render_grid() - - self.recursive_render(self.scene.rootnode, None, mode=HELPERS) - - ### First, the silhouette - - if False: - shader = self.silhouette_shader - - # glDepthMask(GL_FALSE) - glCullFace(GL_FRONT) # cull front faces - - glUseProgram(shader) - glUniform1f(shader.u_bordersize, 0.01) - - glUniformMatrix4fv(shader.u_viewProjectionMatrix, 1, GL_TRUE, - numpy.dot(self.projection_matrix, self.view_matrix)) - - self.recursive_render(self.scene.rootnode, shader, mode=SILHOUETTE) - - glUseProgram(0) - - ### Then, inner shading - # glDepthMask(GL_TRUE) - glCullFace(GL_BACK) - - use_gooch = False - if use_gooch: - shader = self.gooch_shader - - glUseProgram(shader) - glUniform3f(shader.u_lightPos, -.5, -.5, .5) - - ##### GOOCH specific - glUniform3f(shader.u_coolColor, 159.0 / 255, 148.0 / 255, 255.0 / 255) - glUniform3f(shader.u_warmColor, 255.0 / 255, 75.0 / 255, 75.0 / 255) - glUniform1f(shader.u_alpha, .25) - glUniform1f(shader.u_beta, .25) - ######### - else: - shader = self.shader - glUseProgram(shader) - glUniform3f(shader.u_lightPos, -.5, -.5, .5) - - glUniformMatrix4fv(shader.u_viewProjectionMatrix, 1, GL_TRUE, - numpy.dot(self.projection_matrix, self.view_matrix)) - - self.recursive_render(self.scene.rootnode, shader) - - glUseProgram(0) - - def render_axis(self, - transformation=numpy.identity(4, dtype=numpy.float32), - label=None, - size=0.2, - selected=False): - m = transformation.transpose() # OpenGL row major - - glPushMatrix() - glMultMatrixf(m) - - glLineWidth(3 if selected else 1) - - size = 2 * size if selected else size - - glBegin(GL_LINES) - - # draw line for x axis - glColor3f(1.0, 0.0, 0.0) - glVertex3f(0.0, 0.0, 0.0) - glVertex3f(size, 0.0, 0.0) - - # draw line for y axis - glColor3f(0.0, 1.0, 0.0) - glVertex3f(0.0, 0.0, 0.0) - glVertex3f(0.0, size, 0.0) - - # draw line for Z axis - glColor3f(0.0, 0.0, 1.0) - glVertex3f(0.0, 0.0, 0.0) - glVertex3f(0.0, 0.0, size) - - glEnd() - - if label: - self.showtext(label) - - glPopMatrix() - - @staticmethod - def render_camera(camera, transformation): - - m = transformation.transpose() # OpenGL row major - - aspect = camera.aspect - - u = 0.1 # unit size (in m) - l = 3 * u # length of the camera cone - f = 3 * u # aperture of the camera cone - - glPushMatrix() - glMultMatrixf(m) - - glLineWidth(2) - glBegin(GL_LINE_STRIP) - - glColor3f(.2, .2, .2) - - glVertex3f(u, u, -u) - glVertex3f(u, -u, -u) - glVertex3f(-u, -u, -u) - glVertex3f(-u, u, -u) - glVertex3f(u, u, -u) - - glVertex3f(u, u, 0.0) - glVertex3f(u, -u, 0.0) - glVertex3f(-u, -u, 0.0) - glVertex3f(-u, u, 0.0) - glVertex3f(u, u, 0.0) - - glVertex3f(f * aspect, f, l) - glVertex3f(f * aspect, -f, l) - glVertex3f(-f * aspect, -f, l) - glVertex3f(-f * aspect, f, l) - glVertex3f(f * aspect, f, l) - - glEnd() - - glBegin(GL_LINE_STRIP) - glVertex3f(u, -u, -u) - glVertex3f(u, -u, 0.0) - glVertex3f(f * aspect, -f, l) - glEnd() - - glBegin(GL_LINE_STRIP) - glVertex3f(-u, -u, -u) - glVertex3f(-u, -u, 0.0) - glVertex3f(-f * aspect, -f, l) - glEnd() - - glBegin(GL_LINE_STRIP) - glVertex3f(-u, u, -u) - glVertex3f(-u, u, 0.0) - glVertex3f(-f * aspect, f, l) - glEnd() - - glPopMatrix() - - @staticmethod - def render_grid(): - - glLineWidth(1) - glColor3f(0.5, 0.5, 0.5) - glBegin(GL_LINES) - for i in range(-10, 11): - glVertex3f(i, -10.0, 0.0) - glVertex3f(i, 10.0, 0.0) - - for i in range(-10, 11): - glVertex3f(-10.0, i, 0.0) - glVertex3f(10.0, i, 0.0) - glEnd() - - def recursive_render(self, node, shader, mode=BASE, with_normals=True): - """ Main recursive rendering method. - """ - - normals = with_normals - - if mode == COLORS: - normals = False - - - if not hasattr(node, "selected"): - node.selected = False - - m = get_world_transform(self.scene, node) - - # HELPERS mode - ### - if mode == HELPERS: - # if node.type == ENTITY: - self.render_axis(m, - label=node.name if node != self.scene.rootnode else None, - selected=node.selected if hasattr(node, "selected") else False) - - if node.type == CAMERA: - self.render_camera(node, m) - - for child in node.children: - self.recursive_render(child, shader, mode) - - return - - # Mesh rendering modes - ### - if node.type == MESH: - - for mesh in node.meshes: - - stride = 24 # 6 * 4 bytes - - if node.selected and mode == SILHOUETTE: - glUniform4f(shader.u_materialDiffuse, 1.0, 0.0, 0.0, 1.0) - glUniformMatrix4fv(shader.u_modelViewMatrix, 1, GL_TRUE, - numpy.dot(self.view_matrix, m)) - - else: - if mode == COLORS: - colorid = self.node2colorid[node.name] - r, g, b = self.get_rgb_from_colorid(colorid) - glUniform4f(shader.u_materialDiffuse, r / 255.0, g / 255.0, b / 255.0, 1.0) - elif mode == SILHOUETTE: - glUniform4f(shader.u_materialDiffuse, .0, .0, .0, 1.0) - else: - if node.selected: - diffuse = (1.0, 0.0, 0.0, 1.0) # selected nodes in red - else: - diffuse = mesh.material.properties["diffuse"] - if len(diffuse) == 3: # RGB instead of expected RGBA - diffuse.append(1.0) - glUniform4f(shader.u_materialDiffuse, *diffuse) - # if ambient: - # glUniform4f( shader.Material_ambient, *mat["ambient"] ) - - if mode == BASE: # not in COLORS or SILHOUETTE - normal_matrix = linalg.inv(numpy.dot(self.view_matrix, m)[0:3, 0:3]).transpose() - glUniformMatrix3fv(shader.u_normalMatrix, 1, GL_TRUE, normal_matrix) - - glUniformMatrix4fv(shader.u_modelMatrix, 1, GL_TRUE, m) - - vbo = mesh.gl["vbo"] - vbo.bind() - - glEnableVertexAttribArray(shader.a_vertex) - if normals: - glEnableVertexAttribArray(shader.a_normal) - - glVertexAttribPointer( - shader.a_vertex, - 3, GL_FLOAT, False, stride, vbo - ) - - if normals: - glVertexAttribPointer( - shader.a_normal, - 3, GL_FLOAT, False, stride, vbo + 12 - ) - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl["faces"]) - glDrawElements(GL_TRIANGLES, mesh.gl["nbfaces"] * 3, GL_UNSIGNED_INT, None) - - vbo.unbind() - glDisableVertexAttribArray(shader.a_vertex) - - if normals: - glDisableVertexAttribArray(shader.a_normal) - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) - - for child in node.children: - self.recursive_render(child, shader, mode) - - - def switch_to_overlay(self): - glPushMatrix() - self.set_overlay_projection() - - def switch_from_overlay(self): - self.set_camera_projection() - glPopMatrix() - - def select_node(self, node): - self.currently_selected = node - self.update_node_select(self.scene.rootnode) - - def update_node_select(self, node): - if node is self.currently_selected: - node.selected = True - else: - node.selected = False - - for child in node.children: - self.update_node_select(child) - - def loop(self): - - pygame.display.flip() - - if not self.process_events(): - return False # ESC has been pressed - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - - return True - - def process_events(self): - - LEFT_BUTTON = 1 - MIDDLE_BUTTON = 2 - RIGHT_BUTTON = 3 - WHEEL_UP = 4 - WHEEL_DOWN = 5 - - dx, dy = pygame.mouse.get_rel() - mousex, mousey = pygame.mouse.get_pos() - - zooming_one_shot = False - - ok = True - - for evt in pygame.event.get(): - if evt.type == pygame.MOUSEBUTTONDOWN and evt.button == LEFT_BUTTON: - hovered = self.get_hovered_node(mousex, self.h - mousey) - if hovered: - if self.currently_selected and self.currently_selected == hovered: - self.select_node(None) - else: - logger.info("Node %s selected" % hovered) - self.select_node(hovered) - else: - self.is_rotating = True - if evt.type == pygame.MOUSEBUTTONUP and evt.button == LEFT_BUTTON: - self.is_rotating = False - - if evt.type == pygame.MOUSEBUTTONDOWN and evt.button == MIDDLE_BUTTON: - self.is_panning = True - if evt.type == pygame.MOUSEBUTTONUP and evt.button == MIDDLE_BUTTON: - self.is_panning = False - - if evt.type == pygame.MOUSEBUTTONDOWN and evt.button == RIGHT_BUTTON: - self.is_zooming = True - if evt.type == pygame.MOUSEBUTTONUP and evt.button == RIGHT_BUTTON: - self.is_zooming = False - - if evt.type == pygame.MOUSEBUTTONDOWN and evt.button in [WHEEL_UP, WHEEL_DOWN]: - zooming_one_shot = True - self.is_zooming = True - dy = -10 if evt.button == WHEEL_UP else 10 - - if evt.type == pygame.KEYDOWN: - ok = (ok and self.process_keystroke(evt.key, evt.mod)) - - self.controls_3d(dx, dy, zooming_one_shot) - - return ok - - def process_keystroke(self, key, mod): - - # process arrow keys if an object is selected - if self.currently_selected: - up = 0 - strafe = 0 - - if key == pygame.K_UP: - up = 1 - if key == pygame.K_DOWN: - up = -1 - if key == pygame.K_LEFT: - strafe = -1 - if key == pygame.K_RIGHT: - strafe = 1 - - self.move_selected_node(up, strafe) - - if key == pygame.K_f: - pygame.display.toggle_fullscreen() - - if key == pygame.K_TAB: - self.cycle_cameras() - - if key in [pygame.K_ESCAPE, pygame.K_q]: - return False - - return True - - def controls_3d(self, dx, dy, zooming_one_shot=False): - - CAMERA_TRANSLATION_FACTOR = 0.01 - CAMERA_ROTATION_FACTOR = 0.01 - - if not (self.is_rotating or self.is_panning or self.is_zooming): - return - - current_pos = self.current_cam.transformation[:3, 3].copy() - distance = numpy.linalg.norm(self.focal_point - current_pos) - - if self.is_rotating: - """ Orbiting the camera is implemented the following way: - - - the rotation is split into a rotation around the *world* Z axis - (controlled by the horizontal mouse motion along X) and a - rotation around the *X* axis of the camera (pitch) *shifted to - the focal origin* (the world origin for now). This is controlled - by the vertical motion of the mouse (Y axis). - - - as a result, the resulting transformation of the camera in the - world frame C' is: - C' = (T · Rx · T⁻¹ · (Rz · C)⁻¹)⁻¹ - - where: - - C is the original camera transformation in the world frame, - - Rz is the rotation along the Z axis (in the world frame) - - T is the translation camera -> world (ie, the inverse of the - translation part of C - - Rx is the rotation around X in the (translated) camera frame - """ - - rotation_camera_x = dy * CAMERA_ROTATION_FACTOR - rotation_world_z = dx * CAMERA_ROTATION_FACTOR - world_z_rotation = transformations.euler_matrix(0, 0, rotation_world_z) - cam_x_rotation = transformations.euler_matrix(rotation_camera_x, 0, 0) - - after_world_z_rotation = numpy.dot(world_z_rotation, self.current_cam.transformation) - - inverse_transformation = transformations.inverse_matrix(after_world_z_rotation) - - translation = transformations.translation_matrix( - transformations.decompose_matrix(inverse_transformation)[3]) - inverse_translation = transformations.inverse_matrix(translation) - - new_inverse = numpy.dot(inverse_translation, inverse_transformation) - new_inverse = numpy.dot(cam_x_rotation, new_inverse) - new_inverse = numpy.dot(translation, new_inverse) - - self.current_cam.transformation = transformations.inverse_matrix(new_inverse).astype(numpy.float32) - - if self.is_panning: - tx = -dx * CAMERA_TRANSLATION_FACTOR * distance - ty = dy * CAMERA_TRANSLATION_FACTOR * distance - cam_transform = transformations.translation_matrix((tx, ty, 0)).astype(numpy.float32) - self.current_cam.transformation = numpy.dot(self.current_cam.transformation, cam_transform) - - if self.is_zooming: - tz = dy * CAMERA_TRANSLATION_FACTOR * distance - cam_transform = transformations.translation_matrix((0, 0, tz)).astype(numpy.float32) - self.current_cam.transformation = numpy.dot(self.current_cam.transformation, cam_transform) - - if zooming_one_shot: - self.is_zooming = False - - self.update_view_camera() - - def update_view_camera(self): - - self.view_matrix = linalg.inv(self.current_cam.transformation) - - # Rotate by 180deg around X to have Z pointing backward (OpenGL convention) - self.view_matrix = numpy.dot(ROTATION_180_X, self.view_matrix) - - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - glMultMatrixf(self.view_matrix.transpose()) - - def move_selected_node(self, up, strafe): - self.currently_selected.transformation[0][3] += strafe - self.currently_selected.transformation[2][3] += up - - @staticmethod - def showtext(text, x=0, y=0, z=0, size=20): - - # TODO: alpha blending does not work... - # glEnable(GL_BLEND) - # glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - font = pygame.font.Font(None, size) - text_surface = font.render(text, True, (10, 10, 10, 255), - (255 * 0.18, 255 * 0.18, 255 * 0.18, 0)) - text_data = pygame.image.tostring(text_surface, "RGBA", True) - glRasterPos3d(x, y, z) - glDrawPixels(text_surface.get_width(), - text_surface.get_height(), - GL_RGBA, GL_UNSIGNED_BYTE, - text_data) - - # glDisable(GL_BLEND) - - -def main(model, width, height): - app = PyAssimp3DViewer(model, w=width, h=height) - - clock = pygame.time.Clock() - - while app.loop(): - - app.update_view_camera() - - ## Main rendering - app.render() - - ## GUI text display - app.switch_to_overlay() - app.showtext("Active camera: %s" % str(app.current_cam), 10, app.h - 30) - if app.currently_selected: - app.showtext("Selected node: %s" % app.currently_selected, 10, app.h - 50) - pos = app.h - 70 - - app.showtext("(%sm, %sm, %sm)" % (app.currently_selected.transformation[0, 3], - app.currently_selected.transformation[1, 3], - app.currently_selected.transformation[2, 3]), 30, pos) - - app.switch_from_overlay() - - # Make sure we do not go over 30fps - clock.tick(30) - - logger.info("Quitting! Bye bye!") - - -######################################################################### -######################################################################### - -if __name__ == '__main__': - if not len(sys.argv) > 1: - print("Usage: " + __file__ + " <model>") - sys.exit(2) - - main(model=sys.argv[1], width=1024, height=768) diff --git a/libs/assimp/port/PyAssimp/scripts/3d_viewer_py3.py b/libs/assimp/port/PyAssimp/scripts/3d_viewer_py3.py deleted file mode 100755 index fcee637..0000000 --- a/libs/assimp/port/PyAssimp/scripts/3d_viewer_py3.py +++ /dev/null @@ -1,1316 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- - -""" This program loads a model with PyASSIMP, and display it. - -Based on: -- pygame code from http://3dengine.org/Spectator_%28PyOpenGL%29 -- http://www.lighthouse3d.com/tutorials -- http://www.songho.ca/opengl/gl_transform.html -- http://code.activestate.com/recipes/325391/ -- ASSIMP's C++ SimpleOpenGL viewer - -Authors: Séverin Lemaignan, 2012-2016 -""" -import sys -import logging - -from functools import reduce - -logger = logging.getLogger("pyassimp") -gllogger = logging.getLogger("OpenGL") -gllogger.setLevel(logging.WARNING) -logging.basicConfig(level=logging.INFO) - -import OpenGL - -OpenGL.ERROR_CHECKING = False -OpenGL.ERROR_LOGGING = False -# OpenGL.ERROR_ON_COPY = True -# OpenGL.FULL_LOGGING = True -from OpenGL.GL import * -from OpenGL.arrays import vbo -from OpenGL.GL import shaders - -import pygame -import pygame.font -import pygame.image - -import math, random -from numpy import linalg - -import pyassimp -from pyassimp.postprocess import * -from pyassimp.helper import * -import transformations - -ROTATION_180_X = numpy.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], dtype=numpy.float32) - -# rendering mode -BASE = "BASE" -COLORS = "COLORS" -SILHOUETTE = "SILHOUETTE" -HELPERS = "HELPERS" - -# Entities type -ENTITY = "entity" -CAMERA = "camera" -MESH = "mesh" - -FLAT_VERTEX_SHADER_120 = """ -#version 120 - -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelMatrix; - -uniform vec4 u_materialDiffuse; - -attribute vec3 a_vertex; - -varying vec4 v_color; - -void main(void) -{ - v_color = u_materialDiffuse; - gl_Position = u_viewProjectionMatrix * u_modelMatrix * vec4(a_vertex, 1.0); -} -""" - -FLAT_VERTEX_SHADER_130 = """ -#version 130 - -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelMatrix; - -uniform vec4 u_materialDiffuse; - -in vec3 a_vertex; - -out vec4 v_color; - -void main(void) -{ - v_color = u_materialDiffuse; - gl_Position = u_viewProjectionMatrix * u_modelMatrix * vec4(a_vertex, 1.0); -} -""" - -BASIC_VERTEX_SHADER_120 = """ -#version 120 - -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelMatrix; -uniform mat3 u_normalMatrix; -uniform vec3 u_lightPos; - -uniform vec4 u_materialDiffuse; - -attribute vec3 a_vertex; -attribute vec3 a_normal; - -varying vec4 v_color; - -void main(void) -{ - // Now the normal is in world space, as we pass the light in world space. - vec3 normal = u_normalMatrix * a_normal; - - float dist = distance(a_vertex, u_lightPos); - - // go to https://www.desmos.com/calculator/nmnaud1hrw to play with the parameters - // att is not used for now - float att=1.0/(1.0+0.8*dist*dist); - - vec3 surf2light = normalize(u_lightPos - a_vertex); - vec3 norm = normalize(normal); - float dcont=max(0.0,dot(norm,surf2light)); - - float ambient = 0.3; - float intensity = dcont + 0.3 + ambient; - - v_color = u_materialDiffuse * intensity; - - gl_Position = u_viewProjectionMatrix * u_modelMatrix * vec4(a_vertex, 1.0); -} -""" - -BASIC_VERTEX_SHADER_130 = """ -#version 130 - -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelMatrix; -uniform mat3 u_normalMatrix; -uniform vec3 u_lightPos; - -uniform vec4 u_materialDiffuse; - -in vec3 a_vertex; -in vec3 a_normal; - -out vec4 v_color; - -void main(void) -{ - // Now the normal is in world space, as we pass the light in world space. - vec3 normal = u_normalMatrix * a_normal; - - float dist = distance(a_vertex, u_lightPos); - - // go to https://www.desmos.com/calculator/nmnaud1hrw to play with the parameters - // att is not used for now - float att=1.0/(1.0+0.8*dist*dist); - - vec3 surf2light = normalize(u_lightPos - a_vertex); - vec3 norm = normalize(normal); - float dcont=max(0.0,dot(norm,surf2light)); - - float ambient = 0.3; - float intensity = dcont + 0.3 + ambient; - - v_color = u_materialDiffuse * intensity; - - gl_Position = u_viewProjectionMatrix * u_modelMatrix * vec4(a_vertex, 1.0); -} -""" - -BASIC_FRAGMENT_SHADER_120 = """ -#version 120 - -varying vec4 v_color; - -void main() { - gl_FragColor = v_color; -} -""" - -BASIC_FRAGMENT_SHADER_130 = """ -#version 130 - -in vec4 v_color; - -void main() { - gl_FragColor = v_color; -} -""" - -GOOCH_VERTEX_SHADER_120 = """ -#version 120 - -// attributes -attribute vec3 a_vertex; // xyz - position -attribute vec3 a_normal; // xyz - normal - -// uniforms -uniform mat4 u_modelMatrix; -uniform mat4 u_viewProjectionMatrix; -uniform mat3 u_normalMatrix; -uniform vec3 u_lightPos; -uniform vec3 u_camPos; - -// output data from vertex to fragment shader -varying vec3 o_normal; -varying vec3 o_lightVector; - -/////////////////////////////////////////////////////////////////// - -void main(void) -{ - // transform position and normal to world space - vec4 positionWorld = u_modelMatrix * vec4(a_vertex, 1.0); - vec3 normalWorld = u_normalMatrix * a_normal; - - // calculate and pass vectors required for lighting - o_lightVector = u_lightPos - positionWorld.xyz; - o_normal = normalWorld; - - // project world space position to the screen and output it - gl_Position = u_viewProjectionMatrix * positionWorld; -} -""" - -GOOCH_VERTEX_SHADER_130 = """ -#version 130 - -// attributes -in vec3 a_vertex; // xyz - position -in vec3 a_normal; // xyz - normal - -// uniforms -uniform mat4 u_modelMatrix; -uniform mat4 u_viewProjectionMatrix; -uniform mat3 u_normalMatrix; -uniform vec3 u_lightPos; -uniform vec3 u_camPos; - -// output data from vertex to fragment shader -out vec3 o_normal; -out vec3 o_lightVector; - -/////////////////////////////////////////////////////////////////// - -void main(void) -{ - // transform position and normal to world space - vec4 positionWorld = u_modelMatrix * vec4(a_vertex, 1.0); - vec3 normalWorld = u_normalMatrix * a_normal; - - // calculate and pass vectors required for lighting - o_lightVector = u_lightPos - positionWorld.xyz; - o_normal = normalWorld; - - // project world space position to the screen and output it - gl_Position = u_viewProjectionMatrix * positionWorld; -} -""" - -GOOCH_FRAGMENT_SHADER_120 = """ -#version 120 - -// data from vertex shader -varying vec3 o_normal; -varying vec3 o_lightVector; - -// diffuse color of the object -uniform vec4 u_materialDiffuse; -// cool color of gooch shading -uniform vec3 u_coolColor; -// warm color of gooch shading -uniform vec3 u_warmColor; -// how much to take from object color in final cool color -uniform float u_alpha; -// how much to take from object color in final warm color -uniform float u_beta; - -/////////////////////////////////////////////////////////// - -void main(void) -{ - // normlize vectors for lighting - vec3 normalVector = normalize(o_normal); - vec3 lightVector = normalize(o_lightVector); - // intensity of diffuse lighting [-1, 1] - float diffuseLighting = dot(lightVector, normalVector); - // map intensity of lighting from range [-1; 1] to [0, 1] - float interpolationValue = (1.0 + diffuseLighting)/2; - - ////////////////////////////////////////////////////////////////// - - // cool color mixed with color of the object - vec3 coolColorMod = u_coolColor + vec3(u_materialDiffuse) * u_alpha; - // warm color mixed with color of the object - vec3 warmColorMod = u_warmColor + vec3(u_materialDiffuse) * u_beta; - // interpolation of cool and warm colors according - // to lighting intensity. The lower the light intensity, - // the larger part of the cool color is used - vec3 colorOut = mix(coolColorMod, warmColorMod, interpolationValue); - - ////////////////////////////////////////////////////////////////// - - // save color - gl_FragColor.rgb = colorOut; - gl_FragColor.a = 1; -} -""" - -GOOCH_FRAGMENT_SHADER_130 = """ -#version 130 - -// data from vertex shader -in vec3 o_normal; -in vec3 o_lightVector; - -// diffuse color of the object -uniform vec4 u_materialDiffuse; -// cool color of gooch shading -uniform vec3 u_coolColor; -// warm color of gooch shading -uniform vec3 u_warmColor; -// how much to take from object color in final cool color -uniform float u_alpha; -// how much to take from object color in final warm color -uniform float u_beta; - -// output to framebuffer -out vec4 resultingColor; - -/////////////////////////////////////////////////////////// - -void main(void) -{ - // normlize vectors for lighting - vec3 normalVector = normalize(o_normal); - vec3 lightVector = normalize(o_lightVector); - // intensity of diffuse lighting [-1, 1] - float diffuseLighting = dot(lightVector, normalVector); - // map intensity of lighting from range [-1; 1] to [0, 1] - float interpolationValue = (1.0 + diffuseLighting)/2; - - ////////////////////////////////////////////////////////////////// - - // cool color mixed with color of the object - vec3 coolColorMod = u_coolColor + vec3(u_materialDiffuse) * u_alpha; - // warm color mixed with color of the object - vec3 warmColorMod = u_warmColor + vec3(u_materialDiffuse) * u_beta; - // interpolation of cool and warm colors according - // to lighting intensity. The lower the light intensity, - // the larger part of the cool color is used - vec3 colorOut = mix(coolColorMod, warmColorMod, interpolationValue); - - ////////////////////////////////////////////////////////////////// - - // save color - resultingColor.rgb = colorOut; - resultingColor.a = 1; -} -""" - -SILHOUETTE_VERTEX_SHADER_120 = """ -#version 120 - -attribute vec3 a_vertex; // xyz - position -attribute vec3 a_normal; // xyz - normal - -uniform mat4 u_modelMatrix; -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelViewMatrix; -uniform vec4 u_materialDiffuse; -uniform float u_bordersize; // width of the border - -varying vec4 v_color; - -void main(void){ - v_color = u_materialDiffuse; - float distToCamera = -(u_modelViewMatrix * vec4(a_vertex, 1.0)).z; - vec4 tPos = vec4(a_vertex + a_normal * u_bordersize * distToCamera, 1.0); - gl_Position = u_viewProjectionMatrix * u_modelMatrix * tPos; -} -""" - -SILHOUETTE_VERTEX_SHADER_130 = """ -#version 130 - -in vec3 a_vertex; // xyz - position -in vec3 a_normal; // xyz - normal - -uniform mat4 u_modelMatrix; -uniform mat4 u_viewProjectionMatrix; -uniform mat4 u_modelViewMatrix; -uniform vec4 u_materialDiffuse; -uniform float u_bordersize; // width of the border - -out vec4 v_color; - -void main(void){ - v_color = u_materialDiffuse; - float distToCamera = -(u_modelViewMatrix * vec4(a_vertex, 1.0)).z; - vec4 tPos = vec4(a_vertex + a_normal * u_bordersize * distToCamera, 1.0); - gl_Position = u_viewProjectionMatrix * u_modelMatrix * tPos; -} -""" -DEFAULT_CLIP_PLANE_NEAR = 0.001 -DEFAULT_CLIP_PLANE_FAR = 1000.0 - - -def get_world_transform(scene, node): - if node == scene.rootnode: - return numpy.identity(4, dtype=numpy.float32) - - parents = reversed(_get_parent_chain(scene, node, [])) - parent_transform = reduce(numpy.dot, [p.transformation for p in parents]) - return numpy.dot(parent_transform, node.transformation) - - -def _get_parent_chain(scene, node, parents): - parent = node.parent - - parents.append(parent) - - if parent == scene.rootnode: - return parents - - return _get_parent_chain(scene, parent, parents) - - -class DefaultCamera: - def __init__(self, w, h, fov): - self.name = "default camera" - self.type = CAMERA - self.clipplanenear = DEFAULT_CLIP_PLANE_NEAR - self.clipplanefar = DEFAULT_CLIP_PLANE_FAR - self.aspect = w / h - self.horizontalfov = fov * math.pi / 180 - self.transformation = numpy.array([[0.68, -0.32, 0.65, 7.48], - [0.73, 0.31, -0.61, -6.51], - [-0.01, 0.89, 0.44, 5.34], - [0., 0., 0., 1.]], dtype=numpy.float32) - - self.transformation = numpy.dot(self.transformation, ROTATION_180_X) - - def __str__(self): - return self.name - - -class PyAssimp3DViewer: - base_name = "PyASSIMP 3D viewer" - - def __init__(self, model, w=1024, h=768): - - self.w = w - self.h = h - - pygame.init() - pygame.display.set_caption(self.base_name) - pygame.display.set_mode((w, h), pygame.OPENGL | pygame.DOUBLEBUF) - - glClearColor(0.18, 0.18, 0.18, 1.0) - - shader_compilation_succeeded = False - try: - self.set_shaders_v130() - self.prepare_shaders() - except RuntimeError as message: - sys.stderr.write("%s\n" % message) - sys.stdout.write("Could not compile shaders in version 1.30, trying version 1.20\n") - - if not shader_compilation_succeeded: - self.set_shaders_v120() - self.prepare_shaders() - - self.scene = None - self.meshes = {} # stores the OpenGL vertex/faces/normals buffers pointers - - self.node2colorid = {} # stores a color ID for each node. Useful for mouse picking and visibility checking - self.colorid2node = {} # reverse dict of node2colorid - - self.currently_selected = None - self.moving = False - self.moving_situation = None - - self.default_camera = DefaultCamera(self.w, self.h, fov=70) - self.cameras = [self.default_camera] - - self.current_cam_index = 0 - self.current_cam = self.default_camera - self.set_camera_projection() - - self.load_model(model) - - # user interactions - self.focal_point = [0, 0, 0] - self.is_rotating = False - self.is_panning = False - self.is_zooming = False - - def set_shaders_v120(self): - self.BASIC_VERTEX_SHADER = BASIC_VERTEX_SHADER_120 - self.FLAT_VERTEX_SHADER = FLAT_VERTEX_SHADER_120 - self.SILHOUETTE_VERTEX_SHADER = SILHOUETTE_VERTEX_SHADER_120 - self.GOOCH_VERTEX_SHADER = GOOCH_VERTEX_SHADER_120 - - self.BASIC_FRAGMENT_SHADER = BASIC_FRAGMENT_SHADER_120 - self.GOOCH_FRAGMENT_SHADER = GOOCH_FRAGMENT_SHADER_120 - - def set_shaders_v130(self): - self.BASIC_VERTEX_SHADER = BASIC_VERTEX_SHADER_130 - self.FLAT_VERTEX_SHADER = FLAT_VERTEX_SHADER_130 - self.SILHOUETTE_VERTEX_SHADER = SILHOUETTE_VERTEX_SHADER_130 - self.GOOCH_VERTEX_SHADER = GOOCH_VERTEX_SHADER_130 - - self.BASIC_FRAGMENT_SHADER = BASIC_FRAGMENT_SHADER_130 - self.GOOCH_FRAGMENT_SHADER = GOOCH_FRAGMENT_SHADER_130 - - def prepare_shaders(self): - - ### Base shader - vertex = shaders.compileShader(self.BASIC_VERTEX_SHADER, GL_VERTEX_SHADER) - fragment = shaders.compileShader(self.BASIC_FRAGMENT_SHADER, GL_FRAGMENT_SHADER) - - self.shader = shaders.compileProgram(vertex, fragment) - - self.set_shader_accessors(('u_modelMatrix', - 'u_viewProjectionMatrix', - 'u_normalMatrix', - 'u_lightPos', - 'u_materialDiffuse'), - ('a_vertex', - 'a_normal'), self.shader) - - ### Flat shader - flatvertex = shaders.compileShader(self.FLAT_VERTEX_SHADER, GL_VERTEX_SHADER) - self.flatshader = shaders.compileProgram(flatvertex, fragment) - - self.set_shader_accessors(('u_modelMatrix', - 'u_viewProjectionMatrix', - 'u_materialDiffuse',), - ('a_vertex',), self.flatshader) - - ### Silhouette shader - silh_vertex = shaders.compileShader(self.SILHOUETTE_VERTEX_SHADER, GL_VERTEX_SHADER) - self.silhouette_shader = shaders.compileProgram(silh_vertex, fragment) - - self.set_shader_accessors(('u_modelMatrix', - 'u_viewProjectionMatrix', - 'u_modelViewMatrix', - 'u_materialDiffuse', - 'u_bordersize' # width of the silhouette - ), - ('a_vertex', - 'a_normal'), self.silhouette_shader) - - ### Gooch shader - gooch_vertex = shaders.compileShader(self.GOOCH_VERTEX_SHADER, GL_VERTEX_SHADER) - gooch_fragment = shaders.compileShader(self.GOOCH_FRAGMENT_SHADER, GL_FRAGMENT_SHADER) - self.gooch_shader = shaders.compileProgram(gooch_vertex, gooch_fragment) - - self.set_shader_accessors(('u_modelMatrix', - 'u_viewProjectionMatrix', - 'u_normalMatrix', - 'u_lightPos', - 'u_materialDiffuse', - 'u_coolColor', - 'u_warmColor', - 'u_alpha', - 'u_beta' - ), - ('a_vertex', - 'a_normal'), self.gooch_shader) - - @staticmethod - def set_shader_accessors(uniforms, attributes, shader): - # add accessors to the shaders uniforms and attributes - for uniform in uniforms: - location = glGetUniformLocation(shader, uniform) - if location in (None, -1): - raise RuntimeError('No uniform: %s (maybe it is not used ' - 'anymore and has been optimized out by' - ' the shader compiler)' % uniform) - setattr(shader, uniform, location) - - for attribute in attributes: - location = glGetAttribLocation(shader, attribute) - if location in (None, -1): - raise RuntimeError('No attribute: %s' % attribute) - setattr(shader, attribute, location) - - @staticmethod - def prepare_gl_buffers(mesh): - - mesh.gl = {} - - # Fill the buffer for vertex and normals positions - v = numpy.array(mesh.vertices, 'f') - n = numpy.array(mesh.normals, 'f') - - mesh.gl["vbo"] = vbo.VBO(numpy.hstack((v, n))) - - # Fill the buffer for vertex positions - mesh.gl["faces"] = glGenBuffers(1) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl["faces"]) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - numpy.array(mesh.faces, dtype=numpy.int32), - GL_STATIC_DRAW) - - mesh.gl["nbfaces"] = len(mesh.faces) - - # Unbind buffers - glBindBuffer(GL_ARRAY_BUFFER, 0) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) - - @staticmethod - def get_rgb_from_colorid(colorid): - r = (colorid >> 0) & 0xff - g = (colorid >> 8) & 0xff - b = (colorid >> 16) & 0xff - - return r, g, b - - def get_color_id(self): - id = random.randint(0, 256 * 256 * 256) - if id not in self.colorid2node: - return id - else: - return self.get_color_id() - - def glize(self, scene, node): - - logger.info("Loading node <%s>" % node) - node.selected = True if self.currently_selected and self.currently_selected == node else False - - node.transformation = node.transformation.astype(numpy.float32) - - if node.meshes: - node.type = MESH - colorid = self.get_color_id() - self.colorid2node[colorid] = node - self.node2colorid[node.name] = colorid - - elif node.name in [c.name for c in scene.cameras]: - - # retrieve the ASSIMP camera object - [cam] = [c for c in scene.cameras if c.name == node.name] - node.type = CAMERA - logger.info("Added camera <%s>" % node.name) - logger.info("Camera position: %.3f, %.3f, %.3f" % tuple(node.transformation[:, 3][:3].tolist())) - self.cameras.append(node) - node.clipplanenear = cam.clipplanenear - node.clipplanefar = cam.clipplanefar - - if numpy.allclose(cam.lookat, [0, 0, -1]) and numpy.allclose(cam.up, [0, 1, 0]): # Cameras in .blend files - - # Rotate by 180deg around X to have Z pointing forward - node.transformation = numpy.dot(node.transformation, ROTATION_180_X) - else: - raise RuntimeError( - "I do not know how to normalize this camera orientation: lookat=%s, up=%s" % (cam.lookat, cam.up)) - - if cam.aspect == 0.0: - logger.warning("Camera aspect not set. Setting to default 4:3") - node.aspect = 1.333 - else: - node.aspect = cam.aspect - - node.horizontalfov = cam.horizontalfov - - else: - node.type = ENTITY - - for child in node.children: - self.glize(scene, child) - - def load_model(self, path, postprocess=aiProcessPreset_TargetRealtime_MaxQuality): - logger.info("Loading model:" + path + "...") - - if postprocess: - self.scene = pyassimp.load(path, processing=postprocess) - else: - self.scene = pyassimp.load(path) - logger.info("Done.") - - scene = self.scene - # log some statistics - logger.info(" meshes: %d" % len(scene.meshes)) - logger.info(" total faces: %d" % sum([len(mesh.faces) for mesh in scene.meshes])) - logger.info(" materials: %d" % len(scene.materials)) - self.bb_min, self.bb_max = get_bounding_box(self.scene) - logger.info(" bounding box:" + str(self.bb_min) + " - " + str(self.bb_max)) - - self.scene_center = [(a + b) / 2. for a, b in zip(self.bb_min, self.bb_max)] - - for index, mesh in enumerate(scene.meshes): - self.prepare_gl_buffers(mesh) - - self.glize(scene, scene.rootnode) - - # Finally release the model - pyassimp.release(scene) - logger.info("Ready for 3D rendering!") - - def cycle_cameras(self): - - self.current_cam_index = (self.current_cam_index + 1) % len(self.cameras) - self.current_cam = self.cameras[self.current_cam_index] - self.set_camera_projection(self.current_cam) - logger.info("Switched to camera <%s>" % self.current_cam) - - def set_overlay_projection(self): - glViewport(0, 0, self.w, self.h) - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - glOrtho(0.0, self.w - 1.0, 0.0, self.h - 1.0, -1.0, 1.0) - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - - def set_camera_projection(self, camera=None): - - if not camera: - camera = self.current_cam - - znear = camera.clipplanenear or DEFAULT_CLIP_PLANE_NEAR - zfar = camera.clipplanefar or DEFAULT_CLIP_PLANE_FAR - aspect = camera.aspect - fov = camera.horizontalfov - - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - - # Compute gl frustrum - tangent = math.tan(fov / 2.) - h = znear * tangent - w = h * aspect - - # params: left, right, bottom, top, near, far - glFrustum(-w, w, -h, h, znear, zfar) - # equivalent to: - # gluPerspective(fov * 180/math.pi, aspect, znear, zfar) - - self.projection_matrix = glGetFloatv(GL_PROJECTION_MATRIX).transpose() - - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - - def render_colors(self): - - glEnable(GL_DEPTH_TEST) - glDepthFunc(GL_LEQUAL) - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) - glEnable(GL_CULL_FACE) - - glUseProgram(self.flatshader) - - glUniformMatrix4fv(self.flatshader.u_viewProjectionMatrix, 1, GL_TRUE, - numpy.dot(self.projection_matrix, self.view_matrix)) - - self.recursive_render(self.scene.rootnode, self.flatshader, mode=COLORS) - - glUseProgram(0) - - def get_hovered_node(self, mousex, mousey): - """ - Attention: The performances of this method relies heavily on the size of the display! - """ - - # mouse out of the window? - if mousex < 0 or mousex >= self.w or mousey < 0 or mousey >= self.h: - return None - - self.render_colors() - # Capture image from the OpenGL buffer - buf = (GLubyte * (3 * self.w * self.h))(0) - glReadPixels(0, 0, self.w, self.h, GL_RGB, GL_UNSIGNED_BYTE, buf) - - # Reinterpret the RGB pixel buffer as a 1-D array of 24bits colors - a = numpy.ndarray(len(buf), numpy.dtype('>u1'), buf) - colors = numpy.zeros(len(buf) // 3, numpy.dtype('<u4')) - for i in range(3): - colors.view(dtype='>u1')[i::4] = a.view(dtype='>u1')[i::3] - - colorid = colors[mousex + mousey * self.w] - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - - if colorid in self.colorid2node: - return self.colorid2node[colorid] - - def render(self, wireframe=False, twosided=False): - - glEnable(GL_DEPTH_TEST) - glDepthFunc(GL_LEQUAL) - - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE if wireframe else GL_FILL) - glDisable(GL_CULL_FACE) if twosided else glEnable(GL_CULL_FACE) - - self.render_grid() - - self.recursive_render(self.scene.rootnode, None, mode=HELPERS) - - ### First, the silhouette - - if False: - shader = self.silhouette_shader - - # glDepthMask(GL_FALSE) - glCullFace(GL_FRONT) # cull front faces - - glUseProgram(shader) - glUniform1f(shader.u_bordersize, 0.01) - - glUniformMatrix4fv(shader.u_viewProjectionMatrix, 1, GL_TRUE, - numpy.dot(self.projection_matrix, self.view_matrix)) - - self.recursive_render(self.scene.rootnode, shader, mode=SILHOUETTE) - - glUseProgram(0) - - ### Then, inner shading - # glDepthMask(GL_TRUE) - glCullFace(GL_BACK) - - use_gooch = False - if use_gooch: - shader = self.gooch_shader - - glUseProgram(shader) - glUniform3f(shader.u_lightPos, -.5, -.5, .5) - - ##### GOOCH specific - glUniform3f(shader.u_coolColor, 159.0 / 255, 148.0 / 255, 255.0 / 255) - glUniform3f(shader.u_warmColor, 255.0 / 255, 75.0 / 255, 75.0 / 255) - glUniform1f(shader.u_alpha, .25) - glUniform1f(shader.u_beta, .25) - ######### - else: - shader = self.shader - glUseProgram(shader) - glUniform3f(shader.u_lightPos, -.5, -.5, .5) - - glUniformMatrix4fv(shader.u_viewProjectionMatrix, 1, GL_TRUE, - numpy.dot(self.projection_matrix, self.view_matrix)) - - self.recursive_render(self.scene.rootnode, shader) - - glUseProgram(0) - - def render_axis(self, - transformation=numpy.identity(4, dtype=numpy.float32), - label=None, - size=0.2, - selected=False): - m = transformation.transpose() # OpenGL row major - - glPushMatrix() - glMultMatrixf(m) - - glLineWidth(3 if selected else 1) - - size = 2 * size if selected else size - - glBegin(GL_LINES) - - # draw line for x axis - glColor3f(1.0, 0.0, 0.0) - glVertex3f(0.0, 0.0, 0.0) - glVertex3f(size, 0.0, 0.0) - - # draw line for y axis - glColor3f(0.0, 1.0, 0.0) - glVertex3f(0.0, 0.0, 0.0) - glVertex3f(0.0, size, 0.0) - - # draw line for Z axis - glColor3f(0.0, 0.0, 1.0) - glVertex3f(0.0, 0.0, 0.0) - glVertex3f(0.0, 0.0, size) - - glEnd() - - if label: - self.showtext(label) - - glPopMatrix() - - @staticmethod - def render_camera(camera, transformation): - - m = transformation.transpose() # OpenGL row major - - aspect = camera.aspect - - u = 0.1 # unit size (in m) - l = 3 * u # length of the camera cone - f = 3 * u # aperture of the camera cone - - glPushMatrix() - glMultMatrixf(m) - - glLineWidth(2) - glBegin(GL_LINE_STRIP) - - glColor3f(.2, .2, .2) - - glVertex3f(u, u, -u) - glVertex3f(u, -u, -u) - glVertex3f(-u, -u, -u) - glVertex3f(-u, u, -u) - glVertex3f(u, u, -u) - - glVertex3f(u, u, 0.0) - glVertex3f(u, -u, 0.0) - glVertex3f(-u, -u, 0.0) - glVertex3f(-u, u, 0.0) - glVertex3f(u, u, 0.0) - - glVertex3f(f * aspect, f, l) - glVertex3f(f * aspect, -f, l) - glVertex3f(-f * aspect, -f, l) - glVertex3f(-f * aspect, f, l) - glVertex3f(f * aspect, f, l) - - glEnd() - - glBegin(GL_LINE_STRIP) - glVertex3f(u, -u, -u) - glVertex3f(u, -u, 0.0) - glVertex3f(f * aspect, -f, l) - glEnd() - - glBegin(GL_LINE_STRIP) - glVertex3f(-u, -u, -u) - glVertex3f(-u, -u, 0.0) - glVertex3f(-f * aspect, -f, l) - glEnd() - - glBegin(GL_LINE_STRIP) - glVertex3f(-u, u, -u) - glVertex3f(-u, u, 0.0) - glVertex3f(-f * aspect, f, l) - glEnd() - - glPopMatrix() - - @staticmethod - def render_grid(): - - glLineWidth(1) - glColor3f(0.5, 0.5, 0.5) - glBegin(GL_LINES) - for i in range(-10, 11): - glVertex3f(i, -10.0, 0.0) - glVertex3f(i, 10.0, 0.0) - - for i in range(-10, 11): - glVertex3f(-10.0, i, 0.0) - glVertex3f(10.0, i, 0.0) - glEnd() - - def recursive_render(self, node, shader, mode=BASE, with_normals=True): - """ Main recursive rendering method. - """ - - normals = with_normals - - if mode == COLORS: - normals = False - - - if not hasattr(node, "selected"): - node.selected = False - - m = get_world_transform(self.scene, node) - - # HELPERS mode - ### - if mode == HELPERS: - # if node.type == ENTITY: - self.render_axis(m, - label=node.name if node != self.scene.rootnode else None, - selected=node.selected if hasattr(node, "selected") else False) - - if node.type == CAMERA: - self.render_camera(node, m) - - for child in node.children: - self.recursive_render(child, shader, mode) - - return - - # Mesh rendering modes - ### - if node.type == MESH: - - for mesh in node.meshes: - - stride = 24 # 6 * 4 bytes - - if node.selected and mode == SILHOUETTE: - glUniform4f(shader.u_materialDiffuse, 1.0, 0.0, 0.0, 1.0) - glUniformMatrix4fv(shader.u_modelViewMatrix, 1, GL_TRUE, - numpy.dot(self.view_matrix, m)) - - else: - if mode == COLORS: - colorid = self.node2colorid[node.name] - r, g, b = self.get_rgb_from_colorid(colorid) - glUniform4f(shader.u_materialDiffuse, r / 255.0, g / 255.0, b / 255.0, 1.0) - elif mode == SILHOUETTE: - glUniform4f(shader.u_materialDiffuse, .0, .0, .0, 1.0) - else: - if node.selected: - diffuse = (1.0, 0.0, 0.0, 1.0) # selected nodes in red - else: - diffuse = mesh.material.properties["diffuse"] - if len(diffuse) == 3: # RGB instead of expected RGBA - diffuse.append(1.0) - glUniform4f(shader.u_materialDiffuse, *diffuse) - # if ambient: - # glUniform4f( shader.Material_ambient, *mat["ambient"] ) - - if mode == BASE: # not in COLORS or SILHOUETTE - normal_matrix = linalg.inv(numpy.dot(self.view_matrix, m)[0:3, 0:3]).transpose() - glUniformMatrix3fv(shader.u_normalMatrix, 1, GL_TRUE, normal_matrix) - - glUniformMatrix4fv(shader.u_modelMatrix, 1, GL_TRUE, m) - - vbo = mesh.gl["vbo"] - vbo.bind() - - glEnableVertexAttribArray(shader.a_vertex) - if normals: - glEnableVertexAttribArray(shader.a_normal) - - glVertexAttribPointer( - shader.a_vertex, - 3, GL_FLOAT, False, stride, vbo - ) - - if normals: - glVertexAttribPointer( - shader.a_normal, - 3, GL_FLOAT, False, stride, vbo + 12 - ) - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl["faces"]) - glDrawElements(GL_TRIANGLES, mesh.gl["nbfaces"] * 3, GL_UNSIGNED_INT, None) - - vbo.unbind() - glDisableVertexAttribArray(shader.a_vertex) - - if normals: - glDisableVertexAttribArray(shader.a_normal) - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) - - for child in node.children: - self.recursive_render(child, shader, mode) - - - def switch_to_overlay(self): - glPushMatrix() - self.set_overlay_projection() - - def switch_from_overlay(self): - self.set_camera_projection() - glPopMatrix() - - def select_node(self, node): - self.currently_selected = node - self.update_node_select(self.scene.rootnode) - - def update_node_select(self, node): - if node is self.currently_selected: - node.selected = True - else: - node.selected = False - - for child in node.children: - self.update_node_select(child) - - def loop(self): - - pygame.display.flip() - - if not self.process_events(): - return False # ESC has been pressed - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - - return True - - def process_events(self): - - LEFT_BUTTON = 1 - MIDDLE_BUTTON = 2 - RIGHT_BUTTON = 3 - WHEEL_UP = 4 - WHEEL_DOWN = 5 - - dx, dy = pygame.mouse.get_rel() - mousex, mousey = pygame.mouse.get_pos() - - zooming_one_shot = False - - ok = True - - for evt in pygame.event.get(): - if evt.type == pygame.MOUSEBUTTONDOWN and evt.button == LEFT_BUTTON: - hovered = self.get_hovered_node(mousex, self.h - mousey) - if hovered: - if self.currently_selected and self.currently_selected == hovered: - self.select_node(None) - else: - logger.info("Node %s selected" % hovered) - self.select_node(hovered) - else: - self.is_rotating = True - if evt.type == pygame.MOUSEBUTTONUP and evt.button == LEFT_BUTTON: - self.is_rotating = False - - if evt.type == pygame.MOUSEBUTTONDOWN and evt.button == MIDDLE_BUTTON: - self.is_panning = True - if evt.type == pygame.MOUSEBUTTONUP and evt.button == MIDDLE_BUTTON: - self.is_panning = False - - if evt.type == pygame.MOUSEBUTTONDOWN and evt.button == RIGHT_BUTTON: - self.is_zooming = True - if evt.type == pygame.MOUSEBUTTONUP and evt.button == RIGHT_BUTTON: - self.is_zooming = False - - if evt.type == pygame.MOUSEBUTTONDOWN and evt.button in [WHEEL_UP, WHEEL_DOWN]: - zooming_one_shot = True - self.is_zooming = True - dy = -10 if evt.button == WHEEL_UP else 10 - - if evt.type == pygame.KEYDOWN: - ok = (ok and self.process_keystroke(evt.key, evt.mod)) - - self.controls_3d(dx, dy, zooming_one_shot) - - return ok - - def process_keystroke(self, key, mod): - - # process arrow keys if an object is selected - if self.currently_selected: - up = 0 - strafe = 0 - - if key == pygame.K_UP: - up = 1 - if key == pygame.K_DOWN: - up = -1 - if key == pygame.K_LEFT: - strafe = -1 - if key == pygame.K_RIGHT: - strafe = 1 - - self.move_selected_node(up, strafe) - - if key == pygame.K_f: - pygame.display.toggle_fullscreen() - - if key == pygame.K_TAB: - self.cycle_cameras() - - if key in [pygame.K_ESCAPE, pygame.K_q]: - return False - - return True - - def controls_3d(self, dx, dy, zooming_one_shot=False): - """ Orbiting the camera is implemented the following way: - - - the rotation is split into a rotation around the *world* Z axis - (controlled by the horizontal mouse motion along X) and a - rotation around the *X* axis of the camera (pitch) *shifted to - the focal origin* (the world origin for now). This is controlled - by the vertical motion of the mouse (Y axis). - - as a result, the resulting transformation of the camera in the - world frame C' is: - C' = (T · Rx · T⁻¹ · (Rz · C)⁻¹)⁻¹ - where: - - C is the original camera transformation in the world frame, - - Rz is the rotation along the Z axis (in the world frame) - - T is the translation camera -> world (ie, the inverse of the - translation part of C - - Rx is the rotation around X in the (translated) camera frame """ - - CAMERA_TRANSLATION_FACTOR = 0.01 - CAMERA_ROTATION_FACTOR = 0.01 - - if not (self.is_rotating or self.is_panning or self.is_zooming): - return - - current_pos = self.current_cam.transformation[:3, 3].copy() - distance = numpy.linalg.norm(self.focal_point - current_pos) - - if self.is_rotating: - rotation_camera_x = dy * CAMERA_ROTATION_FACTOR - rotation_world_z = dx * CAMERA_ROTATION_FACTOR - world_z_rotation = transformations.euler_matrix(0, 0, rotation_world_z) - cam_x_rotation = transformations.euler_matrix(rotation_camera_x, 0, 0) - - after_world_z_rotation = numpy.dot(world_z_rotation, self.current_cam.transformation) - - inverse_transformation = transformations.inverse_matrix(after_world_z_rotation) - - translation = transformations.translation_matrix( - transformations.decompose_matrix(inverse_transformation)[3]) - inverse_translation = transformations.inverse_matrix(translation) - - new_inverse = numpy.dot(inverse_translation, inverse_transformation) - new_inverse = numpy.dot(cam_x_rotation, new_inverse) - new_inverse = numpy.dot(translation, new_inverse) - - self.current_cam.transformation = transformations.inverse_matrix(new_inverse).astype(numpy.float32) - - if self.is_panning: - tx = -dx * CAMERA_TRANSLATION_FACTOR * distance - ty = dy * CAMERA_TRANSLATION_FACTOR * distance - cam_transform = transformations.translation_matrix((tx, ty, 0)).astype(numpy.float32) - self.current_cam.transformation = numpy.dot(self.current_cam.transformation, cam_transform) - - if self.is_zooming: - tz = dy * CAMERA_TRANSLATION_FACTOR * distance - cam_transform = transformations.translation_matrix((0, 0, tz)).astype(numpy.float32) - self.current_cam.transformation = numpy.dot(self.current_cam.transformation, cam_transform) - - if zooming_one_shot: - self.is_zooming = False - - self.update_view_camera() - - def update_view_camera(self): - - self.view_matrix = linalg.inv(self.current_cam.transformation) - - # Rotate by 180deg around X to have Z pointing backward (OpenGL convention) - self.view_matrix = numpy.dot(ROTATION_180_X, self.view_matrix) - - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - glMultMatrixf(self.view_matrix.transpose()) - - def move_selected_node(self, up, strafe): - self.currently_selected.transformation[0][3] += strafe - self.currently_selected.transformation[2][3] += up - - @staticmethod - def showtext(text, x=0, y=0, z=0, size=20): - - # TODO: alpha blending does not work... - # glEnable(GL_BLEND) - # glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - font = pygame.font.Font(None, size) - text_surface = font.render(text, True, (10, 10, 10, 255), - (255 * 0.18, 255 * 0.18, 255 * 0.18, 0)) - text_data = pygame.image.tostring(text_surface, "RGBA", True) - glRasterPos3d(x, y, z) - glDrawPixels(text_surface.get_width(), - text_surface.get_height(), - GL_RGBA, GL_UNSIGNED_BYTE, - text_data) - - # glDisable(GL_BLEND) - - -def main(model, width, height): - app = PyAssimp3DViewer(model, w=width, h=height) - - clock = pygame.time.Clock() - - while app.loop(): - - app.update_view_camera() - - ## Main rendering - app.render() - - ## GUI text display - app.switch_to_overlay() - app.showtext("Active camera: %s" % str(app.current_cam), 10, app.h - 30) - if app.currently_selected: - app.showtext("Selected node: %s" % app.currently_selected, 10, app.h - 50) - pos = app.h - 70 - - app.showtext("(%sm, %sm, %sm)" % (app.currently_selected.transformation[0, 3], - app.currently_selected.transformation[1, 3], - app.currently_selected.transformation[2, 3]), 30, pos) - - app.switch_from_overlay() - - # Make sure we do not go over 30fps - clock.tick(30) - - logger.info("Quitting! Bye bye!") - - -######################################################################### -######################################################################### - -if __name__ == '__main__': - if not len(sys.argv) > 1: - print("Usage: " + __file__ + " <model>") - sys.exit(2) - - main(model=sys.argv[1], width=1024, height=768) diff --git a/libs/assimp/port/PyAssimp/scripts/README.md b/libs/assimp/port/PyAssimp/scripts/README.md deleted file mode 100644 index 42caa27..0000000 --- a/libs/assimp/port/PyAssimp/scripts/README.md +++ /dev/null @@ -1,13 +0,0 @@ -pyassimp examples -================= - -- `sample.py`: shows how to load a model with pyassimp, and display some statistics. -- `3d_viewer.py`: an OpenGL 3D viewer that requires shaders -- `fixed_pipeline_3d_viewer`: an OpenGL 3D viewer using the old fixed-pipeline. - Only for illustration example. Base new projects on `3d_viewer.py`. - - -Requirements for the 3D viewers: - -- `pyopengl` (on Ubuntu/Debian, `sudo apt-get install python-opengl`) -- `pygame` (on Ubuntu/Debian, `sudo apt-get install python-pygame`) diff --git a/libs/assimp/port/PyAssimp/scripts/fixed_pipeline_3d_viewer.py b/libs/assimp/port/PyAssimp/scripts/fixed_pipeline_3d_viewer.py deleted file mode 100755 index c2f6ceb..0000000 --- a/libs/assimp/port/PyAssimp/scripts/fixed_pipeline_3d_viewer.py +++ /dev/null @@ -1,372 +0,0 @@ -#!/usr/bin/env python -#-*- coding: UTF-8 -*- - -""" This program demonstrates the use of pyassimp to load and -render objects with OpenGL. - -'c' cycles between cameras (if any available) -'q' to quit - -This example mixes 'old' OpenGL fixed-function pipeline with -Vertex Buffer Objects. - -Materials are supported but textures are currently ignored. - -For a more advanced example (with shaders + keyboard/mouse -controls), check scripts/sdl_viewer.py - -Author: Séverin Lemaignan, 2012 - -This sample is based on several sources, including: - - http://www.lighthouse3d.com/tutorials - - http://www.songho.ca/opengl/gl_transform.html - - http://code.activestate.com/recipes/325391/ - - ASSIMP's C++ SimpleOpenGL viewer -""" - -import sys -from OpenGL.GLUT import * -from OpenGL.GLU import * -from OpenGL.GL import * - -import logging -logger = logging.getLogger("pyassimp_opengl") -logging.basicConfig(level=logging.INFO) - -import math -import numpy - -import pyassimp -from pyassimp.postprocess import * -from pyassimp.helper import * - - -name = 'pyassimp OpenGL viewer' -height = 600 -width = 900 - -class GLRenderer(): - def __init__(self): - - self.scene = None - - self.using_fixed_cam = False - self.current_cam_index = 0 - - # store the global scene rotation - self.angle = 0. - - # for FPS calculation - self.prev_time = 0 - self.prev_fps_time = 0 - self.frames = 0 - - def prepare_gl_buffers(self, mesh): - """ Creates 3 buffer objets for each mesh, - to store the vertices, the normals, and the faces - indices. - """ - - mesh.gl = {} - - # Fill the buffer for vertex positions - mesh.gl["vertices"] = glGenBuffers(1) - glBindBuffer(GL_ARRAY_BUFFER, mesh.gl["vertices"]) - glBufferData(GL_ARRAY_BUFFER, - mesh.vertices, - GL_STATIC_DRAW) - - # Fill the buffer for normals - mesh.gl["normals"] = glGenBuffers(1) - glBindBuffer(GL_ARRAY_BUFFER, mesh.gl["normals"]) - glBufferData(GL_ARRAY_BUFFER, - mesh.normals, - GL_STATIC_DRAW) - - - # Fill the buffer for vertex positions - mesh.gl["triangles"] = glGenBuffers(1) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl["triangles"]) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - mesh.faces, - GL_STATIC_DRAW) - - # Unbind buffers - glBindBuffer(GL_ARRAY_BUFFER,0) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0) - - def load_model(self, path, postprocess = None): - logger.info("Loading model:" + path + "...") - - if postprocess: - self.scene = pyassimp.load(path, processing=postprocess) - else: - self.scene = pyassimp.load(path) - logger.info("Done.") - - scene = self.scene - #log some statistics - logger.info(" meshes: %d" % len(scene.meshes)) - logger.info(" total faces: %d" % sum([len(mesh.faces) for mesh in scene.meshes])) - logger.info(" materials: %d" % len(scene.materials)) - self.bb_min, self.bb_max = get_bounding_box(self.scene) - logger.info(" bounding box:" + str(self.bb_min) + " - " + str(self.bb_max)) - - self.scene_center = [(a + b) / 2. for a, b in zip(self.bb_min, self.bb_max)] - - for index, mesh in enumerate(scene.meshes): - self.prepare_gl_buffers(mesh) - - # Finally release the model - pyassimp.release(scene) - - def cycle_cameras(self): - self.current_cam_index - if not self.scene.cameras: - return None - self.current_cam_index = (self.current_cam_index + 1) % len(self.scene.cameras) - cam = self.scene.cameras[self.current_cam_index] - logger.info("Switched to camera " + str(cam)) - return cam - - def set_default_camera(self): - - if not self.using_fixed_cam: - glLoadIdentity() - - gluLookAt(0.,0.,3., - 0.,0.,-5., - 0.,1.,0.) - - - - def set_camera(self, camera): - - if not camera: - return - - self.using_fixed_cam = True - - znear = camera.clipplanenear - zfar = camera.clipplanefar - aspect = camera.aspect - fov = camera.horizontalfov - - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - - # Compute gl frustrum - tangent = math.tan(fov/2.) - h = znear * tangent - w = h * aspect - - # params: left, right, bottom, top, near, far - glFrustum(-w, w, -h, h, znear, zfar) - # equivalent to: - #gluPerspective(fov * 180/math.pi, aspect, znear, zfar) - - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - - cam = transform(camera.position, camera.transformation) - at = transform(camera.lookat, camera.transformation) - gluLookAt(cam[0], cam[2], -cam[1], - at[0], at[2], -at[1], - 0, 1, 0) - - def fit_scene(self, restore = False): - """ Compute a scale factor and a translation to fit and center - the whole geometry on the screen. - """ - - x_max = self.bb_max[0] - self.bb_min[0] - y_max = self.bb_max[1] - self.bb_min[1] - tmp = max(x_max, y_max) - z_max = self.bb_max[2] - self.bb_min[2] - tmp = max(z_max, tmp) - - if not restore: - tmp = 1. / tmp - - logger.info("Scaling the scene by %.03f" % tmp) - glScalef(tmp, tmp, tmp) - - # center the model - direction = -1 if not restore else 1 - glTranslatef( direction * self.scene_center[0], - direction * self.scene_center[1], - direction * self.scene_center[2] ) - - return x_max, y_max, z_max - - def apply_material(self, mat): - """ Apply an OpenGL, using one OpenGL display list per material to cache - the operation. - """ - - if not hasattr(mat, "gl_mat"): # evaluate once the mat properties, and cache the values in a glDisplayList. - diffuse = numpy.array(mat.properties.get("diffuse", [0.8, 0.8, 0.8, 1.0])) - specular = numpy.array(mat.properties.get("specular", [0., 0., 0., 1.0])) - ambient = numpy.array(mat.properties.get("ambient", [0.2, 0.2, 0.2, 1.0])) - emissive = numpy.array(mat.properties.get("emissive", [0., 0., 0., 1.0])) - shininess = min(mat.properties.get("shininess", 1.0), 128) - wireframe = mat.properties.get("wireframe", 0) - twosided = mat.properties.get("twosided", 1) - - setattr(mat, "gl_mat", glGenLists(1)) - glNewList(mat.gl_mat, GL_COMPILE) - - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse) - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular) - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient) - glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emissive) - glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess) - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE if wireframe else GL_FILL) - glDisable(GL_CULL_FACE) if twosided else glEnable(GL_CULL_FACE) - - glEndList() - - glCallList(mat.gl_mat) - - - - def do_motion(self): - - gl_time = glutGet(GLUT_ELAPSED_TIME) - - self.angle = (gl_time - self.prev_time) * 0.1 - - self.prev_time = gl_time - - # Compute FPS - self.frames += 1 - if gl_time - self.prev_fps_time >= 1000: - current_fps = self.frames * 1000 / (gl_time - self.prev_fps_time) - logger.info('%.0f fps' % current_fps) - self.frames = 0 - self.prev_fps_time = gl_time - - glutPostRedisplay() - - def recursive_render(self, node): - """ Main recursive rendering method. - """ - - # save model matrix and apply node transformation - glPushMatrix() - m = node.transformation.transpose() # OpenGL row major - glMultMatrixf(m) - - for mesh in node.meshes: - self.apply_material(mesh.material) - - glBindBuffer(GL_ARRAY_BUFFER, mesh.gl["vertices"]) - glEnableClientState(GL_VERTEX_ARRAY) - glVertexPointer(3, GL_FLOAT, 0, None) - - glBindBuffer(GL_ARRAY_BUFFER, mesh.gl["normals"]) - glEnableClientState(GL_NORMAL_ARRAY) - glNormalPointer(GL_FLOAT, 0, None) - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl["triangles"]) - glDrawElements(GL_TRIANGLES,len(mesh.faces) * 3, GL_UNSIGNED_INT, None) - - glDisableClientState(GL_VERTEX_ARRAY) - glDisableClientState(GL_NORMAL_ARRAY) - - glBindBuffer(GL_ARRAY_BUFFER, 0) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) - - for child in node.children: - self.recursive_render(child) - - glPopMatrix() - - - def display(self): - """ GLUT callback to redraw OpenGL surface - """ - glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) - - glRotatef(self.angle,0.,1.,0.) - self.recursive_render(self.scene.rootnode) - - glutSwapBuffers() - self.do_motion() - return - - #################################################################### - ## GLUT keyboard and mouse callbacks ## - #################################################################### - def onkeypress(self, key, x, y): - if key == 'c': - self.fit_scene(restore = True) - self.set_camera(self.cycle_cameras()) - if key == 'q': - sys.exit(0) - - def render(self, filename=None, fullscreen = False, autofit = True, postprocess = None): - """ - - :param autofit: if true, scale the scene to fit the whole geometry - in the viewport. - """ - - # First initialize the openGL context - glutInit(sys.argv) - glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) - if not fullscreen: - glutInitWindowSize(width, height) - glutCreateWindow(name) - else: - glutGameModeString("1024x768") - if glutGameModeGet(GLUT_GAME_MODE_POSSIBLE): - glutEnterGameMode() - else: - print("Fullscreen mode not available!") - sys.exit(1) - - self.load_model(filename, postprocess = postprocess) - - - glClearColor(0.1,0.1,0.1,1.) - #glShadeModel(GL_SMOOTH) - - glEnable(GL_LIGHTING) - - glEnable(GL_CULL_FACE) - glEnable(GL_DEPTH_TEST) - - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE) - glEnable(GL_NORMALIZE) - glEnable(GL_LIGHT0) - - glutDisplayFunc(self.display) - - - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - gluPerspective(35.0, width/float(height) , 0.10, 100.0) - glMatrixMode(GL_MODELVIEW) - self.set_default_camera() - - if autofit: - # scale the whole asset to fit into our view frustum· - self.fit_scene() - - glPushMatrix() - - glutKeyboardFunc(self.onkeypress) - glutIgnoreKeyRepeat(1) - - glutMainLoop() - - -if __name__ == '__main__': - if not len(sys.argv) > 1: - print("Usage: " + __file__ + " <model>") - sys.exit(0) - - glrender = GLRenderer() - glrender.render(sys.argv[1], fullscreen = False, postprocess = aiProcessPreset_TargetRealtime_MaxQuality) - diff --git a/libs/assimp/port/PyAssimp/scripts/quicktest.py b/libs/assimp/port/PyAssimp/scripts/quicktest.py deleted file mode 100755 index cbeccb4..0000000 --- a/libs/assimp/port/PyAssimp/scripts/quicktest.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -#-*- coding: UTF-8 -*- - -""" -This module uses the sample.py script to load all test models it finds. - -Note: this is not an exhaustive test suite, it does not check the -data structures in detail. It just verifies whether basic -loading and querying of 3d models using pyassimp works. -""" - -import os -import sys - -# Make the development (ie. GIT repo) version of PyAssimp available for import. -sys.path.insert(0, '..') - -import sample -from pyassimp import errors - -# Paths to model files. -basepaths = [os.path.join('..', '..', '..', 'test', 'models'), - os.path.join('..', '..', '..', 'test', 'models-nonbsd')] - -# Valid extensions for 3D model files. -extensions = ['.3ds', '.x', '.lwo', '.obj', '.md5mesh', '.dxf', '.ply', '.stl', - '.dae', '.md5anim', '.lws', '.irrmesh', '.nff', '.off', '.blend'] - - -def run_tests(): - ok, err = 0, 0 - for path in basepaths: - print("Looking for models in %s..." % path) - for root, dirs, files in os.walk(path): - for afile in files: - base, ext = os.path.splitext(afile) - if ext in extensions: - try: - sample.main(os.path.join(root, afile)) - ok += 1 - except errors.AssimpError as error: - # Assimp error is fine; this is a controlled case. - print(error) - err += 1 - except Exception: - print("Error encountered while loading <%s>" - % os.path.join(root, afile)) - print('** Loaded %s models, got controlled errors for %s files' - % (ok, err)) - - -if __name__ == '__main__': - run_tests() diff --git a/libs/assimp/port/PyAssimp/scripts/sample.py b/libs/assimp/port/PyAssimp/scripts/sample.py deleted file mode 100755 index 3cd4b3e..0000000 --- a/libs/assimp/port/PyAssimp/scripts/sample.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python -#-*- coding: UTF-8 -*- - -""" -This module demonstrates the functionality of PyAssimp. -""" - -import sys -import logging -logging.basicConfig(level=logging.INFO) - -import pyassimp -import pyassimp.postprocess - -def recur_node(node,level = 0): - print(" " + "\t" * level + "- " + str(node)) - for child in node.children: - recur_node(child, level + 1) - - -def main(filename=None): - - scene = pyassimp.load(filename, processing=pyassimp.postprocess.aiProcess_Triangulate) - - #the model we load - print("MODEL:" + filename) - print - - #write some statistics - print("SCENE:") - print(" meshes:" + str(len(scene.meshes))) - print(" materials:" + str(len(scene.materials))) - print(" textures:" + str(len(scene.textures))) - print - - print("NODES:") - recur_node(scene.rootnode) - - print - print("MESHES:") - for index, mesh in enumerate(scene.meshes): - print(" MESH" + str(index+1)) - print(" material id:" + str(mesh.materialindex+1)) - print(" vertices:" + str(len(mesh.vertices))) - print(" first 3 verts:\n" + str(mesh.vertices[:3])) - if mesh.normals.any(): - print(" first 3 normals:\n" + str(mesh.normals[:3])) - else: - print(" no normals") - print(" colors:" + str(len(mesh.colors))) - tcs = mesh.texturecoords - if tcs.any(): - for tc_index, tc in enumerate(tcs): - print(" texture-coords "+ str(tc_index) + ":" + str(len(tcs[tc_index])) + "first3:" + str(tcs[tc_index][:3])) - - else: - print(" no texture coordinates") - print(" uv-component-count:" + str(len(mesh.numuvcomponents))) - print(" faces:" + str(len(mesh.faces)) + " -> first:\n" + str(mesh.faces[:3])) - print(" bones:" + str(len(mesh.bones)) + " -> first:" + str([str(b) for b in mesh.bones[:3]])) - print - - print("MATERIALS:") - for index, material in enumerate(scene.materials): - print(" MATERIAL (id:" + str(index+1) + ")") - for key, value in material.properties.items(): - print(" %s: %s" % (key, value)) - print - - print("TEXTURES:") - for index, texture in enumerate(scene.textures): - print(" TEXTURE" + str(index+1)) - print(" width:" + str(texture.width)) - print(" height:" + str(texture.height)) - print(" hint:" + str(texture.achformathint)) - print(" data (size):" + str(len(texture.data))) - - # Finally release the model - pyassimp.release(scene) - -def usage(): - print("Usage: sample.py <3d model>") - -if __name__ == "__main__": - - if len(sys.argv) != 2: - usage() - else: - main(sys.argv[1]) diff --git a/libs/assimp/port/PyAssimp/scripts/transformations.py b/libs/assimp/port/PyAssimp/scripts/transformations.py deleted file mode 100644 index bf0cac9..0000000 --- a/libs/assimp/port/PyAssimp/scripts/transformations.py +++ /dev/null @@ -1,1705 +0,0 @@ -# -*- coding: utf-8 -*- -# transformations.py - -# Copyright (c) 2006, Christoph Gohlke -# Copyright (c) 2006-2009, The Regents of the University of California -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the copyright holders nor the names of any -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -"""Homogeneous Transformation Matrices and Quaternions. - -A library for calculating 4x4 matrices for translating, rotating, reflecting, -scaling, shearing, projecting, orthogonalizing, and superimposing arrays of -3D homogeneous coordinates as well as for converting between rotation matrices, -Euler angles, and quaternions. Also includes an Arcball control object and -functions to decompose transformation matrices. - -:Authors: - `Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>`__, - Laboratory for Fluorescence Dynamics, University of California, Irvine - -:Version: 20090418 - -Requirements ------------- - -* `Python 2.6 <http://www.python.org>`__ -* `Numpy 1.3 <http://numpy.scipy.org>`__ -* `transformations.c 20090418 <http://www.lfd.uci.edu/~gohlke/>`__ - (optional implementation of some functions in C) - -Notes ------ - -Matrices (M) can be inverted using numpy.linalg.inv(M), concatenated using -numpy.dot(M0, M1), or used to transform homogeneous coordinates (v) using -numpy.dot(M, v) for shape (4, \*) "point of arrays", respectively -numpy.dot(v, M.T) for shape (\*, 4) "array of points". - -Calculations are carried out with numpy.float64 precision. - -This Python implementation is not optimized for speed. - -Vector, point, quaternion, and matrix function arguments are expected to be -"array like", i.e. tuple, list, or numpy arrays. - -Return types are numpy arrays unless specified otherwise. - -Angles are in radians unless specified otherwise. - -Quaternions ix+jy+kz+w are represented as [x, y, z, w]. - -Use the transpose of transformation matrices for OpenGL glMultMatrixd(). - -A triple of Euler angles can be applied/interpreted in 24 ways, which can -be specified using a 4 character string or encoded 4-tuple: - - *Axes 4-string*: e.g. 'sxyz' or 'ryxy' - - - first character : rotations are applied to 's'tatic or 'r'otating frame - - remaining characters : successive rotation axis 'x', 'y', or 'z' - - *Axes 4-tuple*: e.g. (0, 0, 0, 0) or (1, 1, 1, 1) - - - inner axis: code of axis ('x':0, 'y':1, 'z':2) of rightmost matrix. - - parity : even (0) if inner axis 'x' is followed by 'y', 'y' is followed - by 'z', or 'z' is followed by 'x'. Otherwise odd (1). - - repetition : first and last axis are same (1) or different (0). - - frame : rotations are applied to static (0) or rotating (1) frame. - -References ----------- - -(1) Matrices and transformations. Ronald Goldman. - In "Graphics Gems I", pp 472-475. Morgan Kaufmann, 1990. -(2) More matrices and transformations: shear and pseudo-perspective. - Ronald Goldman. In "Graphics Gems II", pp 320-323. Morgan Kaufmann, 1991. -(3) Decomposing a matrix into simple transformations. Spencer Thomas. - In "Graphics Gems II", pp 320-323. Morgan Kaufmann, 1991. -(4) Recovering the data from the transformation matrix. Ronald Goldman. - In "Graphics Gems II", pp 324-331. Morgan Kaufmann, 1991. -(5) Euler angle conversion. Ken Shoemake. - In "Graphics Gems IV", pp 222-229. Morgan Kaufmann, 1994. -(6) Arcball rotation control. Ken Shoemake. - In "Graphics Gems IV", pp 175-192. Morgan Kaufmann, 1994. -(7) Representing attitude: Euler angles, unit quaternions, and rotation - vectors. James Diebel. 2006. -(8) A discussion of the solution for the best rotation to relate two sets - of vectors. W Kabsch. Acta Cryst. 1978. A34, 827-828. -(9) Closed-form solution of absolute orientation using unit quaternions. - BKP Horn. J Opt Soc Am A. 1987. 4(4), 629-642. -(10) Quaternions. Ken Shoemake. - http://www.sfu.ca/~jwa3/cmpt461/files/quatut.pdf -(11) From quaternion to matrix and back. JMP van Waveren. 2005. - http://www.intel.com/cd/ids/developer/asmo-na/eng/293748.htm -(12) Uniform random rotations. Ken Shoemake. - In "Graphics Gems III", pp 124-132. Morgan Kaufmann, 1992. - - -Examples --------- - ->>> alpha, beta, gamma = 0.123, -1.234, 2.345 ->>> origin, xaxis, yaxis, zaxis = (0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1) ->>> I = identity_matrix() ->>> Rx = rotation_matrix(alpha, xaxis) ->>> Ry = rotation_matrix(beta, yaxis) ->>> Rz = rotation_matrix(gamma, zaxis) ->>> R = concatenate_matrices(Rx, Ry, Rz) ->>> euler = euler_from_matrix(R, 'rxyz') ->>> numpy.allclose([alpha, beta, gamma], euler) -True ->>> Re = euler_matrix(alpha, beta, gamma, 'rxyz') ->>> is_same_transform(R, Re) -True ->>> al, be, ga = euler_from_matrix(Re, 'rxyz') ->>> is_same_transform(Re, euler_matrix(al, be, ga, 'rxyz')) -True ->>> qx = quaternion_about_axis(alpha, xaxis) ->>> qy = quaternion_about_axis(beta, yaxis) ->>> qz = quaternion_about_axis(gamma, zaxis) ->>> q = quaternion_multiply(qx, qy) ->>> q = quaternion_multiply(q, qz) ->>> Rq = quaternion_matrix(q) ->>> is_same_transform(R, Rq) -True ->>> S = scale_matrix(1.23, origin) ->>> T = translation_matrix((1, 2, 3)) ->>> Z = shear_matrix(beta, xaxis, origin, zaxis) ->>> R = random_rotation_matrix(numpy.random.rand(3)) ->>> M = concatenate_matrices(T, R, Z, S) ->>> scale, shear, angles, trans, persp = decompose_matrix(M) ->>> numpy.allclose(scale, 1.23) -True ->>> numpy.allclose(trans, (1, 2, 3)) -True ->>> numpy.allclose(shear, (0, math.tan(beta), 0)) -True ->>> is_same_transform(R, euler_matrix(axes='sxyz', *angles)) -True ->>> M1 = compose_matrix(scale, shear, angles, trans, persp) ->>> is_same_transform(M, M1) -True - -""" - -from __future__ import division - -import warnings -import math - -import numpy - -# Documentation in HTML format can be generated with Epydoc -__docformat__ = "restructuredtext en" - - -def identity_matrix(): - """Return 4x4 identity/unit matrix. - - >>> I = identity_matrix() - >>> numpy.allclose(I, numpy.dot(I, I)) - True - >>> numpy.sum(I), numpy.trace(I) - (4.0, 4.0) - >>> numpy.allclose(I, numpy.identity(4, dtype=numpy.float64)) - True - - """ - return numpy.identity(4, dtype=numpy.float64) - - -def translation_matrix(direction): - """Return matrix to translate by direction vector. - - >>> v = numpy.random.random(3) - 0.5 - >>> numpy.allclose(v, translation_matrix(v)[:3, 3]) - True - - """ - M = numpy.identity(4) - M[:3, 3] = direction[:3] - return M - - -def translation_from_matrix(matrix): - """Return translation vector from translation matrix. - - >>> v0 = numpy.random.random(3) - 0.5 - >>> v1 = translation_from_matrix(translation_matrix(v0)) - >>> numpy.allclose(v0, v1) - True - - """ - return numpy.array(matrix, copy=False)[:3, 3].copy() - - -def reflection_matrix(point, normal): - """Return matrix to mirror at plane defined by point and normal vector. - - >>> v0 = numpy.random.random(4) - 0.5 - >>> v0[3] = 1.0 - >>> v1 = numpy.random.random(3) - 0.5 - >>> R = reflection_matrix(v0, v1) - >>> numpy.allclose(2., numpy.trace(R)) - True - >>> numpy.allclose(v0, numpy.dot(R, v0)) - True - >>> v2 = v0.copy() - >>> v2[:3] += v1 - >>> v3 = v0.copy() - >>> v2[:3] -= v1 - >>> numpy.allclose(v2, numpy.dot(R, v3)) - True - - """ - normal = unit_vector(normal[:3]) - M = numpy.identity(4) - M[:3, :3] -= 2.0 * numpy.outer(normal, normal) - M[:3, 3] = (2.0 * numpy.dot(point[:3], normal)) * normal - return M - - -def reflection_from_matrix(matrix): - """Return mirror plane point and normal vector from reflection matrix. - - >>> v0 = numpy.random.random(3) - 0.5 - >>> v1 = numpy.random.random(3) - 0.5 - >>> M0 = reflection_matrix(v0, v1) - >>> point, normal = reflection_from_matrix(M0) - >>> M1 = reflection_matrix(point, normal) - >>> is_same_transform(M0, M1) - True - - """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False) - # normal: unit eigenvector corresponding to eigenvalue -1 - l, V = numpy.linalg.eig(M[:3, :3]) - i = numpy.where(abs(numpy.real(l) + 1.0) < 1e-8)[0] - if not len(i): - raise ValueError("no unit eigenvector corresponding to eigenvalue -1") - normal = numpy.real(V[:, i[0]]).squeeze() - # point: any unit eigenvector corresponding to eigenvalue 1 - l, V = numpy.linalg.eig(M) - i = numpy.where(abs(numpy.real(l) - 1.0) < 1e-8)[0] - if not len(i): - raise ValueError("no unit eigenvector corresponding to eigenvalue 1") - point = numpy.real(V[:, i[-1]]).squeeze() - point /= point[3] - return point, normal - - -def rotation_matrix(angle, direction, point=None): - """Return matrix to rotate about axis defined by point and direction. - - >>> angle = (random.random() - 0.5) * (2*math.pi) - >>> direc = numpy.random.random(3) - 0.5 - >>> point = numpy.random.random(3) - 0.5 - >>> R0 = rotation_matrix(angle, direc, point) - >>> R1 = rotation_matrix(angle-2*math.pi, direc, point) - >>> is_same_transform(R0, R1) - True - >>> R0 = rotation_matrix(angle, direc, point) - >>> R1 = rotation_matrix(-angle, -direc, point) - >>> is_same_transform(R0, R1) - True - >>> I = numpy.identity(4, numpy.float64) - >>> numpy.allclose(I, rotation_matrix(math.pi*2, direc)) - True - >>> numpy.allclose(2., numpy.trace(rotation_matrix(math.pi/2, - ... direc, point))) - True - - """ - sina = math.sin(angle) - cosa = math.cos(angle) - direction = unit_vector(direction[:3]) - # rotation matrix around unit vector - R = numpy.array(((cosa, 0.0, 0.0), - (0.0, cosa, 0.0), - (0.0, 0.0, cosa)), dtype=numpy.float64) - R += numpy.outer(direction, direction) * (1.0 - cosa) - direction *= sina - R += numpy.array((( 0.0, -direction[2], direction[1]), - ( direction[2], 0.0, -direction[0]), - (-direction[1], direction[0], 0.0)), - dtype=numpy.float64) - M = numpy.identity(4) - M[:3, :3] = R - if point is not None: - # rotation not around origin - point = numpy.array(point[:3], dtype=numpy.float64, copy=False) - M[:3, 3] = point - numpy.dot(R, point) - return M - - -def rotation_from_matrix(matrix): - """Return rotation angle and axis from rotation matrix. - - >>> angle = (random.random() - 0.5) * (2*math.pi) - >>> direc = numpy.random.random(3) - 0.5 - >>> point = numpy.random.random(3) - 0.5 - >>> R0 = rotation_matrix(angle, direc, point) - >>> angle, direc, point = rotation_from_matrix(R0) - >>> R1 = rotation_matrix(angle, direc, point) - >>> is_same_transform(R0, R1) - True - - """ - R = numpy.array(matrix, dtype=numpy.float64, copy=False) - R33 = R[:3, :3] - # direction: unit eigenvector of R33 corresponding to eigenvalue of 1 - l, W = numpy.linalg.eig(R33.T) - i = numpy.where(abs(numpy.real(l) - 1.0) < 1e-8)[0] - if not len(i): - raise ValueError("no unit eigenvector corresponding to eigenvalue 1") - direction = numpy.real(W[:, i[-1]]).squeeze() - # point: unit eigenvector of R33 corresponding to eigenvalue of 1 - l, Q = numpy.linalg.eig(R) - i = numpy.where(abs(numpy.real(l) - 1.0) < 1e-8)[0] - if not len(i): - raise ValueError("no unit eigenvector corresponding to eigenvalue 1") - point = numpy.real(Q[:, i[-1]]).squeeze() - point /= point[3] - # rotation angle depending on direction - cosa = (numpy.trace(R33) - 1.0) / 2.0 - if abs(direction[2]) > 1e-8: - sina = (R[1, 0] + (cosa-1.0)*direction[0]*direction[1]) / direction[2] - elif abs(direction[1]) > 1e-8: - sina = (R[0, 2] + (cosa-1.0)*direction[0]*direction[2]) / direction[1] - else: - sina = (R[2, 1] + (cosa-1.0)*direction[1]*direction[2]) / direction[0] - angle = math.atan2(sina, cosa) - return angle, direction, point - - -def scale_matrix(factor, origin=None, direction=None): - """Return matrix to scale by factor around origin in direction. - - Use factor -1 for point symmetry. - - >>> v = (numpy.random.rand(4, 5) - 0.5) * 20.0 - >>> v[3] = 1.0 - >>> S = scale_matrix(-1.234) - >>> numpy.allclose(numpy.dot(S, v)[:3], -1.234*v[:3]) - True - >>> factor = random.random() * 10 - 5 - >>> origin = numpy.random.random(3) - 0.5 - >>> direct = numpy.random.random(3) - 0.5 - >>> S = scale_matrix(factor, origin) - >>> S = scale_matrix(factor, origin, direct) - - """ - if direction is None: - # uniform scaling - M = numpy.array(((factor, 0.0, 0.0, 0.0), - (0.0, factor, 0.0, 0.0), - (0.0, 0.0, factor, 0.0), - (0.0, 0.0, 0.0, 1.0)), dtype=numpy.float64) - if origin is not None: - M[:3, 3] = origin[:3] - M[:3, 3] *= 1.0 - factor - else: - # nonuniform scaling - direction = unit_vector(direction[:3]) - factor = 1.0 - factor - M = numpy.identity(4) - M[:3, :3] -= factor * numpy.outer(direction, direction) - if origin is not None: - M[:3, 3] = (factor * numpy.dot(origin[:3], direction)) * direction - return M - - -def scale_from_matrix(matrix): - """Return scaling factor, origin and direction from scaling matrix. - - >>> factor = random.random() * 10 - 5 - >>> origin = numpy.random.random(3) - 0.5 - >>> direct = numpy.random.random(3) - 0.5 - >>> S0 = scale_matrix(factor, origin) - >>> factor, origin, direction = scale_from_matrix(S0) - >>> S1 = scale_matrix(factor, origin, direction) - >>> is_same_transform(S0, S1) - True - >>> S0 = scale_matrix(factor, origin, direct) - >>> factor, origin, direction = scale_from_matrix(S0) - >>> S1 = scale_matrix(factor, origin, direction) - >>> is_same_transform(S0, S1) - True - - """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False) - M33 = M[:3, :3] - factor = numpy.trace(M33) - 2.0 - try: - # direction: unit eigenvector corresponding to eigenvalue factor - l, V = numpy.linalg.eig(M33) - i = numpy.where(abs(numpy.real(l) - factor) < 1e-8)[0][0] - direction = numpy.real(V[:, i]).squeeze() - direction /= vector_norm(direction) - except IndexError: - # uniform scaling - factor = (factor + 2.0) / 3.0 - direction = None - # origin: any eigenvector corresponding to eigenvalue 1 - l, V = numpy.linalg.eig(M) - i = numpy.where(abs(numpy.real(l) - 1.0) < 1e-8)[0] - if not len(i): - raise ValueError("no eigenvector corresponding to eigenvalue 1") - origin = numpy.real(V[:, i[-1]]).squeeze() - origin /= origin[3] - return factor, origin, direction - - -def projection_matrix(point, normal, direction=None, - perspective=None, pseudo=False): - """Return matrix to project onto plane defined by point and normal. - - Using either perspective point, projection direction, or none of both. - - If pseudo is True, perspective projections will preserve relative depth - such that Perspective = dot(Orthogonal, PseudoPerspective). - - >>> P = projection_matrix((0, 0, 0), (1, 0, 0)) - >>> numpy.allclose(P[1:, 1:], numpy.identity(4)[1:, 1:]) - True - >>> point = numpy.random.random(3) - 0.5 - >>> normal = numpy.random.random(3) - 0.5 - >>> direct = numpy.random.random(3) - 0.5 - >>> persp = numpy.random.random(3) - 0.5 - >>> P0 = projection_matrix(point, normal) - >>> P1 = projection_matrix(point, normal, direction=direct) - >>> P2 = projection_matrix(point, normal, perspective=persp) - >>> P3 = projection_matrix(point, normal, perspective=persp, pseudo=True) - >>> is_same_transform(P2, numpy.dot(P0, P3)) - True - >>> P = projection_matrix((3, 0, 0), (1, 1, 0), (1, 0, 0)) - >>> v0 = (numpy.random.rand(4, 5) - 0.5) * 20.0 - >>> v0[3] = 1.0 - >>> v1 = numpy.dot(P, v0) - >>> numpy.allclose(v1[1], v0[1]) - True - >>> numpy.allclose(v1[0], 3.0-v1[1]) - True - - """ - M = numpy.identity(4) - point = numpy.array(point[:3], dtype=numpy.float64, copy=False) - normal = unit_vector(normal[:3]) - if perspective is not None: - # perspective projection - perspective = numpy.array(perspective[:3], dtype=numpy.float64, - copy=False) - M[0, 0] = M[1, 1] = M[2, 2] = numpy.dot(perspective-point, normal) - M[:3, :3] -= numpy.outer(perspective, normal) - if pseudo: - # preserve relative depth - M[:3, :3] -= numpy.outer(normal, normal) - M[:3, 3] = numpy.dot(point, normal) * (perspective+normal) - else: - M[:3, 3] = numpy.dot(point, normal) * perspective - M[3, :3] = -normal - M[3, 3] = numpy.dot(perspective, normal) - elif direction is not None: - # parallel projection - direction = numpy.array(direction[:3], dtype=numpy.float64, copy=False) - scale = numpy.dot(direction, normal) - M[:3, :3] -= numpy.outer(direction, normal) / scale - M[:3, 3] = direction * (numpy.dot(point, normal) / scale) - else: - # orthogonal projection - M[:3, :3] -= numpy.outer(normal, normal) - M[:3, 3] = numpy.dot(point, normal) * normal - return M - - -def projection_from_matrix(matrix, pseudo=False): - """Return projection plane and perspective point from projection matrix. - - Return values are same as arguments for projection_matrix function: - point, normal, direction, perspective, and pseudo. - - >>> point = numpy.random.random(3) - 0.5 - >>> normal = numpy.random.random(3) - 0.5 - >>> direct = numpy.random.random(3) - 0.5 - >>> persp = numpy.random.random(3) - 0.5 - >>> P0 = projection_matrix(point, normal) - >>> result = projection_from_matrix(P0) - >>> P1 = projection_matrix(*result) - >>> is_same_transform(P0, P1) - True - >>> P0 = projection_matrix(point, normal, direct) - >>> result = projection_from_matrix(P0) - >>> P1 = projection_matrix(*result) - >>> is_same_transform(P0, P1) - True - >>> P0 = projection_matrix(point, normal, perspective=persp, pseudo=False) - >>> result = projection_from_matrix(P0, pseudo=False) - >>> P1 = projection_matrix(*result) - >>> is_same_transform(P0, P1) - True - >>> P0 = projection_matrix(point, normal, perspective=persp, pseudo=True) - >>> result = projection_from_matrix(P0, pseudo=True) - >>> P1 = projection_matrix(*result) - >>> is_same_transform(P0, P1) - True - - """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False) - M33 = M[:3, :3] - l, V = numpy.linalg.eig(M) - i = numpy.where(abs(numpy.real(l) - 1.0) < 1e-8)[0] - if not pseudo and len(i): - # point: any eigenvector corresponding to eigenvalue 1 - point = numpy.real(V[:, i[-1]]).squeeze() - point /= point[3] - # direction: unit eigenvector corresponding to eigenvalue 0 - l, V = numpy.linalg.eig(M33) - i = numpy.where(abs(numpy.real(l)) < 1e-8)[0] - if not len(i): - raise ValueError("no eigenvector corresponding to eigenvalue 0") - direction = numpy.real(V[:, i[0]]).squeeze() - direction /= vector_norm(direction) - # normal: unit eigenvector of M33.T corresponding to eigenvalue 0 - l, V = numpy.linalg.eig(M33.T) - i = numpy.where(abs(numpy.real(l)) < 1e-8)[0] - if len(i): - # parallel projection - normal = numpy.real(V[:, i[0]]).squeeze() - normal /= vector_norm(normal) - return point, normal, direction, None, False - else: - # orthogonal projection, where normal equals direction vector - return point, direction, None, None, False - else: - # perspective projection - i = numpy.where(abs(numpy.real(l)) > 1e-8)[0] - if not len(i): - raise ValueError( - "no eigenvector not corresponding to eigenvalue 0") - point = numpy.real(V[:, i[-1]]).squeeze() - point /= point[3] - normal = - M[3, :3] - perspective = M[:3, 3] / numpy.dot(point[:3], normal) - if pseudo: - perspective -= normal - return point, normal, None, perspective, pseudo - - -def clip_matrix(left, right, bottom, top, near, far, perspective=False): - """Return matrix to obtain normalized device coordinates from frustrum. - - The frustrum bounds are axis-aligned along x (left, right), - y (bottom, top) and z (near, far). - - Normalized device coordinates are in range [-1, 1] if coordinates are - inside the frustrum. - - If perspective is True the frustrum is a truncated pyramid with the - perspective point at origin and direction along z axis, otherwise an - orthographic canonical view volume (a box). - - Homogeneous coordinates transformed by the perspective clip matrix - need to be dehomogenized (divided by w coordinate). - - >>> frustrum = numpy.random.rand(6) - >>> frustrum[1] += frustrum[0] - >>> frustrum[3] += frustrum[2] - >>> frustrum[5] += frustrum[4] - >>> M = clip_matrix(*frustrum, perspective=False) - >>> numpy.dot(M, [frustrum[0], frustrum[2], frustrum[4], 1.0]) - array([-1., -1., -1., 1.]) - >>> numpy.dot(M, [frustrum[1], frustrum[3], frustrum[5], 1.0]) - array([ 1., 1., 1., 1.]) - >>> M = clip_matrix(*frustrum, perspective=True) - >>> v = numpy.dot(M, [frustrum[0], frustrum[2], frustrum[4], 1.0]) - >>> v / v[3] - array([-1., -1., -1., 1.]) - >>> v = numpy.dot(M, [frustrum[1], frustrum[3], frustrum[4], 1.0]) - >>> v / v[3] - array([ 1., 1., -1., 1.]) - - """ - if left >= right or bottom >= top or near >= far: - raise ValueError("invalid frustrum") - if perspective: - if near <= _EPS: - raise ValueError("invalid frustrum: near <= 0") - t = 2.0 * near - M = ((-t/(right-left), 0.0, (right+left)/(right-left), 0.0), - (0.0, -t/(top-bottom), (top+bottom)/(top-bottom), 0.0), - (0.0, 0.0, -(far+near)/(far-near), t*far/(far-near)), - (0.0, 0.0, -1.0, 0.0)) - else: - M = ((2.0/(right-left), 0.0, 0.0, (right+left)/(left-right)), - (0.0, 2.0/(top-bottom), 0.0, (top+bottom)/(bottom-top)), - (0.0, 0.0, 2.0/(far-near), (far+near)/(near-far)), - (0.0, 0.0, 0.0, 1.0)) - return numpy.array(M, dtype=numpy.float64) - - -def shear_matrix(angle, direction, point, normal): - """Return matrix to shear by angle along direction vector on shear plane. - - The shear plane is defined by a point and normal vector. The direction - vector must be orthogonal to the plane's normal vector. - - A point P is transformed by the shear matrix into P" such that - the vector P-P" is parallel to the direction vector and its extent is - given by the angle of P-P'-P", where P' is the orthogonal projection - of P onto the shear plane. - - >>> angle = (random.random() - 0.5) * 4*math.pi - >>> direct = numpy.random.random(3) - 0.5 - >>> point = numpy.random.random(3) - 0.5 - >>> normal = numpy.cross(direct, numpy.random.random(3)) - >>> S = shear_matrix(angle, direct, point, normal) - >>> numpy.allclose(1.0, numpy.linalg.det(S)) - True - - """ - normal = unit_vector(normal[:3]) - direction = unit_vector(direction[:3]) - if abs(numpy.dot(normal, direction)) > 1e-6: - raise ValueError("direction and normal vectors are not orthogonal") - angle = math.tan(angle) - M = numpy.identity(4) - M[:3, :3] += angle * numpy.outer(direction, normal) - M[:3, 3] = -angle * numpy.dot(point[:3], normal) * direction - return M - - -def shear_from_matrix(matrix): - """Return shear angle, direction and plane from shear matrix. - - >>> angle = (random.random() - 0.5) * 4*math.pi - >>> direct = numpy.random.random(3) - 0.5 - >>> point = numpy.random.random(3) - 0.5 - >>> normal = numpy.cross(direct, numpy.random.random(3)) - >>> S0 = shear_matrix(angle, direct, point, normal) - >>> angle, direct, point, normal = shear_from_matrix(S0) - >>> S1 = shear_matrix(angle, direct, point, normal) - >>> is_same_transform(S0, S1) - True - - """ - M = numpy.array(matrix, dtype=numpy.float64, copy=False) - M33 = M[:3, :3] - # normal: cross independent eigenvectors corresponding to the eigenvalue 1 - l, V = numpy.linalg.eig(M33) - i = numpy.where(abs(numpy.real(l) - 1.0) < 1e-4)[0] - if len(i) < 2: - raise ValueError("No two linear independent eigenvectors found %s" % l) - V = numpy.real(V[:, i]).squeeze().T - lenorm = -1.0 - for i0, i1 in ((0, 1), (0, 2), (1, 2)): - n = numpy.cross(V[i0], V[i1]) - l = vector_norm(n) - if l > lenorm: - lenorm = l - normal = n - normal /= lenorm - # direction and angle - direction = numpy.dot(M33 - numpy.identity(3), normal) - angle = vector_norm(direction) - direction /= angle - angle = math.atan(angle) - # point: eigenvector corresponding to eigenvalue 1 - l, V = numpy.linalg.eig(M) - i = numpy.where(abs(numpy.real(l) - 1.0) < 1e-8)[0] - if not len(i): - raise ValueError("no eigenvector corresponding to eigenvalue 1") - point = numpy.real(V[:, i[-1]]).squeeze() - point /= point[3] - return angle, direction, point, normal - - -def decompose_matrix(matrix): - """Return sequence of transformations from transformation matrix. - - matrix : array_like - Non-degenerative homogeneous transformation matrix - - Return tuple of: - scale : vector of 3 scaling factors - shear : list of shear factors for x-y, x-z, y-z axes - angles : list of Euler angles about static x, y, z axes - translate : translation vector along x, y, z axes - perspective : perspective partition of matrix - - Raise ValueError if matrix is of wrong type or degenerative. - - >>> T0 = translation_matrix((1, 2, 3)) - >>> scale, shear, angles, trans, persp = decompose_matrix(T0) - >>> T1 = translation_matrix(trans) - >>> numpy.allclose(T0, T1) - True - >>> S = scale_matrix(0.123) - >>> scale, shear, angles, trans, persp = decompose_matrix(S) - >>> scale[0] - 0.123 - >>> R0 = euler_matrix(1, 2, 3) - >>> scale, shear, angles, trans, persp = decompose_matrix(R0) - >>> R1 = euler_matrix(*angles) - >>> numpy.allclose(R0, R1) - True - - """ - M = numpy.array(matrix, dtype=numpy.float64, copy=True).T - if abs(M[3, 3]) < _EPS: - raise ValueError("M[3, 3] is zero") - M /= M[3, 3] - P = M.copy() - P[:, 3] = 0, 0, 0, 1 - if not numpy.linalg.det(P): - raise ValueError("Matrix is singular") - - scale = numpy.zeros((3, ), dtype=numpy.float64) - shear = [0, 0, 0] - angles = [0, 0, 0] - - if any(abs(M[:3, 3]) > _EPS): - perspective = numpy.dot(M[:, 3], numpy.linalg.inv(P.T)) - M[:, 3] = 0, 0, 0, 1 - else: - perspective = numpy.array((0, 0, 0, 1), dtype=numpy.float64) - - translate = M[3, :3].copy() - M[3, :3] = 0 - - row = M[:3, :3].copy() - scale[0] = vector_norm(row[0]) - row[0] /= scale[0] - shear[0] = numpy.dot(row[0], row[1]) - row[1] -= row[0] * shear[0] - scale[1] = vector_norm(row[1]) - row[1] /= scale[1] - shear[0] /= scale[1] - shear[1] = numpy.dot(row[0], row[2]) - row[2] -= row[0] * shear[1] - shear[2] = numpy.dot(row[1], row[2]) - row[2] -= row[1] * shear[2] - scale[2] = vector_norm(row[2]) - row[2] /= scale[2] - shear[1:] /= scale[2] - - if numpy.dot(row[0], numpy.cross(row[1], row[2])) < 0: - scale *= -1 - row *= -1 - - angles[1] = math.asin(-row[0, 2]) - if math.cos(angles[1]): - angles[0] = math.atan2(row[1, 2], row[2, 2]) - angles[2] = math.atan2(row[0, 1], row[0, 0]) - else: - #angles[0] = math.atan2(row[1, 0], row[1, 1]) - angles[0] = math.atan2(-row[2, 1], row[1, 1]) - angles[2] = 0.0 - - return scale, shear, angles, translate, perspective - - -def compose_matrix(scale=None, shear=None, angles=None, translate=None, - perspective=None): - """Return transformation matrix from sequence of transformations. - - This is the inverse of the decompose_matrix function. - - Sequence of transformations: - scale : vector of 3 scaling factors - shear : list of shear factors for x-y, x-z, y-z axes - angles : list of Euler angles about static x, y, z axes - translate : translation vector along x, y, z axes - perspective : perspective partition of matrix - - >>> scale = numpy.random.random(3) - 0.5 - >>> shear = numpy.random.random(3) - 0.5 - >>> angles = (numpy.random.random(3) - 0.5) * (2*math.pi) - >>> trans = numpy.random.random(3) - 0.5 - >>> persp = numpy.random.random(4) - 0.5 - >>> M0 = compose_matrix(scale, shear, angles, trans, persp) - >>> result = decompose_matrix(M0) - >>> M1 = compose_matrix(*result) - >>> is_same_transform(M0, M1) - True - - """ - M = numpy.identity(4) - if perspective is not None: - P = numpy.identity(4) - P[3, :] = perspective[:4] - M = numpy.dot(M, P) - if translate is not None: - T = numpy.identity(4) - T[:3, 3] = translate[:3] - M = numpy.dot(M, T) - if angles is not None: - R = euler_matrix(angles[0], angles[1], angles[2], 'sxyz') - M = numpy.dot(M, R) - if shear is not None: - Z = numpy.identity(4) - Z[1, 2] = shear[2] - Z[0, 2] = shear[1] - Z[0, 1] = shear[0] - M = numpy.dot(M, Z) - if scale is not None: - S = numpy.identity(4) - S[0, 0] = scale[0] - S[1, 1] = scale[1] - S[2, 2] = scale[2] - M = numpy.dot(M, S) - M /= M[3, 3] - return M - - -def orthogonalization_matrix(lengths, angles): - """Return orthogonalization matrix for crystallographic cell coordinates. - - Angles are expected in degrees. - - The de-orthogonalization matrix is the inverse. - - >>> O = orthogonalization_matrix((10., 10., 10.), (90., 90., 90.)) - >>> numpy.allclose(O[:3, :3], numpy.identity(3, float) * 10) - True - >>> O = orthogonalization_matrix([9.8, 12.0, 15.5], [87.2, 80.7, 69.7]) - >>> numpy.allclose(numpy.sum(O), 43.063229) - True - - """ - a, b, c = lengths - angles = numpy.radians(angles) - sina, sinb, _ = numpy.sin(angles) - cosa, cosb, cosg = numpy.cos(angles) - co = (cosa * cosb - cosg) / (sina * sinb) - return numpy.array(( - ( a*sinb*math.sqrt(1.0-co*co), 0.0, 0.0, 0.0), - (-a*sinb*co, b*sina, 0.0, 0.0), - ( a*cosb, b*cosa, c, 0.0), - ( 0.0, 0.0, 0.0, 1.0)), - dtype=numpy.float64) - - -def superimposition_matrix(v0, v1, scaling=False, usesvd=True): - """Return matrix to transform given vector set into second vector set. - - v0 and v1 are shape (3, \*) or (4, \*) arrays of at least 3 vectors. - - If usesvd is True, the weighted sum of squared deviations (RMSD) is - minimized according to the algorithm by W. Kabsch [8]. Otherwise the - quaternion based algorithm by B. Horn [9] is used (slower when using - this Python implementation). - - The returned matrix performs rotation, translation and uniform scaling - (if specified). - - >>> v0 = numpy.random.rand(3, 10) - >>> M = superimposition_matrix(v0, v0) - >>> numpy.allclose(M, numpy.identity(4)) - True - >>> R = random_rotation_matrix(numpy.random.random(3)) - >>> v0 = ((1,0,0), (0,1,0), (0,0,1), (1,1,1)) - >>> v1 = numpy.dot(R, v0) - >>> M = superimposition_matrix(v0, v1) - >>> numpy.allclose(v1, numpy.dot(M, v0)) - True - >>> v0 = (numpy.random.rand(4, 100) - 0.5) * 20.0 - >>> v0[3] = 1.0 - >>> v1 = numpy.dot(R, v0) - >>> M = superimposition_matrix(v0, v1) - >>> numpy.allclose(v1, numpy.dot(M, v0)) - True - >>> S = scale_matrix(random.random()) - >>> T = translation_matrix(numpy.random.random(3)-0.5) - >>> M = concatenate_matrices(T, R, S) - >>> v1 = numpy.dot(M, v0) - >>> v0[:3] += numpy.random.normal(0.0, 1e-9, 300).reshape(3, -1) - >>> M = superimposition_matrix(v0, v1, scaling=True) - >>> numpy.allclose(v1, numpy.dot(M, v0)) - True - >>> M = superimposition_matrix(v0, v1, scaling=True, usesvd=False) - >>> numpy.allclose(v1, numpy.dot(M, v0)) - True - >>> v = numpy.empty((4, 100, 3), dtype=numpy.float64) - >>> v[:, :, 0] = v0 - >>> M = superimposition_matrix(v0, v1, scaling=True, usesvd=False) - >>> numpy.allclose(v1, numpy.dot(M, v[:, :, 0])) - True - - """ - v0 = numpy.array(v0, dtype=numpy.float64, copy=False)[:3] - v1 = numpy.array(v1, dtype=numpy.float64, copy=False)[:3] - - if v0.shape != v1.shape or v0.shape[1] < 3: - raise ValueError("Vector sets are of wrong shape or type.") - - # move centroids to origin - t0 = numpy.mean(v0, axis=1) - t1 = numpy.mean(v1, axis=1) - v0 = v0 - t0.reshape(3, 1) - v1 = v1 - t1.reshape(3, 1) - - if usesvd: - # Singular Value Decomposition of covariance matrix - u, s, vh = numpy.linalg.svd(numpy.dot(v1, v0.T)) - # rotation matrix from SVD orthonormal bases - R = numpy.dot(u, vh) - if numpy.linalg.det(R) < 0.0: - # R does not constitute right handed system - R -= numpy.outer(u[:, 2], vh[2, :]*2.0) - s[-1] *= -1.0 - # homogeneous transformation matrix - M = numpy.identity(4) - M[:3, :3] = R - else: - # compute symmetric matrix N - xx, yy, zz = numpy.sum(v0 * v1, axis=1) - xy, yz, zx = numpy.sum(v0 * numpy.roll(v1, -1, axis=0), axis=1) - xz, yx, zy = numpy.sum(v0 * numpy.roll(v1, -2, axis=0), axis=1) - N = ((xx+yy+zz, yz-zy, zx-xz, xy-yx), - (yz-zy, xx-yy-zz, xy+yx, zx+xz), - (zx-xz, xy+yx, -xx+yy-zz, yz+zy), - (xy-yx, zx+xz, yz+zy, -xx-yy+zz)) - # quaternion: eigenvector corresponding to most positive eigenvalue - l, V = numpy.linalg.eig(N) - q = V[:, numpy.argmax(l)] - q /= vector_norm(q) # unit quaternion - q = numpy.roll(q, -1) # move w component to end - # homogeneous transformation matrix - M = quaternion_matrix(q) - - # scale: ratio of rms deviations from centroid - if scaling: - v0 *= v0 - v1 *= v1 - M[:3, :3] *= math.sqrt(numpy.sum(v1) / numpy.sum(v0)) - - # translation - M[:3, 3] = t1 - T = numpy.identity(4) - T[:3, 3] = -t0 - M = numpy.dot(M, T) - return M - - -def euler_matrix(ai, aj, ak, axes='sxyz'): - """Return homogeneous rotation matrix from Euler angles and axis sequence. - - ai, aj, ak : Euler's roll, pitch and yaw angles - axes : One of 24 axis sequences as string or encoded tuple - - >>> R = euler_matrix(1, 2, 3, 'syxz') - >>> numpy.allclose(numpy.sum(R[0]), -1.34786452) - True - >>> R = euler_matrix(1, 2, 3, (0, 1, 0, 1)) - >>> numpy.allclose(numpy.sum(R[0]), -0.383436184) - True - >>> ai, aj, ak = (4.0*math.pi) * (numpy.random.random(3) - 0.5) - >>> for axes in _AXES2TUPLE.keys(): - ... R = euler_matrix(ai, aj, ak, axes) - >>> for axes in _TUPLE2AXES.keys(): - ... R = euler_matrix(ai, aj, ak, axes) - - """ - try: - firstaxis, parity, repetition, frame = _AXES2TUPLE[axes] - except (AttributeError, KeyError): - _ = _TUPLE2AXES[axes] - firstaxis, parity, repetition, frame = axes - - i = firstaxis - j = _NEXT_AXIS[i+parity] - k = _NEXT_AXIS[i-parity+1] - - if frame: - ai, ak = ak, ai - if parity: - ai, aj, ak = -ai, -aj, -ak - - si, sj, sk = math.sin(ai), math.sin(aj), math.sin(ak) - ci, cj, ck = math.cos(ai), math.cos(aj), math.cos(ak) - cc, cs = ci*ck, ci*sk - sc, ss = si*ck, si*sk - - M = numpy.identity(4) - if repetition: - M[i, i] = cj - M[i, j] = sj*si - M[i, k] = sj*ci - M[j, i] = sj*sk - M[j, j] = -cj*ss+cc - M[j, k] = -cj*cs-sc - M[k, i] = -sj*ck - M[k, j] = cj*sc+cs - M[k, k] = cj*cc-ss - else: - M[i, i] = cj*ck - M[i, j] = sj*sc-cs - M[i, k] = sj*cc+ss - M[j, i] = cj*sk - M[j, j] = sj*ss+cc - M[j, k] = sj*cs-sc - M[k, i] = -sj - M[k, j] = cj*si - M[k, k] = cj*ci - return M - - -def euler_from_matrix(matrix, axes='sxyz'): - """Return Euler angles from rotation matrix for specified axis sequence. - - axes : One of 24 axis sequences as string or encoded tuple - - Note that many Euler angle triplets can describe one matrix. - - >>> R0 = euler_matrix(1, 2, 3, 'syxz') - >>> al, be, ga = euler_from_matrix(R0, 'syxz') - >>> R1 = euler_matrix(al, be, ga, 'syxz') - >>> numpy.allclose(R0, R1) - True - >>> angles = (4.0*math.pi) * (numpy.random.random(3) - 0.5) - >>> for axes in _AXES2TUPLE.keys(): - ... R0 = euler_matrix(axes=axes, *angles) - ... R1 = euler_matrix(axes=axes, *euler_from_matrix(R0, axes)) - ... if not numpy.allclose(R0, R1): print axes, "failed" - - """ - try: - firstaxis, parity, repetition, frame = _AXES2TUPLE[axes.lower()] - except (AttributeError, KeyError): - _ = _TUPLE2AXES[axes] - firstaxis, parity, repetition, frame = axes - - i = firstaxis - j = _NEXT_AXIS[i+parity] - k = _NEXT_AXIS[i-parity+1] - - M = numpy.array(matrix, dtype=numpy.float64, copy=False)[:3, :3] - if repetition: - sy = math.sqrt(M[i, j]*M[i, j] + M[i, k]*M[i, k]) - if sy > _EPS: - ax = math.atan2( M[i, j], M[i, k]) - ay = math.atan2( sy, M[i, i]) - az = math.atan2( M[j, i], -M[k, i]) - else: - ax = math.atan2(-M[j, k], M[j, j]) - ay = math.atan2( sy, M[i, i]) - az = 0.0 - else: - cy = math.sqrt(M[i, i]*M[i, i] + M[j, i]*M[j, i]) - if cy > _EPS: - ax = math.atan2( M[k, j], M[k, k]) - ay = math.atan2(-M[k, i], cy) - az = math.atan2( M[j, i], M[i, i]) - else: - ax = math.atan2(-M[j, k], M[j, j]) - ay = math.atan2(-M[k, i], cy) - az = 0.0 - - if parity: - ax, ay, az = -ax, -ay, -az - if frame: - ax, az = az, ax - return ax, ay, az - - -def euler_from_quaternion(quaternion, axes='sxyz'): - """Return Euler angles from quaternion for specified axis sequence. - - >>> angles = euler_from_quaternion([0.06146124, 0, 0, 0.99810947]) - >>> numpy.allclose(angles, [0.123, 0, 0]) - True - - """ - return euler_from_matrix(quaternion_matrix(quaternion), axes) - - -def quaternion_from_euler(ai, aj, ak, axes='sxyz'): - """Return quaternion from Euler angles and axis sequence. - - ai, aj, ak : Euler's roll, pitch and yaw angles - axes : One of 24 axis sequences as string or encoded tuple - - >>> q = quaternion_from_euler(1, 2, 3, 'ryxz') - >>> numpy.allclose(q, [0.310622, -0.718287, 0.444435, 0.435953]) - True - - """ - try: - firstaxis, parity, repetition, frame = _AXES2TUPLE[axes.lower()] - except (AttributeError, KeyError): - _ = _TUPLE2AXES[axes] - firstaxis, parity, repetition, frame = axes - - i = firstaxis - j = _NEXT_AXIS[i+parity] - k = _NEXT_AXIS[i-parity+1] - - if frame: - ai, ak = ak, ai - if parity: - aj = -aj - - ai /= 2.0 - aj /= 2.0 - ak /= 2.0 - ci = math.cos(ai) - si = math.sin(ai) - cj = math.cos(aj) - sj = math.sin(aj) - ck = math.cos(ak) - sk = math.sin(ak) - cc = ci*ck - cs = ci*sk - sc = si*ck - ss = si*sk - - quaternion = numpy.empty((4, ), dtype=numpy.float64) - if repetition: - quaternion[i] = cj*(cs + sc) - quaternion[j] = sj*(cc + ss) - quaternion[k] = sj*(cs - sc) - quaternion[3] = cj*(cc - ss) - else: - quaternion[i] = cj*sc - sj*cs - quaternion[j] = cj*ss + sj*cc - quaternion[k] = cj*cs - sj*sc - quaternion[3] = cj*cc + sj*ss - if parity: - quaternion[j] *= -1 - - return quaternion - - -def quaternion_about_axis(angle, axis): - """Return quaternion for rotation about axis. - - >>> q = quaternion_about_axis(0.123, (1, 0, 0)) - >>> numpy.allclose(q, [0.06146124, 0, 0, 0.99810947]) - True - - """ - quaternion = numpy.zeros((4, ), dtype=numpy.float64) - quaternion[:3] = axis[:3] - qlen = vector_norm(quaternion) - if qlen > _EPS: - quaternion *= math.sin(angle/2.0) / qlen - quaternion[3] = math.cos(angle/2.0) - return quaternion - - -def quaternion_matrix(quaternion): - """Return homogeneous rotation matrix from quaternion. - - >>> R = quaternion_matrix([0.06146124, 0, 0, 0.99810947]) - >>> numpy.allclose(R, rotation_matrix(0.123, (1, 0, 0))) - True - - """ - q = numpy.array(quaternion[:4], dtype=numpy.float64, copy=True) - nq = numpy.dot(q, q) - if nq < _EPS: - return numpy.identity(4) - q *= math.sqrt(2.0 / nq) - q = numpy.outer(q, q) - return numpy.array(( - (1.0-q[1, 1]-q[2, 2], q[0, 1]-q[2, 3], q[0, 2]+q[1, 3], 0.0), - ( q[0, 1]+q[2, 3], 1.0-q[0, 0]-q[2, 2], q[1, 2]-q[0, 3], 0.0), - ( q[0, 2]-q[1, 3], q[1, 2]+q[0, 3], 1.0-q[0, 0]-q[1, 1], 0.0), - ( 0.0, 0.0, 0.0, 1.0) - ), dtype=numpy.float64) - - -def quaternion_from_matrix(matrix): - """Return quaternion from rotation matrix. - - >>> R = rotation_matrix(0.123, (1, 2, 3)) - >>> q = quaternion_from_matrix(R) - >>> numpy.allclose(q, [0.0164262, 0.0328524, 0.0492786, 0.9981095]) - True - - """ - q = numpy.empty((4, ), dtype=numpy.float64) - M = numpy.array(matrix, dtype=numpy.float64, copy=False)[:4, :4] - t = numpy.trace(M) - if t > M[3, 3]: - q[3] = t - q[2] = M[1, 0] - M[0, 1] - q[1] = M[0, 2] - M[2, 0] - q[0] = M[2, 1] - M[1, 2] - else: - i, j, k = 0, 1, 2 - if M[1, 1] > M[0, 0]: - i, j, k = 1, 2, 0 - if M[2, 2] > M[i, i]: - i, j, k = 2, 0, 1 - t = M[i, i] - (M[j, j] + M[k, k]) + M[3, 3] - q[i] = t - q[j] = M[i, j] + M[j, i] - q[k] = M[k, i] + M[i, k] - q[3] = M[k, j] - M[j, k] - q *= 0.5 / math.sqrt(t * M[3, 3]) - return q - - -def quaternion_multiply(quaternion1, quaternion0): - """Return multiplication of two quaternions. - - >>> q = quaternion_multiply([1, -2, 3, 4], [-5, 6, 7, 8]) - >>> numpy.allclose(q, [-44, -14, 48, 28]) - True - - """ - x0, y0, z0, w0 = quaternion0 - x1, y1, z1, w1 = quaternion1 - return numpy.array(( - x1*w0 + y1*z0 - z1*y0 + w1*x0, - -x1*z0 + y1*w0 + z1*x0 + w1*y0, - x1*y0 - y1*x0 + z1*w0 + w1*z0, - -x1*x0 - y1*y0 - z1*z0 + w1*w0), dtype=numpy.float64) - - -def quaternion_conjugate(quaternion): - """Return conjugate of quaternion. - - >>> q0 = random_quaternion() - >>> q1 = quaternion_conjugate(q0) - >>> q1[3] == q0[3] and all(q1[:3] == -q0[:3]) - True - - """ - return numpy.array((-quaternion[0], -quaternion[1], - -quaternion[2], quaternion[3]), dtype=numpy.float64) - - -def quaternion_inverse(quaternion): - """Return inverse of quaternion. - - >>> q0 = random_quaternion() - >>> q1 = quaternion_inverse(q0) - >>> numpy.allclose(quaternion_multiply(q0, q1), [0, 0, 0, 1]) - True - - """ - return quaternion_conjugate(quaternion) / numpy.dot(quaternion, quaternion) - - -def quaternion_slerp(quat0, quat1, fraction, spin=0, shortestpath=True): - """Return spherical linear interpolation between two quaternions. - - >>> q0 = random_quaternion() - >>> q1 = random_quaternion() - >>> q = quaternion_slerp(q0, q1, 0.0) - >>> numpy.allclose(q, q0) - True - >>> q = quaternion_slerp(q0, q1, 1.0, 1) - >>> numpy.allclose(q, q1) - True - >>> q = quaternion_slerp(q0, q1, 0.5) - >>> angle = math.acos(numpy.dot(q0, q)) - >>> numpy.allclose(2.0, math.acos(numpy.dot(q0, q1)) / angle) or \ - numpy.allclose(2.0, math.acos(-numpy.dot(q0, q1)) / angle) - True - - """ - q0 = unit_vector(quat0[:4]) - q1 = unit_vector(quat1[:4]) - if fraction == 0.0: - return q0 - elif fraction == 1.0: - return q1 - d = numpy.dot(q0, q1) - if abs(abs(d) - 1.0) < _EPS: - return q0 - if shortestpath and d < 0.0: - # invert rotation - d = -d - q1 *= -1.0 - angle = math.acos(d) + spin * math.pi - if abs(angle) < _EPS: - return q0 - isin = 1.0 / math.sin(angle) - q0 *= math.sin((1.0 - fraction) * angle) * isin - q1 *= math.sin(fraction * angle) * isin - q0 += q1 - return q0 - - -def random_quaternion(rand=None): - """Return uniform random unit quaternion. - - rand: array like or None - Three independent random variables that are uniformly distributed - between 0 and 1. - - >>> q = random_quaternion() - >>> numpy.allclose(1.0, vector_norm(q)) - True - >>> q = random_quaternion(numpy.random.random(3)) - >>> q.shape - (4,) - - """ - if rand is None: - rand = numpy.random.rand(3) - else: - assert len(rand) == 3 - r1 = numpy.sqrt(1.0 - rand[0]) - r2 = numpy.sqrt(rand[0]) - pi2 = math.pi * 2.0 - t1 = pi2 * rand[1] - t2 = pi2 * rand[2] - return numpy.array((numpy.sin(t1)*r1, - numpy.cos(t1)*r1, - numpy.sin(t2)*r2, - numpy.cos(t2)*r2), dtype=numpy.float64) - - -def random_rotation_matrix(rand=None): - """Return uniform random rotation matrix. - - rnd: array like - Three independent random variables that are uniformly distributed - between 0 and 1 for each returned quaternion. - - >>> R = random_rotation_matrix() - >>> numpy.allclose(numpy.dot(R.T, R), numpy.identity(4)) - True - - """ - return quaternion_matrix(random_quaternion(rand)) - - -class Arcball(object): - """Virtual Trackball Control. - - >>> ball = Arcball() - >>> ball = Arcball(initial=numpy.identity(4)) - >>> ball.place([320, 320], 320) - >>> ball.down([500, 250]) - >>> ball.drag([475, 275]) - >>> R = ball.matrix() - >>> numpy.allclose(numpy.sum(R), 3.90583455) - True - >>> ball = Arcball(initial=[0, 0, 0, 1]) - >>> ball.place([320, 320], 320) - >>> ball.setaxes([1,1,0], [-1, 1, 0]) - >>> ball.setconstrain(True) - >>> ball.down([400, 200]) - >>> ball.drag([200, 400]) - >>> R = ball.matrix() - >>> numpy.allclose(numpy.sum(R), 0.2055924) - True - >>> ball.next() - - """ - - def __init__(self, initial=None): - """Initialize virtual trackball control. - - initial : quaternion or rotation matrix - - """ - self._axis = None - self._axes = None - self._radius = 1.0 - self._center = [0.0, 0.0] - self._vdown = numpy.array([0, 0, 1], dtype=numpy.float64) - self._constrain = False - - if initial is None: - self._qdown = numpy.array([0, 0, 0, 1], dtype=numpy.float64) - else: - initial = numpy.array(initial, dtype=numpy.float64) - if initial.shape == (4, 4): - self._qdown = quaternion_from_matrix(initial) - elif initial.shape == (4, ): - initial /= vector_norm(initial) - self._qdown = initial - else: - raise ValueError("initial not a quaternion or matrix.") - - self._qnow = self._qpre = self._qdown - - def place(self, center, radius): - """Place Arcball, e.g. when window size changes. - - center : sequence[2] - Window coordinates of trackball center. - radius : float - Radius of trackball in window coordinates. - - """ - self._radius = float(radius) - self._center[0] = center[0] - self._center[1] = center[1] - - def setaxes(self, *axes): - """Set axes to constrain rotations.""" - if axes is None: - self._axes = None - else: - self._axes = [unit_vector(axis) for axis in axes] - - def setconstrain(self, constrain): - """Set state of constrain to axis mode.""" - self._constrain = constrain == True - - def getconstrain(self): - """Return state of constrain to axis mode.""" - return self._constrain - - def down(self, point): - """Set initial cursor window coordinates and pick constrain-axis.""" - self._vdown = arcball_map_to_sphere(point, self._center, self._radius) - self._qdown = self._qpre = self._qnow - - if self._constrain and self._axes is not None: - self._axis = arcball_nearest_axis(self._vdown, self._axes) - self._vdown = arcball_constrain_to_axis(self._vdown, self._axis) - else: - self._axis = None - - def drag(self, point): - """Update current cursor window coordinates.""" - vnow = arcball_map_to_sphere(point, self._center, self._radius) - - if self._axis is not None: - vnow = arcball_constrain_to_axis(vnow, self._axis) - - self._qpre = self._qnow - - t = numpy.cross(self._vdown, vnow) - if numpy.dot(t, t) < _EPS: - self._qnow = self._qdown - else: - q = [t[0], t[1], t[2], numpy.dot(self._vdown, vnow)] - self._qnow = quaternion_multiply(q, self._qdown) - - def next(self, acceleration=0.0): - """Continue rotation in direction of last drag.""" - q = quaternion_slerp(self._qpre, self._qnow, 2.0+acceleration, False) - self._qpre, self._qnow = self._qnow, q - - def matrix(self): - """Return homogeneous rotation matrix.""" - return quaternion_matrix(self._qnow) - - -def arcball_map_to_sphere(point, center, radius): - """Return unit sphere coordinates from window coordinates.""" - v = numpy.array(((point[0] - center[0]) / radius, - (center[1] - point[1]) / radius, - 0.0), dtype=numpy.float64) - n = v[0]*v[0] + v[1]*v[1] - if n > 1.0: - v /= math.sqrt(n) # position outside of sphere - else: - v[2] = math.sqrt(1.0 - n) - return v - - -def arcball_constrain_to_axis(point, axis): - """Return sphere point perpendicular to axis.""" - v = numpy.array(point, dtype=numpy.float64, copy=True) - a = numpy.array(axis, dtype=numpy.float64, copy=True) - v -= a * numpy.dot(a, v) # on plane - n = vector_norm(v) - if n > _EPS: - if v[2] < 0.0: - v *= -1.0 - v /= n - return v - if a[2] == 1.0: - return numpy.array([1, 0, 0], dtype=numpy.float64) - return unit_vector([-a[1], a[0], 0]) - - -def arcball_nearest_axis(point, axes): - """Return axis, which arc is nearest to point.""" - point = numpy.array(point, dtype=numpy.float64, copy=False) - nearest = None - mx = -1.0 - for axis in axes: - t = numpy.dot(arcball_constrain_to_axis(point, axis), point) - if t > mx: - nearest = axis - mx = t - return nearest - - -# epsilon for testing whether a number is close to zero -_EPS = numpy.finfo(float).eps * 4.0 - -# axis sequences for Euler angles -_NEXT_AXIS = [1, 2, 0, 1] - -# map axes strings to/from tuples of inner axis, parity, repetition, frame -_AXES2TUPLE = { - 'sxyz': (0, 0, 0, 0), 'sxyx': (0, 0, 1, 0), 'sxzy': (0, 1, 0, 0), - 'sxzx': (0, 1, 1, 0), 'syzx': (1, 0, 0, 0), 'syzy': (1, 0, 1, 0), - 'syxz': (1, 1, 0, 0), 'syxy': (1, 1, 1, 0), 'szxy': (2, 0, 0, 0), - 'szxz': (2, 0, 1, 0), 'szyx': (2, 1, 0, 0), 'szyz': (2, 1, 1, 0), - 'rzyx': (0, 0, 0, 1), 'rxyx': (0, 0, 1, 1), 'ryzx': (0, 1, 0, 1), - 'rxzx': (0, 1, 1, 1), 'rxzy': (1, 0, 0, 1), 'ryzy': (1, 0, 1, 1), - 'rzxy': (1, 1, 0, 1), 'ryxy': (1, 1, 1, 1), 'ryxz': (2, 0, 0, 1), - 'rzxz': (2, 0, 1, 1), 'rxyz': (2, 1, 0, 1), 'rzyz': (2, 1, 1, 1)} - -_TUPLE2AXES = dict((v, k) for k, v in _AXES2TUPLE.items()) - -# helper functions - -def vector_norm(data, axis=None, out=None): - """Return length, i.e. eucledian norm, of ndarray along axis. - - >>> v = numpy.random.random(3) - >>> n = vector_norm(v) - >>> numpy.allclose(n, numpy.linalg.norm(v)) - True - >>> v = numpy.random.rand(6, 5, 3) - >>> n = vector_norm(v, axis=-1) - >>> numpy.allclose(n, numpy.sqrt(numpy.sum(v*v, axis=2))) - True - >>> n = vector_norm(v, axis=1) - >>> numpy.allclose(n, numpy.sqrt(numpy.sum(v*v, axis=1))) - True - >>> v = numpy.random.rand(5, 4, 3) - >>> n = numpy.empty((5, 3), dtype=numpy.float64) - >>> vector_norm(v, axis=1, out=n) - >>> numpy.allclose(n, numpy.sqrt(numpy.sum(v*v, axis=1))) - True - >>> vector_norm([]) - 0.0 - >>> vector_norm([1.0]) - 1.0 - - """ - data = numpy.array(data, dtype=numpy.float64, copy=True) - if out is None: - if data.ndim == 1: - return math.sqrt(numpy.dot(data, data)) - data *= data - out = numpy.atleast_1d(numpy.sum(data, axis=axis)) - numpy.sqrt(out, out) - return out - else: - data *= data - numpy.sum(data, axis=axis, out=out) - numpy.sqrt(out, out) - - -def unit_vector(data, axis=None, out=None): - """Return ndarray normalized by length, i.e. eucledian norm, along axis. - - >>> v0 = numpy.random.random(3) - >>> v1 = unit_vector(v0) - >>> numpy.allclose(v1, v0 / numpy.linalg.norm(v0)) - True - >>> v0 = numpy.random.rand(5, 4, 3) - >>> v1 = unit_vector(v0, axis=-1) - >>> v2 = v0 / numpy.expand_dims(numpy.sqrt(numpy.sum(v0*v0, axis=2)), 2) - >>> numpy.allclose(v1, v2) - True - >>> v1 = unit_vector(v0, axis=1) - >>> v2 = v0 / numpy.expand_dims(numpy.sqrt(numpy.sum(v0*v0, axis=1)), 1) - >>> numpy.allclose(v1, v2) - True - >>> v1 = numpy.empty((5, 4, 3), dtype=numpy.float64) - >>> unit_vector(v0, axis=1, out=v1) - >>> numpy.allclose(v1, v2) - True - >>> list(unit_vector([])) - [] - >>> list(unit_vector([1.0])) - [1.0] - - """ - if out is None: - data = numpy.array(data, dtype=numpy.float64, copy=True) - if data.ndim == 1: - data /= math.sqrt(numpy.dot(data, data)) - return data - else: - if out is not data: - out[:] = numpy.array(data, copy=False) - data = out - length = numpy.atleast_1d(numpy.sum(data*data, axis)) - numpy.sqrt(length, length) - if axis is not None: - length = numpy.expand_dims(length, axis) - data /= length - if out is None: - return data - - -def random_vector(size): - """Return array of random doubles in the half-open interval [0.0, 1.0). - - >>> v = random_vector(10000) - >>> numpy.all(v >= 0.0) and numpy.all(v < 1.0) - True - >>> v0 = random_vector(10) - >>> v1 = random_vector(10) - >>> numpy.any(v0 == v1) - False - - """ - return numpy.random.random(size) - - -def inverse_matrix(matrix): - """Return inverse of square transformation matrix. - - >>> M0 = random_rotation_matrix() - >>> M1 = inverse_matrix(M0.T) - >>> numpy.allclose(M1, numpy.linalg.inv(M0.T)) - True - >>> for size in range(1, 7): - ... M0 = numpy.random.rand(size, size) - ... M1 = inverse_matrix(M0) - ... if not numpy.allclose(M1, numpy.linalg.inv(M0)): print size - - """ - return numpy.linalg.inv(matrix) - - -def concatenate_matrices(*matrices): - """Return concatenation of series of transformation matrices. - - >>> M = numpy.random.rand(16).reshape((4, 4)) - 0.5 - >>> numpy.allclose(M, concatenate_matrices(M)) - True - >>> numpy.allclose(numpy.dot(M, M.T), concatenate_matrices(M, M.T)) - True - - """ - M = numpy.identity(4) - for i in matrices: - M = numpy.dot(M, i) - return M - - -def is_same_transform(matrix0, matrix1): - """Return True if two matrices perform same transformation. - - >>> is_same_transform(numpy.identity(4), numpy.identity(4)) - True - >>> is_same_transform(numpy.identity(4), random_rotation_matrix()) - False - - """ - matrix0 = numpy.array(matrix0, dtype=numpy.float64, copy=True) - matrix0 /= matrix0[3, 3] - matrix1 = numpy.array(matrix1, dtype=numpy.float64, copy=True) - matrix1 /= matrix1[3, 3] - return numpy.allclose(matrix0, matrix1) - - -def _import_module(module_name, warn=True, prefix='_py_', ignore='_'): - """Try import all public attributes from module into global namespace. - - Existing attributes with name clashes are renamed with prefix. - Attributes starting with underscore are ignored by default. - - Return True on successful import. - - """ - try: - module = __import__(module_name) - except ImportError: - if warn: - warnings.warn("Failed to import module " + module_name) - else: - for attr in dir(module): - if ignore and attr.startswith(ignore): - continue - if prefix: - if attr in globals(): - globals()[prefix + attr] = globals()[attr] - elif warn: - warnings.warn("No Python implementation of " + attr) - globals()[attr] = getattr(module, attr) - return True diff --git a/libs/assimp/port/PyAssimp/setup.py b/libs/assimp/port/PyAssimp/setup.py deleted file mode 100644 index a3497d6..0000000 --- a/libs/assimp/port/PyAssimp/setup.py +++ /dev/null @@ -1,26 +0,0 @@ - #!/usr/bin/env python - # -*- coding: utf-8 -*- -import os -from distutils.core import setup - -def readme(): - with open('README.rst') as f: - return f.read() - -setup(name='pyassimp', - version='4.1.4', - license='ISC', - description='Python bindings for the Open Asset Import Library (ASSIMP)', - long_description=readme(), - url='https://github.com/assimp/assimp', - author='ASSIMP developers', - author_email='assimp-discussions@lists.sourceforge.net', - maintainer='Séverin Lemaignan', - maintainer_email='severin@guakamole.org', - packages=['pyassimp'], - data_files=[ - ('share/pyassimp', ['README.rst']), - ('share/examples/pyassimp', ['scripts/' + f for f in os.listdir('scripts/')]) - ], - requires=['numpy'] - ) |