diff --git a/README.md b/README.md index 4100e78..14302a4 100644 --- a/README.md +++ b/README.md @@ -59,4 +59,4 @@ Advantages: Disadvantages: - code needs to be ported from cpp to java - code needs to be maintained -- a little slower compared to cpp when loading big meshes if not using assbin +- a little slower compared to cpp when loading big meshes if not using assbin \ No newline at end of file diff --git a/src/main/kotlin/assimp/BaseImporter.kt b/src/main/kotlin/assimp/BaseImporter.kt index 475ff2b..d765c19 100644 --- a/src/main/kotlin/assimp/BaseImporter.kt +++ b/src/main/kotlin/assimp/BaseImporter.kt @@ -19,7 +19,6 @@ abstract class BaseImporter { /** Currently set progress handler. */ var progress: ProgressHandler? = null - var ioSystem: IOSystem = ASSIMP.defaultIOSystem /** Returns whether the class can handle the format of the given file. *. * The implementation should be as quick as possible. A check for the file extension is enough. If no suitable @@ -47,7 +46,8 @@ abstract class BaseImporter { * exception is thrown somewhere in internReadFile(), this function will catch it and transform it into a suitable * response to the caller. */ - fun readFile(imp: Importer, pIOHandler: IOSystem = ioSystem, filePath: String): AiScene? { + fun readFile(imp: Importer, ioHandler: IOSystem = ASSIMP.defaultIOSystem, filePath: String): AiScene? { + progress = imp.progressHandler assert(progress != null) @@ -59,7 +59,7 @@ abstract class BaseImporter { // dispatch importing try { - internReadFile(filePath, pIOHandler, sc) + internReadFile(filePath, ioHandler, sc) } catch (err: Exception) { // extract error description err.printStackTrace() @@ -119,9 +119,7 @@ abstract class BaseImporter { * @param file Path of the file to be imported. * @param scene The scene object to hold the imported data. Null is not a valid parameter. * */ - open fun internReadFile(file: String, ioSystem: IOSystem = this.ioSystem, scene: AiScene) = Unit//internReadFile(file.uri, scene) - - //open fun internReadFile(file: URI, pIOHandler: IOSystem, scene: AiScene) = Unit + open fun internReadFile(file: String, ioSystem: IOSystem, scene: AiScene) = Unit companion object { /** Extract file extension from a string diff --git a/src/main/kotlin/assimp/DefaultIOSystem.kt b/src/main/kotlin/assimp/DefaultIOSystem.kt index db1ada9..f4cc654 100644 --- a/src/main/kotlin/assimp/DefaultIOSystem.kt +++ b/src/main/kotlin/assimp/DefaultIOSystem.kt @@ -1,33 +1,48 @@ package assimp import java.io.* -import java.nio.file.Files +import java.nio.* +import java.nio.channels.* import java.nio.file.Path import java.nio.file.Paths class DefaultIOSystem : IOSystem { - override fun exists(pFile: String) = File(pFile).exists() + override fun exists(file: String) = File(file).exists() - override fun open(pFile: String): IOStream { + override fun open(file: String): IOStream { - val path: Path = Paths.get(pFile) - if (!Files.exists(path)) - throw IOException("File doesn't exist: $pFile") + val path: Path = Paths.get(file) + if (!exists(file)) + throw IOException("File doesn't exist: $file") - - return FileIOStream(path) + return FileIOStream(path, this) } - class FileIOStream(override val path: Path) : IOStream { + class FileIOStream(private val pathObject: Path, override val osSystem: DefaultIOSystem) : IOStream { + + override val path: String + get() = pathObject.toString() - override fun read() = FileInputStream(path.toFile()) + override fun read() = FileInputStream(file) - override fun reader() = BufferedReader(FileReader(path.toFile())) + override fun reader() = BufferedReader(FileReader(file)) override val filename: String - get() = path.fileName.toString() + get() = pathObject.fileName.toString() + + override val parentPath = pathObject.parent.toAbsolutePath().toString() + + override val length: Long + get() = file.length() + + override fun readBytes(): ByteBuffer { + RandomAccessFile(file, "r").channel.use {fileChannel -> + return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).order(ByteOrder.nativeOrder()) + } + } - override fun parentPath() = path.parent.toAbsolutePath().toString() + val file: File + get() = pathObject.toFile() } } \ No newline at end of file diff --git a/src/main/kotlin/assimp/IOStream.kt b/src/main/kotlin/assimp/IOStream.kt index 4431001..392e30e 100644 --- a/src/main/kotlin/assimp/IOStream.kt +++ b/src/main/kotlin/assimp/IOStream.kt @@ -2,11 +2,11 @@ package assimp import java.io.BufferedReader import java.io.InputStream -import java.nio.file.Path +import java.nio.* interface IOStream { - val path : Path? + val path : String val filename: String @@ -14,5 +14,18 @@ interface IOStream { fun reader() : BufferedReader - fun parentPath() : String + val parentPath : String + + /** + * length of the IOStream in bytes + */ + val length: Long + + /** + * reads the ioStream into a byte buffer. + * The byte order of the buffer is be [ByteOrder.nativeOrder]. + */ + fun readBytes(): ByteBuffer + + val osSystem: IOSystem } \ No newline at end of file diff --git a/src/main/kotlin/assimp/IOSystem.kt b/src/main/kotlin/assimp/IOSystem.kt index 15d33a8..d3e64e2 100644 --- a/src/main/kotlin/assimp/IOSystem.kt +++ b/src/main/kotlin/assimp/IOSystem.kt @@ -5,11 +5,11 @@ import java.io.File /** Interface to the file system. */ interface IOSystem { - fun exists(pFile: String): Boolean + fun exists(file: String): Boolean - fun open(pFile : String): IOStream + fun open(file : String): IOStream fun close(ioStream: IOStream) = Unit // TODO unused ? - fun getOsSeperator(): String = File.separator + val osSeparator: String get() = File.separator } \ No newline at end of file diff --git a/src/main/kotlin/assimp/Importer.kt b/src/main/kotlin/assimp/Importer.kt index fd3b901..d83d908 100644 --- a/src/main/kotlin/assimp/Importer.kt +++ b/src/main/kotlin/assimp/Importer.kt @@ -48,7 +48,6 @@ import assimp.postProcess.ValidateDSProcess import glm_.BYTES import glm_.i import glm_.size -import java.io.File import java.net.URI import java.net.URL import java.nio.ByteBuffer @@ -303,7 +302,7 @@ constructor() { } // Get file size for progress handler - val fileSize = File(file).length().i + val fileSize = ioSystem.open(file).length.i // Dispatch the reading to the worker class for this format val desc = imp.info @@ -373,8 +372,7 @@ constructor() { * * @note This is a straightforward way to decode models from memory buffers, but it doesn't handle model formats * that spread their data across multiple files or even directories. Examples include OBJ or MD3, which outsource - * parts of their material info into external scripts. If you need full functionality, provide a custom IOSystem - * to make Assimp find these files and use the regular readFile() API. + * parts of their material info into external scripts. If you need full functionality you can use [readFilesFromMemory] */ fun readFileFromMemory(buffer: ByteBuffer, flags: Int, hint: String = ""): AiScene? { if (buffer.size == 0 || hint.length > MaxLenHint) { @@ -385,9 +383,52 @@ constructor() { // prevent deletion of previous IOSystem val io = impl.ioSystem - ioHandler = MemoryIOSystem(buffer) - val fileName = "$AI_MEMORYIO_MAGIC_FILENAME.$hint" + ioHandler = MemoryIOSystem(fileName to buffer) + + readFile(fileName, flags) + + impl.ioSystem = io + + return impl.scene + } + + /**Reads the given file from a memory buffer and returns its contents if successful. + * + * If the call succeeds, the contents of the file are returned as a pointer to an AiScene object. The returned data + * is intended to be read-only, the importer object keeps ownership of the data and will destroy it upon + * destruction. If the import fails, null is returned. + * A human-readable error description can be retrieved by accessing errorString. The previous scene will be deleted + * during this call. + * Calling this method doesn't affect the active IOSystem. + * + * @param fileName name of the base file + * @param files a map containing the names and all the files required to read the scene (base file, materials, + * textures, etc). + * @param flags Optional post processing steps to be executed after a successful import. Provide a bitwise + * combination of the AiPostProcessSteps flags. If you wish to inspect the imported scene first in order to + * fine-tune your post-processing setup, consider to use applyPostProcessing(). + * @return A pointer to the imported data, null if the import failed. + * The pointer to the scene remains in possession of the Importer instance. Use getOrphanedScene() to take + * ownership of it. + */ + fun readFilesFromMemory(fileName: String, files: Map, flags: Int): AiScene? { + + for((name, buffer) in files) { + if(buffer.size == 0){ + impl.errorString = "buffer $name is empty" + return null + } + } + if(!files.containsKey(fileName)){ + impl.errorString = "fileName ($fileName) not in files" + return null + } + + val io = impl.ioSystem + + ioHandler = MemoryIOSystem(files) + readFile(fileName, flags) impl.ioSystem = io @@ -395,6 +436,28 @@ constructor() { return impl.scene } + /**Reads the given file from a memory buffer and returns its contents if successful. + * + * If the call succeeds, the contents of the file are returned as a pointer to an AiScene object. The returned data + * is intended to be read-only, the importer object keeps ownership of the data and will destroy it upon + * destruction. If the import fails, null is returned. + * A human-readable error description can be retrieved by accessing errorString. The previous scene will be deleted + * during this call. + * Calling this method doesn't affect the active IOSystem. + * + * @param fileName name of the base file + * @param flags Optional post processing steps to be executed after a successful import. Provide a bitwise + * combination of the AiPostProcessSteps flags. If you wish to inspect the imported scene first in order to + * fine-tune your post-processing setup, consider to use applyPostProcessing(). + * @param files the files required to read the scene (base file, materials, textures, etc) as a pair with their name. + * @return A pointer to the imported data, null if the import failed. + * The pointer to the scene remains in possession of the Importer instance. Use getOrphanedScene() to take + * ownership of it. + */ + fun readFilesFromMemory(fileName: String, vararg files: Pair, flags: Int = 0): AiScene? { + return readFilesFromMemory(fileName, files.toMap(), flags) + } + /** Apply post-processing to an already-imported scene. * * This is strictly equivalent to calling readFile() with the same flags. However, you can use this separate diff --git a/src/main/kotlin/assimp/MemoryIOWrapper.kt b/src/main/kotlin/assimp/MemoryIOWrapper.kt index d5e8824..5573b70 100644 --- a/src/main/kotlin/assimp/MemoryIOWrapper.kt +++ b/src/main/kotlin/assimp/MemoryIOWrapper.kt @@ -3,7 +3,6 @@ package assimp import glm_.* import java.io.* import java.nio.* -import java.nio.file.* import java.io.IOException @@ -11,35 +10,59 @@ import java.io.IOException const val AI_MEMORYIO_MAGIC_FILENAME = "\$\$\$___magic___\$\$\$" const val AI_MEMORYIO_MAGIC_FILENAME_LENGTH = 17 -class MemoryIOSystem(val buffer: ByteBuffer) : IOSystem{ +class MemoryIOSystem : IOSystem{ - /** Tests for the existence of a file at the given path. */ - override fun exists(pFile: String): Boolean = pFile.startsWith(AI_MEMORYIO_MAGIC_FILENAME) + val memoryFiles: MutableMap = hashMapOf() + + constructor(buffer: ByteBuffer) { + memoryFiles[AI_MEMORYIO_MAGIC_FILENAME] = buffer + } + + constructor(vararg buffers: Pair): this(buffers.toMap()) + constructor(buffers: Map){ + memoryFiles.putAll(buffers) + } + + /** Tests for the existence of a file at the given path. */ + override fun exists(file: String): Boolean = memoryFiles.containsKey(file) - override fun open(pFile: String): IOStream { + override fun open(file: String): IOStream { - // TODO assimp originally returns null, but this would be against the current interface. - // I guess it should never happen anyways so an exception is fine - if(!pFile.startsWith(AI_MEMORYIO_MAGIC_FILENAME)) throw IOException("File does not exist! $pFile") + val buffer = memoryFiles[file] ?: throw IOException("File does not exist! $file") - return MemoryIOStream(buffer, pFile) + return MemoryIOStream(buffer, file, this) } - class MemoryIOStream(val buffer: ByteBuffer, override val filename: String = "") : IOStream { + class MemoryIOStream(val buffer: ByteBuffer, override val path: String, override val osSystem: MemoryIOSystem) : IOStream { - override val path: Path? - get() = null + override val filename: String = run { + val lastIndex = path.lastIndexOf(osSystem.osSeparator) + path.substring(lastIndex + 1) + } override fun read(): InputStream { - return ByteBufferBackedInputStream(buffer) + return ByteBufferBackedInputStream(readBytes()) } override fun reader(): BufferedReader { return BufferedReader(InputStreamReader(read())) } - override fun parentPath(): String = "" + override val parentPath: String = run { + var parent = path.removeSuffix(filename) + parent = parent.removeSuffix(osSystem.osSeparator) + + // ensures that if the path starts with "./" it will always be at least that + if(parent == ".") parent = ".${osSystem.osSeparator}" + + parent + } + + override val length: Long + get() = buffer.size.toLong() + + override fun readBytes(): ByteBuffer = buffer.duplicate().order(ByteOrder.nativeOrder()) } } @@ -63,7 +86,7 @@ private class ByteBufferBackedInputStream(val buf: ByteBuffer) : InputStream() { override fun read(): Int { return if (!buf.hasRemaining()) { -1 - } else (buf.get() and 0xFF).toInt() + } else buf.get().toInt() and 0xFF } /** diff --git a/src/main/kotlin/assimp/format/X/XFileImporter.kt b/src/main/kotlin/assimp/format/X/XFileImporter.kt index 4145a41..8f96148 100644 --- a/src/main/kotlin/assimp/format/X/XFileImporter.kt +++ b/src/main/kotlin/assimp/format/X/XFileImporter.kt @@ -2,7 +2,7 @@ package assimp.format.X import assimp.* import assimp.AiFace -import java.io.File +import java.io.* class XFileImporter : BaseImporter() { @@ -24,16 +24,15 @@ class XFileImporter : BaseImporter() { } override fun internReadFile(file: String, ioSystem: IOSystem, scene: AiScene) { - // Read file into memory - val file_ = File(file) - if (!file_.canRead()) throw FileSystemException(file_, null, "Failed to open file \$pFile.") + + val stream = ioSystem.open(file).read() + val bytes = stream.readBytes() // Get the file-size and validate it, throwing an exception when fails - val fileSize = file_.length() + val fileSize = bytes.size if (fileSize < 16) throw Error("XFile is too small.") - val bytes = file_.readBytes() mBuffer = Pointer(Array(bytes.size, { i -> bytes[i].toChar() })) //Assuming every byte is a char. // parse the file into a temporary representation val parser = XFileParser(mBuffer) diff --git a/src/main/kotlin/assimp/format/blender/BlenderLoader.kt b/src/main/kotlin/assimp/format/blender/BlenderLoader.kt index c58179e..2873f7e 100644 --- a/src/main/kotlin/assimp/format/blender/BlenderLoader.kt +++ b/src/main/kotlin/assimp/format/blender/BlenderLoader.kt @@ -7,7 +7,6 @@ import glm_.toUnsignedInt import uno.kotlin.parseInt import java.io.File import java.io.RandomAccessFile -import java.net.URI import java.nio.ByteBuffer import java.nio.ByteOrder import java.nio.channels.FileChannel @@ -47,8 +46,7 @@ class BlenderImporter : BaseImporter() { override fun internReadFile(file: String, ioSystem: IOSystem, scene: AiScene) { - val fileChannel = RandomAccessFile(File(file), "r").channel - buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).order(ByteOrder.nativeOrder()) + buffer = ioSystem.open(file).readBytes() var match = buffer.strncmp(tokens) if (!match) { diff --git a/src/main/kotlin/assimp/format/fbx/fbxImporter.kt b/src/main/kotlin/assimp/format/fbx/fbxImporter.kt index dc7f211..04738ce 100644 --- a/src/main/kotlin/assimp/format/fbx/fbxImporter.kt +++ b/src/main/kotlin/assimp/format/fbx/fbxImporter.kt @@ -44,12 +44,7 @@ package assimp.format.fbx import assimp.* import assimp.format.AiConfig import tokenizeBinary -import java.io.File -import java.io.RandomAccessFile -import java.net.URI -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.nio.channels.FileChannel +import java.nio.* /** @file FbxImporter.h * @brief Declaration of the FBX main importer class */ @@ -112,14 +107,10 @@ class FbxImporter : BaseImporter() { override fun internReadFile(file: String, ioSystem: IOSystem, scene: AiScene) { - val f = File(file) - if (!f.canRead()) throw Error("Could not open file for reading") - /* read entire file into memory - no streaming for this, fbx files can grow large, but the assimp output data structure then becomes very large, too. Assimp doesn't support streaming for its output data structures so the net win with streaming input data would be very low. */ - val fileChannel = RandomAccessFile(f, "r").channel - val input = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).order(ByteOrder.nativeOrder()) + val input = ioSystem.open(file).readBytes() /* broadphase tokenizing pass in which we identify the core syntax elements of FBX (brackets, commas, key:value mappings) */ diff --git a/src/main/kotlin/assimp/format/md2/Md2Loader.kt b/src/main/kotlin/assimp/format/md2/Md2Loader.kt index e5336b1..6ae271b 100644 --- a/src/main/kotlin/assimp/format/md2/Md2Loader.kt +++ b/src/main/kotlin/assimp/format/md2/Md2Loader.kt @@ -52,7 +52,6 @@ import glm_.f import glm_.i import java.io.File import java.io.RandomAccessFile -import java.net.URI import java.nio.ByteOrder import java.nio.channels.FileChannel @@ -109,18 +108,13 @@ class Md2Importer : BaseImporter() { * See BaseImporter.internReadFile() for details */ override fun internReadFile(file: String, ioSystem: IOSystem, scene: AiScene) { - val file = File(file) - - // Check whether we can read from the file - if (!file.canRead()) - throw Error("Failed to open MD2 file $file") + val stream = ioSystem.open(file) // check whether the md3 file is large enough to contain at least the file header - fileSize = file.length().i + fileSize = stream.length.i if (fileSize < MD2.Header.size) throw Error("MD2 File is too small") - val fileChannel = RandomAccessFile(file, "r").channel - val buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).order(ByteOrder.nativeOrder()) + val buffer = stream.readBytes() header = MD2.Header(buffer).apply { validate(fileSize, configFrameID) } @@ -171,7 +165,7 @@ class Md2Importer : BaseImporter() { // apply a default material helper.color = AiMaterial.Color(diffuse = AiColor3D(0.6f), specular = AiColor3D(0.6f), ambient = AiColor3D(0.05f)) helper.name = AI_DEFAULT_MATERIAL_NAME - val fileName = file.name.substringAfterLast('\\').substringBeforeLast('.') + val fileName = file.substringAfterLast('\\').substringBeforeLast('.') helper.textures.add(AiMaterial.Texture(file = "$fileName.bmp", type = AiTexture.Type.diffuse)) } diff --git a/src/main/kotlin/assimp/format/md3/Md3Loader.kt b/src/main/kotlin/assimp/format/md3/Md3Loader.kt index 1d38974..321cf64 100644 --- a/src/main/kotlin/assimp/format/md3/Md3Loader.kt +++ b/src/main/kotlin/assimp/format/md3/Md3Loader.kt @@ -48,7 +48,6 @@ import glm_.i import glm_.size import java.io.File import java.io.RandomAccessFile -import java.net.URI import java.nio.ByteBuffer import java.nio.ByteOrder import java.nio.channels.FileChannel @@ -119,13 +118,15 @@ object Q3Shader { * @param io IOSystem to be used for reading * @return false if file is not accessible */ - fun loadShader(fill: ShaderData, file: String): Boolean { - val f = File(file) - if (!f.exists()) return false // if we can't access the file, don't worry and return + fun loadShader(fill: ShaderData, file: String, ioSystem: IOSystem): Boolean { + + if (!ioSystem.exists(file)) return false // if we can't access the file, don't worry and return + + val reader = ioSystem.open(file).reader() logger.info { "Loading Quake3 shader file $file" } // read file in memory and remove comments from it (C++ style) and empty or blank lines - val lines = f.readLines().filter { !it.startsWith("//") && it.isNotEmpty() && it.isNotBlank() } + val lines = reader.readLines().filter { !it.startsWith("//") && it.isNotEmpty() && it.isNotBlank() } .map { it.trim() } // and trim it var curData: Q3Shader.ShaderDataBlock? = null @@ -272,14 +273,16 @@ object Q3Shader { * @param io IOSystem to be used for reading * @return false if file is not accessible */ - fun loadSkin(fill: SkinData, file: String): Boolean { - val f = File(file) - if (!f.canRead()) return false // if we can't access the file, don't worry and return + fun loadSkin(fill: SkinData, file: String, ioSystem: IOSystem): Boolean { + + if (!ioSystem.exists(file)) return false // if we can't access the file, don't worry and return logger.info { "Loading Quake3 skin file $file" } + val ioStream = ioSystem.open(file) + // read file in memory - val s = f.length() + val s = ioStream.length TODO() // std::vector _buff(s+1);const char* buff = &_buff[0]; // f->Read(&_buff[0],s,1); @@ -392,17 +395,13 @@ class Md3Importer : BaseImporter() { // Load multi-part model file, if necessary if (configHandleMP && readMultipartFile()) return - // Check whether we can read from the file - val f = File(file) - if (!f.canRead()) throw Error("Failed to open MD3 file $file.") + val stream = ioSystem.open(file) // Check whether the md3 file is large enough to contain the header - fileSize = f.length().i + fileSize = stream.length.i if (fileSize < MD3.Header.size) throw Error("MD3 File is too small.") - // Allocate storage and copy the contents of the file to a memory buffer - val fileChannel = RandomAccessFile(f, "r").channel - val buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).order(ByteOrder.nativeOrder()) + val buffer = stream.readBytes() header = MD3.Header(buffer) // Validate the file header @@ -421,10 +420,10 @@ class Md3Importer : BaseImporter() { scene.numMaterials = header.numSurfaces // Now read possible skins from .skin file val skins = Q3Shader.SkinData() - readSkin(skins) + readSkin(skins, ioSystem) // And check whether we can locate a shader file for this model val shadersData = Q3Shader.ShaderData() - readShader(shadersData) + readShader(shadersData, ioSystem) // Adjust all texture paths in the shader val headerName = header.name @@ -733,7 +732,7 @@ class Md3Importer : BaseImporter() { /** Try to read the skin for a MD3 file * @param fill Receives output information */ - fun readSkin(fill: Q3Shader.SkinData) { + fun readSkin(fill: Q3Shader.SkinData, ioSystem: IOSystem) { // skip any postfixes (e.g. lower_1.md3) var s = filename.lastIndexOf('_') if (s == -1) { @@ -742,20 +741,22 @@ class Md3Importer : BaseImporter() { } assert(s != -1) val skinFile = path + filename.substring(0, s) + "_$configSkinFile.skin" - Q3Shader.loadSkin(fill, skinFile) + Q3Shader.loadSkin(fill, skinFile, ioSystem) } /** Try to read the shader for a MD3 file * @param fill Receives output information */ - fun readShader(fill: Q3Shader.ShaderData) { + fun readShader(fill: Q3Shader.ShaderData, ioSystem: IOSystem) { // Determine Q3 model name from given path val last = path.substring(0, path.length - 2).lastIndexOf(File.separatorChar) val modelFile = path.substring(last + 1, path.length - 1) // If no specific dir or file is given, use our default search behaviour if (configShaderFile.isEmpty()) { - if (!Q3Shader.loadShader(fill, "$path../../../scripts/$modelFile.shader")) - Q3Shader.loadShader(fill, "$path../../../scripts/$filename.shader") + // TODO read from memory: how do we resolve ../../.. + val relativePath = "../../../scripts/".replace("/", ioSystem.osSeparator) // I hate windoof paths + if (!Q3Shader.loadShader(fill, "$path$relativePath$modelFile.shader", ioSystem)) + Q3Shader.loadShader(fill, "$path$relativePath$filename.shader", ioSystem) } else { TODO() // // If the given string specifies a file, load this file. diff --git a/src/main/kotlin/assimp/format/md5/MD5Loader.kt b/src/main/kotlin/assimp/format/md5/MD5Loader.kt index eb92521..683993d 100644 --- a/src/main/kotlin/assimp/format/md5/MD5Loader.kt +++ b/src/main/kotlin/assimp/format/md5/MD5Loader.kt @@ -151,7 +151,7 @@ class Md5Importer : BaseImporter() { ioFile = ioSystem.open(file + "md5mesh") } catch(e : IOException) { // Check whether we can read from the file - logger.warn { "Failed to access MD5MESH file: $file" } + logger.warn { "Failed to access MD5MESH file: ${file}md5mesh" } return } loadFileIntoMemory(ioFile.reader()) @@ -320,7 +320,7 @@ class Md5Importer : BaseImporter() { ioFile = ioSystem.open(file + "md5anim") } catch(e : IOException) { // Check whether we can read from the file - logger.warn { "Failed to access MD5ANIM file: $file" } + logger.warn { "Failed to access MD5ANIM file: ${file}md5anim" } return } loadFileIntoMemory(ioFile.reader()) diff --git a/src/main/kotlin/assimp/format/obj/ObjFileImporter.kt b/src/main/kotlin/assimp/format/obj/ObjFileImporter.kt index ee871e5..309340c 100644 --- a/src/main/kotlin/assimp/format/obj/ObjFileImporter.kt +++ b/src/main/kotlin/assimp/format/obj/ObjFileImporter.kt @@ -1,8 +1,12 @@ package assimp.format.obj import assimp.* -import gli_.gli -import java.io.IOException +import gli_.* +import gli_.tga.* +import java.io.* +import java.lang.IllegalArgumentException +import javax.imageio.* +import javax.imageio.spi.* /** * Created by elect on 21/11/2016. @@ -34,16 +38,16 @@ class ObjFileImporter : BaseImporter() { override fun internReadFile(file: String, ioSystem: IOSystem, scene: AiScene) { // Read file into memory - this.file = file//File(file) - if (!ioSystem.exists(file)) throw IOException("Failed to open file $file.") + this.file = file // Get the file-size and validate it, throwing an exception when fails - //val fileSize = this.file.length() + val stream = ioSystem.open(file) + val fileSize = stream.length - //if (fileSize < ObjMinSize) throw Error("OBJ-file is too small.") + if (fileSize < ObjMinSize) throw Error("OBJ-file is too small.") // parse the file into a temporary representation - val parser = ObjFileParser(ioSystem.open(file), ioSystem) + val parser = ObjFileParser(stream, ioSystem) // And create the proper return structures out of it createDataFromImport(parser.m_pModel, scene, ioSystem) @@ -406,7 +410,7 @@ class ObjFileImporter : BaseImporter() { } /** Load textures */ - fun loadTextures(scene: AiScene, ioSystem: IOSystem = this.ioSystem) { + fun loadTextures(scene: AiScene, ioSystem: IOSystem = ASSIMP.defaultIOSystem) { scene.materials.forEach { mtl -> @@ -420,31 +424,28 @@ class ObjFileImporter : BaseImporter() { while (!name[i].isLetter()) i++ val cleaned = name.substring(i) // e.g: .\wal67ar_small.jpg -> wal67ar_small.jpg - if(ioSystem is DefaultIOSystem) { + val parentPath = ioSystem.open(file).parentPath + ioSystem.osSeparator - //If the default io system is in place, we can use the java.io.File api and list directories - //to match files even where case is mangled + when { + ioSystem.exists(parentPath + cleaned) -> { - val actualFile = (ioSystem.open(file) as DefaultIOSystem.FileIOStream).path.toFile() + val texFile = ioSystem.open(parentPath + cleaned) - when { - actualFile.parentFile.listFiles().any { it.name == cleaned } -> { + val typeStart = texFile.filename.lastIndexOf(".") + 1 + val type = texFile.filename.substring(typeStart) - val texFile = actualFile.parentFile.listFiles().first { it.name == cleaned }!! - scene.textures[name] = gli.load(texFile.toPath()) + scene.textures[name] = loadImageFromMemory(texFile, type) + } + ioSystem.exists(parentPath + cleaned.toUpperCase()) -> { + // try case insensitive + val texFile = ioSystem.open(parentPath + cleaned.toUpperCase()) - } - actualFile.parentFile.listFiles().any { it.name.toUpperCase() == cleaned.toUpperCase() } -> { - // try case insensitive - val texFile = actualFile.parentFile.listFiles().first { it.name.toUpperCase() == cleaned.toUpperCase() }!! - scene.textures[name] = gli.load(texFile.toPath()) + val typeStart = texFile.filename.lastIndexOf(".") + 1 + val type = texFile.filename.substring(typeStart).toLowerCase() - } - else -> logger.warn { "OBJ/MTL: Texture image not found --> $cleaned" } + scene.textures[name] = loadImageFromMemory(texFile, type) } - } else { - //no such luck with custom io systems i'm afraid - //TODO gli load from bytebuf ? + else -> logger.warn { "OBJ/MTL: Texture image not found --> $cleaned" } } } else { @@ -456,6 +457,31 @@ class ObjFileImporter : BaseImporter() { } +// TODO this is pretty much a copy past from gli.read(...) and should be added there +private fun loadImageFromMemory(file: IOStream, type: String): Texture { + return when(type) { + "dds" -> gli.loadDds(file.readBytes()) + "kmg" -> gli.loadKmg(file.readBytes()) + "ktx" -> gli.loadKtx(file.readBytes()) + "jpeg", "jpg", "png", "gif", "bmp", "wbmp" -> { + val image = ImageIO.read(file.read()) + gli.createTexture(image) + } + "tga" -> { + if(!tgaAdded){ + IIORegistry.getDefaultInstance().registerServiceProvider(TgaImageReaderSpi()) + tgaAdded = true + } + val image = ImageIO.read(file.read()) + gli.createTexture(image) + } + else -> throw IllegalArgumentException("Type not supported") + } +} + + +private var tgaAdded = false + diff --git a/src/main/kotlin/assimp/format/obj/ObjFileParser.kt b/src/main/kotlin/assimp/format/obj/ObjFileParser.kt index 560de67..515a819 100644 --- a/src/main/kotlin/assimp/format/obj/ObjFileParser.kt +++ b/src/main/kotlin/assimp/format/obj/ObjFileParser.kt @@ -256,16 +256,18 @@ class ObjFileParser(private val file: IOStream, val ioSystem: IOSystem) { if (words.size < 2) throw Error("File name of the material is absent.") // get the name of the mat file with spaces - var filename = ObjTools.getNameWithSpace(words, 1) + val filename = ObjTools.getNameWithSpace(words, 1) - val pFile = file.parentPath() + "/" + filename //windows can just suck it + val pFile = "${file.parentPath}${ioSystem.osSeparator}$filename" println(pFile) if (!ioSystem.exists(pFile)) { - System.err.println("OBJ: Unable to locate material file $filename") + logger.error { "OBJ: Unable to locate material file $filename" } + + // TODO ?? what happens here? val strMatFallbackName = filename.substring(0, filename.length - 3) + "mtl" println("OBJ: Opening fallback material file $strMatFallbackName") - if (!File(strMatFallbackName).exists()) { + if (!ioSystem.exists(strMatFallbackName)) { System.err.println("OBJ: Unable to locate fallback material file $strMatFallbackName") return } diff --git a/src/main/kotlin/assimp/format/ply/PlyLoader.kt b/src/main/kotlin/assimp/format/ply/PlyLoader.kt index fff0e51..a01bb85 100644 --- a/src/main/kotlin/assimp/format/ply/PlyLoader.kt +++ b/src/main/kotlin/assimp/format/ply/PlyLoader.kt @@ -3,12 +3,8 @@ package assimp.format.ply import glm_.* import glm_.vec3.Vec3 import assimp.* -import java.io.File import java.io.IOException -import java.io.RandomAccessFile -import java.net.URI import java.nio.ByteOrder -import java.nio.channels.FileChannel /** * Created by elect on 10/12/2016. @@ -40,41 +36,35 @@ class PlyLoader : BaseImporter() { // Imports the given file into the given scene structure. override fun internReadFile(file: String, ioSystem: IOSystem, scene: AiScene) { - val file = File(file) - - // Check whether we can read from the file - if (!file.canRead()) throw IOException("Failed to open PLY file $file.") - // allocate storage and copy the contents of the file to a memory buffer - val fileChannel = RandomAccessFile(file, "r").channel - val mBuffer2 = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).order(ByteOrder.nativeOrder()) + val buffer = ioSystem.open(file).readBytes() // the beginning of the file must be PLY - magic, magic - if (mBuffer2.nextWord().toLowerCase() != "ply") throw Error("Invalid .ply file: Magic number 'ply' is no there") + if (buffer.nextWord().toLowerCase() != "ply") throw Error("Invalid .ply file: Magic number 'ply' is no there") - mBuffer2.skipSpacesAndLineEnd() + buffer.skipSpacesAndLineEnd() // determine the format of the file data val sPlyDom = DOM() - if (mBuffer2.nextWord() == "format") { + if (buffer.nextWord() == "format") { - val format = mBuffer2.nextWord() + val format = buffer.nextWord() if (format == "ascii") { - mBuffer2.skipLine() - if (!DOM.parseInstance(mBuffer2, sPlyDom)) + buffer.skipLine() + if (!DOM.parseInstance(buffer, sPlyDom)) throw Error("Invalid .ply file: Unable to build DOM (#1)") } else { // revert ascii - mBuffer2.position(mBuffer2.position() - format.length) + buffer.position(buffer.position() - format.length) - if (mBuffer2.startsWith("binary_")) { + if (buffer.startsWith("binary_")) { - val bIsBE = mBuffer2.get(mBuffer2.position()) == 'b'.b || mBuffer2.get(mBuffer2.position()) == 'B'.b - mBuffer2.order(if (bIsBE) ByteOrder.BIG_ENDIAN else ByteOrder.LITTLE_ENDIAN) + val bIsBE = buffer.get(buffer.position()) == 'b'.b || buffer.get(buffer.position()) == 'B'.b + buffer.order(if (bIsBE) ByteOrder.BIG_ENDIAN else ByteOrder.LITTLE_ENDIAN) // skip the line, parse the rest of the header and build the DOM - mBuffer2.skipLine() - if (!DOM.parseInstanceBinary(mBuffer2, sPlyDom)) + buffer.skipLine() + if (!DOM.parseInstanceBinary(buffer, sPlyDom)) throw Error("Invalid .ply file: Unable to build DOM (#2)") } else throw Error("Invalid .ply file: Unknown file format") diff --git a/src/main/kotlin/assimp/format/stl/STLLoader.kt b/src/main/kotlin/assimp/format/stl/STLLoader.kt index 0e70806..719f5a6 100644 --- a/src/main/kotlin/assimp/format/stl/STLLoader.kt +++ b/src/main/kotlin/assimp/format/stl/STLLoader.kt @@ -3,13 +3,8 @@ package assimp.format.stl import assimp.* import glm_.* import unsigned.ushr -import java.io.File import java.io.IOException -import java.io.RandomAccessFile -import java.net.URI import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.nio.channels.FileChannel import java.util.* /** @@ -100,21 +95,14 @@ class StlImporter : BaseImporter() { // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. - override fun internReadFile(pFile: String, ioSystem: IOSystem, scene: AiScene) { + override fun internReadFile(file: String, ioSystem: IOSystem, scene: AiScene) { - val file = File(pFile) + val stream = ioSystem.open(file) - // Check whether we can read from the file - if (!file.canRead()) throw IOException("Failed to open STL file $pFile.") - - fileSize = file.length().i - - // allocate storage and copy the contents of the file to a memory buffer - val fileChannel = RandomAccessFile(file, "r").channel - val mBuffer2 = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()).order(ByteOrder.nativeOrder()) + fileSize = stream.length.i this.pScene = scene - this.mBuffer = mBuffer2 + this.mBuffer = stream.readBytes() // the default vertex color is light gray. clrColorDefault put 0.6f @@ -128,7 +116,7 @@ class StlImporter : BaseImporter() { bMatClr = loadBinaryFile() else if (isAsciiSTL(mBuffer, fileSize)) loadASCIIFile() - else throw Error("Failed to determine STL storage representation for $pFile.") + else throw Error("Failed to determine STL storage representation for $file.") // add all created meshes to the single node scene.rootNode.numMeshes = scene.numMeshes diff --git a/src/test/kotlin/X/helpers.kt b/src/test/kotlin/X/helpers.kt index fc4b254..90885db 100644 --- a/src/test/kotlin/X/helpers.kt +++ b/src/test/kotlin/X/helpers.kt @@ -59,13 +59,13 @@ fun compareTextures(texture: Texture, texture1: Texture) { texture.baseLayer shouldBe texture1.baseLayer texture.baseLevel shouldBe texture1.baseLevel compareCache(texture.cache, texture1.cache) - texture.format.shouldEqual(texture1.format) + texture.format shouldBe texture1.format texture.maxFace shouldBe texture1.maxFace texture.maxLayer shouldBe texture1.maxLayer texture.maxLevel shouldBe texture1.maxLevel texture.size shouldBe texture1.size - texture.swizzles.shouldEqual(texture1.swizzles) - texture.target.shouldEqual(texture1.target) + texture.swizzles shouldBe texture1.swizzles + texture.target shouldBe texture1.target } fun compareCache(cache: Cache, cache1: Cache) { diff --git a/src/test/kotlin/assimp/anchor.kt b/src/test/kotlin/assimp/anchor.kt index 33fceef..d4a601a 100644 --- a/src/test/kotlin/assimp/anchor.kt +++ b/src/test/kotlin/assimp/anchor.kt @@ -1,9 +1,10 @@ package assimp import io.kotlintest.* -import org.lwjgl.BufferUtils import java.io.* -import java.net.URL +import java.net.* +import java.nio.* +import java.nio.file.* /** * Created by elect on 14/01/2017. @@ -29,8 +30,11 @@ fun getResource(resource: String): URL = ClassLoader.getSystemResource(resource) * * @return the result of [Importer.readFile] */ -fun Importer.testFile(path: URL, flags: AiPostProcessStepsFlags = 0, failOnNull: Boolean = true, verify: AiScene.() -> Unit = {}): AiScene? { - return testFile(path.file, flags, failOnNull, verify) +fun Importer.testFile(path: URL, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + return testFile(path.toURI(), flags, failOnNull, verify) } /** @@ -39,21 +43,40 @@ fun Importer.testFile(path: URL, flags: AiPostProcessStepsFlags = 0, failOnNull: * * @return the result of [Importer.readFile] */ -fun Importer.testFile(path: String, flags: AiPostProcessStepsFlags = 0, failOnNull: Boolean = true, verify: AiScene.() -> Unit = {}): AiScene? { +fun Importer.testFile(path: URI, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + return testFile(Paths.get(path).toAbsolutePath().toString(), flags, failOnNull, verify) +} - // test readFile - val scene = readFile(path, flags) +/** + * calls both [Importer.readFile] and [Importer.readFileFromMemory] and verifies it using [verify]. + * This fails if [failOnNull] is set and either of the above returns null. + * + * @return the result of [Importer.readFile] + */ +fun Importer.testFile(path: String, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + + logger.info { "Testing read $path:" } + var scene: AiScene? = null + + logger.info { "reading from file:"} + + scene = readFile(path, flags) if (scene == null && failOnNull) { fail("readFile returned 'null' for $path") } else { scene?.verify() } - /* - TODO enable tests once read from memory is properly implemented - // test readFileFromMemory + logger.info { "reading from memory:" } + val bytes = FileInputStream(File(path)).readBytes() - val buffer = BufferUtils.createByteBuffer(bytes.size).also { it.put(bytes); it.flip() } + val buffer = ByteBuffer.wrap(bytes) val hintStart = path.indexOfLast { it == '.' } val hint = path.substring(hintStart + 1) @@ -64,7 +87,126 @@ fun Importer.testFile(path: String, flags: AiPostProcessStepsFlags = 0, failOnNu } else { memScene?.verify() } - */ + + if(scene == null) scene = memScene + + return scene +} + +/** + * calls both [Importer.readFile] and [Importer.readFilesFromMemory] and verifies it using [verify]. + * This fails if [failOnNull] is set and either of the above returns null. + * + * The first path in [paths] will be used for the base path + * + * @return the result of [Importer.readFile] + */ +fun Importer.testURLs(vararg paths: URL, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + return testURLs(listOf(*paths), flags, failOnNull, verify) +} + +/** + * calls both [Importer.readFile] and [Importer.readFilesFromMemory] and verifies it using [verify]. + * This fails if [failOnNull] is set and either of the above returns null. + * + * The first path in [paths] will be used for the base path + * + * @return the result of [Importer.readFile] + */ +fun Importer.testURLs(paths: List, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + return testURIs(paths.map(URL::toURI), flags, failOnNull, verify) +} + +/** + * calls both [Importer.readFile] and [Importer.readFilesFromMemory] and verifies it using [verify]. + * This fails if [failOnNull] is set and either of the above returns null. + * + * The first path in [paths] will be used for the base path + * + * @return the result of [Importer.readFile] + */ +fun Importer.testURIs(vararg paths: URI, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + return testURIs(listOf(*paths), flags, failOnNull, verify) +} + +/** + * calls both [Importer.readFile] and [Importer.readFilesFromMemory] and verifies it using [verify]. + * This fails if [failOnNull] is set and either of the above returns null. + * + * The first path in [paths] will be used for the base path + * + * @return the result of [Importer.readFile] + */ +fun Importer.testURIs(paths: List, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + return testFiles(paths.map { Paths.get(it).toAbsolutePath().toString() }, flags, failOnNull, verify) +} + +/** + * calls both [Importer.readFile] and [Importer.readFilesFromMemory] and verifies it using [verify]. + * This fails if [failOnNull] is set and either of the above returns null. + * + * The first path in [paths] will be used for the base path + * + * @return the result of [Importer.readFile] + */ +fun Importer.testFiles(vararg paths: String, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + return testFiles(listOf(*paths), flags, failOnNull, verify) +} + +/** + * calls both [Importer.readFile] and [Importer.readFilesFromMemory] and verifies it using [verify]. + * This fails if [failOnNull] is set and either of the above returns null. + * + * The first path in [paths] will be used for the base path + * + * @return the result of [Importer.readFile] + */ +fun Importer.testFiles(paths: List, + flags: AiPostProcessStepsFlags = 0, + failOnNull: Boolean = true, + verify: AiScene.() -> Unit = {}): AiScene? { + + val baseFile = paths[0] + + logger.info { "Testing read $baseFile:" } + + logger.info { "reading from file:"} + // test readFile + var scene = readFile(baseFile, flags) + if (scene == null && failOnNull) { + fail("readFile returned 'null' for $baseFile") + } else { + scene?.verify() + } + + + logger.info { "reading from memory:" } + + val files = paths.map { it to ByteBuffer.wrap(FileInputStream(File(it)).readBytes()) }.toMap() + + val memScene = readFilesFromMemory(baseFile, files, flags) + if (memScene == null && failOnNull) { + fail("readFileFromMemory returned 'null' for $baseFile") + } else { + memScene?.verify() + } + + if(scene == null) scene = memScene return scene } \ No newline at end of file diff --git a/src/test/kotlin/assimp/assbin/boblampclean_assbin.kt b/src/test/kotlin/assimp/assbin/boblampclean_assbin.kt deleted file mode 100644 index 01ddd4b..0000000 --- a/src/test/kotlin/assimp/assbin/boblampclean_assbin.kt +++ /dev/null @@ -1,40 +0,0 @@ -package assimp.assbin - -import assimp.* -import io.kotlintest.specs.StringSpec - -// TODO -class `boblampclean assbin` : StringSpec() { - - val boblampclean = "ogldev/boblampclean.assbin" - - init { - -// val anims_with_full_rotations_between_keys = "anims_with_full_rotations_between_keys.assbin" -// -// anims_with_full_rotations_between_keys { -// -// val scene = Importer().readFile(assbin + anims_with_full_rotations_between_keys)!! -// println() -//// scene.flags shouldBe 0 -// } -// - - - boblampclean { - - val scene = Importer().testFile(getResource("$assbin/$boblampclean")) - - println() - } - - val minigun = "MDL/MDL3 (3DGS A4)/minigun.assbin" - - minigun { - - val scene = Importer().testFile(getResource("$assbin/$minigun")) - - println() - } - } -} \ No newline at end of file diff --git a/src/test/kotlin/assimp/blender/blender.kt b/src/test/kotlin/assimp/blender/blender.kt index ded4164..8a30f8c 100644 --- a/src/test/kotlin/assimp/blender/blender.kt +++ b/src/test/kotlin/assimp/blender/blender.kt @@ -1,6 +1,7 @@ package assimp.blender import assimp.models +import assimp.obj.* import io.kotlintest.specs.StringSpec class blender : StringSpec() { @@ -8,9 +9,9 @@ class blender : StringSpec() { val path = models + "/BLEND/" init { -// "blender default 250 compressed"{ blenderDefault_250_compressed(path + "BlenderDefault_250_Compressed.blend") } -// "spider"{ spider(path + "spider.obj") } -// "nanosuit" { nanosuit(path + "nanosuit/nanosuit.obj") } -// "shelter" { shelter(path + "statie B01.obj")} + "blender default 250 compressed".config(enabled = false){ blenderDefault_250_compressed(path + "BlenderDefault_250_Compressed.blend") } + //"spider"{ spider(path + "spider.obj") } + //"nanosuit" { nanosuit(path + "nanosuit/nanosuit.obj") } + //"shelter" { shelter(path + "statie B01.obj")} } } \ No newline at end of file diff --git a/src/test/kotlin/assimp/collada/color teapot spheres.kt b/src/test/kotlin/assimp/collada/color teapot spheres.kt index a4cbf68..175ea58 100644 --- a/src/test/kotlin/assimp/collada/color teapot spheres.kt +++ b/src/test/kotlin/assimp/collada/color teapot spheres.kt @@ -3,7 +3,7 @@ package assimp.collada import assimp.* import glm_.mat4x4.Mat4 import glm_.vec3.Vec3 -import io.kotlintest.matchers.shouldBe +import io.kotlintest.shouldBe import java.net.URL object `color teapot spheres` { diff --git a/src/test/kotlin/assimp/collada/treasure_smooth Pretransform.kt b/src/test/kotlin/assimp/collada/treasure_smooth Pretransform.kt index c5cfe50..cdf70e1 100644 --- a/src/test/kotlin/assimp/collada/treasure_smooth Pretransform.kt +++ b/src/test/kotlin/assimp/collada/treasure_smooth Pretransform.kt @@ -3,7 +3,7 @@ package assimp.collada import assimp.* import glm_.mat4x4.Mat4 import glm_.vec3.Vec3 -import io.kotlintest.matchers.shouldBe +import io.kotlintest.shouldBe import java.net.URL object `treasure_smooth Pretransform` { diff --git a/src/test/kotlin/assimp/collada/treasure_smooth.kt b/src/test/kotlin/assimp/collada/treasure_smooth.kt index 0a8fa0d..5c454f0 100644 --- a/src/test/kotlin/assimp/collada/treasure_smooth.kt +++ b/src/test/kotlin/assimp/collada/treasure_smooth.kt @@ -3,7 +3,7 @@ package assimp.collada import assimp.* import glm_.mat4x4.Mat4 import glm_.vec3.Vec3 -import io.kotlintest.matchers.shouldBe +import io.kotlintest.shouldBe import java.net.URL object treasure_smooth { diff --git a/src/test/kotlin/assimp/fbx/fbx.kt b/src/test/kotlin/assimp/fbx/fbx.kt index 0802e10..0ba717f 100644 --- a/src/test/kotlin/assimp/fbx/fbx.kt +++ b/src/test/kotlin/assimp/fbx/fbx.kt @@ -2,6 +2,7 @@ package assimp.fbx import assimp.* import io.kotlintest.specs.StringSpec +import java.nio.* /** * Created by elect on 24/01/2017. diff --git a/src/test/kotlin/assimp/md2/faerie.kt b/src/test/kotlin/assimp/md2/faerie.kt index e06b873..0b51e11 100644 --- a/src/test/kotlin/assimp/md2/faerie.kt +++ b/src/test/kotlin/assimp/md2/faerie.kt @@ -3,13 +3,14 @@ package assimp.md2 import assimp.* import glm_.mat4x4.Mat4 import glm_.vec3.Vec3 -import io.kotlintest.shouldBe +import io.kotlintest.* +import io.kotlintest.matchers.endWith object faerie { - operator fun invoke(fileName: String) { + operator fun invoke(fileName: String, imgFile: String) { - Importer().testFile(getResource(fileName)){ + Importer().testURLs(getResource(fileName), getResource(imgFile)){ flags shouldBe 0 @@ -59,7 +60,7 @@ object faerie { ambient shouldBe Vec3(0.0500000007) } textures[0].type shouldBe AiTexture.Type.diffuse - textures[0].file shouldBe "faerie.bmp" + textures[0].file!! should endWith("faerie.bmp") name shouldBe "DefaultMaterial" } } diff --git a/src/test/kotlin/assimp/md2/md2.kt b/src/test/kotlin/assimp/md2/md2.kt index 9f9f211..d82ad91 100644 --- a/src/test/kotlin/assimp/md2/md2.kt +++ b/src/test/kotlin/assimp/md2/md2.kt @@ -12,6 +12,6 @@ class md2 : StringSpec() { val path = "$models/MD2/" init { - "faerie" { faerie(path + "faerie.md2") } + "faerie" { faerie(path + "faerie.md2", path + "faerie2.bmp") } } } \ No newline at end of file diff --git a/src/test/kotlin/assimp/md3/europeanFnt.kt b/src/test/kotlin/assimp/md3/europeanFnt.kt index e55a1c1..7855142 100644 --- a/src/test/kotlin/assimp/md3/europeanFnt.kt +++ b/src/test/kotlin/assimp/md3/europeanFnt.kt @@ -3,217 +3,238 @@ package assimp.md3 import assimp.* import glm_.mat4x4.Mat4 import glm_.vec3.Vec3 -import io.kotlintest.shouldBe +import io.kotlintest.* +import java.io.* +import java.nio.* +import java.nio.file.* object europeanFnt { - operator fun invoke(fileName: String) { + operator fun invoke(fileName: String, shaderName: String) { + val fileName = fileName.replace('/', File.separatorChar) + val shaderName = shaderName.replace('/', File.separatorChar) - Importer().testFile(getResource(fileName)) { + logger.info("load from file $fileName") + Importer().readFile(getResource(fileName))?.verify() ?: fail("could not load $fileName") - flags shouldBe 0 + logger.info("load from memory $fileName") + // TODO temp workaround for relative paths in load from memory + fun String.loadFile(): ByteBuffer { + val file = Paths.get(getResource(this).toURI()).toAbsolutePath().toFile() + return ByteBuffer.wrap(FileInputStream(file).readBytes()) + } - with(rootNode) { - name shouldBe "" - transformation shouldBe Mat4( - 1f, 0f, 0f, 0f, - 0f, 0f, -1f, 0f, - 0f, 1f, 0f, 0f, - 0f, 0f, 0f, 1f) - parent shouldBe null - numChildren shouldBe 0 - children.isEmpty() shouldBe true + val baseFileBuffer = fileName.loadFile() + val shaderBuffer = shaderName.loadFile() - numMeshes shouldBe 5 - meshes[0] shouldBe 0 - } + Importer().readFilesFromMemory(fileName,fileName to baseFileBuffer, shaderName to shaderBuffer) + ?.verify() ?: fail("could not load $fileName from memory") + } - with(meshes[0]) { + private fun AiScene.verify() { - primitiveTypes shouldBe 4 - numVertices shouldBe 336 - numFaces shouldBe 112 + flags shouldBe 0 - vertices[0] shouldBe Vec3(84.7812500f, 39.1875000f, 27.0468750f) - vertices[167] shouldBe Vec3(-63.3906250f, -40.9218750f, 13.2656250f) - vertices[335] shouldBe Vec3(-66.7500000f, 38.8437500f, 15.7343750f) + with(rootNode) { + name shouldBe "" + transformation shouldBe Mat4( + 1f, 0f, 0f, 0f, + 0f, 0f, -1f, 0f, + 0f, 1f, 0f, 0f, + 0f, 0f, 0f, 1f) + parent shouldBe null + numChildren shouldBe 0 + children.isEmpty() shouldBe true + + numMeshes shouldBe 5 + meshes[0] shouldBe 0 + } + + with(meshes[0]) { - normals[0] shouldBe Vec3(0.5348365f, -0.013129499f, -0.8448536f) - normals[167] shouldBe Vec3(-1.6606013E-16f, -0.9039893f, -0.42755508f) - normals[335] shouldBe Vec3(-0.9423873f, 0.31132272f, -0.12241068f) + primitiveTypes shouldBe 4 + numVertices shouldBe 336 + numFaces shouldBe 112 - textureCoords[0][0][0] shouldBe 0.0498498604f - textureCoords[0][0][1] shouldBe 0.616048455f - textureCoords[0][167][0] shouldBe 0.0527787320f - textureCoords[0][167][1] shouldBe 0.612726867f - textureCoords[0][335][0] shouldBe 0.0801433548f - textureCoords[0][335][1] shouldBe 0.583810389f + vertices[0] shouldBe Vec3(84.7812500f, 39.1875000f, 27.0468750f) + vertices[167] shouldBe Vec3(-63.3906250f, -40.9218750f, 13.2656250f) + vertices[335] shouldBe Vec3(-66.7500000f, 38.8437500f, 15.7343750f) - for (i in 0..111 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 1, i + 2) + normals[0] shouldBe Vec3(0.5348365f, -0.013129499f, -0.8448536f) + normals[167] shouldBe Vec3(-1.6606013E-16f, -0.9039893f, -0.42755508f) + normals[335] shouldBe Vec3(-0.9423873f, 0.31132272f, -0.12241068f) - name.isEmpty() shouldBe true - } - with(meshes[1]) { + textureCoords[0][0][0] shouldBe 0.0498498604f + textureCoords[0][0][1] shouldBe 0.616048455f + textureCoords[0][167][0] shouldBe 0.0527787320f + textureCoords[0][167][1] shouldBe 0.612726867f + textureCoords[0][335][0] shouldBe 0.0801433548f + textureCoords[0][335][1] shouldBe 0.583810389f + + for (i in 0..111 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 1, i + 2) - primitiveTypes shouldBe 4 - numVertices shouldBe 528 - numFaces shouldBe 176 - - vertices[0] shouldBe Vec3(76.5937500f, 21.5312500f, 16.8750000f) - vertices[263] shouldBe Vec3(-39.5312500f, -38.6250000f, 34.0468750f) - vertices[527] shouldBe Vec3(50.3281250f, -37.1718750f, 16.9843750f) - - normals[0] shouldBe Vec3(0.5348365f, -0.8445991f, 0.024541229f) - normals[263] shouldBe Vec3(0.18502302f, -0.30869228f, 0.9329928f) - normals[527] shouldBe Vec3(-0.5139479f, -0.8574703f, 0.024541229f) - - textureCoords[0][0][0] shouldBe 0.0963503048f - textureCoords[0][0][1] shouldBe 0.951211274f - textureCoords[0][263][0] shouldBe 0.948739946f - textureCoords[0][263][1] shouldBe 0.898761094f - textureCoords[0][527][0] shouldBe 0.948739946f - textureCoords[0][527][1] shouldBe 0.755133688f - - for (i in 0..175 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 2, i + 1) - - name.isEmpty() shouldBe true - } - with(meshes[2]) { - - primitiveTypes shouldBe 4 - numVertices shouldBe 1050 - numFaces shouldBe 350 - - vertices[0] shouldBe Vec3(65.5312500f, 12.1093750f, 54.4531250f) - vertices[524] shouldBe Vec3(87.4843750f, -36.6250000f, 40.0625000f) - vertices[1049] shouldBe Vec3(-79.0781250f, -33.5781250f, 29.2343750f) - - normals[0] shouldBe Vec3(-0.31792322f, 0.31792322f, 0.8932243f) - normals[524] shouldBe Vec3(0.89080405f, -0.44839308f, 0.07356457f) - normals[1049] shouldBe Vec3(-0.794514f, -0.58925176f, 0.14673047f) - - textureCoords[0][0][0] shouldBe 0.387118787f - textureCoords[0][0][1] shouldBe 0.285972953f - textureCoords[0][524][0] shouldBe 0.908088386f - textureCoords[0][524][1] shouldBe 0.706875503f - textureCoords[0][1049][0] shouldBe 0.923744082f - textureCoords[0][1049][1] shouldBe 0.848480046f - - for (i in 0..349 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 2, i + 1) - - name.isEmpty() shouldBe true - } - with(meshes[3]) { - - primitiveTypes shouldBe 4 - numVertices shouldBe 114 - numFaces shouldBe 38 - - vertices[0] shouldBe Vec3(29.1406250f, 12.8437500f, 43.5156250f) - vertices[56] shouldBe Vec3(33.1093750f, 12.8125000f, 50.9531250f) - vertices[113] shouldBe Vec3(29.1406250f, 12.8437500f, 43.5156250f) - - normals[0] shouldBe Vec3(-0.9984946f, -0.024511667f, 0.049067676f) - normals[56] shouldBe Vec3(-0.47125477f, 0.011568655f, -0.8819213f) - normals[113] shouldBe Vec3(-0.9984946f, -0.024511667f, 0.049067676f) - - textureCoords[0][0][0] shouldBe 0.125758305f - textureCoords[0][0][1] shouldBe 0.741084099f - textureCoords[0][56][0] shouldBe 0.250173688f - textureCoords[0][56][1] shouldBe 0.923741937f - textureCoords[0][113][0] shouldBe 0.0367676802f - textureCoords[0][113][1] shouldBe 0.726932108f - - for (i in 0..37 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 1, i + 2) - - name.isEmpty() shouldBe true - } - with(meshes[4]) { - - primitiveTypes shouldBe 4 - numVertices shouldBe 6 - numFaces shouldBe 2 - - vertices[0] shouldBe Vec3(45.9218750f, 23.6718750f, 53.6406250f) - vertices[2] shouldBe Vec3(36.2812500f, 23.6718750f, 73.0781250f) - vertices[5] shouldBe Vec3(45.9375000f, -23.5156250f, 53.5312500f) - - normals[0] shouldBe Vec3(0.8932243f, 0.0f, 0.44961134f) - normals[2] shouldBe Vec3(0.8932243f, 0.0f, 0.44961134f) - normals[5] shouldBe Vec3(0.8932243f, 0.0f, 0.44961134f) - - textureCoords[0][0][0] shouldBe 0.999992669f - textureCoords[0][0][1] shouldBe 0.00579905510f - textureCoords[0][2][0] shouldBe 1.00000000f - textureCoords[0][2][1] shouldBe 0.999242306f - textureCoords[0][5][0] shouldBe 0f - textureCoords[0][5][1] shouldBe 0f - - for (i in 0..1 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 2, i + 1) - - name.isEmpty() shouldBe true - } - numMaterials shouldBe 5 - - with(materials[0]) { - shadingModel shouldBe AiShadingMode.gouraud - with(color!!) { - ambient shouldBe Vec3(0.0500000007) - diffuse shouldBe Vec3(1f) - specular shouldBe Vec3(1f) - } - name shouldBe "MD3_[default][windscreen]" - textures[0].file shouldBe "textures/sfx/glass.tga.tga" - textures[0].flags shouldBe AiTexture.Flags.ignoreAlpha.i - } - with(materials[1]) { - shadingModel shouldBe AiShadingMode.gouraud - with(color!!) { - ambient shouldBe Vec3(0.0500000007) - diffuse shouldBe Vec3(1f) - specular shouldBe Vec3(1f) - } - name shouldBe "MD3_[default][steering]" - twoSided shouldBe true - blendFunc shouldBe AiBlendMode.default - textures[0].file shouldBe "euro_frnt_2.tga" - textures[0].flags shouldBe AiTexture.Flags.useAlpha.i - } - with(materials[2]) { - shadingModel shouldBe AiShadingMode.gouraud - with(color!!) { - ambient shouldBe Vec3(0.0500000007) - diffuse shouldBe Vec3(1f) - specular shouldBe Vec3(1f) - } - name shouldBe "MD3_[default][body]" - textures[0].file shouldBe "european_fnt.tga" - textures[0].flags shouldBe AiTexture.Flags.ignoreAlpha.i - } - with(materials[3]) { - shadingModel shouldBe AiShadingMode.gouraud - with(color!!) { - ambient shouldBe Vec3(0.0500000007) - diffuse shouldBe Vec3(1f) - specular shouldBe Vec3(1f) - } - name shouldBe "MD3_[default][wheels]" - textures[0].file shouldBe "european_fnt.tga" - textures[0].flags shouldBe AiTexture.Flags.ignoreAlpha.i - } - with(materials[4]) { - shadingModel shouldBe AiShadingMode.gouraud - with(color!!) { - ambient shouldBe Vec3(0.0500000007) - diffuse shouldBe Vec3(1f) - specular shouldBe Vec3(1f) - } - name shouldBe "MD3_[default][wheel_arches]" - twoSided shouldBe true - blendFunc shouldBe AiBlendMode.default - textures[0].file shouldBe "euro_frnt_2.tga" - textures[0].flags shouldBe AiTexture.Flags.useAlpha.i - } - } - } + name.isEmpty() shouldBe true + } + with(meshes[1]) { + + primitiveTypes shouldBe 4 + numVertices shouldBe 528 + numFaces shouldBe 176 + + vertices[0] shouldBe Vec3(76.5937500f, 21.5312500f, 16.8750000f) + vertices[263] shouldBe Vec3(-39.5312500f, -38.6250000f, 34.0468750f) + vertices[527] shouldBe Vec3(50.3281250f, -37.1718750f, 16.9843750f) + + normals[0] shouldBe Vec3(0.5348365f, -0.8445991f, 0.024541229f) + normals[263] shouldBe Vec3(0.18502302f, -0.30869228f, 0.9329928f) + normals[527] shouldBe Vec3(-0.5139479f, -0.8574703f, 0.024541229f) + + textureCoords[0][0][0] shouldBe 0.0963503048f + textureCoords[0][0][1] shouldBe 0.951211274f + textureCoords[0][263][0] shouldBe 0.948739946f + textureCoords[0][263][1] shouldBe 0.898761094f + textureCoords[0][527][0] shouldBe 0.948739946f + textureCoords[0][527][1] shouldBe 0.755133688f + + for (i in 0..175 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 2, i + 1) + + name.isEmpty() shouldBe true + } + with(meshes[2]) { + + primitiveTypes shouldBe 4 + numVertices shouldBe 1050 + numFaces shouldBe 350 + + vertices[0] shouldBe Vec3(65.5312500f, 12.1093750f, 54.4531250f) + vertices[524] shouldBe Vec3(87.4843750f, -36.6250000f, 40.0625000f) + vertices[1049] shouldBe Vec3(-79.0781250f, -33.5781250f, 29.2343750f) + + normals[0] shouldBe Vec3(-0.31792322f, 0.31792322f, 0.8932243f) + normals[524] shouldBe Vec3(0.89080405f, -0.44839308f, 0.07356457f) + normals[1049] shouldBe Vec3(-0.794514f, -0.58925176f, 0.14673047f) + + textureCoords[0][0][0] shouldBe 0.387118787f + textureCoords[0][0][1] shouldBe 0.285972953f + textureCoords[0][524][0] shouldBe 0.908088386f + textureCoords[0][524][1] shouldBe 0.706875503f + textureCoords[0][1049][0] shouldBe 0.923744082f + textureCoords[0][1049][1] shouldBe 0.848480046f + + for (i in 0..349 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 2, i + 1) + + name.isEmpty() shouldBe true + } + with(meshes[3]) { + + primitiveTypes shouldBe 4 + numVertices shouldBe 114 + numFaces shouldBe 38 + + vertices[0] shouldBe Vec3(29.1406250f, 12.8437500f, 43.5156250f) + vertices[56] shouldBe Vec3(33.1093750f, 12.8125000f, 50.9531250f) + vertices[113] shouldBe Vec3(29.1406250f, 12.8437500f, 43.5156250f) + + normals[0] shouldBe Vec3(-0.9984946f, -0.024511667f, 0.049067676f) + normals[56] shouldBe Vec3(-0.47125477f, 0.011568655f, -0.8819213f) + normals[113] shouldBe Vec3(-0.9984946f, -0.024511667f, 0.049067676f) + + textureCoords[0][0][0] shouldBe 0.125758305f + textureCoords[0][0][1] shouldBe 0.741084099f + textureCoords[0][56][0] shouldBe 0.250173688f + textureCoords[0][56][1] shouldBe 0.923741937f + textureCoords[0][113][0] shouldBe 0.0367676802f + textureCoords[0][113][1] shouldBe 0.726932108f + + for (i in 0..37 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 1, i + 2) + + name.isEmpty() shouldBe true + } + with(meshes[4]) { + + primitiveTypes shouldBe 4 + numVertices shouldBe 6 + numFaces shouldBe 2 + + vertices[0] shouldBe Vec3(45.9218750f, 23.6718750f, 53.6406250f) + vertices[2] shouldBe Vec3(36.2812500f, 23.6718750f, 73.0781250f) + vertices[5] shouldBe Vec3(45.9375000f, -23.5156250f, 53.5312500f) + + normals[0] shouldBe Vec3(0.8932243f, 0.0f, 0.44961134f) + normals[2] shouldBe Vec3(0.8932243f, 0.0f, 0.44961134f) + normals[5] shouldBe Vec3(0.8932243f, 0.0f, 0.44961134f) + + textureCoords[0][0][0] shouldBe 0.999992669f + textureCoords[0][0][1] shouldBe 0.00579905510f + textureCoords[0][2][0] shouldBe 1.00000000f + textureCoords[0][2][1] shouldBe 0.999242306f + textureCoords[0][5][0] shouldBe 0f + textureCoords[0][5][1] shouldBe 0f + + for (i in 0..1 * 3 step 3) faces[i / 3] shouldBe mutableListOf(i, i + 2, i + 1) + + name.isEmpty() shouldBe true + } + numMaterials shouldBe 5 + + with(materials[0]) { + shadingModel shouldBe AiShadingMode.gouraud + with(color!!) { + ambient shouldBe Vec3(0.0500000007) + diffuse shouldBe Vec3(1f) + specular shouldBe Vec3(1f) + } + name shouldBe "MD3_[default][windscreen]" + textures[0].file shouldBe "textures/sfx/glass.tga.tga" + textures[0].flags shouldBe AiTexture.Flags.ignoreAlpha.i + } + with(materials[1]) { + shadingModel shouldBe AiShadingMode.gouraud + with(color!!) { + ambient shouldBe Vec3(0.0500000007) + diffuse shouldBe Vec3(1f) + specular shouldBe Vec3(1f) + } + name shouldBe "MD3_[default][steering]" + twoSided shouldBe true + blendFunc shouldBe AiBlendMode.default + textures[0].file shouldBe "euro_frnt_2.tga" + textures[0].flags shouldBe AiTexture.Flags.useAlpha.i + } + with(materials[2]) { + shadingModel shouldBe AiShadingMode.gouraud + with(color!!) { + ambient shouldBe Vec3(0.0500000007) + diffuse shouldBe Vec3(1f) + specular shouldBe Vec3(1f) + } + name shouldBe "MD3_[default][body]" + textures[0].file shouldBe "european_fnt.tga" + textures[0].flags shouldBe AiTexture.Flags.ignoreAlpha.i + } + with(materials[3]) { + shadingModel shouldBe AiShadingMode.gouraud + with(color!!) { + ambient shouldBe Vec3(0.0500000007) + diffuse shouldBe Vec3(1f) + specular shouldBe Vec3(1f) + } + name shouldBe "MD3_[default][wheels]" + textures[0].file shouldBe "european_fnt.tga" + textures[0].flags shouldBe AiTexture.Flags.ignoreAlpha.i + } + with(materials[4]) { + shadingModel shouldBe AiShadingMode.gouraud + with(color!!) { + ambient shouldBe Vec3(0.0500000007) + diffuse shouldBe Vec3(1f) + specular shouldBe Vec3(1f) + } + name shouldBe "MD3_[default][wheel_arches]" + twoSided shouldBe true + blendFunc shouldBe AiBlendMode.default + textures[0].file shouldBe "euro_frnt_2.tga" + textures[0].flags shouldBe AiTexture.Flags.useAlpha.i + } + } } \ No newline at end of file diff --git a/src/test/kotlin/assimp/md3/md3.kt b/src/test/kotlin/assimp/md3/md3.kt index 754f285..f90a11b 100644 --- a/src/test/kotlin/assimp/md3/md3.kt +++ b/src/test/kotlin/assimp/md3/md3.kt @@ -14,6 +14,7 @@ class md3 : StringSpec() { init { // "watercan" { watercan(path + "watercan.md3") } - "european font" { europeanFnt(path + "q3root/models/mapobjects/kt_kubalwagon/european_fnt_v2.md3") } + "european font" { europeanFnt(path + "q3root/models/mapobjects/kt_kubalwagon/european_fnt_v2.md3", + path + "q3root/models/mapobjects/kt_kubalwagon/../../../scripts/kt_kubalwagon.shader") } } } \ No newline at end of file diff --git a/src/test/kotlin/assimp/md5/boarMan.kt b/src/test/kotlin/assimp/md5/boarMan.kt index 5d6361c..6e9d8e8 100644 --- a/src/test/kotlin/assimp/md5/boarMan.kt +++ b/src/test/kotlin/assimp/md5/boarMan.kt @@ -9,7 +9,7 @@ object boarMan { operator fun invoke(fileName: String) { - Importer().testFile(getResource(fileName)) { + Importer().testURLs(getResource(fileName)) { flags shouldBe 0 diff --git a/src/test/kotlin/assimp/md5/md5.kt b/src/test/kotlin/assimp/md5/md5.kt index e8728bc..f65af91 100644 --- a/src/test/kotlin/assimp/md5/md5.kt +++ b/src/test/kotlin/assimp/md5/md5.kt @@ -16,5 +16,6 @@ class md5 : StringSpec() { init { "simple cube" { simpleCube(path + "SimpleCube.md5mesh") } "boar man" { boarMan(path_ + "BoarMan.md5mesh") } + // TODO no tests for anim files } } \ No newline at end of file diff --git a/src/test/kotlin/assimp/md5/simpleCube.kt b/src/test/kotlin/assimp/md5/simpleCube.kt index c7bd589..4834b59 100644 --- a/src/test/kotlin/assimp/md5/simpleCube.kt +++ b/src/test/kotlin/assimp/md5/simpleCube.kt @@ -9,7 +9,7 @@ object simpleCube { operator fun invoke(fileName: String) { - Importer().testFile(getResource(fileName)) { + Importer().testURLs(getResource(fileName)) { flags shouldBe 0 diff --git a/src/test/kotlin/assimp/obj/box.kt b/src/test/kotlin/assimp/obj/box.kt index ac04194..255c812 100644 --- a/src/test/kotlin/assimp/obj/box.kt +++ b/src/test/kotlin/assimp/obj/box.kt @@ -4,8 +4,6 @@ import assimp.* import glm_.mat4x4.Mat4 import glm_.vec3.Vec3 import io.kotlintest.shouldBe -import uno.kotlin.uri -import uno.kotlin.url /** * Created by elect on 16/11/2016. @@ -15,7 +13,7 @@ object box { operator fun invoke(fileName: String) { - Importer().testFile(getResource(fileName)) { + Importer().testURLs(getResource(fileName)) { with(rootNode) { diff --git a/src/test/kotlin/assimp/obj/nanosuit.kt b/src/test/kotlin/assimp/obj/nanosuit.kt index d1006bd..2ac2811 100644 --- a/src/test/kotlin/assimp/obj/nanosuit.kt +++ b/src/test/kotlin/assimp/obj/nanosuit.kt @@ -8,8 +8,11 @@ import java.util.* object nanosuit { - operator fun invoke(fileName: String) { - Importer().testFile(getResource(fileName)) { + operator fun invoke(fileName: String, matName: String, vararg imagePath: String) { + + val imageURLs = imagePath.map { getResource(it) }.toTypedArray() + + Importer().testURLs(getResource(fileName), getResource(matName), *imageURLs) { with(rootNode) { @@ -430,6 +433,9 @@ object nanosuit { uvwsrc shouldBe 0 } } + + // numTextures shouldBe 23 // TODO not set + textures.size shouldBe 23 } } } \ No newline at end of file diff --git a/src/test/kotlin/assimp/obj/obj.kt b/src/test/kotlin/assimp/obj/obj.kt index 50bad51..f266c88 100644 --- a/src/test/kotlin/assimp/obj/obj.kt +++ b/src/test/kotlin/assimp/obj/obj.kt @@ -11,8 +11,41 @@ class obj : StringSpec() { "cube"{ cube(path + "cube.obj") } "wall"{ wall(path + "wall.obj") } "box"{ box(path + "box.obj") } - "spider"{ spider(path + "spider.obj") } - "nanosuit" { nanosuit(path + "nanosuit/nanosuit.obj") } - "shelter" { shelter(path + "statie B01.obj")} + + "spider"{ spider(path + "spider.obj", + path + "spider.mtl", + path + "wal67ar_small.jpg", + path + "wal69ar_small.jpg", + path + "SpiderTex.jpg", + path + "drkwood2.jpg", + path + "engineflare1.jpg") } + + "nanosuit" { nanosuit(path + "nanosuit/nanosuit.obj", + path + "nanosuit/nanosuit.mtl", + path + "nanosuit/arm_showroom_ddn.png", + path + "nanosuit/arm_showroom_refl.png", + path + "nanosuit/arm_dif.png", + path + "nanosuit/arm_showroom_spec.png", + path + "nanosuit/body_dif.png", + path + "nanosuit/body_showroom_ddn.png", + path + "nanosuit/body_showroom_refl.png", + path + "nanosuit/body_showroom_spec.png", + path + "nanosuit/glass_ddn.png", + path + "nanosuit/glass_refl.png", + path + "nanosuit/glass_dif.png", + path + "nanosuit/hand_showroom_ddn.png", + path + "nanosuit/hand_showroom_refl.png", + path + "nanosuit/hand_dif.png", + path + "nanosuit/hand_showroom_spec.png", + path + "nanosuit/helmet_showroom_ddn.png", + path + "nanosuit/helmet_showroom_refl.png", + path + "nanosuit/helmet_diff.png", + path + "nanosuit/helmet_showroom_spec.png", + path + "nanosuit/leg_showroom_ddn.png", + path + "nanosuit/leg_showroom_refl.png", + path + "nanosuit/leg_dif.png", + path + "nanosuit/leg_showroom_spec.png") } + + "shelter" { shelter(path + "statie B01.obj",path + "statie B01.mtl")} } } \ No newline at end of file diff --git a/src/test/kotlin/assimp/obj/shelter.kt b/src/test/kotlin/assimp/obj/shelter.kt index fd2624e..0fdcaaa 100644 --- a/src/test/kotlin/assimp/obj/shelter.kt +++ b/src/test/kotlin/assimp/obj/shelter.kt @@ -4,8 +4,6 @@ import assimp.* import glm_.mat4x4.Mat4 import glm_.vec3.Vec3 import io.kotlintest.shouldBe -import java.io.File -import java.net.URI /** * Created by Sunny on 19/01/2018. @@ -13,9 +11,9 @@ import java.net.URI object shelter { - operator fun invoke(fileName: String) { + operator fun invoke(fileName: String, matName: String) { - Importer().testFile(getResource(fileName)) { + Importer().testURLs(getResource(fileName), getResource(matName)) { with(rootNode) { diff --git a/src/test/kotlin/assimp/obj/spider.kt b/src/test/kotlin/assimp/obj/spider.kt index f5c4f84..5102c0c 100644 --- a/src/test/kotlin/assimp/obj/spider.kt +++ b/src/test/kotlin/assimp/obj/spider.kt @@ -8,9 +8,10 @@ import java.util.* object spider { - operator fun invoke(fileName: String) { + operator fun invoke(fileName: String, matName: String, img1: String, img2: String, img3: String, img4: String, img5: String) { - Importer().testFile(getResource(fileName)){ + Importer().testURLs(getResource(fileName), getResource(matName), + getResource(img1), getResource(img2), getResource(img3), getResource(img4), getResource(img5)){ with(rootNode) { @@ -199,6 +200,9 @@ object spider { } textures[0].file shouldBe ".\\engineflare1.jpg" } + + // numTextures shouldBe 5 // TODO numTextures is not set + textures.size shouldBe 5 } } } \ No newline at end of file