[sldev] Sculpted prim works in progress

John Hurliman jhurliman at wsu.edu
Sun Apr 29 16:11:55 PDT 2007


I got the MEL script for creating the sculpted prim textures working but 
had no way to test if I did it right or not, so I wrote this:

http://www.jhurliman.org/images/sculptedpreview03.png

It's a C# XNA app, code is attached. One thing that I noticed was my own 
position maps (is there a better name for them? that's what I've been 
calling the textures) create geometry virtually identical to what I see 
in Maya, but the ones I downloaded off of flickr appear to suffer from 
slight JPEG compression artifacts. Will we be able to upload the 
position maps using lossless JPEG2000 settings? Even the raw image files 
only take up ~12.5KB.

John Hurliman
-------------- next part --------------
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace sculptedpreview
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class SculptedPreview : Microsoft.Xna.Framework.Game
    {
        const string filename = "input.bmp";
        const int LOD = 64;

        GraphicsDeviceManager graphics;
        ContentManager content;
        private BasicEffect effect;
        Texture2D positionMap;
        VertexPositionColor[] vertexGrid;
        ushort[] inds;
        VertexBuffer vertexBuffer;
        IndexBuffer indexBuffer;
        SpriteBatch batch;
        Rectangle topleft = new Rectangle(0, 0, 64, 64);

        public SculptedPreview()
        {
            graphics = new GraphicsDeviceManager(this);
            content = new ContentManager(Services);

            Window.AllowUserResizing = true;
        }


        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            vertexGrid = new VertexPositionColor[LOD * LOD];
            inds = new ushort[LOD * LOD * 6];

            batch = new SpriteBatch(graphics.GraphicsDevice);

            // Setup the effect. Turn off all the lighting stuff until we are calculating normals
            effect = new BasicEffect(graphics.GraphicsDevice, null);
            //effect.Alpha = 1.0f;
            //effect.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f);
            //effect.AmbientLightColor = new Vector3(0.2f, 0.2f, 0.2f);
            //effect.SpecularColor = new Vector3(0.0f, 0.0f, 0.0f);
            //effect.SpecularPower = 0.2f;
            //effect.AmbientLightColor = new Vector3(0.15f, 0.15f, 0.2f);
            //effect.DirectionalLight0.Enabled = true;
            //effect.DirectionalLight0.DiffuseColor = Vector3.One;
            //effect.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(0.0f, 0.0f, -1.0f));
            //effect.DirectionalLight0.SpecularColor = Vector3.One;
            //effect.LightingEnabled = true;

            effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                                  (float)Math.PI / 4.0f,  // 2 PI Radians is 360 degrees, so this is 45 degrees
                                  (float)Window.ClientBounds.Width / (float)Window.ClientBounds.Height,
                                  0.1f, 2048.0f);
            effect.World = Matrix.CreateTranslation(new Vector3(0.0f, 0.0f, 0.0f));
            effect.View = Matrix.CreateLookAt(new Vector3(1.0f, 1.0f, 1.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));

            base.Initialize();
        }


        /// <summary>
        /// Load your graphics content.  If loadAllContent is true, you should
        /// load content from both ResourceManagementMode pools.  Otherwise, just
        /// load ResourceManagementMode.Manual content.
        /// </summary>
        /// <param name="loadAllContent">Which type of content to load.</param>
        protected override void LoadGraphicsContent(bool loadAllContent)
        {
            if (loadAllContent)
            {
                // TODO: Load any ResourceManagementMode.Automatic content
            }

            try
            {
                // Load the position map input file
                positionMap = Texture2D.FromFile(graphics.GraphicsDevice, filename);
            }
            catch (System.IO.FileNotFoundException)
            {
                Exit();
                return;
            }

            // Create the vertex buffer
            for (int y = 0; y < LOD; y++)
            {
                for (int x = 0; x < LOD; x++)
                {
                    // Get the position map data
                    Color[] bits = new Color[positionMap.Width * positionMap.Height];
                    positionMap.GetData<Color>(bits);

                    // Create a vertex position from the RGB channels in the current pixel
                    Vector3 position = new Vector3(
                        ((float)bits[y * LOD + x].R - 128) / 256.0f,
                        ((float)bits[y * LOD + x].G - 128) / 256.0f,
                        ((float)bits[y * LOD + x].B - 128) / 256.0f);

                    vertexGrid[y * LOD + x] = new VertexPositionColor(position, Color.Red);
                }
            }

            vertexBuffer = new VertexBuffer(graphics.GraphicsDevice, typeof(VertexPositionColor), vertexGrid.Length, 
                ResourceUsage.WriteOnly);
            vertexBuffer.SetData<VertexPositionColor>(vertexGrid);

            // Create the index buffer
            int i = 0;
            for (int y = 0; y < LOD - 1; y++)
            {
                for (int x = 0; x < LOD - 1; x++)
                {
                    inds[i++] = (ushort)(y * LOD + x);
                    inds[i++] = (ushort)(y * LOD + (x + 1));
                    inds[i++] = (ushort)((y + 1) * LOD + (x + 1));

                    inds[i++] = (ushort)(y * LOD + x);
                    inds[i++] = (ushort)((y + 1) * LOD + x);
                    inds[i++] = (ushort)((y + 1) * LOD + (x + 1));
                }

                // Wrap the last cell in the row around
                inds[i++] = (ushort)(y * LOD + (LOD - 1));
                inds[i++] = (ushort)(y * LOD);
                inds[i++] = (ushort)((y + 1) * LOD);
            }

            indexBuffer = new IndexBuffer(graphics.GraphicsDevice, sizeof(ushort) * inds.Length, ResourceUsage.None, 
                IndexElementSize.SixteenBits);
            indexBuffer.SetData<ushort>(inds, 0, inds.Length, SetDataOptions.None);
        }


        /// <summary>
        /// Unload your graphics content.  If unloadAllContent is true, you should
        /// unload content from both ResourceManagementMode pools.  Otherwise, just
        /// unload ResourceManagementMode.Manual content.  Manual content will get
        /// Disposed by the GraphicsDevice during a Reset.
        /// </summary>
        /// <param name="unloadAllContent">Which type of content to unload.</param>
        protected override void UnloadGraphicsContent(bool unloadAllContent)
        {
            if (unloadAllContent)
            {
                content.Unload();
            }

            // Unload any ResourceManagementMode.Manual content
        }


        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            base.Update(gameTime);
        }


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            graphics.GraphicsDevice.RenderState.FillMode = FillMode.Solid;

            // Draw the original position map in the top-left corner
            batch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
            batch.Draw(positionMap, topleft, Color.White);
            batch.End();

            using (VertexDeclaration decl =
                   new VertexDeclaration(graphics.GraphicsDevice, VertexPositionColor.VertexElements))
            {
                graphics.GraphicsDevice.VertexDeclaration = decl;
                graphics.GraphicsDevice.RenderState.CullMode = CullMode.None;
                graphics.GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;

                graphics.GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionColor.SizeInBytes);
                graphics.GraphicsDevice.Indices = indexBuffer;

                effect.Begin();
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertexGrid.Length,
                        0, inds.Length / 3);
                    pass.End();
                }
                effect.End();
            }

            base.Draw(gameTime);
        }
    }
}


More information about the SLDev mailing list