Load and display obj model on the scene with FastObjImporter - c#

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

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;

Related

Showing picture taken in Quad not working (Unity/Hololens 2)

I'm following this tutorial to take a picture using the Hololens 2 camera and show it using a billboard (Quad object). The code I'm using is the following:
using UnityEngine;
using System.Collections;
using System.Linq;
using UnityEngine.Windows.WebCam;
public class PhotoCaptureExample : MonoBehaviour
{
PhotoCapture photoCaptureObject = null;
Texture2D targetTexture = null;
// Use this for initialization
void Start()
{
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
// Create a PhotoCapture object
PhotoCapture.CreateAsync(false, delegate(PhotoCapture captureObject) {
photoCaptureObject = captureObject;
CameraParameters cameraParameters = new CameraParameters();
cameraParameters.hologramOpacity = 0.0f;
cameraParameters.cameraResolutionWidth = cameraResolution.width;
cameraParameters.cameraResolutionHeight = cameraResolution.height;
cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;
// Activate the camera
photoCaptureObject.StartPhotoModeAsync(cameraParameters, delegate(PhotoCapture.PhotoCaptureResult result) {
// Take a picture
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
});
});
}
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
// Copy the raw image data into our target texture
photoCaptureFrame.UploadImageDataToTexture(targetTexture);
// Create a gameobject that we can apply our texture to
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
Renderer quadRenderer = quad.GetComponent<Renderer>() as Renderer;
quadRenderer.material = new Material(Shader.Find("Unlit/Texture"));
quad.transform.parent = this.transform;
quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);
quadRenderer.material.SetTexture("_MainTex", targetTexture);
// Deactivate our camera
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
// Shutdown our photo capture resource
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
}
I've added some log messages that I can see on the scene as well when I deploy the app. I've managed to get the picture and save it in a file in other script, so I know for a fact that the code is working. However, the Quad object added to the scene is blank.
I'm running this in an empty scene which runs the code shown above. The GameObject1 is a GameObject with the script attached, and debugText is my TextMeshPro element to show debugging messages.
I've tried this tutorial as well but with the same result. I'm using Unity 2019.4.26f1 (I cannot change it because of the project requirements) and Visual Studio 2019.

How can I get images from "assets/resource/mat "?

I have some images in "assets/resource/mat" . I want to get this images and put them to array . But when I try to get this images I'm getting ArrayIndexOutOfBoundsException . I think that there is problem with Resource.LoadAll("mat") method . But I can't fix it . Please help me
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class test : MonoBehaviour
{
private string t;
public Sprite[] Icons;
void Start()
{
Object[] loadedIcons = Resources.LoadAll("mat");
Icons = new Sprite[loadedIcons.Length];
for (int x = 0; x < loadedIcons.Length; x++)
{
Icons[x] = (Sprite)loadedIcons[x];
Debug.Log("Loading....");
}
GameObject sp = new GameObject();
sp.GetComponent<SpriteRenderer>().sprite = Icons[0];
}
}
Loading all images from a particular folder, with LINQ, would look like this...
using UnityEngine;
using System.Linq;
public class Four : MonoBehaviour
{
public Sprite[] icons;
void Start()
{
icons= Resources.LoadAll("met", typeof(Sprite)).Cast<Sprite>().ToArray();
}
}
I'm not sure but I guess you instead of Sprite first have to load a Texture2D and then create a Sprite from it.
Also note you used GetComponent<SpriteRenderer>() on a newly created empty GameObject so obviously there will never be a SpriteRenderer component attached. Instead use AddComponent.
Sprite[] Icons;
Texture2D LoadedTextures;
private void Start()
{
LoadedTextures = (Texture2D[])Resources.LoadAll("mat", typeof(Texture2D));
Icons = new Sprite[loadedIcons.Length];
for (int x = 0; x < loadedIcons.Length; x++)
{
Icons[x] = Sprite.Create(
LoadedTextures[x],
new Rect(0.0f, 0.0f, LoadedTextures[x].width, LoadedTextures[x].height),
new Vector2(0.5f, 0.5f),
100.0f);
}
GameObject sp = new GameObject();
// Note that here you created a new empty GameObject
// so it obviously won't have any component of type SpriteRenderer
// so add it instead of GetComponent
sp.AddComponent<SpriteRenderer>().sprite = Icons[0];
}

How to create object as parent of Canvas

