- Home /
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! :D (I apologize for any mistake in my English...)
I'm having trouble understanding what you are trying to do. Do you have a screen shot or can you reference a $$anonymous$$inecraft video that shows this behavior (include time offset)?
I think the objects in $$anonymous$$ecraft are 3d models.
edit : http://hackaday.com/2012/09/19/turning-video-game-sprites-into-3d-objects/
looks like it is a thing, but its tricky
Here's what I want: http://i.imgur.com/IHeAX.gif
I think I can manage the floating and the rotation part, the most important is making a 2D sprite into a 3D sprite.
This is what I've done so far (I still have to figure out how I'm gonna "paint" the 3D model...) http://gyazo.com/12edad1a6638f6846ef5de38bcc79c0a
I'm unclear about your solution. If you are generating your 3D game object using Cubes (i.e. cube game objects), then yes there are major efficiency issues. If your 3D object is generated as a single mesh where each cube is triangles in the mesh, then you are in a much better position. You'll have to test to see if you need more efficiency. $$anonymous$$inecraft likely generates these object as part of a larger mesh. That means in Unity they can get 100 - 400 of these small objects into a single game object that draws in a single draw call.
As an alternate, it is possible to simplify these objects so that they don't take as many vertices and triangles.
I've come with a solution to reduce the number of triangles/vertices that was to make every cube connected vertically as one, but still I'm not sure if that's enough...
Answer by Bunny83 · Oct 24, 2014 at 02:39 AM
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 ;)
Super useful, you should make an asset from this.
Answer by Auhmaan · Oct 23, 2014 at 11:11 PM
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.