Jump to content

  • Log In with Google      Sign In   
  • Create Account

slayemin

Member Since 23 Feb 2001
Offline Last Active Today, 12:41 AM

#5192738 Corrupting of the view if far from world center

Posted by slayemin on 13 November 2014 - 04:48 PM

Yep, its almost certainly caused by floating point precision errors. The farther from 0 a value gets, the less precise a float gets. I think the step size between float values increases exponentially as you get further and further away from zero.

 

With your airplane model, what ends up happening is that the calculated vertex positions hit on a value which is between the ranges of a float, so the float just truncates the value? (or it might round?). ie, 9.00000000001 would fall between the value 9.0 and 9.0000000000234, and just become 9.0. The visual effect is that the airplane model verticies get mushed together and the end result is something that looks like a bad low-poly version of the plane.

 

What's the fix?

A) try to keep your floating point values near 0 because the step values are very close

B) use doubles to get more precision on higher numbers (Though you're just kicking the problem farther down the road)

 

Techniques & options:

A) keep the camera at 0 (as mentioned above) and move the world around the camera, easiest solution with least complexity

B) At certain fixed distances from 0, translate everything in the world to recenter on zero (ie, at 10k, shift everything by 10k to get back to 0). This can get tricky and cause bugs.

C) Just use smaller worlds in your design

D) Scale the world down




#5191387 XML vs Database

Posted by slayemin on 05 November 2014 - 01:24 PM

If you need multiple users to connect to and access data which may change at any time, use a database.

 

If you need to protect the integrity of your data and have a way to update it across the board for all users, use a database.

 

If you don't care about players tampering with the game data stored on their local disk (ie, it's their fault if they break the game), then you can just store the config/data files on their local machines and load it into memory when the game loads.

 

 

When it comes to storing the data, whether you use XML or a SQL table, you'll need to figure out some way to map the stored data to your internal class variables. I can't really tell you how to do this since it's very dependent on how you want to manage your data. Generally, what I do is create two methods in each class which needs to be persistent, and each method is responsible for saving and loading its state to some location.

Class MyGameObject
{
    int myValue = 5;

    public void SaveObject(string path)
    {
         //saves myValue to disk by writing "5" to the file in the given path
         SaveValueToFile(path, myValue);
    }

    public void LoadObject(string path) //<-- could also be a constructor arg
    {
        //loads the value from disk and sets it
        int diskValue = GetValueFromFile(path);
        
        //sanity check on the loaded value
        if(diskValue < 100 && diskValue >= 0)
            myValue = diskValue;
        else
            myValue = 5;
    }

}



#5191381 Resource management

Posted by slayemin on 05 November 2014 - 01:04 PM

I implemented pretty much what you're describing.

* It tries to keep a reference counter on each resource. When it reaches zero, I have the option to unload the resource from memory. I'm not really using this feature yet because I haven't needed to.

* I don't actually load an asset into memory until it's actually needed. I just store the disk path to the asset. When an object calls the "get" method for the asset, it will pull it from disk and put it into memory if its not in memory yet.

* Users of the class interact with their assets by giving them a name. They can get an object by name. The integer ID is used on the backend, but is very unfriendly for people. I ended up asking "okay, what texture do I get when I ask for ID# 23? I have no idea!"

* My asset database is used as a singleton within another class, though there's no reason you can't have multiple asset managers.
* I use separate content datastructures for each asset type, so one for textures, one for models, one for effects, one for fonts, etc. There's no reason assets can't use the same name so long as they're different asset types. This may make it easier for the user to manage their bundled assets, ie, "GetTexture("Peasant"), GetModel("Peasant"), GetEffect("Peasant")"

 

 

Here is my complete implementation class (~950 lines of code):