I am attempting to create a new element for my unity project but I need unity to render my whole object as a UI object how do I make this happen. My current script thus far.
using UnityEditor;
using UnityEngine;
public class OptionSwitch : MonoBehaviour {
[MenuItem ("GameObject/UI/Switch")]
static void Switch(){
GameObject go = new GameObject("switch");
go.transform.parent = Selection.activeTransform;
go.transform.localPosition = Vector3.zero;
}
}
It will be a option switch with a button on either side of a textbox indicating the active option.
I did more research and came up with this code.
using UnityEditor;
using UnityEngine;
public class OptionSwitch : MonoBehaviour {
[MenuItem ("GameObject/UI/Switch")]
static void Switch(){
if (GameObject.Find ("Canvas") != null) {
// Define Components
GameObject Canvas = GameObject.Find("Canvas");
GameObject Switch = new GameObject("Switch");
GameObject Previous = new GameObject("Previous Button");
GameObject Next = new GameObject("Next Button");
GameObject Text = new GameObject("Textbox");
// Place Switch
Switch.transform.parent = Canvas.transform;
Switch.transform.localPosition = Vector3.zero;
// Place Previous Button
Previous.transform.parent = Switch.transform;
Previous.transform.localPosition = Vector3.zero;
// Place Text Field
Text.transform.parent = Switch.transform;
Text.transform.localPosition = Vector3.zero;
// Place Next Field
Next.transform.parent = Switch.transform;
Next.transform.localPosition = Vector3.zero;
}
}
}
You have have to find the Canvas GameObject first. The Canvas GameObject has Canvas component attached to it. So, you use FindObjectOfType to find the Canvas component. You can then then convert the instance of that Canvas script into a GameObject. The fix for your original code:
[MenuItem("GameObject/UI/Switch")]
static void Switch()
{
//Create new GameObject
GameObject go = new GameObject("switch");
//Find Canvas in the Scene
Canvas canvas = (Canvas)GameObject.FindObjectOfType(typeof(Canvas));
//Get Canvas GameObject
GameObject canvasGameObject = canvas.gameObject;
//Make the new GameObject child of the Canvas
go.transform.parent = canvasGameObject.transform;
go.transform.localPosition = Vector3.zero;
//Change the new GameObject Layer to UI
go.layer = 5; //Or go.layer = canvasGameObject.layer;
//Add Rect Transform to it
go.AddComponent<RectTransform>();
}

Particles to create star field in unity

I have a issue with a script. I am trying to create a star field randomly in a sphere for my unity scene. But I am new to unity and c# so I am a bit confused.
The stars have a fixed place so they should not move and so are created in Start(); and then are drawn in Update();
The problem is I get this error:
MissingComponentException: There is no 'ParticleSystem' attached to the "StarField" game object, but a script is trying to access it.
You probably need to add a ParticleSystem to the game object "StarField". Or your script needs to check if the component is attached before using it.
Stars.Update () (at Assets/Stars.cs:31)
If i add a particle system component manually it causes a load of big flashing orange spots, which i don't want, so i want to add the component in the script some how.
This is my script attached to an empty game object:
using UnityEngine;
using System.Collections;
public class Stars : MonoBehaviour {
public int maxStars = 1000;
public int universeSize = 10;
private ParticleSystem.Particle[] points;
private void Create(){
points = new ParticleSystem.Particle[maxStars];
for (int i = 0; i < maxStars; i++) {
points[i].position = Random.insideUnitSphere * universeSize;
points[i].startSize = Random.Range (0.05f, 0.05f);
points[i].startColor = new Color (1, 1, 1, 1);
}
}
void Start() {
Create ();
}
// Update is called once per frame
void Update () {
if (points != null) {
GetComponent<ParticleSystem>().SetParticles (points, points.Length);
}
}
}
How can i set it to get a static star field, because adding a particle system component manually gives me these annoying orange particles and am wanting to do it purely via scripts.
It will be easier for you if you add the particle system manually and change the settings so that you don't see any funny shapes at runtime or in the Editor.
As a side note, you don't need to set the particles every frame in Update. Even if you did, calling GetComponent is expensive, so you should save the ParticleSystem as a field of the class in the Start() method.
Here is some modified code that worked for me:
using UnityEngine;
public class Starfield : MonoBehaviour
{
public int maxStars = 1000;
public int universeSize = 10;
private ParticleSystem.Particle[] points;
private ParticleSystem particleSystem;
private void Create()
{
points = new ParticleSystem.Particle[maxStars];
for (int i = 0; i < maxStars; i++)
{
points[i].position = Random.insideUnitSphere * universeSize;
points[i].startSize = Random.Range(0.05f, 0.05f);
points[i].startColor = new Color(1, 1, 1, 1);
}
particleSystem = gameObject.GetComponent<ParticleSystem>();
particleSystem.SetParticles(points, points.Length);
}
void Start()
{
Create();
}
void Update()
{
//You can access the particleSystem here if you wish
}
}
Here is a screenshot of the starfield with the settings used in the particle system. Note that I switched off looping and play on awake.

How to edit mesh/vertices in Unity

