From 0229a3acf359ccc1b8ee597a1f92541ba38cbcbe Mon Sep 17 00:00:00 2001 From: Jesper Smith Date: Thu, 27 Jul 2017 17:42:01 -0500 Subject: [PATCH 1/9] Added supported for custom IO Systems in Java. Implemented ClassLoader IO System --- port/jassimp/jassimp-native/src/jassimp.cpp | 258 +++++++++++++++++- port/jassimp/jassimp-native/src/jassimp.h | 2 +- .../src/jassimp/AiClassLoaderIOSystem.java | 130 +++++++++ .../jassimp/src/jassimp/AiIOStream.java | 55 ++++ .../jassimp/src/jassimp/AiIOSystem.java | 54 ++++ .../src/jassimp/AiInputStreamIOStream.java | 102 +++++++ port/jassimp/jassimp/src/jassimp/Jassimp.java | 36 ++- 7 files changed, 619 insertions(+), 18 deletions(-) create mode 100644 port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiIOStream.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiIOSystem.java create mode 100644 port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 226e416aa..475f6c5a0 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -1,7 +1,9 @@ #include "jassimp.h" -#include +#include #include +#include +#include #ifdef JNI_LOG @@ -12,9 +14,11 @@ #define lprintf(...) printf (__VA_ARGS__) #endif /* ANDROID */ #else -#define lprintf +#define lprintf #endif +static std::string gLastErrorString; + // Automatically deletes a local ref when it goes out of scope class SmartLocalRef { private: @@ -270,6 +274,81 @@ static bool callv(JNIEnv *env, jobject object, const char* typeName, return true; } +static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature,/* const*/ jvalue* params) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return NULL; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return NULL; + } + + jobject jReturnValue = env->CallObjectMethodA(object, mid, params); + + return jReturnValue; +} + +static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return false; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return false; + } + + jint jReturnValue = env->CallIntMethod(object, mid); + + return (int) jReturnValue; +} + +static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, + const char* signature) +{ + jclass clazz = env->FindClass(typeName); + SmartLocalRef clazzRef(env, clazz); + + if (NULL == clazz) + { + lprintf("could not find class %s\n", typeName); + return false; + } + + jmethodID mid = env->GetMethodID(clazz, methodName, signature); + + if (NULL == mid) + { + lprintf("could not find method %s with signature %s in type %s\n", methodName, signature, typeName); + return false; + } + + jint jReturnValue = env->CallCharMethod(object, mid); + + return (int) jReturnValue; +} + static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params, jobject& returnValue) @@ -359,6 +438,155 @@ static bool copyBufferArray(JNIEnv *env, jobject jMesh, const char* jBufferName, return true; } +class JavaIOStream : public Assimp::IOStream +{ +private: + size_t pos; + size_t size; + char* buffer; + jobject jIOStream; + + +public: + JavaIOStream(size_t size, char* buffer, jobject jIOStream) : + pos(0), + size(size), + buffer(buffer), + jIOStream(jIOStream) + {}; + + + ~JavaIOStream(void) + { + free(buffer); + }; + + size_t Read(void* pvBuffer, size_t pSize, size_t pCount) + { + const size_t cnt = std::min(pCount,(size - pos)/pSize); + const size_t ofs = pSize*cnt; + + memcpy(pvBuffer, buffer + pos, ofs); + pos += ofs; + + return cnt; + }; + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) {}; + + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) + { + if (aiOrigin_SET == pOrigin) { + if (pOffset >= size) { + return AI_FAILURE; + } + pos = pOffset; + } + else if (aiOrigin_END == pOrigin) { + if (pOffset >= size) { + return AI_FAILURE; + } + pos = size-pOffset; + } + else { + if (pOffset + pos >= size) { + return AI_FAILURE; + } + pos += pOffset; + } + return AI_SUCCESS; + }; + + size_t Tell(void) const + { + return pos; + }; + + size_t FileSize() const + { + return size; + }; + + void Flush() {}; + + + jobject javaObject() + { + return jIOStream; + }; + + +}; + + +class JavaIOSystem : public Assimp::IOSystem { + private: + JNIEnv* mJniEnv; + jobject& mJavaIOSystem; + + public: + JavaIOSystem(JNIEnv* env, jobject& javaIOSystem) : + mJniEnv(env), + mJavaIOSystem(javaIOSystem) + {}; + + bool Exists( const char* pFile) const + { + jvalue params[1]; + params[0].l = mJniEnv->NewStringUTF(pFile); + return call(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "exists", "(Ljava/lang/String;)Z", params); + + }; + char getOsSeparator() const + { + return (char) callc(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "getOsSeparator", "()C"); + }; + + Assimp::IOStream* Open(const char* pFile,const char* pMode = "rb") + { + jvalue params[2]; + params[0].l = mJniEnv->NewStringUTF(pFile); + params[1].l = mJniEnv->NewStringUTF(pMode); + + + jobject jStream = callo(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "open", "(Ljava/lang/String;Ljava/lang/String;)Ljassimp/AiIOStream;", params); + if(NULL == jStream) + { + lprintf("NULL object from AiIOSystem.open\n"); + return NULL; + } + + size_t size = calli(mJniEnv, jStream, "jassimp/AiIOStream", "getFileSize", "()I"); + lprintf("Model file size is %d\n", size); + + char* buffer = (char*)malloc(size); + jobject javaBuffer = mJniEnv->NewDirectByteBuffer(buffer, size); + + jvalue readParams[1]; + readParams[0].l = javaBuffer; + if(call(mJniEnv, jStream, "jassimp/AiIOStream", "read", "(Ljava/nio/ByteBuffer;)Z", readParams)) + { + return new JavaIOStream(size, buffer, jStream); + } + else + { + lprintf("Read failure on AiIOStream.read"); + free(buffer); + return NULL; + } + + }; + void Close( Assimp::IOStream* pFile) + { + + jvalue params[1]; + params[0].l = ((JavaIOStream*) pFile)->javaObject(); + callv(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "close", "(Ljassimp/AiIOStream;)V", params); + delete pFile; + }; + + + +}; static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) @@ -1474,7 +1702,7 @@ JNIEXPORT jint JNICALL Java_jassimp_Jassimp_getlongsize JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString (JNIEnv *env, jclass jClazz) { - const char *err = aiGetErrorString(); + const char *err = gLastErrorString.c_str(); if (NULL == err) { @@ -1486,18 +1714,26 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile - (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess) + (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess, jobject ioSystem) { jobject jScene = NULL; /* convert params */ const char* cFilename = env->GetStringUTFChars(jFilename, NULL); + + Assimp::Importer imp; - + + if(ioSystem != NULL) + { + imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); + lprintf("Created aiFileIO\n"); + } + lprintf("opening file: %s\n", cFilename); /* do import */ - const aiScene *cScene = aiImportFile(cFilename, (unsigned int) postProcess); + const aiScene *cScene = imp.ReadFile(cFilename, (unsigned int) postProcess); if (!cScene) { @@ -1552,19 +1788,13 @@ error: /* thats really a problem because we cannot throw in this case */ env->FatalError("could not throw java.io.IOException"); } - - env->ThrowNew(exception, aiGetErrorString()); + gLastErrorString = imp.GetErrorString(); + env->ThrowNew(exception, gLastErrorString.c_str()); lprintf("problem detected\n"); } end: - /* - * NOTE: this releases all memory used in the native domain. - * Ensure all data has been passed to java before! - */ - aiReleaseImport(cScene); - /* free params */ env->ReleaseStringUTFChars(jFilename, cFilename); diff --git a/port/jassimp/jassimp-native/src/jassimp.h b/port/jassimp/jassimp-native/src/jassimp.h index f448dc2c2..7d4b66e29 100644 --- a/port/jassimp/jassimp-native/src/jassimp.h +++ b/port/jassimp/jassimp-native/src/jassimp.h @@ -39,7 +39,7 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString * Signature: (Ljava/lang/String;J)Ljassimp/AiScene; */ JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile - (JNIEnv *, jclass, jstring, jlong); + (JNIEnv *, jclass, jstring, jlong, jobject); #ifdef __cplusplus } diff --git a/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java b/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java new file mode 100644 index 000000000..04d638610 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiClassLoaderIOSystem.java @@ -0,0 +1,130 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +/** + * IOSystem based on the Java classloader.