#define DEBUG_MODE

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

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace EricsLib.ContentDatabases
{
    /// <summary>
    /// This is the asset database for storing and managing all assets being used within the game.
    /// </summary>
    /// <example>
    /// Usage:
    /// AssetDB m_db = new AssetDB();
    /// m_db.AddTexture("Textures\\Grass", "Grass");        //insertion
    /// Texture2D grassTexture = m_db.GetTexture("Grass");  //retrieval
    /// m_db.ReleaseTexture("Grass");                       //release handle
    /// </example>
    public class AssetDB
    {
        /*Implemented feature: We keep an internal database for asset tracking. If the asset hasn't been used for a long time or the asset is no longer being
         used by any objects, we can release the main memory of the asset. If the asset is requested later on, we can load it back into memory. This feature would
         allow us to have a smaller memory footprint.
         
         Optional Feature: We could use multi-threading to load external assets into and out of memory so that the main game doesn't have to wait for an asset to be loaded.
         */
        #region Fields
        struct _Texture
        {
            public Texture2D asset;
            public int refCount;
            public string path;
            public bool loaded;

            /// <summary>
            /// Creates a new texture object
            /// </summary>
            /// <param name="AssetPath">The relative content path to the asset</param>
            public _Texture(string AssetPath)
            {
                asset = null;
                refCount = 0;
                loaded = false;
                path = AssetPath;
            }

            /// <summary>
            /// This unloads the asset but keeps the path in case we want to reload it later.
            /// </summary>
            public void Unload()
            {
                loaded = false;
                asset = null;
                refCount = 0;
            }

            /// <summary>
            /// This loads the asset into main memory by using the given content manager.
            /// </summary>
            /// <param name="content">the content manager to use for loading the asset</param>
            public void Load(ContentManager content)
            {
                asset = content.Load<Texture2D>(path);
                loaded = true;
                refCount = 0;   //just because we've loaded the asset doesn't guarantee that its being used...
            }
        }

        struct _Model
        {
            public Model asset;
            public int refCount;
            public string path;
            public bool loaded;
            public BoundingBox boundingBox;
            public BoundingSphere boundingSphere;

            public _Model(string Path)
            {
                asset = null;
                refCount = 0;
                path = Path;
                loaded = false;
                boundingBox = new BoundingBox();
                boundingSphere = new BoundingSphere();
            }

            public void Unload()
            {
                loaded = false;
                asset = null;
                refCount = 0;
            }

            public void Load(ContentManager content)
            {
                asset = content.Load<Model>(path);
                loaded = true;
                refCount = 0;
                CalculateBounds();
            }

            public BoundingBox CalculateBounds()
            {
                if (loaded)
                {
                    List<Vector3> vertList = new List<Vector3>();
                    foreach (ModelMesh mm in asset.Meshes)
                    {
                        foreach (ModelMeshPart mmp in mm.MeshParts)
                        {
                            VertexBuffer vb = mmp.VertexBuffer;
                            Vector3[] data = new Vector3[vb.VertexCount];
                            vb.GetData<Vector3>(data);
                            vertList.AddRange(data);
                        }
                    }
                    boundingBox = BoundingBox.CreateFromPoints(vertList);
                    boundingSphere = BoundingSphere.CreateFromPoints(vertList);
                }

                return boundingBox;
            }
        }

        struct _Effect
        {
            public Effect asset;
            public int refCount;
            public string path;
            public bool loaded;

            public _Effect(string AssetPath)
            {
                asset = null;
                refCount = 0;
                path = AssetPath;
                loaded = false;
            }

            public void Unload()
            {
                loaded = false;
                asset = null;
                refCount = 0;
            }

            public void Load(ContentManager content)
            {
                asset = content.Load<Effect>(path);
                asset.Name = path;
                loaded = true;
                refCount = 0;
            }
        }

        struct _Font
        {
            public SpriteFont asset;
            public int refCount;
            public string path;
            public bool loaded;

            public _Font(string AssetPath)
            {
                asset = null;
                refCount = 0;
                path = AssetPath;
                loaded = false;
            }

            public void Unload()
            {
                loaded = false;
                asset = null;
                refCount = 0;
            }

            public void Load(ContentManager content)
            {
                asset = content.Load<SpriteFont>(path);
                loaded = true;
                refCount = 0;
            }
        }

        /* The readonly keyword is different from the const keyword. A const field can only be initialized at the declaration of the field. 
         * A readonly field can be initialized either at the declaration or in a constructor. Therefore, readonly fields can have different 
         * values depending on the constructor used. Also, while a const field is a compile-time constant, the readonly field can be used 
         * for runtime constants.
         * See: http://msdn.microsoft.com/en-us/library/acdd6hb7.aspx
         */

        //The data is keyed by integers
        readonly Dictionary<int, _Texture> m_textureDB;
        readonly Dictionary<int, _Model> m_modelDB;
        readonly Dictionary<int, _Effect> m_effectDB;
        readonly Dictionary<int, _Font> m_fontDB;

        

        //these are lookup keys which can be used to find the internal key 
        Dictionary<string, int> m_textureLookup;
        Dictionary<string, int> m_modelLookup;
        Dictionary<string, int> m_effectLookup;
        Dictionary<string, int> m_fontLookup;

        int m_textureIndex;
        int m_modelIndex;
        int m_effectIndex;
        int m_fontIndex;

        ContentManager m_content;
        #endregion

        #region Initialization

        public AssetDB(ContentManager content)
        {
            m_content = content;

            m_textureIndex = 0;
            m_modelIndex = 0;
            m_effectIndex = 0;
            m_fontIndex = 0;

            m_textureDB = new Dictionary<int, _Texture>();
            m_modelDB = new Dictionary<int, _Model>();
            m_effectDB = new Dictionary<int, _Effect>();
            m_fontDB = new Dictionary<int, _Font>();

            m_textureLookup = new Dictionary<string, int>();
            m_modelLookup = new Dictionary<string, int>();
            m_effectLookup = new Dictionary<string, int>();
            m_fontLookup = new Dictionary<string, int>();

            CreateSolidTexture();
            CreateDebugTexture();
        }
        #endregion

        #region Texture Interface

        /// <summary>
        /// There's no guarantee that your user will know to add a solid texture, so let's create it dynamically.
        /// </summary>
        private void CreateSolidTexture()
        {
            Texture2D solid = new Texture2D(BaseSettings.Graphics, 128, 128);
            int size = 128*128;
            Color[] col = new Color[size];

            for (int a = 0; a < size; a++)
                col[a] = Color.White;

            solid.SetData<Color>(col);

            if (!m_textureLookup.ContainsKey("Solid"))
            {
                _Texture tmp = new _Texture();
                tmp.asset = solid;
                tmp.loaded = true;
                tmp.refCount = 1;

                m_textureLookup.Add("Solid", m_textureIndex);
                m_textureDB.Add(m_textureIndex, tmp);
                m_textureIndex++;
            }
        }

        private void CreateDebugTexture()
        {
            //Rather than throwing exceptions when a texture doesn't exist, we just present this one!

            const int SIZE = 256;
            Texture2D solid = new Texture2D(BaseSettings.Graphics, SIZE, SIZE);
            int size = SIZE * SIZE;
            Color[] col = new Color[size];


            for (int y = 0; y < SIZE; y++)
            {
                int fy = (int)(y / 32f) % 2;
                for (int x = 0; x < SIZE; x++)
                {
                    int fx = (int)(x / 32f) % 2;
                    if (fy == 0)
                    {
                        if (fx == 0)
                            col[y * SIZE + x] = Color.Gray;
                        else
                            col[y * SIZE + x] = Color.Magenta;
                    }
                    else
                    {
                        if (fx == 0)
                            col[y * SIZE + x] = Color.Magenta;
                        else
                            col[y * SIZE + x] = Color.Gray;
                    }
                }
            }

            solid.SetData<Color>(col);

            if (!m_textureLookup.ContainsKey("Invalid"))
            {
                _Texture tmp = new _Texture();
                tmp.asset = solid;
                tmp.loaded = true;
                tmp.refCount = 1;

                m_textureLookup.Add("Invalid", m_textureIndex);
                m_textureDB.Add(m_textureIndex, tmp);
                m_textureIndex++;
            }
        }

        public bool HasTexture(string Handle)
        {
            return m_textureLookup.ContainsKey(Handle);
        }

        public bool HasTexture(int TextureID)
        {
            return m_textureDB.ContainsKey(TextureID);
        }

        /// <summary>
        /// Gets the requested texture from the database, provided you've added a texture with the given handle
        /// </summary>
        /// <param name="AssetName">The handle to the texture</param>
        /// <returns>The requested texture</returns>
        /// <remarks>This will load the texture from disk into main memory if it has not been loaded already.</remarks>
        public Texture2D GetTexture(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
            {
                return GetTexture(m_textureLookup[AssetName]);
            }
            else
            {
                #if DEBUG_MODE
                throw new Exception("GetTexture(): " + AssetName + " key doesn't exist in the database");
                #else
                return GetTexture(m_textureLookup["Invalid"]);
                #endif
            }
        }

        public Rectangle GetTextureSize(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
            {
                Texture2D tmp = GetTexture(m_textureLookup[AssetName]);
                return new Rectangle(0, 0, tmp.Width, tmp.Height);
            }
            else
            {
#if DEBUG_MODE
                throw new Exception("GetTextureSize(): " + AssetName + " key doesn't exist in the database.");
#else
                Texture2D tmp = GetTexture(m_textureLookup["Invalid"]);
                return new Rectangle(0, 0, tmp.Width, tmp.Height);
#endif
            }
                
        }


        /// <summary>
        /// Gets the texture.
        /// </summary>
        /// <param name="TextureID">The texture identifier.</param>
        /// <returns>Texture2D.</returns>
        /// <exception cref="System.Exception">GetTexture():  + TextureID +  key doesn't exist in the database</exception>
        public Texture2D GetTexture(int TextureID)
        {
            if (m_textureDB.ContainsKey(TextureID))
            {
                _Texture tmp = m_textureDB[TextureID];

                if (tmp.loaded)
                    return m_textureDB[TextureID].asset;
                else
                {
                    tmp.Load(m_content);
                    tmp.refCount += 1;
                    m_textureDB[TextureID] = tmp;
                    return tmp.asset;
                }
            }
            else
            {
#if DEBUG_MODE
                throw new Exception("GetTexture(): " + TextureID + " key doesn't exist in the database");
#else
                return GetTexture(m_textureLookup["Invalid"]);
#endif
            }
                
        }

        /// <summary>
        /// Gets the texture identifier.
        /// </summary>
        /// <param name="AssetName">Name of the asset.</param>
        /// <returns>The ID used to reference the texture.</returns>
        /// <remarks>Returns -1 if the asset doesn't exist.</remarks>
        public int GetTextureID(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
                return m_textureLookup[AssetName];
            else
                return m_textureLookup["Invalid"];
        }

        /// <summary>
        /// Inserts a texture object into the database.
        /// </summary>
        /// <param name="AssetPath">The path to the texture asset within your content project</param>
        /// <param name="AssetName">The name you want to use to access your texture</param>
        public void AddTexture(string AssetPath, string AssetName)
        {
            if (!m_textureLookup.ContainsKey(AssetName))
            {
                _Texture tmp = new _Texture(AssetPath);
                
                m_textureLookup.Add(AssetName, m_textureIndex);
                m_textureDB.Add(m_textureIndex, tmp);
                m_textureIndex++;
            }
            else
            {
                //the texture already exists, so just increment its reference counter
                _Texture tmp = m_textureDB[m_textureLookup[AssetName]];
                tmp.refCount += 1;
                m_textureDB[m_textureLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Inserts a texture object into the database
        /// </summary>
        /// <param name="AssetPath">The path within your content project to the texture asset. Note: The filename will be the asset name.</param>
        public void AddTexture(string AssetPath)
        {
            string[] tmp = AssetPath.Split('\\');
            string AssetName = tmp[tmp.Length];

            AddTexture(AssetPath, AssetName);
        }

        /// <summary>
        /// Call this whenever an object no longer needs access to this shared asset.
        /// When this texture is no longer used by any objects, the texture data is released from main memory.
        /// However, the meta data still exists. If you need to access the texture again, the texture will be
        /// reloaded from disk.
        /// </summary>
        /// <param name="AssetName">The asset lookup key</param>
        public void ReleaseTexture(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
            {
                _Texture tmp = m_textureDB[m_textureLookup[AssetName]];

                if (tmp.refCount <= 1)
                {
                    tmp.asset = null;
                }
                else
                    tmp.refCount -= 1;

                m_textureDB[m_textureLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Completely deletes a texture from the asset database.
        /// If this is run on a shared asset, you're gonna have problems.
        /// </summary>
        /// <param name="AssetName">A handle to the texture name</param>
        public void DeleteTexture(string AssetName)
        {
            if (m_textureLookup.ContainsKey(AssetName))
            {
                m_textureDB.Remove(m_textureLookup[AssetName]);
                m_textureLookup.Remove(AssetName);
            }
        }

        #endregion

        #region Model Interface
        public bool HasModel(string AssetName)
        {
            return m_modelLookup.ContainsKey(AssetName);
        }

        public bool HasModel(int AssetID)
        {
            return m_modelDB.ContainsKey(AssetID);
        }

        /// <summary>
        /// Gets the model.
        /// </summary>
        /// <param name="AssetID">The asset identifier.</param>
        /// <returns>Model.</returns>
        /// <exception cref="System.Exception">Key doesn't exist in the database</exception>
        public Model GetModel(int AssetID)
        {
            if (m_modelDB.ContainsKey(AssetID))
            {
                _Model tmp = m_modelDB[AssetID];

                if (tmp.loaded)
                    return m_modelDB[AssetID].asset;
                else
                {
                    tmp.Load(m_content);
                    tmp.refCount += 1;
                    m_modelDB[AssetID] = tmp;
                    return tmp.asset;
                }
            }
            else
                throw new Exception("Key doesn't exist in the database");
        }

        /// <summary>
        /// Gets the model.
        /// </summary>
        /// <param name="AssetName">Name of the asset.</param>
        /// <returns>Model.</returns>
        /// <exception cref="System.Exception">Model doesn't exist in the database</exception>
        public Model GetModel(string AssetName)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                return GetModel(m_modelLookup[AssetName]);
            }
            else
                throw new Exception("GetModel(): " + AssetName + " Model doesn't exist in the database");
        }

        public Model GetModel(string AssetName, ref BoundingBox boundingBox, ref BoundingSphere boundingSphere)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                int ID = m_modelLookup[AssetName];
                if (m_modelDB.ContainsKey(ID))
                {
                    _Model tmp = m_modelDB[ID];

                    if (tmp.loaded)
                    {
                        boundingBox = m_modelDB[ID].boundingBox;
                        boundingSphere = m_modelDB[ID].boundingSphere;
                        return m_modelDB[ID].asset;
                    }
                    else
                    {
                        tmp.Load(m_content);
                        tmp.refCount += 1;
                        m_modelDB[ID] = tmp;

                        boundingBox = m_modelDB[ID].boundingBox;
                        boundingSphere = m_modelDB[ID].boundingSphere;

                        return tmp.asset;
                    }
                }
                else
                {
                    throw new Exception("GetModel(): " + AssetName + " Model doesn't exist in the database");
                }
            }
            else
                throw new Exception("GetModel(): " + AssetName + " Model doesn't exist in the database");
        }

        public void AddModel(string AssetPath, string AssetName)
        {
            if (!m_modelLookup.ContainsKey(AssetName))
            {
                _Model tmp = new _Model(AssetPath);

                m_modelLookup.Add(AssetName, m_modelIndex);
                m_modelDB.Add(m_modelIndex, tmp);
                m_modelIndex++;
            }
            else
            {
                //the texture already exists, so just increment its reference counter
                _Model tmp = m_modelDB[m_modelLookup[AssetName]];
                tmp.refCount += 1;
                m_modelDB[m_modelLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Changes the model associated with an asset name
        /// </summary>
        /// <param name="AssetName">The name you want to use to reference the asset</param>
        /// <param name="model">The model object you want to change</param>
        public void UpdateModel(string AssetName, Model model)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                _Model tmp = m_modelDB[m_modelLookup[AssetName]];
                tmp.asset = model;
                m_modelDB[m_modelLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Call this whenever an object no longer needs access to this shared asset.
        /// When this asset is no longer used by any objects, it is deleted.
        /// </summary>
        /// <param name="AssetName">The asset lookup key</param>
        public void ReleaseModel(string AssetName)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                _Model tmp = m_modelDB[m_modelLookup[AssetName]];

                if (tmp.refCount <= 1)
                {
                    tmp.asset = null;
                }
                else
                    tmp.refCount -= 1;

                m_modelDB[m_modelLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Completely deletes a model from the asset database.
        /// If this is run on a shared asset, you're gonna have problems.
        /// </summary>
        /// <param name="AssetName">A handle to the model name</param>
        public void DeleteModel(string AssetName)
        {
            if (m_modelLookup.ContainsKey(AssetName))
            {
                m_modelDB.Remove(m_modelLookup[AssetName]);
                m_modelLookup.Remove(AssetName);
            }
        }
        #endregion

        #region Effect Interface

        /// <summary>
        /// Determines whether the effect database has the effect.
        /// </summary>
        /// <param name="AssetID">The asset identifier.</param>
        /// <returns><c>true</c> if the specified asset identifier has effect; otherwise, <c>false</c>.</returns>
        public bool HasEffect(int AssetID)
        {
            return m_effectDB.ContainsKey(AssetID);
        }

        /// <summary>
        /// Determines whether the effect database has the effect.
        /// </summary>
        /// <param name="AssetName">Name of the asset.</param>
        public bool HasEffect(string AssetName)
        {
            return m_effectLookup.ContainsKey(AssetName);
        }

        /// <summary>
        /// Gets an effect by the asset ID
        /// </summary>
        /// <param name="AssetID">The asset identifier.</param>
        /// <returns>Effect.</returns>
        /// <exception cref="System.Exception">Key doesn't exist in the database</exception>
        public Effect GetEffect(int AssetID)
        {
            if (m_effectDB.ContainsKey(AssetID))
            {
                _Effect tmp = m_effectDB[AssetID];

                if (tmp.loaded)
                    return m_effectDB[AssetID].asset;
                else
                {
                    tmp.Load(m_content);
                    tmp.refCount += 1;
                    m_effectDB[AssetID] = tmp;
                    return tmp.asset;
                }
            }
            else
                throw new Exception("Key doesn't exist in the database");
        }

        /// <summary>
        /// Gets the requested asset from the database, provided you've added a asset with the given handle
        /// </summary>
        /// <param name="AssetName">The handle to the asset</param>
        /// <returns>The requested asset</returns>
        /// <remarks>This will load the asset into main memory if it has not been loaded.</remarks>
        public Effect GetEffect(string AssetName)
        {
            if (m_effectLookup.ContainsKey(AssetName))
            {
                return GetEffect(m_effectLookup[AssetName]);
            }
            else
                throw new Exception("Key doesn't exist in the database");
        }

        /// <summary>
        /// Inserts a asset object into the database.
        /// </summary>
        /// <param name="AssetPath">The path to the asset asset within your content project</param>
        /// <param name="AssetName">The name you want to use to access your asset</param>
        public void AddEffect(string AssetPath, string AssetName)
        {
            if (!m_effectLookup.ContainsKey(AssetName))
            {
                _Effect tmp = new _Effect(AssetPath);
                m_effectLookup.Add(AssetName, m_effectIndex);
                m_effectDB.Add(m_effectIndex, tmp);
                m_effectIndex++;
            }
            else
            {
                _Effect tmp = m_effectDB[m_effectLookup[AssetName]];
                tmp.refCount += 1;
                m_effectDB[m_effectLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Call this whenever an object no longer needs access to this shared asset.
        /// When this asset is no longer used by any objects, it is released from main memory.
        /// </summary>
        /// <param name="Handle">The asset lookup key</param>
        public void ReleaseEffect(string AssetName)
        {
            if (m_effectLookup.ContainsKey(AssetName))
            {
                _Effect tmp = m_effectDB[m_effectLookup[AssetName]];

                if (tmp.refCount <= 1)
                {
                    tmp.asset = null;
                }
                else
                {
                    tmp.refCount -= 1;
                }

                m_effectDB[m_effectLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Completely deletes a asset from the asset database.
        /// If this is run on a shared asset, you're gonna have problems.
        /// </summary>
        /// <param name="AssetName">A handle to the asset name</param>
        public void DeleteEffect(string AssetName)
        {
            if (m_effectLookup.ContainsKey(AssetName))
            {
                m_effectDB.Remove(m_effectLookup[AssetName]);
                m_effectLookup.Remove(AssetName);
            }
        }
        #endregion

        #region Font Interface

        public bool HasFont(string AssetName)
        {
            return m_fontLookup.ContainsKey(AssetName);
        }

        public bool HasFont(int FontID)
        {
            return m_fontDB.ContainsKey(FontID);
        }

        public int GetFontID(string AssetName)
        {
            if (m_fontLookup.ContainsKey(AssetName))
                return m_fontLookup[AssetName];
            return -1;
        }

        /// <summary>
        /// Gets the requested asset from the database, provided you've added a asset with the given handle
        /// </summary>
        /// <param name="AssetName">The handle to the asset</param>
        /// <returns>The requested asset</returns>
        /// <remarks>This will load the asset into main memory if it has not been loaded.</remarks>
        public SpriteFont GetFont(string AssetName)
        {
            if (m_fontLookup.ContainsKey(AssetName))
            {
                return GetFont(m_fontLookup[AssetName]);
            }
            else
                throw new Exception("Key doesn't exist in the database");
        }

        public SpriteFont GetFont(int FontID)
        {
            if (m_fontDB.ContainsKey(FontID))
            {
                _Font tmp = m_fontDB[FontID];

                if (tmp.loaded)
                    return m_fontDB[FontID].asset;
                else
                {
                    tmp.Load(m_content);
                    tmp.refCount += 1;
                    m_fontDB[FontID] = tmp;
                    return tmp.asset;
                }
            }
            else
                return null;
        }

        /// <summary>
        /// Inserts a asset object into the database.
        /// </summary>
        /// <param name="AssetPath">The path to the asset asset within your content project</param>
        /// <param name="AssetName">The name you want to use to access your asset</param>
        public void AddFont(string AssetPath, string AssetName)
        {
            if (!m_fontLookup.ContainsKey(AssetName))
            {
                _Font tmp = new _Font(AssetPath);

                m_fontLookup.Add(AssetName, m_fontIndex);
                m_fontDB.Add(m_fontIndex, tmp);
                m_fontIndex++;
            }
            else
            {
                _Font tmp = m_fontDB[m_fontLookup[AssetName]];
                tmp.refCount += 1;
                m_fontDB[m_fontLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Call this whenever an object no longer needs access to this shared asset.
        /// When this asset is no longer used by any objects, it is released from main memory.
        /// </summary>
        /// <param name="AssetName">The asset lookup key</param>
        public void ReleaseFont(string AssetName)
        {
            if (m_fontLookup.ContainsKey(AssetName))
            {
                _Font tmp = m_fontDB[m_fontLookup[AssetName]];

                if (tmp.refCount <= 1)
                {
                    tmp.asset = null;
                }
                else
                    tmp.refCount -= 1;

                m_fontDB[m_fontLookup[AssetName]] = tmp;
            }
        }

        /// <summary>
        /// Completely deletes a asset from the asset database.
        /// If this is run on a shared asset, you're gonna have problems.
        /// </summary>
        /// <param name="AssetName">A handle to the asset name</param>
        public void DeleteFont(string AssetName)
        {
            if (m_fontLookup.ContainsKey(AssetName))
            {

                m_fontDB.Remove(m_fontLookup[AssetName]);
                m_fontLookup.Remove(AssetName);
            }
        }
        #endregion

        /// <summary>
        /// This removes all of the contents of all asset databases.
        /// </summary>
        public void UnloadContent()
        {
            m_effectDB.Clear();
            m_modelDB.Clear();
            m_textureDB.Clear();
            m_fontDB.Clear();

            m_fontLookup.Clear();
            m_textureLookup.Clear();
            m_modelLookup.Clear();
            m_effectLookup.Clear();

            m_textureIndex = 0;
            m_fontIndex = 0;
            m_modelIndex = 0;
            m_effectIndex = 0;
        }
    }
}




#5188957 When to Recruit

Posted by slayemin on 24 October 2014 - 12:46 PM

If he's talking about getting people to work on the project for free, though, these things become much more complicated.

Yes, I agree. Free labor is cheap, but you get what you pay for. I think volunteer projects are great for learning new things and getting experience, but it's excruciatingly difficult to seriously build a game which you want to release based on free labor. I personally would never attempt it or join a volunteer project. That's not to say it can't work though. I met a team of developers upstairs in our building who are all working together for free on an HTML5 MMO (top down shooter). Each of them are living off of personal savings from prior jobs and have been slaving away on this project for almost two years. One of them has until next summer until his bank account runs dry, so you can guess what kind of stress and pressure they're under. So, I suppose it's worth noting that "free" labor is never free for everyone -- bills and living expenses don't stop.

Game design needs to be done from day 1.

 
Only as little as possible to avoid severe creative differences.
 
Nobody wants to work for free on somebody else's game design.  It's much more useful to motivate people by giving them input in the design itself, so it's not "my game", it's "our game", which is a kind of creative reward that has value in itself even without pay.
 
You have to narrow the game design down enough to avoid a complete lack of direction, feature creep, or conflicts between team members.  Such as:  one only likes JRPGs, the other only likes FPS - how do you resolve this?  You don't.  You only recruit one of them based on their having similar interests in line with the overall goal.
 
Narrow it down only just enough, but leave it open so contributors can feel like it's their game.  Achieving that balance in itself is something of an art.

I disagree and still stand by my original claim.

Imagine you're brought on as an artist onto a project and are told by someone, who doesn't really know what they want, to make the art for their game. You have no idea what they're trying to build and it could change at a moments notice. A detailed game design is something solid to stand on and something to work off of. It tells you exactly what needs to be built and how it should all work together with other parts of the game. Without it, it's like you're standing on a constantly shifting sand dune which changes form with the winds. When you have to build concrete assets, it's excellent to have as much requirements definition as possible. Naturally, the game designer can't detail out every single nuanced detail, so the *creative* part for an artist is in the actual creative production of the asset. The look, feel, character, style, etc.

If you think the artist is going to be annoyed by a lack of definition, just imagine how much more annoyed a programmer would be (speaking as a programmer myself). I WILL not write up a complete game system if I don't know exactly how it needs to work. There isn't any "figure it out as you go". I also happen to design my own game, but I haven't designed all software I've built. There is nothing more sexy to me as a programmer than an air tight design. There's no thinking required, just implement it to the spec! There is no confusion. You know what needs to be built. It's very refreshing to work on projects with clear definition.

One thing you do allude to, which is critical, is "stakeholder buy-in". You don't need to give everyone on the team creative control / input on the game design. Chances are actually really good that most people are actually not very good game designers. It's actually *really hard* to do correctly. A programmer is an expert at writing code. An artist is an expert at art. A producer is supposed to be an expert at project management. A writer is an expert at writing. Game designers are experts at building game systems. You wouldn't want your artist writing code, or your programmer creating art, so why would you have either of them doing game design? That's not their specialty, and not the only way they have creative input into the production of a game.

Art and style guides needs to be done from day 1.

Where volunteer projects are concerned, that's almost impossible.  People will come and go from week to week, maybe only contribute a couple sketches, or one sprite.  Things are unfortunately very unlikely to match.  
 
And most artists who will work for free are both unwilling and unable to follow a strict style guide.  
 
The rare and magical exception to this is FAN games, which are ripping off a very popular IP that the artists want to copy.
For example, some Zelda fan games have gone pretty far in getting various artists to work together to make consistent assets.  They are also more motivated by fandom.
However, this is also illegal.
 
For programming, it's a little easier for somebody to drop in and contribute a function or two.
 
In the case of artists, you may either have to learn to accept inconsistency, or bite the bullet and pay for art.
 
If you're really lucky, you may find a half decent artist who will contribute to the project in a big way for free, but you'll have to pay them in spades in terms of letting them reign as king over game design and story in order to get that kind of investment.  You'd basically just be finding an artist to work for to make his or her game idea.  And even then, they may still flake off one by one, and have to be replaced, completely gutting all of the game's story and art for a new artist to move in.  
 
Keep it simple, and make sure all the art for the game can be finished in a few weeks by one artist; that will maximize your chances of making it work.

Yes, this is bad. What you describe is exactly what you want to avoid in a serious project. Pick any well produced game and really look at the art style in it. It's consistent. The color palettes are intentionally chosen to create an intended feeling / atmosphere. Think of Skyrim, how they have a bit of a saturated color palette and pretty realistic character models and environments. Now, imagine what Skyrim would have looked like if they had 30+ artists come in, each with their very own unique interpretations on what the game should look like. Some would go for a my stylized approach, others go for hyper-realistic, some go for cartoonish, etc. In a vacuum, each art asset would look good, but when you put them all into the same universe together, you can't convince the player that the game world has consistency and you break the suspension of disbelief, which causes a loss of player immersion (which some mods break).

As for programming, it's equally problematic to have people pop in and write up a function or two and disappear. As a programmer, you *have* to be familiar with the code base / framework you're working within. That costs time for us to get familiar with it (ramp up time). If a programmer doesn't spend time getting familiar with what they're working with, they run a huge risk of breaking something or writing code which has already been done.

Anyways, I guess my whole point here is that people are super important to the success of a game project. In some jobs, people can be treated like interchangeable components -- not in game development! The ramp-up time for a person to really get into a project and become an expert is long, and you can't ever carelessly throw away your most valuable resource (staff) through people mismanagement. If you're going to be serious about building a game and releasing it commercially, you can't afford to screw up the building of your team. If you're just working on a school project or hobby project, recruit away!


#5188799 When to Recruit

Posted by slayemin on 23 October 2014 - 01:31 PM

Depends on what you're recruiting for and what the work load is. You'll want to recruit different skill sets at different phases of the project.

 

Game design needs to be done from day 1.

Art and style guides needs to be done from day 1.

Programming can be done when 50% of the design has been figured out, though there's no harm in starting early.

Business planning needs to be done before the project even starts.

Marketing can be done when the project is about 90% complete (keep in mind, the last 10% takes the longest)

Writers can be brought in roughly around 50%-60% production phase (my guess).
Testing needs to be done when you've got something playable, but the testing work load ramps up as the project nears completion. You can probably get away with not having dedicated testers for the first half of a project. Keep in mind though, issues not found through testing snowball in the sheer amount of effort required to fix them. Finding and fixing problems early can save you TONS of time and money -- fixing hypothetical bug A at day of introduction + 2 costs a programmer 2 hours of work, which translates to $40 * 2 = $80. Fixing bug A at day of introduction + 200 costs a programmer 30 hours of work (due to refactoring all dependent systems), which translates to $40 * 30 = $1200.

When do you need to hire someone? When you have too much work built up for one person to handle or when you can't do the work yourself.

IF you have a lot of work, something someone can spend 40 hours/week doing, then you want to hire an employee.

IF you have a bit of piecemeal work, consider contracting it out instead.

Quick note on this: Contractors are generally more expensive per hour than employees, but will save you money if you don't have a lot of consistent work. You don't want to pay employees to sit around doing nothing. Employees on the other hand, give you consistency and continuity which you don't really get with contractors / freelancers. You generally don't want to contract out stuff which has a lot of stylistic variety, such as artwork.

IF you don't want to pay someone to work for you, be prepared to have them quit on you at any moment. Unpaid people have very little incentive other than their own morale / personal interests to stick around. If your making an unpaid person fill a vital role in your project, you're taking on a huge risk in terms of project management.

IF you want to build a geographically remote team of unpaid people, good luck. Not only may members drop off the project at any moment, you will also have huge communication challenges. You've got people all over the world with different time zones, languages, schedules, and cultural backgrounds. Project management and coordination of effort will be a nightmare. You're essentially operating a free open source project, where you should expect people to pop in and tinker a bit and disappear. Don't expect a lot of progress, or for the progress to go in the direction you want, to your level of satisfaction.




#5188393 Starting Slow

Posted by slayemin on 21 October 2014 - 02:52 PM

Start with Tic-tac-toe. It's pretty much the game version of "hello world". It'll get you familiar with building for a mobile platform and you won't get stuck in some game design / programming problem BS. If you can't do tic-tac-toe on the phone, you need to increase your mobile dev technical chops -- building anything more complicated than that is just a recipe for failure and disappointment. Once you've got that handled and know how the mobile development workflow works, you can build more complicated projects.




#5187257 what is the correct way to move object along direction

Posted by slayemin on 15 October 2014 - 04:46 PM

You can also get a direction vector from an angle:

//theta is a float value between 0->2pi
dirX = speed * cos(theta);
dirY = speed * sin(theta);

So, movement in a direction is pretty simple:

Position.X += dirX;
Position.Y += dirY;

Then you can wire up some input controls to change the direction and speed.




#5186218 Your thoughts on me hiring a game developer/studio

Posted by slayemin on 10 October 2014 - 12:16 PM

I avoided the word "hire" because he'll need to feel like this is as much his project he's interested in doing.



So you're not going to pay someone to work for you?!

No, no no. I get what you're trying to say, but this is a really bad move. If you go through the effort and trouble to find that perfect fit for your project and team, you want to lock that person down so that they stay on the project/team! The best way to do this is to take care of them by satisfying their interests, which include getting paid well, regularly, and on time. This is business 101. You need this programmer to be a stakeholder in your project, and if you aren't a stakeholder invested in them, they aren't going to be a stakeholder invested in you. They'll jump ship as soon as something else comes along which satisfies their interests / desires more than you do. This would be a catastrophe and spell doom for your project since its such a large setback. It's hard to bounce back from. So, don't make the foolish mistake of letting it happen in the first place. Hire people.
 

My ideas are just ideas for him to see whether he wants to be interested in doing it and pretty much making it his own, with the input of my ideas as well

I'm a programmer and design my own games. This is a huge red flag. I underestimated the difficulty of good game design, and understand it takes a lot of effort to do it right. If *you* are going to be the game designer, then you need to be beyond stellar and work harder than anyone else on the team, and spell out in precise, exact detail how every minute aspect of the game is going to work together. The fact that you want to do some vague hand waving and essentially tell the programmer to fill in the gaps with their own ideas, screams the fact that you don't know exactly what you want. There's nothing more frustrating than to spend a ton of effort writing code to implement something which ends up changing completely or getting scrapped on the whims of an uncertain and constantly shifting design. If you're expecting the programmer to take control on the direction of this project, you reduce yourself to a niggling background voice which needs to be appeased while the programmer works on his own ideas of what should be built. Warning: Programmers aren't necessarily good designers!!!
 

I tell them to make a site like this site here, and if they cannot, then I regard them as failing to meet client's expectations.


This is not necessarily the right conclusion to make. Just because a developer fails to "build a site" doesn't mean that the developer can't do the job and is thus incompetent. No project exists in a vacuum. The project can be doomed before it even begins due to a failure in any number of things which lead up to the project and its management. I strongly recommend that you familiarize yourself with the software development life cycle. Whether you're building websites, video games, or applications, it's all the same process.
Just to refresh your memory on the lead up to constructing software, you have the following phases:
-1. Visioning step - What would be awesome to do?
0. Feasibility assessment - Can the vision actually be done within the limitations?
1. Requirements gathering - What *must* the software do? Does it meet the vision? Is it feasible?
2. Design - How will the software work to satisfy the requirements? Is it feasible? does it satisfy the vision?
3. Construction - build the damned thing! write the code! create the assets! make it come to life! Does it meet the design? Does it meet the requirements? Can it even work with the technical limitations / constraints?

Notice how each phase of the process depends on the phases preceding it? If any of the steps leading up to the construction phase are shit, it doesn't matter how good the programmer may be, the project is going to fail. The only way a shit project can be saved is if the programmer realizes he got set up for failure and goes through and redoes the work proceeding the work he's supposed to be doing. The right move to do when this happens is to just abandon the project/client and move onto working for/with someone who actually knows what the hell they're doing (some more unscrupulous devs will just string the client along and suck their money dry to get a steady paycheck). Sure, the shit client will bad mouth the programmer for being incompetent/incapable, but what does the programmer care? They've got better people to work for than to concern themselves with what bad clients have to say about them.
 

Yea, maybe that could be more my role, as I'm already an investor for two other companies. I feel maybe being an investor for this one also could be a possibility. But for this one, I have that itch of wanting to have that perfect game I've always wanted to play be realized. Alot of games out there may have 90% of what I'm looking for, and they are all very great games, just that I have that itch to make it 100% so if I can't find it out there already, then only other option is to try to make it. Investing, I'm always going to be doing even after I die.

I'm also an investor (95% stocks). I have also invested in my own indie game studio and put my money where my mouth is. My investment is more than just throwing money at a project/company and hoping for success, it's also a very deeply personal investment of my own time, commitment, and 100% energies. I'm totally invested in myself, and it's a do-or-die situation. With every investment, someone somewhere down the money stream needs to stand up and make use of the financial resources they have been granted to further the goal of the project/organization. The worst way to 'invest' in this sense is to throw money at a new studio/project with a half-assed game design and non-commitaly hiring some shammy programmer to make it happen -- you need to be the leader in the trenches, bringing your troops to victory by being the point man leading the charge. If you aren't/can't, your ROI will be -100%. Even if you have $0 financially invested, you can still be heavily invested in the success of a project. If you aren't 100% invested in a project/company in every way, you can't be the leader who asks others to invest 100%.




#5184142 Geometry shader-generated camera-aligned particles seemingly lacking Z writing

Posted by slayemin on 30 September 2014 - 03:09 PM

I had this problem last week and I was struggling to figure out what exactly was going on. Then I found this super awesome article by Shawn Hargreaves:

http://blogs.msdn.com/b/shawnhar/archive/2009/02/18/depth-sorting-alpha-blended-objects.aspx

 

He pretty much explains in perfect detail what the problem is and provides some solutions. IF you are using alpha blending, the "best" solution is to sort your objects based on their distance from the camera and draw them from back to front.

 

I too decided to write my billboards into HLSL code. Unfortunately since I'm using XNA, I only have the vertex shader and pixel shader at my disposal, so I can't add extra verticies as you would be able to do in DX10+. This means that I just have to include the vertex positions in the quad which gets rendered instead of inferring the vertices from a point positition. In my implementation, I take advantage of instancing which allows me to send a huge batch of vertex instance data to the GPU once, and then I let the shader process the primitive data and draw updates, using just one draw call. 

 

Here is the HLSL code I'm using for DX9:
 

float4x4 World;
float4x4 View;
float4x4 Projection;
float3 CameraPosition;
float3 CameraUp;
float CurrentTime;			//current time in seconds
float4 Tint : COLOR0 = (float4)1;
bool UseWorldTransforms;
float4 Ambient : COLOR = (float4)1;			//the ambient light color in the scene
float3 LightDirection;			//a vector3 for the directional light
float4 LightColor : COLOR;		//the directional light color

const float Tau = 6.283185307179586476925286766559;

//------- Texture Samplers --------
Texture g_Texture;
sampler TextureSampler = sampler_state { texture = <g_Texture>; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = wrap; AddressV = wrap;}; 

struct QuadTemplate
{
	float3 Position	    : POSITION0;
	float2 TexCoord	    : TEXCOORD0;
};

struct QuadInstance
{
	float3 Position		: POSITION1;
	float3 Velocity		: POSITION2;
	float3 Normal		: NORMAL0;
	float3 Up			: NORMAL1;
	float3 ScaleRot		: POSITION3;
	float3 EndScaleRot	: POSITION4;
	float2 Time			: POSITION5;
	float4 Color		: COLOR0;
	float4 EndColor		: COLOR1;
};

struct LineSegmentInput
{
	float3 Position		: POSITION0;
	float4 StartColor	: COLOR0;
	float4 EndColor		: COLOR1;
	float3 Velocity		: TEXCOORD0;
	float2 Time			: TEXCOORD1;
	float2 UV		    : TEXCOORD2;
};

struct VSOUT
{
    float4 Position		: POSITION0;			//screen space coordinate of pixel
	float4 Color		: COLOR;				//vertex color
	float2 TexCoord		: TEXCOORD0;			//texture coordinate
};

float3x3 CreateRotation(float myAngle, float3 rotAxis)
{
	float c = cos(myAngle);
	float s = sin(myAngle);
	float3 u = rotAxis;

	return float3x3(
	c + (u.x*u.x)*(1-c), u.x*u.y*(1-c) - u.z*s, u.x*u.z * (1-c) + u.y*s,
	u.y*u.x*(1-c) + u.z * s, c + u.y*u.y*(1-c), u.y*u.z*(1-c) - u.x*s,
	u.z*u.x*(1-c) - u.y * s, u.z*u.y*(1-c)+u.x*s, c + u.z*u.z*(1-c)
	);
}

//3D TEXTURED//////////////////////////////////////////////////////////////////////////////////
VSOUT VS_3DTex(QuadTemplate input)
{
    VSOUT output = (VSOUT)0;

    //float4 worldPosition = mul(input.Position, World);
    //float4 viewPosition = mul(worldPosition, View);
    //output.Position = mul(viewPosition, Projection);
	output.Position = (float4)0;
	output.Color = (float4)1;
	//output.Color.r = input.TextureCoord.x;
	//output.Color.g = input.TextureCoord.y;
	//output.TexCoord = input.TextureCoord;
	output.TexCoord = (float2)0;

    return output;
}

//3D Textured thick lines/////////////////////////////////////////////////////////////////
VSOUT VS_3DTexturedLine(LineSegmentInput input)
{
	VSOUT output = (VSOUT)0;

	float age = CurrentTime - input.Time.x;
	float lifeAmt = 0;														//the life amount is a percentage between birth and death, if we're not -1
	if(input.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float4 pos = (float4)1;
	pos.xyz = input.Position + (input.Velocity * age);
	if(UseWorldTransforms == false)
		pos.xyz += CameraPosition;

	output.Position = mul(mul(pos, View), Projection);
	output.Color = lerp(input.StartColor, input.EndColor, lifeAmt);
	output.TexCoord = input.UV;

	return output;
}

//3D colored 0px lines////////////////////////////////////////////////////////////////////
VSOUT VS_3DLineSegment(LineSegmentInput input)
{
	VSOUT output = (VSOUT)0;

	float age = CurrentTime - input.Time.x;
	float lifeAmt = 0;														//the life amount is a percentage between birth and death, if we're not -1
	if(input.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float4 pos = (float4)1;
	pos.xyz = input.Position + (input.Velocity * age);
	if(UseWorldTransforms == false)
		pos.xyz += CameraPosition;

	output.Position = mul(mul(pos, View), Projection);
	output.Color = lerp(input.StartColor, input.EndColor, lifeAmt);

	return output;
}

//3D Textured Quads///////////////////////////////////////////////////////////////////////
VSOUT VS_3DQuadTex(QuadTemplate input, QuadInstance instance)
{

	float age = CurrentTime - instance.Time.x;

	float lifeAmt = 0;														//the life amount is a percentage between birth and death, if we're not -1
	if(instance.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float3 m_scale = lerp(instance.ScaleRot, instance.EndScaleRot, lifeAmt);		//linear interpolate the scale values to get current scale
	float m_rotation = instance.ScaleRot.z + (instance.EndScaleRot.z * age);	//current rotation is initial rotation + sum of rotational speed over time
	float3 m_center = instance.Position;			//this is the transformed center position for the quad.
	
	m_center +=  (instance.Velocity * age);

	//TODO: Handle the case where the normal is set to (0,1,0) or (0,-1,0)
	//Note: this is done in the application, not the shader.

	float3 m_normal = instance.Normal;										//the normal is going to be given to us and is fixed.
	//float3 m_up = float3(0,1,0);											//the up vector is simply a cross of the left vector and normal vector
	float3 m_up = instance.Up;
	float3 m_left = cross(m_normal, m_up);									//the left vector can be derived from the camera orientation and quad normal
	m_up = cross(m_left, m_normal);

	float3x3 m_rot = CreateRotation(-m_rotation, m_normal);					//Create a rotation matrix around the object space normal axis by the given radian amount.
																			//This rotation matrix must then be applied to the left and up vectors.
	m_left = mul(m_left, m_rot) * m_scale.x;								//apply rotation and scale to the left vector
	m_up = mul(m_up, m_rot) * m_scale.y;									//apply rotation and scale to the up vector

	//Since we have to orient our quad to always face the camera, we have to change the input position values based on the left and up vectors.
	//the left and up vectors are in untranslated space. We know the translation, so we just set the vertex position to be the translation added to
	//the rotated and scaled left/up vectors.

	float3 pos = (float)0;
	if(input.Position.x == -1 && input.Position.y == -1)			//bottom left corner
	{
		pos = m_center + (m_left - m_up);	
	}
	else if(input.Position.x == -1 && input.Position.y == 1)		//top left corner
	{
		pos = m_center + (m_left + m_up);
	}
	else if(input.Position.x == 1 && input.Position.y == 1)			//top right corner
	{
		pos = m_center - (m_left - m_up);
	}
	else															//bottom right corner
	{
		pos = m_center - (m_left + m_up);
	}

	//Since we've already manually applied our world transformations, we can skip that matrix multiplication.
	//note that we HAVE to use a Vector4 for the world position because our view & projection matrices are 4x4.
	//the matrix multiplication function isn't smart enough to use a vector3. The "w" value must be 1.

	float4 worldPosition = 1.0f;
	worldPosition.xyz = pos;
	if(UseWorldTransforms == false)
		worldPosition.xyz += CameraPosition;

	VSOUT output;
    output.Position = mul(mul(worldPosition, View), Projection);
	output.Color = lerp(instance.Color, instance.EndColor, lifeAmt);
	output.TexCoord = input.TexCoord;
	return output;
}

//3D Textured point sprites///////////////////////////////////////////////////////////////////////
VSOUT VS_3DPointSpriteTex(QuadTemplate input, QuadInstance instance)
{
	/* 
	SUMMARY: A point sprite is a special type of quad which will always face the camera. The point sprite
	can be scaled and rotated around the camera-sprite axis (normal) by any arbitrary angle. Because of these
	special behaviors, we have to apply some special instructions beyond just multiplying a point by the world
	matrix.
	*/

	float age = CurrentTime - instance.Time.x;

	float lifeAmt = 0;														//the life amount is a percentage between birth and death, if we're not -1
	if(instance.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float3 m_scale = lerp(instance.ScaleRot, instance.EndScaleRot, lifeAmt);		//linear interpolate the scale values to get current scale
	float m_rotation = (instance.ScaleRot.z * Tau) + (instance.EndScaleRot.z * Tau * age);	//current rotation is initial rotation + sum of rotational speed over time
	float3 m_center = instance.Position;			//this is the transformed center position for the quad.
	m_center +=  (instance.Velocity * age);

	float3 m_normal = normalize(CameraPosition - m_center);					//the normal is going to be dependent on the camera position and the center position
	float3 m_left = cross(m_normal, CameraUp);								//the left vector can be derived from the camera orientation and quad normal
	float3 m_up = cross(m_left, m_normal);									//the up vector is simply a cross of the left vector and normal vector
	float3x3 m_rot = CreateRotation(m_rotation, m_normal);					//Create a rotation matrix around the object space normal axis by the given radian amount.
																			//This rotation matrix must then be applied to the left and up vectors.
	m_left = mul(m_left, m_rot) * m_scale.x;								//apply rotation and scale to the left vector
	m_up = mul(m_up, m_rot) * m_scale.y;									//apply rotation and scale to the up vector

	//Since we have to orient our quad to always face the camera, we have to change the input position values based on the left and up vectors.
	//the left and up vectors are in untranslated space. We know the translation, so we just set the vertex position to be the translation added to
	//the rotated and scaled left/up vectors.

	float3 pos = (float)0;
	if(input.Position.x == -1 && input.Position.y == -1)			//bottom left corner
	{
		pos = m_center + (m_left - m_up);	
	}
	else if(input.Position.x == -1 && input.Position.y == 1)		//top left corner
	{
		pos = m_center + (m_left + m_up);
	}
	else if(input.Position.x == 1 && input.Position.y == 1)			//top right corner
	{
		pos = m_center - (m_left - m_up);
	}
	else															//bottom right corner
	{
		pos = m_center - (m_left + m_up);
	}

	//Since we've already manually applied our world transformations, we can skip that matrix multiplication.
	//note that we HAVE to use a Vector4 for the world position because our view & projection matrices are 4x4.
	//the matrix multiplication function isn't smart enough to use a vector3. The "w" value must be 1.

	float4 worldPosition = 1.0f;
	worldPosition.xyz = pos;
	if(UseWorldTransforms == false)
		worldPosition.xyz += CameraPosition;

	VSOUT output;
    output.Position = mul(mul(worldPosition, View), Projection);
	output.Color = lerp(instance.Color, instance.EndColor, lifeAmt);
	output.TexCoord = input.TexCoord;
	return output;
}

//3D Textured Billboard///////////////////////////////////////////////////////////////////////
VSOUT VS_3DBillboardTex(QuadTemplate input, QuadInstance instance)
{
	/* 
	SUMMARY: A billboard is a special type of quad which will always face the camera, but is constrained along the
	y-axis. The billboard can be scaled and rotated around the camera-sprite axis (normal) by any arbitrary angle. 
	Because of these special behaviors, we have to apply some special instructions beyond just multiplying a point 
	by the world matrix.
	*/

	float age = CurrentTime - instance.Time.x;								//total elapsed time since birth

	float lifeAmt = 0;															//the age is a percentage between birth and death, if we're not -1
	if(instance.Time.y != -1.0f)
	{
		lifeAmt = saturate(age / instance.Time.y);
	}

	float3 m_scale = lerp(instance.ScaleRot, instance.EndScaleRot, lifeAmt);		//linear interpolate the scale values to get current scale
	float m_rotation = (instance.ScaleRot.z * Tau) + (instance.EndScaleRot.z * Tau * age);	//current rotation is initial rotation + sum of rotational speed over time
	float3 m_center = instance.Position;			//this is the transformed center position for the quad.
	m_center +=  (instance.Velocity * age);

	float3 m_normal = CameraPosition - m_center;							//the normal is going to be dependent on the camera position and the center position
	m_normal.y = 0;
	m_normal = normalize(m_normal);
	float3 m_up = float3(0,1,0);											//the up vector is simply the unit Y value
	float3 m_left = cross(m_normal, m_up);								//the left vector can be derived from the camera orientation and quad normal
	
	float3x3 m_rot = CreateRotation(m_rotation, m_normal);					//Create a rotation matrix around the object space normal axis by the given radian amount.
																			//This rotation matrix must then be applied to the left and up vectors.
	m_left = mul(m_left, m_rot) * m_scale.x;								//apply rotation and scale to the left vector
	m_up = mul(m_up, m_rot) * m_scale.y;									//apply rotation and scale to the up vector

	//Since we have to orient our quad to always face the camera, we have to change the input position values based on the left and up vectors.
	//the left and up vectors are in untranslated space. We know the translation, so we just set the vertex position to be the translation added to
	//the rotated and scaled left/up vectors.

	float3 pos = (float)0;
	if(input.Position.x == -1 && input.Position.y == -1)			//bottom left corner
	{
		pos = m_center + (m_left - m_up);	
	}
	else if(input.Position.x == -1 && input.Position.y == 1)		//top left corner
	{
		pos = m_center + (m_left + m_up);
	}
	else if(input.Position.x == 1 && input.Position.y == 1)			//top right corner
	{
		pos = m_center - (m_left - m_up);
	}
	else															//bottom right corner
	{
		pos = m_center - (m_left + m_up);
	}

	//Since we've already manually applied our world transformations, we can skip that matrix multiplication.
	//note that we HAVE to use a Vector4 for the world position because our view & projection matrices are 4x4.
	//the matrix multiplication function isn't smart enough to use a vector3. The "w" value must be 1.

	float4 worldPosition = 1.0f;
	worldPosition.xyz = pos;
	if(UseWorldTransforms == false)
		worldPosition.xyz += CameraPosition;

	VSOUT output;
    output.Position = mul(mul(worldPosition, View), Projection);
	output.Color = lerp(instance.Color, instance.EndColor, lifeAmt);
	output.TexCoord = input.TexCoord;
	return output;
}

//3D Vertex colors only///////////////////////////////////////////////////////////////////////////
VSOUT VS_3D(float4 inPosition : POSITION, float4 inColor : COLOR)
{
    VSOUT output;

    float4 worldPosition = mul(inPosition, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
	output.Color = inColor;
	output.TexCoord = 0;

    return output;
}

VSOUT VS_2D(float4 inPos : POSITION, float4 inColor : COLOR)
{
    VSOUT Output = (VSOUT)0;

	Output.Position = inPos;
	Output.Color = inColor;

    return Output;
}
//PIXEL SHADERS///////////////////////////////////////////////////////////////////////////

float4 PS_3D(VSOUT output) : COLOR0
{
	//Output.Color = tex2D(TextureSampler, vs_output.TextureCoord);
	//Output.Color.rgb *= saturate(PSIn.LightingFactor) + xAmbient;

	//return tex2D(TextureSampler, output.TexCoord);

	return output.Color * Tint * Ambient;
}

float4 PS_2D(VSOUT vs_output) : COLOR0
{
	return vs_output.Color * Tint;
}

float4 PS_3DTex(VSOUT input) : COLOR0
{
	float4 c = tex2D(TextureSampler, input.TexCoord);
	if(c.a <= 0.1)
		discard;
	//return c * (input.Color + Ambient);
	float3 Up = (float3)0;
	Up.y = 1;

	float LightFactor = dot(Up, -LightDirection);

	return  (c * input.Color * Tint) * saturate(LightColor + Ambient);
	//return  ((c * input.Color ) * saturate((LightColor * LightFactor) + Ambient)) ;
}

//TECHNIQUES///////////////////////////////////////////////////////////////////////////
technique Technique2D
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 VS_2D();
        PixelShader = compile ps_2_0 PS_2D();
    }
}

technique VertexColor3D
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 VS_3D();
        PixelShader = compile ps_2_0 PS_3D();
    }
}

technique Textured3D
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 VS_3DTex();
        PixelShader = compile ps_2_0 PS_3DTex();
    }
}

technique TexturedQuad3D
{
	pass Pass0
	{
	    VertexShader = compile vs_3_0 VS_3DQuadTex();
            PixelShader = compile ps_3_0 PS_3DTex();
	}
}

technique TexturedPointSprite3D
{
	pass Pass0
	{
	    VertexShader = compile vs_3_0 VS_3DPointSpriteTex();
            PixelShader = compile ps_3_0 PS_3DTex();
	}
}

technique TexturedBillboard3D
{
	pass Pass0
	{
	   VertexShader = compile vs_3_0 VS_3DBillboardTex();
           PixelShader = compile ps_3_0 PS_3DTex();
	}
}

technique LineSegment3D
{
	pass Pass0
	{
	    VertexShader = compile vs_3_0 VS_3DLineSegment();
            PixelShader = compile ps_3_0 PS_3D();
	}
}

technique TexturedLine3D
{
	pass Pass0
	{
		VertexShader = compile vs_3_0 VS_3DTexturedLine();
        PixelShader = compile ps_3_0 PS_3DTex();
	}
}

And here is my complete rendering code for drawing quads, billboards, and point sprites using the above HLSL:
 

public void Render(Camera3D camera, coreTime worldTime)
{
	
	//...snipped irrelevant code...

	if (m_settings.PainterSort)
	{
		PainterSort(camera.Position);
	}

	//rebuild the vertex and index buffers if the collection has been changed.
	if (m_dirtyBuffers > 0)
		RebuildBuffers();

	//activate our buffers
	//m_settings.GraphicsDevice.SetVertexBuffer(m_psVB);
	RasterizerState rs = m_settings.GraphicsDevice.RasterizerState;
	if (m_settings.DoubleSided == true)
	{
		RasterizerState rs2 = new RasterizerState();
		rs2.CullMode = CullMode.None;
		m_settings.GraphicsDevice.RasterizerState = rs2;
	}

	m_settings.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
	m_settings.GraphicsDevice.Indices = m_IB;
	m_settings.GraphicsDevice.BlendState = m_settings.BlendState;

	m_effect.Parameters["g_Texture"].SetValue(m_settings.Texture);
	m_effect.Parameters["UseWorldTransforms"].SetValue(m_settings.UseWorldTransforms);
	m_effect.Parameters["View"].SetValue(camera.View);
	m_effect.Parameters["Projection"].SetValue(camera.Projection);
	m_effect.Parameters["CameraPosition"].SetValue(camera.Position);
	m_effect.Parameters["CameraUp"].SetValue(camera.Up);
	m_effect.Parameters["CurrentTime"].SetValue((float)worldTime.TotalWorldTime.TotalSeconds);
	m_effect.Parameters["Tint"].SetValue(m_settings.Tinting.ToVector4());

	if (m_settings.UseWorldLights)
	{
		m_effect.Parameters["Ambient"].SetValue(BaseSettings.AmbientLight.ToVector4());
		m_effect.Parameters["LightColor"].SetValue(BaseSettings.AllDirLights[0].Color.ToVector4());
		m_effect.Parameters["LightDirection"].SetValue(BaseSettings.AllDirLights[0].Direction);
	}
	

	#region Draw Quads
	if (m_quadVB != null && m_quadVB.VertexCount > 0)
	{
		m_effect.CurrentTechnique = m_effect.Techniques["TexturedQuad3D"];

		m_settings.GraphicsDevice.SetVertexBuffers(
		new VertexBufferBinding(m_VB, 0, 0),
		new VertexBufferBinding(m_quadVB, 0, 1));

		foreach (EffectPass pass in m_effect.CurrentTechnique.Passes)
		{
			pass.Apply();
			//m_settings.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, m_psList.Count * 4, 0, m_psList.Count * 2);
			m_settings.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 
				0, //base vertex
				0,  //min vertex index
				4, //vertex count
				0, //start index
				2, //primitive count
				m_quadVB.VertexCount  //instance count
				);
		}
	}
	#endregion

	#region Draw Point sprites
	if (m_psVB != null && m_psVB.VertexCount > 0)
	{
		m_effect.CurrentTechnique = m_effect.Techniques["TexturedPointSprite3D"];

		m_settings.GraphicsDevice.SetVertexBuffers(
		new VertexBufferBinding(m_VB, 0, 0),
		new VertexBufferBinding(m_psVB, 0, 1));

		foreach (EffectPass pass in m_effect.CurrentTechnique.Passes)
		{
			pass.Apply();
			//m_settings.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, m_psList.Count * 4, 0, m_psList.Count * 2);
			m_settings.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0,
				4, //vertex count
				0, //start index
				2, //primitive count
				m_psVB.VertexCount  //instance count
				);
		}
	}
	#endregion

	#region Draw billboards
	if (m_bbVB != null && m_bbVB.VertexCount > 0)
	{
		m_effect.CurrentTechnique = m_effect.Techniques["TexturedBillboard3D"];

		m_settings.GraphicsDevice.SetVertexBuffers(
		new VertexBufferBinding(m_VB, 0, 0),
		new VertexBufferBinding(m_bbVB, 0, 1));

		foreach (EffectPass pass in m_effect.CurrentTechnique.Passes)
		{
			pass.Apply();
			//m_settings.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, m_psList.Count * 4, 0, m_psList.Count * 2);
			m_settings.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0,
				4, //vertex count
				0, //start index
				2, //primitive count
				m_bbVB.VertexCount  //instance count
				);

		}
	}
	#endregion

	m_settings.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
	m_settings.GraphicsDevice.RasterizerState = rs;
	m_settings.GraphicsDevice.BlendState = BlendState.Opaque;
}

Here are both custom vertex definitions I came up with which are necessary for drawing the instanced primitives using HLSL:
QuadInstanceVertex:
 

public struct QuadInstanceVertex
{

	//Optimize only if you have been able to profile a problem with the vertex byte size. Don't prematurely optimize and over-engineer.

	/// <summary>
	/// Offset from origin
	/// </summary>
	public Vector3 Position;

	/// <summary>
	/// Particle velocity
	/// </summary>
	public Vector3 Velocity;

	/// <summary>
	/// Normal direction for the quad face
	/// Quad: Set value; PointSprite: (0,0,0); BillBoard: (0,0,0)
	/// </summary>
	public Vector3 Normal;

	/// <summary>
	/// We actually have to include the up vector for quads because we just can't derive it within the shader.
	/// </summary>
	/// <remarks>
	/// The problem with trying to derive an up direction within the shader is that the shader compiler will UNROLL
	/// all of your branching logic. If you try to write any code to avoid dividing by zero, one of the branches will
	/// take that path anyways and divide by zero, causing *visual studio* to crash. So, rather than trying to run
	/// logic in the shader, we have to run it in the application.
	/// </remarks>
	public Vector3 Up;

	/// <summary>
	/// The starting width, length, and normal-axis rotation
	/// </summary>
	public Vector3 Scale;

	/// <summary>
	/// the end width, length and normal-axis rotational speed.
	/// length and width with be lerp'd, rotational speed will be added to initial value
	/// </summary>
	public Vector3 EndScale;

	/// <summary>
	/// Crucial timing values for the vertex shader.
	/// X = Spawn time of the particle/quad
	/// Y = lifespan of the quad; 
	///       -1: always alive
	///        0: dead
	///       0+: alive
	/// </summary>
	/// <remarks>Your quad manager is responsible for removing a quad/particle when the lifespan reaches zero.</remarks>
	public Vector2 Time;

	/// <summary>
	/// The starting color.
	/// </summary>
	public Color Color;

	/// <summary>
	/// The ending color. Current value will be lerp'd between this and start color.
	/// </summary>
	public Color EndColor;


	
	/// <summary>
	/// Creates a particle with the following properties.
	/// </summary>
	/// <param name="pos">The position offset from the origin</param>
	/// <param name="norm">the normal of the face</param>
	/// <param name="scaleRot">initial length and width</param>
	/// <param name="color">initial color</param>
	/// <param name="zrot">initial rotation around the z-axis</param>
	/// <param name="endScaleRot">end length and width</param>
	/// <param name="t">x = spawn time; y = lifespan (-1: infinite, 0: dead; gt 0: alive)</param>
	/// <param name="dz">rotational speed</param>
	/// <param name="vel">velocity of the particle (if you want it to move)</param>
	/// <param name="endColor">end color of the particle</param>
	public QuadInstanceVertex(Vector3 pos, Vector3 vel, Vector3 norm, Vector3 up, Vector3 scaleRot, Vector3 endScaleRot, Color color, Color endColor, Vector2 t)
	{
		Position = pos;
		Velocity = vel;
		Normal = norm;
		Up = up;

		Scale = scaleRot;
		EndScale = endScaleRot;

		Color = color;
		EndColor = endColor;
		
		Time = t;
	}

	/// <summary>
	/// Creates a quad instance with the given properties.
	/// </summary>
	/// <param name="pos"></param>
	/// <param name="norm"></param>
	/// <param name="scaleRot"></param>
	/// <param name="color"></param>
	/// <param name="zrot"></param>
	public QuadInstanceVertex(Vector3 pos, Vector3 norm, Vector3 up, Vector3 scaleRot, Color color)
	{
		Position = pos;
		Velocity = Vector3.Zero;
		Normal = norm;
		Up = up;

		Scale = scaleRot;
		EndScale = scaleRot;

		Color = color;
		EndColor = color;
		
		Time = new Vector2(0,-1);
	}

	/*Note: The semantic usage index must be unique across ALL vertex buffers. The geometry vertex buffer already uses Position0 and TexCoord0.*/

	public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(
		new VertexElement( 0, VertexElementFormat.Vector3, VertexElementUsage.Position, 1),          //12 (pos)
		new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Position, 2),         //12 (velocity)
		new VertexElement(24, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),           //12 (norm)
		new VertexElement(36, VertexElementFormat.Vector3, VertexElementUsage.Normal, 1),           //12 (up)
		new VertexElement(48, VertexElementFormat.Vector3, VertexElementUsage.Position, 3),         //12 (scale/rot)
		new VertexElement(60, VertexElementFormat.Vector3, VertexElementUsage.Position, 4),         //12 (end scale/rot)
		new VertexElement(72, VertexElementFormat.Vector2, VertexElementUsage.Position, 5),         //8  (time)
		new VertexElement(80, VertexElementFormat.Color, VertexElementUsage.Color, 0),              //4  (color)
		new VertexElement(84, VertexElementFormat.Color, VertexElementUsage.Color, 1)               //4  (end color)
		); 

	public const int SizeInBytes = 76;
}

QuadVertex:

/// <summary>
/// A vertex structure with Position and Texture coordinate data
/// </summary>
public struct QuadVertex : IVertexType
{
	/*So, we're gonna get funky here. The R,G,B components of the color denote any color TINT for the quad. Since we also have an alpha channel, we're going
	 to store the CornerID of the vertex within it!*/
	public Vector3 Position;
	public Vector2 UV;


	/// <summary>
	/// Creates a vertex which contains position, normal, color, and texture UV info
	/// </summary>
	/// <param name="position">The position of the vertex, relative to the center</param>
	/// <param name="uv">The UV texture coordinates</param>
	/// <param name="rotation">A radian value indicating rotation around the normal axis</param>
	public QuadVertex(Vector3 position, Vector2 uv)
	{
		Position = position;
		UV = uv;
	}

	public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration(
		new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),                              //12 bytes
		new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)                    //8 bytes
		);

	public const int SizeInBytes = 20;

	VertexDeclaration IVertexType.VertexDeclaration
	{
		get { return VertexDeclaration; }
	}
}

Quad Class:

public class Quad
{
	static QuadVertex[] m_verts;
	static int[] m_indices;

	public int Key = -1;

	//This is a vertex which contains all of our instance info. You can use it as either a data container
	//or as a vertex to be used by a vertex shader.
	public QuadInstanceVertex Info;

	private void Init(Vector3 position, Vector3 velocity, Vector3 normal, Vector3 up, Vector3 startSize, Vector3 endSize, Color startColor, Color endColor, Vector2 time)
	{

		BuildVerts();
		BuildIndices();

		Info = new QuadInstanceVertex(position, velocity, normal, up, startSize, endSize, startColor, endColor, time);
	}

	public Quad()
	{
		BuildVerts();
		BuildIndices();
	}

	/// <summary>
	/// Creates a quad based on the given values
	/// </summary>
	/// <param name="position">The center position of the quad</param>
	/// <param name="normal">The normal direction indicates the facing direction for the quad</param>
	/// <param name="orientation">This is the orientation of the quad around the normal axis</param>
	/// <param name="size">This is the scaled size of the quad</param>
	public Quad(Vector3 position, Vector3 normal, Vector3 up, float size, float orientation = 0)
	{
		Init(position, Vector3.Zero, normal, up, new Vector3(size,size, orientation), new Vector3(size,size, orientation), Color.White, Color.White, new Vector2(0,-1));
	}

	/// <summary>
	/// Creates a POINT SPRITE at the given location. Use HLSL code for the rest.
	/// </summary>
	/// <param name="center">the center position of the point sprite</param>
	/// <param name="size">the size of the point sprite</param>
	/// <param name="orientation">the rotation around the normal axis for the point sprite</param>
	public Quad(Vector3 position, float size, float orientation = 0)
	{
		Init(position, Vector3.Zero, Vector3.Zero, Vector3.Up, new Vector3(size, size, orientation), new Vector3(size, size, orientation), Color.White, Color.White, new Vector2(0, -1));
	}

	/// <summary>
	/// Creates a generalized quad for use with hardware instancing.
	/// </summary>
	/// <param name="position">This is the position in the game world</param>
	/// <param name="velocity">This is how much the quad moves each frame</param>
	/// <param name="normal">QUAD Only: This is the facing direction of the quad. Point sprites and billboards will derive this value based on camera position.</param>
	/// <param name="startSize">Starting scale and rotation: X = width, Y = height, Z = initial radian rotation</param>
	/// <param name="endSize">Ending scale and rotation: X = width, Y = height, Z = change in rotation over time</param>
	/// <param name="startColor">The starting color values for tinting. Use Color.White if you don't want tinting</param>
	/// <param name="endColor">The ending color values for tinting. Use Color.White if you don't want tinting</param>
	/// <param name="time">X = Birth time in gametime seconds. Y = lifespan in seconds. Set lifespan to -1 if the quad is static. Default: (0, -1)</param>
	public Quad(Vector3 position, Vector3 velocity, Vector3 normal, Vector3 up, Vector3 startSize, Vector3 endSize, Color startColor, Color endColor, Vector2 time)
	{
		Init(position, velocity, normal, up, startSize, endSize, startColor, endColor, time);
	}

	static void BuildIndices()
	{
		if (m_indices == null)
		{
			m_indices = new int[6];

			//create the indicies for this quad. Note: winding order is in clockwise order.
			m_indices[0] = 0;
			m_indices[1] = 1;
			m_indices[2] = 2;
			m_indices[3] = 0;
			m_indices[4] = 2;
			m_indices[5] = 3;
		}
	}

	/// <summary>
	/// This gets six indicies for this quad.
	/// The indicies can then be inserted into an index buffer.
	/// </summary>
	/// <returns>Six indicies for drawing a triangle list</returns>
	public static int[] Indicies
	{
		get
		{
			if (m_indices == null)
				BuildIndices();

			return m_indices;
		}
	}

	static void BuildVerts()
	{
		if (m_verts == null)
		{
			m_verts = new QuadVertex[4];

			m_verts[0] = new QuadVertex(new Vector3(-1, -1, 0), new Vector2(0, 1));    //bottom left corner
			m_verts[1] = new QuadVertex(new Vector3(-1, 1, 0), new Vector2(0, 0));    //top left corner
			m_verts[2] = new QuadVertex(new Vector3(1, 1, 0), new Vector2(1, 0));    //top right corner
			m_verts[3] = new QuadVertex(new Vector3(1, -1, 0), new Vector2(1, 1));    //bottom right corner
		}
	}

	public static QuadVertex[] Verts
	{
		get
		{
			if (m_verts == null)
				BuildVerts();
			
			return m_verts;
		}
	}

}



#5181090 I need some guidance please

Posted by slayemin on 17 September 2014 - 01:37 PM

Okay... Let's take a step back. (Warning: ample amounts of coffee has been consumed!)

1. It's turn based.

2. It's a card game

3. It's multiplayer

4. It's all in your head at the moment

 

Therefore, the number one first step you NEED to do is get it out of your head and onto something solid. As it stands at the moment, there is no reason this needs to be a computer game at the moment. What you want to do right now is prototype the hell out of this card game. That's really easy for a turn based multiplayer card game. Get a bunch of paper, some scissors, pens/pencils, a couple friends, and make some cards. Explain to your friends how the card game is played and then play it. Does it work? do the mechanics work? Is it fun? Do you actually have a game on your hands? What needs to be tweaked/modified? It's super easy for you to modify all of the game rules when they aren't written into code!

 

Once you have a super fun, well balanced card game prototype working in the physical world, THEN think about creating it on the computer. You'll then seriously know what works and what doesn't work and you won't be scratching your head at the keyboard wondering if a game mechanic works and trying to test out ideas in isolation by writing lots of code which may or may not get thrown away. You'll have the game you want to make, so the rest of the struggle in your development will only consist of writing up the code and the networking, rather than trying to design and develop simultaneously (don't do this! okay?! Design first, develop second! trust me. Doing this will save you months of time and rework!!! You can thank me later.)

 

Let's talk about why you NEED to get this game out of your head and onto physical medium (documentation, spreadsheets, cards, rulebooks, etc). IF you are building this game by yourself, you need to keep your rules straight. If they sit in your head, you will forget. Head space knowledge shifts like the winds, so tamp it down by writing it down. If you don't, you're going to have constantly changing requirements which will be a nightmare to code for. IF you are going to bring OTHER PEOPLE onto the project, then you need to COMMUNICATE to them what needs to be built in very explicit detail! Communication is the word of the day. If there's anything YOU need to do really well, it is communicate your idea perfectly so that everyone understands it exactly the same, exactly how you envisioned it, and everyone on the team knows exactly what needs to be done. None of this can happen if the whole idea is in your head. Nobody is a mind reader. They can't reach in there and figure out what's going on in there, and if you say something, there is a high chance that it can be interpreted wrong. Therefore, super good documentation, preferably a physical prototype of the game, and a way for all team members to talk to each other spontaneously in a face to face setting will be the best thing you can do for your project at this stage.

Chances are also very good that the idea in your head seems to work in your head, but there's this nasty thing about head space ideas: they are like dreams. If you look at them at a quick glance, it all appears rosy and clean. But if you start focusing in on something in particular, it gets murky and uncertain. Determining how exactly that vision should look at the granular level is what separates the pros from the amateurs. Thus, documenting and prototyping the details is to your benefit because it brings up these weak points in the vision and forces you to address them.

 

Once you have all of this done, and only when you have this done, should you start looking at building the game in the digital space.




#5180763 Quad trees vs R-trees vs Spacial hashmaps

Posted by slayemin on 16 September 2014 - 11:33 AM

I've only implemented quad trees and octrees. Here is what I like about them:
*They are easy to understand conceptually

*They don't take very long to implement (4-12 hours)

*They can be used recursively, which shortens code

*The runtime is O(LogN)

*Rather than trying to collide with objects directly, you collide with the object container first to cull out unneeded collision checks
 

Here's what I don't like:

*They can be a pain in the ass to debug. How many branches deep do you want to sift through in your IDE's debugger before you find your object?

*Generally, you just dump the tree and rebuild it every frame. What a waste of CPU time :(  (also, its a waste of programmer time to try to optimize it -- not because it can't be done, but because of all the bugs you'll probably introduce with the additional complexity)

 

I'm sure there are better solutions out there (maybe KD trees?). Today, I'd still implement a quadtree or octree (or maybe try a balanced KD tree).




#5180622 Using C++ codebase

Posted by slayemin on 15 September 2014 - 11:51 PM

Your questions are a bit vague, even though you're trying to be precise. I'll try to answer them though...

 

"What is the general structure of a larger C++ program?"

That really depends on what the program is trying to do and how it is architected. This is very hard to answer because it's similar to asking "What is the general structure of a building?"

So... for most C++ programs, you usually have tons of files broken down by classes. Usually you have a header file which defines all of the class headers, variables, functions, etc. Then the CPP file usually contains the implementations of the "stuff" described in the header file. I'm sure you already know that though, so I'm not sure what you're trying to answer. The second bit is how all of those classes interact with each other (aka, object oriented programming). When enough classes are used often enough and are general enough to be used all over the place, people create "libraries" which expose the commonly used data structures (such as lists/vectors, hash tables, sorting methods, etc). Usually you don't care about the implementation so long as it works as expected.

 

I think that if you're trying to incorporate the exposed functions from "math.h" into UE4, you'd probably want to create wrappers for each of the methods in math.h you'd like to use in UE4. But, as I recall, UE4 already comes with a robust math library so you're probably wasting your time and instead should be trying to figure out how to use their math library. I'm sure the differences in the implementation of Sine and Cosine yield the same results, right? Regardless, it doesn't sound right that you should be having trouble with including a header file into your program. Usually when you include a header file, you're giving the compiler a path to a distinct file which exists somewhere on the hard drive. When the compiler compiles the program, it takes all of the header files and cpp files and creates a single binary executable. If your project includes or references an external library (like a DLL), then the program it compiles will try to reference the DLL instead of adding its binary into your executable. For this reason, it is very important that the DLL on all computers which run your program are the same version or support backwards compatibility. Many games will bundle the dependent DLL's as a part of the installation process just to make sure that the right versions are being used.

If you're using Visual Studio, usually you can step through your code line by line, and step into functions to see where the implementation lives (or just highlight a method and press F12). This should tell you where any calls to math.h are being made. This also gives you a pretty good idea on how stuff is laid out in the framework you're using. For most programs/frameworks/libraries, the best way to get an understanding of their layout is to read the Application Programming Interface documentation. It's always specific to the API, so that's about all the help I can give you on program internals... Hope this helps!




#5180615 Can you guys help get me started making a 3d game?

Posted by slayemin on 15 September 2014 - 10:52 PM

1. Forget about programming languages for a bit and focus on the mathematics. The mathematics is 1000% more important than picking a programming language. The fact is this: You can make a 3D game in any programming language, but the mathematics is going to be the same regardless of whatever language you pick. Programming languages are interchangeable for the most part. Once you know how to do the math and think like a programmer, you will only be asking "How do I implement XYZ in this programming language?! Oh, let me just look that up in the API.". So, the best mathematics you can get good at to prepare yourself for 3D programming are the following:
A) Linear Algebra (vectors, matricies, dot products, cross products, etc)
B) Trigonometry

C) Algebra
D) Calculus

 

2. Don't focus on building a team quite yet. Focus on getting really good at building stuff. You can use "programmer art" as placeholders for the art assets you'll eventually need.

 

3. You actually don't want a tutorial. They're too short and usually don't get deep enough. You want a few really good books. The beauty of books is that they're usually comprehensive, very well written, and you don't have to filter out any chaff to get to the good stuff. It's all good!

 

4. You didn't ask this, but your next question needs to be a question you ask yourself: "How much of my life am I actually willing to commit/dedicate to making games?" If the answer isn't "Decades!", then you need to think long and hard about your motivations. It has literally taken me 16 years to get to where I am today. I have been too stubborn and persistent to quit, and I slogged through some really hard work to get here. Are you willing to do that? To go through the pain and dedication it takes to really get the skills it takes to make a game, and the grind it takes to slog through the hard parts of making a game, ie, the non-glorious, mundane, not fun, repetitious parts?




#5180195 Generating a minimap

Posted by slayemin on 14 September 2014 - 12:49 AM

Hmm, I was able to get a minimap for my terrain system up and running in about 30 minutes. Here is a screenshot sample:
minimap.jpg


These are my design requirements:
1. The minimap dimensions may be any size. There's no relationship to the actual terrain system. The terrain could be 512x512, 1024x1024, 128x768, etc. The minimap is going to be something like 256x256.

2. I want to have height information color coded into the minimap, with contour lines to give it more of a "map" look. You should be able to tell what the elevation is.

 

 

This was my approach:

1. We get a width/height for the minimap from the user and we return a Texture2D to them.

2. We're going to go through each pixel in the minimap and map it to a position on the terrain.

2a: Since the terrain and minimap dimensions are independent, I am going to want to normalize my sample point.

 

For example, if my minimap is 256x256 and my terrain is 512x1024 (arbitrary size), and I am sampling the pixel (50,60) on the minimap, the normalized position is going to be:
normX = 50 / 256;
normY = 60 / 256;


Then, we sample the height map or terrain system by taking the normalized coordinate and switching it into their coordinate space...

sampleX = normX * terrainWidth;
sampleY = normY * terrainHeight;

 

And then we sample the terrain with these coordinates.
You would only super-sample neighboring pixels if the size of the minimap is greater than the size of the terrain (but why would you ever do that?!).
I figure you can just say that a map is a rough sketch of what the terrain actually looks like. If the spacing between sample points skips a few data points on the terrain, who cares? Can the player tell the difference? Nope! So don't over-engineer it.


Anyways, here is my implementation code which generated the minimap above:
 

public Texture2D GenerateMinimap(int width, int height)
{
	Texture2D ret = new Texture2D(BaseSettings.Graphics, width, height);
	Color[] data = new Color[width * height];

	float maxElevation = m_settings.MaxElevation;

	float t0 = 0;                           //sand height
	float t1 = maxElevation / 4.0f;           //grass height
	float t2 = t1 * 2;    //granite height
	float t3 = t1 * 3;    //snow height

	Color sand = new Color(255, 128, 0);
	Color dirt = new Color(128, 64, 0);
	Color grass = new Color(0, 192, 0);
	Color granite = new Color(192, 192, 192);
	Color snow = new Color(240, 240, 240);

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			float h = m_heightMap.SampleTexel(x, y);
			Color c;

			if (h % 32 == 0)
				c = new Color(0, 0, 0);
			else
			{
				if (h < t1)
				{
					//should lerp colors
					float f = h / t1;
					c = Color.Lerp(sand, dirt, f);
				}
				else if (h >= t1 && h < t2)
				{
					float f = (h - t1) / (t2 - t1);
					c = Color.Lerp(dirt, grass, f);
				}
				else if (h >= t2 && h < t3)
				{
					float f = (h - t2) / (t3 - t2);
					c = Color.Lerp(grass, granite, f);
				}
				else
				{
					float f = (h - t3) / (maxElevation - t3);
					c = Color.Lerp(granite, snow, f);
				}
			}

			data[y * height + x] = c;
		}
	}

	ret.SetData<Color>(data);
	return ret;
}



#5180191 Which basics do you need to know

Posted by slayemin on 14 September 2014 - 12:23 AM

Thanks everyone,

 

When i read my post back, while thinking of what you guys said, i understand that it is vague for other people,

so sorry for that.

Basically what i mean't is: Which basic aspects of the c++ language, such as functions, statements, classes, etc.

are required to start making small games (like pong at the beginning and then all the way up to something 3D) 

and which aren't required, but will be very handy to know.

For example: Maybe you can say that knowing about and how to use classes is required, but something like polyformism isn't (I saw this in another topic)

 

I hope this might be less vague and possible to answer,

Dalphin

C++ isn't the only language (or even best language) you can make games in. Everything you learn about programming is another tool you get to put in your tool belt which you can later use to better solve the programming problems you'll face during any programming project (doesn't have to be games!).

 

Think of it like building a house. You are a carpenter. At the most basic level, you can stack boards on top of each other (like a log cabin) to build a very crude, rudimentary house. If you learn how to use a hammer and nails, you can build a slightly less crude house. Maybe, if you learn how to do "framing", you can build together a house which is much more structurally sound and uses less wood. But, all you have is a hammer and nails, so you're a bit limited by the tools you can use. You can build a better house if you can learn how to use a saw to cut wood into smaller pieces. This would let you build something that actually looks something like a shack, though you still won't be able to build anything much more complicated. Perhaps, you eventually learn how to pour concrete and discover how to build a foundation for your house building projects. This makes your houses more stable. Maybe you also learn something about load bearing walls and this lets you build a house with multiple stories. You find that it takes a lot of trial and error to build the houses, so rather than starting to slap some wood together, you decide to spend a bit of time drawing up blueprints for what you're going to build. After all, it's a lot more efficient to make your errors in your blueprints and correct them than to tear down a bit of construction. The more skills, techniques, and tools you have at your disposal, the better and faster you can build something. 

 

The same principles apply to programming. You are just barely learning how to use a hammer and nails for making games. At best, you can build a very crude game like pong, but you're telling us that you don't want to learn how to use the tools and techniques of the trade. By avoiding learning how to use tools/techniques which will greatly help you, you are severely limiting your capabilities. You're essentially saying, "I don't want to learn how to use a saw! Instead, when I need to cut a board in half, I will use my teeth and chew it in half by biting out teeny slivers of wood." How silly, right? Learning the fundamentals of control structures, loops, functions and variables is the very foundation of programming. Learning how to assemble these core primitives into classes is 100% essential to building any sort of game with any complexity. Polymorphism is a great and valuable tool to have at your disposal, even if you're not going to use it all the time. It's there if you need it! Never avoid learning something new because its hard or new.

 

The other great big tool you'll be using every day during the programming of games is mathematics. It too is a super duper valuable and useful tool. Getting better at math is getting better at making games. Don't limit yourself and your capabilities by trying to get by without learning how to use a valuable tool.

 






PARTNERS