I would like to edit 1 vertex on a cube, but I don't know how to. I've tried looking everywhere for this function, but I can't find a solution.
Here is an image of what I want to achieve:
http://answers.unity3d.com/questions/14567/editing-mesh-vertices-in-unity.html
This code is not mine. Bellow is the same code as the link above. I just separated it into two files. (one per class)
It work quite good. BUT make sure to save you scene before using it, it's a bit buggy.
Don't forget to leave edit mode when you are done whit your modification.
You don't need to add "editMesh" tag to the gameobject you are modifying, or it will be erased when you leave edit mode.
Lastly, if you modify a primitive from unity, the modification will be applied for every instance of that primitive ! (if you change a cube for a pyramid, every cube will become a pyramid)
To avoid that make a copy of your primitive mesh, change the mesh you are using in your mesh renderer by your copy and then modify it. (bellow a script to add simple copy function in unity menu)
EditMesh.cs
#if UNITY_EDITOR
using UnityEngine;
using System.Collections;
/// <summary>
/// http://answers.unity3d.com/questions/14567/editing-mesh-vertices-in-unity.html
/// </summary>
[AddComponentMenu("Mesh/Vert Handler")]
[ExecuteInEditMode]
public class EditMesh : MonoBehaviour {
public bool _destroy;
private Mesh mesh;
private Vector3[] verts;
private Vector3 vertPos;
private GameObject[] handles;
private const string TAG_HANDLE = "editMesh";
void OnEnable() {
mesh = GetComponent<MeshFilter>().sharedMesh; // sharedMesh seem equivalent to .mesh
verts = mesh.vertices;
foreach (Vector3 vert in verts) {
vertPos = transform.TransformPoint(vert);
GameObject handle = new GameObject(TAG_HANDLE);
// handle.hideFlags = HideFlags.DontSave;
handle.transform.position = vertPos;
handle.transform.parent = transform;
handle.tag = TAG_HANDLE;
handle.AddComponent<EditMeshGizmo>()._parent = this;
}
}
void OnDisable() {
GameObject[] handles = GameObject.FindGameObjectsWithTag(TAG_HANDLE);
foreach (GameObject handle in handles) {
DestroyImmediate(handle);
}
}
void Update() {
if (_destroy) {
_destroy = false;
DestroyImmediate(this);
return;
}
handles = GameObject.FindGameObjectsWithTag(TAG_HANDLE);
for (int i = 0; i < verts.Length; i++) {
verts[i] = handles[i].transform.localPosition;
}
mesh.vertices = verts;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
}
}
#endif
EditMeshGizmo.cs
#if UNITY_EDITOR
using UnityEngine;
using System.Collections;
/// <summary>
/// http://answers.unity3d.com/questions/14567/editing-mesh-vertices-in-unity.html
/// </summary>
[ExecuteInEditMode]
public class EditMeshGizmo : MonoBehaviour {
private static float CURRENT_SIZE = 0.1f;
public float _size = CURRENT_SIZE;
public EditMesh _parent;
public bool _destroy;
private float _lastKnownSize = CURRENT_SIZE;
void Update() {
// Change the size if the user requests it
if (_lastKnownSize != _size) {
_lastKnownSize = _size;
CURRENT_SIZE = _size;
}
// Ensure the rest of the gizmos know the size has changed...
if (CURRENT_SIZE != _lastKnownSize) {
_lastKnownSize = CURRENT_SIZE;
_size = _lastKnownSize;
}
if (_destroy)
DestroyImmediate(_parent);
}
void OnDrawGizmos() {
Gizmos.color = Color.red;
Gizmos.DrawCube(transform.position, Vector3.one * CURRENT_SIZE);
}
}
#endif
CopyMesh.cs (put it in a directory named "Editor") (you should then find it in your menu bar)
using UnityEditor;
using UnityEngine;
namespace Assets {
/// <summary>
///
/// </summary>
public class CopyMesh : MonoBehaviour {
[MenuItem("Assets/CopyMesh")]
static void DoCopyMesh() {
Mesh mesh = Selection.activeObject as Mesh;
Mesh newmesh = new Mesh();
newmesh.vertices = mesh.vertices;
newmesh.triangles = mesh.triangles;
newmesh.uv = mesh.uv;
newmesh.normals = mesh.normals;
newmesh.colors = mesh.colors;
newmesh.tangents = mesh.tangents;
AssetDatabase.CreateAsset(newmesh, AssetDatabase.GetAssetPath(mesh) + " copy.asset");
}
[MenuItem("Assets/CopyMeshGameObject")]
static void DoCopyMeshGameObject() {
Mesh mesh = (Selection.activeGameObject.GetComponent<MeshFilter>()).sharedMesh;
Mesh newmesh = new Mesh();
newmesh.vertices = mesh.vertices;
newmesh.triangles = mesh.triangles;
newmesh.uv = mesh.uv;
newmesh.normals = mesh.normals;
newmesh.colors = mesh.colors;
newmesh.tangents = mesh.tangents;
print(AssetDatabase.GetAllAssetPaths()[0]);
AssetDatabase.CreateAsset(newmesh, AssetDatabase.GetAllAssetPaths()[0] + "/mesh_copy.asset");
}
}
}
Lately Unity has added access to the ProBuilder package via "Package Manager".
More info here:
https://docs.unity3d.com/Packages/com.unity.probuilder#4.0/manual/index.html
Unity editor has no built-in mesh editor capabilities at the moment.
I can advise you using Prototype plugin for that.
You can easily do that by iterating through the Vertices, which Unity will give you as a Vector3[] through the someObject.GetComponent<MeshFilter>().vertices field. See http://docs.unity3d.com/ScriptReference/Mesh-vertices.html for an example where the vertices are moved upwards with time.

Categories

Resources