Load and display obj model on the scene with FastO

2019-01-20 06:55发布

问题:

In my Unity project I would like to use the FastObjImporter class found on the internet to put obj on the scene. Do I have to create an empty GameObject and assign processed obj to it?

Try with an empty game object:

GameObject go = new GameObject("obj");
myMesh = FastObjImporter.Instance.ImportFile(objPath);
Instantiate(myMesh, Vector3.zero, Quaternion.identity);
go.AddComponent<MeshRenderer>();
go.GetComponent<MeshFilter>().mesh = myMesh;

Earlier I tried this way but it also did not work:

GameObject go;
myMesh = FastObjImporter.Instance.ImportFile(objPath);
Instantiate(myMesh, Vector3.zero, Quaternion.identity);
go.GetComponent<MeshFilter>().mesh = myMesh;

Could someone explain what I'm doing wrong?

CODE:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.IO;

/// <summary>
/// Sample
/// </summary>
public class DriveTest : MonoBehaviour
{
    //public static readonly string REQUIRED_MIME = "image/jpeg";
    //public string filePath;
    public string objPath;
    bool initInProgress = false;
    GoogleDrive drive;
    public string url;
    List<GoogleDrive.File> remoteObjFiles;

    public Mesh myMesh;
    public GameObject go;

    /* ---------------------------------------------------------------------------------------------------------------------------------- */
    void Start()
    {
        objPath = Application.persistentDataPath + "/testObj.obj";
        StartCoroutine(ImportObj());
        go = new GameObject("obj");
    }

    /* ---------------------------------------------------------------------------------------------------------------------------------- */

    void Update()
    {

        if (Input.GetKey(KeyCode.Escape))
            Application.Quit();
    }

    /* ---------------------------------------------------------------------------------------------------------------------------------- */

    IEnumerator ImportObj()
    {
        initInProgress = true;

        drive = new GoogleDrive();
        var driveInit = drive.Initialize();
        while (driveInit.MoveNext())
            yield return null;

        initInProgress = false;

        for (;;)
        {
            ( SEARCHING AND SETTING DOWNLOAD FOLDER IN GDRIVE )

            var download = drive.DownloadFile(remoteObjFiles[0]);
            yield return StartCoroutine(download);
            var data = GoogleDrive.GetResult<byte[]>(download);

            if (File.Exists(objPath))
            {
                File.Delete(objPath);
                File.WriteAllBytes(objPath, data);
            }
            else
                File.WriteAllBytes(objPath, data);

            Debug.Log("Obj: " + remoteObjFiles[0].ToString());

            if (File.Exists(objPath))
            {
                Debug.Log("Loading OBJ from the device");
                myMesh = FastObjImporter.Instance.ImportFile(objPath);
                Debug.Log("Obj imported...");
                Instantiate(myMesh, Vector3.zero, Quaternion.identity);
                go.AddComponent<MeshRenderer>();
                go.GetComponent<MeshFilter>().mesh = myMesh;
            }
            break;
        }
}

Regards

回答1:

There is no need to instantiate the Mesh. It is a component and I am not sure if you can even instantiate it. Just assign it to the material.

The main problem in your code is that you are not creating a material and a shader. You need to create a material, then create a "Standard" shader and assign that shader to the that material.

I noticed that the FastObjImporter script is old and decided to try it to make sure it is functioning properly. I ran into an index exception bug in it then fixed it. You can grab the copy of the fixed version here. See line #57 for the actual fix I made.

Functions to create MeshFilder, Material and MeshRenderer:

void attachMeshFilter(GameObject target, Mesh mesh)
{
    MeshFilter mF = target.AddComponent<MeshFilter>();
    mF.mesh = mesh;
}

Material createMaterial()
{
    Material mat = new Material(Shader.Find("Standard"));
    return mat;
}

void attachMeshRenderer(GameObject target, Material mat)
{
    MeshRenderer mR = target.AddComponent<MeshRenderer>();
    mR.material = mat;
}

Function to create new GameObject, load the model and attach every necessary components to it in order to display it:

GameObject loadAndDisplayMesh(string path)
{
    //Create new GameObject to hold it
    GameObject meshHolder = new GameObject("Loaded Mesh");

    //Load Mesh
    Mesh mesh = FastObjImporter.Instance.ImportFile(path);

    //return null;

    //Attach Mesh Filter
    attachMeshFilter(meshHolder, mesh);

    //Create Material
    Material mat = createMaterial();

    //Attach Mesh Renderer
    attachMeshRenderer(meshHolder, mat);

    return meshHolder;
}

Usage:

void Start()
{
    string objPath = Application.persistentDataPath + "/testObj.obj";

    GameObject obj = loadAndDisplayMesh(objPath);

    //Position it in front od=f the camera. Your ZOffset may be different
    Camera cam = Camera.main;
    float zOffset = 40f;
    obj.transform.position = cam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, cam.nearClipPlane + zOffset));
}

EDIT:

In some rare cases, Unity cannot find the shader on Android when Shader.Find("Standard") is used. You get the error when that happens.

A work around for this would be to create a Material in the Editor and assign the "Standard" shader to it. It should use "Standard" shader by default.

1.Create a material and name is "StandardMat".

2.Put that material inside a folder called "Resources". Create it if it doesn't exist. You must spell this correctly. Again, the folder must be named "Resources" and placed inside the "Asset" folder.

3.You can load the material with Resources.Load("StandardMat") as Material.

In the createMaterial function above,

Change

Material mat = new Material(Shader.Find("Standard"));

to

Material mat = Resources.Load("StandardMat") as Material;