162 lines
5.5 KiB
Python
162 lines
5.5 KiB
Python
|
# Modified from the original OpenCTM python binding
|
||
|
# for inclusion in the `trimesh` package:
|
||
|
# https://github.com/mikedh/trimesh
|
||
|
#
|
||
|
# To get shared library this binding imports, you can download
|
||
|
# and install it on Linux using this bash script:
|
||
|
# https://github.com/mikedh/trimesh/blob/master/docker/builds/openctm.bash
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Copyright (c) 2009-2010 Marcus Geelnard
|
||
|
#
|
||
|
# This software is provided 'as-is', without any express or implied
|
||
|
# warranty. In no event will the authors be held liable for any damages
|
||
|
# arising from the use of this software.
|
||
|
#
|
||
|
# Permission is granted to anyone to use this software for any purpose,
|
||
|
# including commercial applications, and to alter it and redistribute it
|
||
|
# freely, subject to the following restrictions:
|
||
|
#
|
||
|
# 1. The origin of this software must not be misrepresented; you must not
|
||
|
# claim that you wrote the original software. If you use this software
|
||
|
# in a product, an acknowledgment in the product documentation would be
|
||
|
# appreciated but is not required.
|
||
|
#
|
||
|
# 2. Altered source versions must be plainly marked as such, and must not
|
||
|
# be misrepresented as being the original software.
|
||
|
#
|
||
|
# 3. This notice may not be removed or altered from any source
|
||
|
# distribution.
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
import os
|
||
|
import ctypes
|
||
|
|
||
|
_ctm_loaders = {}
|
||
|
_ctm_lib = None
|
||
|
try:
|
||
|
if os.name == 'nt':
|
||
|
# try to import the shared library on windows
|
||
|
_ctm_lib = ctypes.WinDLL('openctm.dll')
|
||
|
else:
|
||
|
# try to import on other platforms
|
||
|
_ctm_lib_name = ctypes.util.find_library('openctm')
|
||
|
if _ctm_lib_name:
|
||
|
_ctm_lib = ctypes.CDLL(_ctm_lib_name)
|
||
|
except BaseException:
|
||
|
pass
|
||
|
|
||
|
if _ctm_lib:
|
||
|
import numpy as np
|
||
|
|
||
|
# Types
|
||
|
CTMfloat = ctypes.c_float
|
||
|
CTMint = ctypes.c_int32
|
||
|
CTMuint = ctypes.c_uint32
|
||
|
CTMcontext = ctypes.c_void_p
|
||
|
CTMenum = ctypes.c_uint32
|
||
|
|
||
|
# boolean
|
||
|
CTM_TRUE = 1
|
||
|
CTM_FALSE = 0
|
||
|
|
||
|
# CTMenum
|
||
|
CTM_NONE = 0x0000
|
||
|
CTM_IMPORT = 0x0101
|
||
|
CTM_EXPORT = 0x0102
|
||
|
CTM_VERTEX_COUNT = 0x0301
|
||
|
CTM_TRIANGLE_COUNT = 0x0302
|
||
|
CTM_HAS_NORMALS = 0x0303
|
||
|
CTM_INDICES = 0x0601
|
||
|
CTM_VERTICES = 0x0602
|
||
|
CTM_NORMALS = 0x0603
|
||
|
|
||
|
# Functions
|
||
|
ctmNewContext = _ctm_lib.ctmNewContext
|
||
|
ctmNewContext.argtypes = [CTMenum]
|
||
|
ctmNewContext.restype = CTMcontext
|
||
|
ctmFreeContext = _ctm_lib.ctmFreeContext
|
||
|
ctmFreeContext.argtypes = [CTMcontext]
|
||
|
ctmGetError = _ctm_lib.ctmGetError
|
||
|
ctmGetError.argtypes = [CTMcontext]
|
||
|
ctmGetError.restype = CTMenum
|
||
|
ctmErrorString = _ctm_lib.ctmErrorString
|
||
|
ctmErrorString.argtypes = [CTMenum]
|
||
|
ctmErrorString.restype = ctypes.c_char_p
|
||
|
ctmGetInteger = _ctm_lib.ctmGetInteger
|
||
|
ctmGetInteger.argtypes = [CTMcontext, CTMenum]
|
||
|
ctmGetInteger.restype = CTMint
|
||
|
ctmGetFloat = _ctm_lib.ctmGetFloat
|
||
|
ctmGetFloat.argtypes = [CTMcontext, CTMenum]
|
||
|
ctmGetFloat.restype = CTMfloat
|
||
|
ctmGetIntegerArray = _ctm_lib.ctmGetIntegerArray
|
||
|
ctmGetIntegerArray.argtypes = [CTMcontext, CTMenum]
|
||
|
ctmGetIntegerArray.restype = ctypes.POINTER(CTMuint)
|
||
|
ctmGetFloatArray = _ctm_lib.ctmGetFloatArray
|
||
|
ctmGetFloatArray.argtypes = [CTMcontext, CTMenum]
|
||
|
ctmGetFloatArray.restype = ctypes.POINTER(CTMfloat)
|
||
|
ctmLoad = _ctm_lib.ctmLoad
|
||
|
ctmLoad.argtypes = [CTMcontext, ctypes.c_char_p]
|
||
|
ctmSave = _ctm_lib.ctmSave
|
||
|
ctmSave.argtypes = [CTMcontext, ctypes.c_char_p]
|
||
|
|
||
|
def load_ctm(file_obj, file_type=None, **kwargs):
|
||
|
"""
|
||
|
Load OpenCTM files from a file object.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
file_obj : open file- like object
|
||
|
|
||
|
Returns
|
||
|
----------
|
||
|
loaded : dict
|
||
|
kwargs for a Trimesh constructor:
|
||
|
{vertices: (n,3) float, vertices
|
||
|
faces: (m,3) int, indexes of vertices}
|
||
|
"""
|
||
|
ctm = ctmNewContext(CTM_IMPORT)
|
||
|
|
||
|
# !!load file from name
|
||
|
# this should be replaced with something that
|
||
|
# actually uses the file object data to support streams
|
||
|
name = str(file_obj.name).encode('utf-8')
|
||
|
ctmLoad(ctm, name)
|
||
|
|
||
|
err = ctmGetError(ctm)
|
||
|
if err != CTM_NONE:
|
||
|
raise IOError("Error loading file: " + str(ctmErrorString(err)))
|
||
|
|
||
|
# get vertices
|
||
|
vertex_count = ctmGetInteger(ctm, CTM_VERTEX_COUNT)
|
||
|
vertex_ctm = ctmGetFloatArray(ctm, CTM_VERTICES)
|
||
|
# use fromiter to avoid loop
|
||
|
vertices = np.fromiter(vertex_ctm,
|
||
|
dtype=np.float,
|
||
|
count=vertex_count * 3).reshape((-1, 3))
|
||
|
# get faces
|
||
|
face_count = ctmGetInteger(ctm, CTM_TRIANGLE_COUNT)
|
||
|
face_ctm = ctmGetIntegerArray(ctm, CTM_INDICES)
|
||
|
faces = np.fromiter(face_ctm,
|
||
|
dtype=np.int,
|
||
|
count=face_count * 3).reshape((-1, 3))
|
||
|
|
||
|
# create kwargs for trimesh constructor
|
||
|
result = {'vertices': vertices,
|
||
|
'faces': faces}
|
||
|
|
||
|
# get face normals if available
|
||
|
if ctmGetInteger(ctm, CTM_HAS_NORMALS) == CTM_TRUE:
|
||
|
normals_ctm = ctmGetFloatArray(ctm, CTM_NORMALS)
|
||
|
normals = np.fromiter(normals_ctm,
|
||
|
dtype=np.float,
|
||
|
count=face_count * 3).reshape((-1, 3))
|
||
|
result['face_normals'] = normals
|
||
|
|
||
|
# free context
|
||
|
ctmFreeContext(ctm)
|
||
|
|
||
|
return result
|
||
|
|
||
|
# we have a library so add load_ctm
|
||
|
_ctm_loaders = {'ctm': load_ctm}
|