Generating 3D item drops from 2D sprites

Basically I want to make item drops from sprites, in run-time, rather than making 3D objects outside.

This does not only gonna work to item drops, but rather to everything that will appear in game, it few workarounds because building aren’t item drops, for example.

My idea it’s do make something like Minecraft does with is own item drops. So my question is, how do I make this happen? Already searched in some forums and did not got the help needed.

Although I know how to make 3D cubic objects in game (and my main idea being generating a little cube per pixel making the sprite 3D) but making a object with 24x24 cubes wouldn’t be very efficient even with optimizations.

So, it is there a better solution for this, or do I have to generate 24 * 24 * nItems?

Thanks for your help in forward! :smiley: (I apologize for any mistake in my English…)

Quite some time ago i’ve written a small script that can create a mesh out of a texture. Can’t remember if this was before the “2d update” of Unity or not anyways it doesn’t use sprites but plain textures:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class ExtrudeSprite : MonoBehaviour
{
    private enum Edge {top, left, bottom, right};
  
    public Material spriteMat;
    public int alphaTheshold = 0;
    public float depth = 0.0625f;
    private Color32[] m_Colors;
    private int m_Width;
    private int m_Height;
 
    private List<Vector3> m_Vertices = new List<Vector3>();
    private List<Vector3> m_Normals = new List<Vector3>();
    private List<Vector2> m_TexCoords = new List<Vector2>();
 
    private bool HasPixel(int aX, int aY)
    {
        return m_Colors[aX + aY*m_Width].a > alphaTheshold;
    }
    void AddQuad(Vector3 aFirstEdgeP1, Vector3 aFirstEdgeP2,Vector3 aSecondRelative, Vector3 aNormal, Vector2 aUV1, Vector2 aUV2, bool aFlipUVs)
    {
        m_Vertices.Add(aFirstEdgeP1);
        m_Vertices.Add(aFirstEdgeP2);
        m_Vertices.Add(aFirstEdgeP2 + aSecondRelative);
        m_Vertices.Add(aFirstEdgeP1 + aSecondRelative);
        m_Normals.Add(aNormal);
        m_Normals.Add(aNormal);
        m_Normals.Add(aNormal);
        m_Normals.Add(aNormal);
        if (aFlipUVs)
        {
            m_TexCoords.Add(new Vector2(aUV1.x,aUV1.y));
            m_TexCoords.Add(new Vector2(aUV2.x,aUV1.y));
            m_TexCoords.Add(new Vector2(aUV2.x,aUV2.y));
            m_TexCoords.Add(new Vector2(aUV1.x,aUV2.y));
        }
        else
        {
            m_TexCoords.Add(new Vector2(aUV1.x,aUV1.y));
            m_TexCoords.Add(new Vector2(aUV1.x,aUV2.y));
            m_TexCoords.Add(new Vector2(aUV2.x,aUV2.y));
            m_TexCoords.Add(new Vector2(aUV2.x,aUV1.y));
        }
  
    }
 
    void AddEdge(int aX, int aY, Edge aEdge)
    {
        Vector2 size = new Vector2(1.0f/m_Width, 1.0f/m_Height);
        Vector2 uv = new Vector3(aX*size.x, aY*size.y);
        Vector2 P = uv - Vector2.one*0.5f;
        uv += size*0.5f;
        Vector2 P2 = P;
        Vector3 normal;
        if (aEdge == Edge.top)
        {
            P += size;
            P2.y += size.y;
            normal =  Vector3.up;
        }
        else if(aEdge == Edge.left)
        {
            P.y += size.y;
            normal =  Vector3.left;
        }
        else if(aEdge == Edge.bottom)
        {
            P2.x += size.x;
            normal =  Vector3.down;
        }
        else
        {
            P2 += size;
            P.x += size.x;
            normal =  Vector3.right;
        }
        AddQuad(P, P2, Vector3.forward*depth, normal, uv,uv,false);
    }
 
    void GenerateMesh()
    {
        Texture2D tex = spriteMat.mainTexture as Texture2D;
        m_Colors = tex.GetPixels32();
        m_Width = tex.width;
        m_Height = tex.height;
        //      first point                     , second point                    , relative 3. P, normal,          lower UV,     Upper UV,    flipUV
        AddQuad(new Vector3(-0.5f, -0.5f, 0    ), new Vector3(-0.5f,  0.5f, 0    ), Vector3.right, Vector3.back,    Vector2.zero, Vector2.one, false);
        AddQuad(new Vector3(-0.5f, -0.5f, depth), new Vector3( 0.5f, -0.5f, depth), Vector3.up,    Vector3.forward, Vector2.zero, Vector2.one, true);
  
        for (int y = 0; y < m_Height; y++) // bottom to top
        {
            for (int x = 0; x < m_Width; x++) // left to right
            {
                if (HasPixel(x,y))
                {
                    if(x==0 || !HasPixel(x-1,y))
                        AddEdge(x,y,Edge.left);
           
                    if(x==m_Width-1 || !HasPixel(x+1,y))
                        AddEdge(x,y,Edge.right);
           
                    if(y==0 || !HasPixel(x,y-1))
                        AddEdge(x,y,Edge.bottom);
           
                    if(y==m_Height-1 || !HasPixel(x,y+1))
                        AddEdge(x,y,Edge.top);
                }
            }
        }
        var mesh = new Mesh();
        mesh.vertices = m_Vertices.ToArray();
        mesh.normals = m_Normals.ToArray();
        mesh.uv = m_TexCoords.ToArray();
        int[] quads = new int[m_Vertices.Count];
        for (int i = 0; i < quads.Length; i++)
            quads[i] = i;
        mesh.SetIndices(quads,MeshTopology.Quads,0);
        GetComponent<MeshFilter>().sharedMesh = mesh;
    }
    void Start()
    {
        GenerateMesh();
    }
}

Just attach the script to an empty gameobject and assign a material with a readable texture to the “spriteMat” variable. The “depth” specifies the extrude depth. (Just in case you wonder why 0.0625 that’s 1/16 because it’s a 16x16 texture. That way the depth is as large as the pixels) The mesh will have a size of one by one world units. The pivot is in the center of the actual image but on the front side. The mesh is extruded along the z axis. So the front side is at local z=0 while the back side is at local z=depth.

I don’t generate a quad or cube for each pixel. I just use one quad for the whole front and one quad for the whole back side. Only the edges in between are generated and only those which aren’t hidden (so edges that have a transparent neighbor). So it’s like a very primitive version of the marching squares algorithm.

edit
Just in case someone wonders why i’ve written this. I’ve started a Turtle simulator but the project is on hold for the moment :wink:

SpriteExtrude

After a few tries I managed to come with a solution.

I’m gonna leave here in the future the code I made to make the Item Drops work, but for now I’ll leave a step-by-step to do it (I think I should do it since somebody would do something similar).

  • Fetch the image and load it into a Texture2D
  • Apply the texture to the main material in the object
  • Cycle for every pixel checking if it is transparent and if not generate a cube on a relative position on the object.
  • Apply the UVs at the same time
  • Done!

I intend to leave here a short piece of code for those who might be interested on something similar.