0

I'm writing a Java web service where it is possible to upload a 3D object, operate on it and store it.

What I'm trying to do is creating an AIScene instance using a byte[] as an input parameter which is the file itself (it's content).

I have found no way to do this in the docs, all import methods require a path.

Right now I'm taking a look at both the lwjgl java version of Assimp as well as the C++ version. It doesn't matter which one is used to solve the issue.

Edit: the code I'm trying to get done:

@Override
public String uploadFile(MultipartFile file) {
    AIFileIO fileIo = AIFileIO.create();
    AIFileOpenProcI fileOpenProc = new AIFileOpenProc() {
        public long invoke(long pFileIO, long fileName, long openMode) {
            AIFile aiFile = AIFile.create();
            final ByteBuffer data;

            try {
                data = ByteBuffer.wrap(file.getBytes());
            } catch (IOException e) {
                throw new RuntimeException();
            }

            AIFileReadProcI fileReadProc = new AIFileReadProc() {
                public long invoke(long pFile, long pBuffer, long size, long count) {
                    long max = Math.min(data.remaining(), size * count);
                    memCopy(memAddress(data) + data.position(), pBuffer, max);
                    return max;
                }
            };
            AIFileSeekI fileSeekProc = new AIFileSeek() {
                public int invoke(long pFile, long offset, int origin) {
                    if (origin == Assimp.aiOrigin_CUR) {
                        data.position(data.position() + (int) offset);
                    } else if (origin == Assimp.aiOrigin_SET) {
                        data.position((int) offset);
                    } else if (origin == Assimp.aiOrigin_END) {
                        data.position(data.limit() + (int) offset);
                    }
                    return 0;
                }
            };
            AIFileTellProcI fileTellProc = new AIFileTellProc() {
                public long invoke(long pFile) {
                    return data.limit();
                }
            };
            aiFile.ReadProc(fileReadProc);
            aiFile.SeekProc(fileSeekProc);
            aiFile.FileSizeProc(fileTellProc);
            return aiFile.address();
        }
    };
    AIFileCloseProcI fileCloseProc = new AIFileCloseProc() {
        public void invoke(long pFileIO, long pFile) {
            /* Nothing to do */
        }
    };
    fileIo.set(fileOpenProc, fileCloseProc, NULL);
    AIScene scene = aiImportFileEx(file.getName(),
            aiProcess_JoinIdenticalVertices | aiProcess_Triangulate, fileIo); // ISSUE HERE. file.getName() is not a path, just a name. so is getOriginalName() in my case. 

    try{
        Long id = scene.mMeshes().get(0);
        AIMesh mesh = AIMesh.create(id);
        AIVector3D vertex = mesh.mVertices().get(0);

        return mesh.mName().toString() + ": " + (vertex.x() + " " + vertex.y() + " " + vertex.z());
    }catch(Exception e){
        e.printStackTrace();
    }
    return "fail";
}

When debugging the method I get an access violation in the method that binds to the native:

public static long naiImportFileEx(long pFile, int pFlags, long pFS)

this is the message:

#

A fatal error has been detected by the Java Runtime Environment:

#

EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000007400125d, pid=6400, tid=0x0000000000003058

#

JRE version: Java(TM) SE Runtime Environment (8.0_201-b09) (build 1.8.0_201-b09)

Java VM: Java HotSpot(TM) 64-Bit Server VM (25.201-b09 mixed mode windows-amd64 compressed oops)

Problematic frame:

V [jvm.dll+0x1e125d]

#

Failed to write core dump. Minidumps are not enabled by default on client versions of Windows

#

An error report file with more information is saved as:

C:\Users\ragos\IdeaProjects\objectstore3d\hs_err_pid6400.log

#

If you would like to submit a bug report, please visit:

http://bugreport.java.com/bugreport/crash.jsp

#

agiro
  • 2,018
  • 2
  • 30
  • 62
  • Thank you for the comment but here https://github.com/assimp/assimp/blob/475e51d157e7826bdcbc0d88e6a73b1764960e0a/code/Assimp.cpp#L192 it shows that the underlying implementation uses the file's path anyway. The defined buffer is a custom IO system. As such it causes read access violation in my code because I have no valid file path coming from Spring's `MultipartFile`. Did I miss something? I'm adding my code to the question. Largely the same as the one you linked though except getting the data. – agiro Mar 24 '19 at 19:53
  • Then it really shouldn't be the problem. Could you help me on where did I mess up? I changed the code starting from my lines 8-10, because `MultipartFile` just passes the object and I already have it's content as a `byte[]`. – agiro Mar 24 '19 at 20:06
  • `public static long naiImportFileEx(long pFile, int pFlags, long pFS)` in this method I get the access violation, I add that to the question as well in a sec. – agiro Mar 24 '19 at 20:10

1 Answers1

0

It is possible if we use the aiImportFileFromMemory method.

The approach I wanted to follow was copied from a github demo and actually copies the buffer around unnecessarily.

The reason for the access violation was the use of indirect buffers (for more info why that is a problem, check this out).

The solution is not nearly as complicated as the code I initially pasted:

@Override
    public String uploadFile(MultipartFile file) throws IOException {
        ByteBuffer buffer = BufferUtils.createByteBuffer((int) file.getSize());
        buffer.put(file.getBytes());
        buffer.flip();
        AIScene scene = Assimp.aiImportFileFromMemory(buffer,aiProcess_Triangulate, (ByteBuffer) null);
        Long id = scene.mMeshes().get(0);
        AIMesh mesh = AIMesh.create(id);
        AIVector3D vertex = mesh.mVertices().get(0);
        return mesh.mName().dataString() + ": " + (vertex.x() + " " + vertex.y() + " " + vertex.z());
    }

Here I create a direct buffer with the appropriate size, load the data and flip it (this part is a must.) After that let Assimp do its magic so you get pointers to the structure. With the return statement I just check if I got the valid data.

edit

As in the comments it was pointed out, this implementation is limited to a single file upload and assumes it gets everything that is necessary from that one MultipartFile, it won't work well with referenced formats. See docs for more detail.

The demo that was linked in the question's comments which was used in the question as a base has a different use case to my original one.

agiro
  • 2,018
  • 2
  • 30
  • 62
  • Thank you for the input. The service assumed single file uploads, in my case it worked with an obj (because I had only one file, no `.mtl` and whatnot). I'll take a note in the answer in case someone stumbles upon this. – agiro Mar 25 '19 at 22:20
  • However in this case I'll think about solving the referencing issue too. This is a practice project after all. – agiro Mar 25 '19 at 22:26