+ * + * This IOSystem allows loading models directly from the + * classpath. No extraction to the file system is + * necessary. + * + * @author Jesper Smith + * + */ +public class AiClassLoaderIOSystem implements AiIOSystem +{ + private final Class clazz; + private final ClassLoader classLoader; + + /** + * Construct a new AiClassLoaderIOSystem.

+ * + * This constructor uses a ClassLoader to resolve + * resources. + * + * @param classLoader classLoader to resolve resources. + */ + public AiClassLoaderIOSystem(ClassLoader classLoader) { + this.clazz = null; + this.classLoader = classLoader; + } + + /** + * Construct a new AiClassLoaderIOSystem.

+ * + * This constructor uses a Class to resolve + * resources. + * + * @param class class to resolve resources. + */ + public AiClassLoaderIOSystem(Class clazz) { + this.clazz = clazz; + this.classLoader = null; + } + + + @Override + public AiInputStreamIOStream open(String filename, String ioMode) { + try { + + InputStream is; + + if(clazz != null) { + is = clazz.getResourceAsStream(filename); + } + else if (classLoader != null) { + is = classLoader.getResourceAsStream(filename); + } + else { + System.err.println("[" + getClass().getSimpleName() + + "] No class or classLoader provided to resolve " + filename); + return null; + } + + if(is != null) { + return new AiInputStreamIOStream(is); + } + else { + System.err.println("[" + getClass().getSimpleName() + + "] Cannot find " + filename); + return null; + } + } + catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void close(AiInputStreamIOStream file) { + } + + @Override + public boolean exists(String path) + { + URL url = null; + if(clazz != null) { + url = clazz.getResource(path); + } + else if (classLoader != null) { + url = classLoader.getResource(path); + } + + + if(url == null) + { + return false; + } + else + { + return true; + } + + } + + @Override + public char getOsSeparator() + { + return '/'; + } + +} diff --git a/port/jassimp/jassimp/src/jassimp/AiIOStream.java b/port/jassimp/jassimp/src/jassimp/AiIOStream.java new file mode 100644 index 000000000..5378da5f8 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiIOStream.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.nio.ByteBuffer; + + +/** + * Interface to allow custom resource loaders for jassimp.

+ * + * The design is based on passing the file wholly in memory, + * because Java inputstreams do not have to support seek.

+ * + * Writing files from Java is unsupported. + * + * + * @author Jesper Smith + * + */ +public interface AiIOStream +{ + + /** + * Read all data into buffer.

+ * + * The whole stream should be read into the buffer. + * No support is provided for partial reads. + * + * @param buffer Target buffer for the model data + * + * @return true if successful, false if an error occurred. + */ + boolean read(ByteBuffer buffer); + + /** + * The total size of this stream.

+ * + * @return total size of this stream + */ + int getFileSize(); + +} diff --git a/port/jassimp/jassimp/src/jassimp/AiIOSystem.java b/port/jassimp/jassimp/src/jassimp/AiIOSystem.java new file mode 100644 index 000000000..d2c741529 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiIOSystem.java @@ -0,0 +1,54 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +public interface AiIOSystem +{ + /** + * + * Open a new file with a given path. + * When the access to the file is finished, call close() to release all associated resources + * + * @param path Path to the file + * @param ioMode file I/O mode. Required are: "wb", "w", "wt", "rb", "r", "rt". + * + * @return AiIOStream or null if an error occurred + */ + public T open(String path, String ioMode); + + + /** + * Tests for the existence of a file at the given path. + * + * @param path path to the file + * @return true if there is a file with this path, else false. + */ + public boolean exists(String path); + + /** + * Returns the system specific directory separator.

+ * + * @return System specific directory separator + */ + public char getOsSeparator(); + + /** + * Closes the given file and releases all resources associated with it. + * + * @param file The file instance previously created by Open(). + */ + public void close(T file); +} diff --git a/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java b/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java new file mode 100644 index 000000000..998401b68 --- /dev/null +++ b/port/jassimp/jassimp/src/jassimp/AiInputStreamIOStream.java @@ -0,0 +1,102 @@ +/* + * Copyright 2017 Florida Institute for Human and Machine Cognition (IHMC) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jassimp; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URL; +import java.nio.ByteBuffer; + + +/** + * Implementation of AiIOStream reading from a InputStream + * + * @author Jesper Smith + * + */ +public class AiInputStreamIOStream implements AiIOStream +{ + private final ByteArrayOutputStream os = new ByteArrayOutputStream(); + + + public AiInputStreamIOStream(URI uri) throws IOException { + this(uri.toURL()); + } + + public AiInputStreamIOStream(URL url) throws IOException { + this(url.openStream()); + } + + public AiInputStreamIOStream(InputStream is) throws IOException { + int read; + byte[] data = new byte[1024]; + while((read = is.read(data, 0, data.length)) != -1) { + os.write(data, 0, read); + } + os.flush(); + + is.close(); + } + + @Override + public int getFileSize() { + return os.size(); + } + + @Override + public boolean read(ByteBuffer buffer) { + ByteBufferOutputStream bos = new ByteBufferOutputStream(buffer); + try + { + os.writeTo(bos); + } + catch (IOException e) + { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * Internal helper class to copy the contents of an OutputStream + * into a ByteBuffer. This avoids a copy. + * + */ + private static class ByteBufferOutputStream extends OutputStream { + + private final ByteBuffer buffer; + + public ByteBufferOutputStream(ByteBuffer buffer) { + this.buffer = buffer; + } + + @Override + public void write(int b) throws IOException + { + buffer.put((byte) b); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + buffer.put(b, off, len); + } + } +} + diff --git a/port/jassimp/jassimp/src/jassimp/Jassimp.java b/port/jassimp/jassimp/src/jassimp/Jassimp.java index 92f4864c7..d1b4aae4e 100644 --- a/port/jassimp/jassimp/src/jassimp/Jassimp.java +++ b/port/jassimp/jassimp/src/jassimp/Jassimp.java @@ -79,6 +79,20 @@ public final class Jassimp { return importFile(filename, EnumSet.noneOf(AiPostProcessSteps.class)); } + /** + * Imports a file via assimp without post processing. + * + * @param filename the file to import + * @param ioSystem ioSystem to load files, or null for default + * @return the loaded scene + * @throws IOException if an error occurs + */ + public static AiScene importFile(String filename, AiIOSystem ioSystem) + throws IOException { + + return importFile(filename, EnumSet.noneOf(AiPostProcessSteps.class), ioSystem); + } + /** * Imports a file via assimp. @@ -89,12 +103,28 @@ public final class Jassimp { * @throws IOException if an error occurs */ public static AiScene importFile(String filename, - Set postProcessing) throws IOException { + Set postProcessing) + throws IOException { + return importFile(filename, postProcessing, null); + } + + /** + * Imports a file via assimp. + * + * @param filename the file to import + * @param postProcessing post processing flags + * @param ioSystem ioSystem to load files, or null for default + * @return the loaded scene, or null if an error occurred + * @throws IOException if an error occurs + */ + public static AiScene importFile(String filename, + Set postProcessing, AiIOSystem ioSystem) + throws IOException { loadLibrary(); return aiImportFile(filename, AiPostProcessSteps.toRawValue( - postProcessing)); + postProcessing), ioSystem); } @@ -310,7 +340,7 @@ public final class Jassimp { * @throws IOException if an error occurs */ private static native AiScene aiImportFile(String filename, - long postProcessing) throws IOException; + long postProcessing, AiIOSystem ioSystem) throws IOException; /** From a7c1dde56e3ae4af5eaef801e938ee9e2265ecfb Mon Sep 17 00:00:00 2001 From: Jesper Smith Date: Fri, 28 Jul 2017 15:08:59 -0500 Subject: [PATCH 2/9] Added return statement to Write --- port/jassimp/jassimp-native/src/jassimp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 475f6c5a0..75b1bc510 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -471,7 +471,10 @@ public: return cnt; }; - size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) {}; + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) + { + return 0; + }; aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { From e662f2dc6f5e073a8ae31e9d9b68d4b41267cdee Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 13 Oct 2017 22:41:38 +0200 Subject: [PATCH 3/9] Blender: fix short overflow. --- code/BlenderDNA.inl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/BlenderDNA.inl b/code/BlenderDNA.inl index 6919c9153..8b669180c 100644 --- a/code/BlenderDNA.inl +++ b/code/BlenderDNA.inl @@ -589,7 +589,10 @@ template <> inline void Structure :: Convert (short& dest,const FileData { // automatic rescaling from short to float and vice versa (seems to be used by normals) if (name == "float") { - dest = static_cast(db.reader->GetF4() * 32767.f); + float f = db.reader->GetF4(); + if ( f > 1.0f ) + f = 1.0f; + dest = static_cast( f * 32767.f); //db.reader->IncPtr(-4); return; } From f56e28ea3c55bbc74d0e9a808104bda9b39b2bbc Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 14 Oct 2017 16:40:39 +0200 Subject: [PATCH 4/9] UnitTest: use correct initialized normals in unittest. --- test/unit/utPretransformVertices.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test/unit/utPretransformVertices.cpp b/test/unit/utPretransformVertices.cpp index 9f40f3ddc..437cbdba5 100644 --- a/test/unit/utPretransformVertices.cpp +++ b/test/unit/utPretransformVertices.cpp @@ -90,28 +90,36 @@ void PretransformVerticesTest::SetUp() // add 5 empty materials scene->mMaterials = new aiMaterial*[scene->mNumMaterials = 5]; - for (unsigned int i = 0; i < 5;++i) + for (unsigned int i = 0; i < 5;++i) { scene->mMaterials[i] = new aiMaterial(); + } // add 25 test meshes scene->mMeshes = new aiMesh*[scene->mNumMeshes = 25]; - for (unsigned int i = 0; i < 25;++i) { - aiMesh* mesh = scene->mMeshes[i] = new aiMesh(); + for ( unsigned int i = 0; i < 25; ++i) { + aiMesh* mesh = scene->mMeshes[ i ] = new aiMesh(); mesh->mPrimitiveTypes = aiPrimitiveType_POINT; mesh->mFaces = new aiFace[ mesh->mNumFaces = 10+i ]; mesh->mVertices = new aiVector3D[mesh->mNumVertices = mesh->mNumFaces]; for (unsigned int a = 0; a < mesh->mNumFaces; ++a ) { - aiFace& f = mesh->mFaces[a]; - f.mIndices = new unsigned int [f.mNumIndices = 1]; + aiFace& f = mesh->mFaces[ a ]; + f.mIndices = new unsigned int [ f.mNumIndices = 1 ]; f.mIndices[0] = a*3; mesh->mVertices[a] = aiVector3D((float)i,(float)a,0.f); } mesh->mMaterialIndex = i%5; - if (i % 2) + if (i % 2) { mesh->mNormals = new aiVector3D[mesh->mNumVertices]; + for ( unsigned int normalIdx=0; normalIdxmNumVertices; ++normalIdx ) { + mesh->mNormals[ normalIdx ].x = 1.0f; + mesh->mNormals[ normalIdx ].y = 1.0f; + mesh->mNormals[ normalIdx ].z = 1.0f; + mesh->mNormals[ normalIdx ].Normalize(); + } + } } // construct some nodes (1+25) From 6efe4e2841d710a7b3db44a9264fc8c2f52f1990 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 17:13:08 +0300 Subject: [PATCH 5/9] CMake: Add support for Undefined Behavior sanitizer --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c30278b7f..bc3731bbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,10 @@ OPTION ( ASSIMP_ASAN "Enable AddressSanitizer." OFF ) +OPTION ( ASSIMP_UBSAN + "Enable Undefined Behavior sanitizer." + OFF +) OPTION ( SYSTEM_IRRXML "Use system installed Irrlicht/IrrXML library." OFF @@ -234,6 +238,12 @@ if (ASSIMP_ASAN) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") endif() +if (ASSIMP_UBSAN) + MESSAGE(STATUS "Undefined Behavior sanitizer enabled") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") +endif() + INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) From 7b73fe8b026ec2dd55fb88a7fe8eccd971f0aad5 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 17:47:06 +0300 Subject: [PATCH 6/9] Travis: Add Clang UBSan build configuration --- .travis.sh | 4 ++++ .travis.yml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.travis.sh b/.travis.sh index f4ef271e4..9786c5321 100755 --- a/.travis.sh +++ b/.travis.sh @@ -26,6 +26,10 @@ function generate() OPTIONS="$OPTIONS -DASSIMP_ASAN=OFF" fi + if [ "$UBSAN" = "ON" ] ; then + OPTIONS="$OPTIONS -DASSIMP_UBSAN=ON" + fi + cmake -G "Unix Makefiles" $OPTIONS } diff --git a/.travis.yml b/.travis.yml index 3ffa63176..fa37e5955 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,9 @@ matrix: - os: linux compiler: clang env: ASAN=ON + - os: linux + compiler: clang + env: UBSAN=ON - os: linux compiler: clang env: SHARED_BUILD=ON From 7cbb5f4d3b7772861044876d570d142f9fc510cc Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 18:36:09 +0300 Subject: [PATCH 7/9] B3DImporter: Replace bad pointer casting with memcpy --- code/B3DImporter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index d15676128..bc888fb66 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -171,7 +171,8 @@ int B3DImporter::ReadByte(){ // ------------------------------------------------------------------------------------------------ int B3DImporter::ReadInt(){ if( _pos+4<=_buf.size() ){ - int n=*(int*)&_buf[_pos]; + int n; + memcpy(&n, &_buf[_pos], 4); _pos+=4; return n; } @@ -182,7 +183,8 @@ int B3DImporter::ReadInt(){ // ------------------------------------------------------------------------------------------------ float B3DImporter::ReadFloat(){ if( _pos+4<=_buf.size() ){ - float n=*(float*)&_buf[_pos]; + float n; + memcpy(&n, &_buf[_pos], 4); _pos+=4; return n; } From 9a6b141568c33c01afd9aeac0a283a72dffcf496 Mon Sep 17 00:00:00 2001 From: Turo Lamminen Date: Sat, 7 Oct 2017 20:40:35 +0300 Subject: [PATCH 8/9] FBX: Replace bad pointer casting with memcpy --- code/FBXBinaryTokenizer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/FBXBinaryTokenizer.cpp b/code/FBXBinaryTokenizer.cpp index ede32d7b7..9ae38386b 100644 --- a/code/FBXBinaryTokenizer.cpp +++ b/code/FBXBinaryTokenizer.cpp @@ -151,7 +151,8 @@ uint32_t ReadWord(const char* input, const char*& cursor, const char* end) TokenizeError("cannot ReadWord, out of bounds",input, cursor); } - uint32_t word = *reinterpret_cast(cursor); + uint32_t word; + memcpy(&word, cursor, 4); AI_SWAP4(word); cursor += k_to_read; From f8c40022941178a58deb8161eef493e98fcb6bed Mon Sep 17 00:00:00 2001 From: Jared Mulconry Date: Sat, 14 Oct 2017 22:45:00 +1100 Subject: [PATCH 9/9] Fixed a divide by zero error in IFCBoolean that was latent, but nevertheless a bug --- code/IFCBoolean.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/IFCBoolean.cpp b/code/IFCBoolean.cpp index 8571a3c79..d250fbe36 100644 --- a/code/IFCBoolean.cpp +++ b/code/IFCBoolean.cpp @@ -272,7 +272,6 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const const IfcVector3& b0 = boundary[i]; const IfcVector3& b1 = boundary[(i + 1) % bcount]; IfcVector3 b = b1 - b0; - IfcFloat b_sqlen_inv = 1.0 / b.SquareLength(); // segment-segment intersection // solve b0 + b*s = e0 + e*t for (s,t) @@ -281,6 +280,7 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const // no solutions (parallel lines) continue; } + IfcFloat b_sqlen_inv = 1.0 / b.SquareLength(); const IfcFloat x = b0.x - e0.x; const IfcFloat y = b0.y - e0.y;