SiliconStudio / xenko

Old repo for Xenko Game Engine. Please use https://github.com/xenko3d/xenko instead.
http://xenko.com
1.54k stars 344 forks source link

Loading asset in Component Renderer breaks Paradox Studio Scene View #315

Open wanderwurst opened 9 years ago

wanderwurst commented 9 years ago

I am trying to create a component similar to the Sprite component, but dumbed down. All it should do is to draw pseudo light effects using the same texture all over (a circular gradient) in additive blend mode. I try to load the texture in InitializeCore() which works fine when I run the game from VisualStudio. But opening it in Paradox Studio stops the main scene from rendering there. When I remove the Assets.Load() call and instead create a square texture in memory, everything works fine.

I guess this is either some bug or I am just not supposed to use the Assets.Load() call in the Renderer. But then, when and where would I load a texture that I want to use here?

Also, I guess PStudio catches an exception on rendering and then stops. Is there some kind of logging for these exceptions that would make finding my bad code somewhat easier?

Here is my component code, just in case: Renderer

using MyGame1.CustomComponents;
using MyGame1.CustomProcessors;
using SiliconStudio.Core.Mathematics;
using SiliconStudio.Paradox.Engine;
using SiliconStudio.Paradox.Graphics;
using SiliconStudio.Paradox.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MyGame1.CustomRenderers
{
    public class SpriteLightRenderer : EntityComponentRendererBase
    {
        private Sprite3DBatch _sprite3DBatch;
        private Quaternion _toScreenRotation = Quaternion.RotationX(-MathUtil.PiOverTwo);
        private Texture _pointLightTexture = null;
        private RectangleF _pointLightRegion;

        protected override void InitializeCore()
        {
            base.InitializeCore();

            _sprite3DBatch = new Sprite3DBatch(GraphicsDevice);

            // --- commenting out from here...
            _pointLightTexture = Assets.IsLoaded("PointLight") ? Assets.Get<Texture>("PointLight") : Assets.Load<Texture>("PointLight");

            if (_pointLightTexture != null)
            {
                _pointLightRegion = new RectangleF(0, 0, _pointLightTexture.Width, _pointLightTexture.Height);
            }
            else
            // --- to here makes it run fine with Paradox Studio
            {
                // Create a square texture from scratch
                const int C_SQUARE_SIZE = 16;
                uint[] particleData = Enumerable.Repeat<uint>(uint.MaxValue, C_SQUARE_SIZE * C_SQUARE_SIZE).ToArray();
                _pointLightTexture = Texture.New2D<UInt32>(GraphicsDevice, C_SQUARE_SIZE, C_SQUARE_SIZE, PixelFormat.B8G8R8A8_UNorm_SRgb, particleData);
                _pointLightRegion = new RectangleF(0, 0, C_SQUARE_SIZE, C_SQUARE_SIZE);
            }
        }

        protected override void PrepareCore(RenderContext context, RenderItemCollection opaqueList, RenderItemCollection transparentList)
        {
            var spriteLightProcessor = SceneInstance.GetProcessor<SpriteLightProcessor>();
            if ((spriteLightProcessor == null) || (spriteLightProcessor.RenderList == null))
                return;

            foreach (var spriteLight in spriteLightProcessor.RenderList)
            {
                // Perform culling on group and accept
                if (!CurrentCullingMask.Contains(spriteLight.Key.Group))
                    continue;

                opaqueList.Add(new RenderItem(this, spriteLight, int.MaxValue));
                return;
            }
        }

        protected override void DrawCore(RenderContext context, RenderItemCollection renderItems, int fromIndex, int toIndex)
        {
            if ((fromIndex < 0) || (toIndex < fromIndex))
                return;

            var graphicsDevice = context.GraphicsDevice;

            CameraComponent cam = context.GetCurrentCamera();
            if (cam == null)
                return;
            Matrix projection = cam.ViewProjectionMatrix;

            _sprite3DBatch.Begin(projection, SpriteSortMode.Deferred, blendState: graphicsDevice.BlendStates.Additive);

            for (int i = fromIndex; i <= toIndex; i++)
            {
                var renderItem = renderItems[i];
                KeyValuePair<Entity, SpriteLightComponent> entityComponent = (KeyValuePair<Entity, SpriteLightComponent>)renderItem.DrawContext;

                Matrix worldPos = Matrix.AffineTransformation(1, _toScreenRotation, entityComponent.Key.Transform.WorldMatrix.TranslationVector);

                _sprite3DBatch.Draw(texture: _pointLightTexture,
                    worldMatrix: ref worldPos,
                    sourceRectangle: ref _pointLightRegion,
                    elementSize: ref entityComponent.Value.Size,
                    color: ref entityComponent.Value.Color
                    );
            }

            _sprite3DBatch.End();
        }
    }
}

Processor

using MyGame1.CustomComponents;
using SiliconStudio.Core;
using SiliconStudio.Paradox.Engine;
using System.Collections.Generic;

namespace MyGame1.CustomProcessors
{
    public class SpriteLightProcessor : EntityProcessor<SpriteLightComponent>
    {
        public Dictionary<Entity, SpriteLightComponent> RenderList
        {
            get
            {
                if (this.Enabled)
                    return enabledEntities;
                return null;
            }
        }

        public SpriteLightProcessor()
            : base(new PropertyKey[] { SpriteLightComponent.Key })
        {
            Order = ProcessorOrders.SpriteLight;
        }

        protected override SpriteLightComponent GenerateAssociatedData(Entity entity)
        {
            SpriteLightComponent spriteLight = entity.Get<SpriteLightComponent>();
            return spriteLight;
        }
    }
}

Component

using MyGame1.CustomProcessors;
using MyGame1.CustomRenderers;
using SiliconStudio.Core;
using SiliconStudio.Core.Mathematics;
using SiliconStudio.Paradox.Engine;
using SiliconStudio.Paradox.Engine.Design;

namespace MyGame1.CustomComponents
{
    [Display(ProcessorOrders.SpriteLightSort, "SpriteLight", "A simple 2D pseudo light", Expand = ExpandRule.Once)]
    [DataContract("SpriteLightComponent")]
    [DefaultEntityComponentRenderer(typeof(SpriteLightRenderer))]
    [DefaultEntityComponentProcessor(typeof(SpriteLightProcessor))]
    public class SpriteLightComponent : EntityComponent
    {
        public static PropertyKey<SpriteLightComponent> Key = new PropertyKey<SpriteLightComponent>("Key", typeof(SpriteLightComponent));

        public override PropertyKey GetDefaultKey()
        {
            return Key;
        }

        [DataMember(1)]
        public Vector2 Size = Vector2.One;

        [DataMember(2)]
        public Color Color = Color.White;

        public SpriteLightComponent()
        {
        }
    }
}
sinkingsugar commented 9 years ago

You probably did not flag the texture asset as "Root" asset in the game studio. So it's not getting included in the package as not referenced anywhere.

wanderwurst commented 9 years ago

Hm, no, it's marked "root". Also, if it wasn't, wouldn't it fail as well when starting from Visual Studio?

xenux commented 9 years ago

Could you should how you load your asset? Do you have any error message or exception thrown?

xoofx commented 9 years ago

Actually, I'm not sure if root asset are currently well supported within the scene editor and component renderer. We will have to check this.

wanderwurst commented 9 years ago

Thanks for your feedback. @xenux The loading code is right at the beginning of the above post, in the InitializeCore implementation:

_pointLightTexture = Assets.IsLoaded("PointLight") ? Assets.Get<Texture>("PointLight") : Assets.Load<Texture>("PointLight");

I do not get any error messages or exceptions. As said, when I launch directly from VisualStudio, everything works just fine. If I use the component in ParadoxStudio though, the scene editor stops rendering. I do suspect that there is some exception thrown somewhere, but as I asked above, I am a bit lost on where I could get to see this? To me it seems that if I have any kind of bad code in a component, the scene editor just sliently discards any exceptions and stops working. Is there some kind of log I've overlooked?

@xoofx If I understand correctly, a possible workaround would be to have that texture also in use on a model or something?

xenux commented 9 years ago

Sorry I didn't understood that the problem was only in the scene editor.

FYI, you can see scene editor logs in debug log window (Help->Show debug window->Scene).

The problem is that currently when opening a scene in the editor we only build all the asset referenced by the scene (and not all the game). Since your asset is not referenced directly by your scene it can't be found. We planned to rework the scene build workflow in the editor soon and we will be careful to fix that issue at the same time. In the meantime, you can add an artificial reference to your asset in your scene as workaround to force the scene editor to build it before opening the scene. For example a reference as field of an empty script added to an entity of your scene or as texture of a null size sprite, etc... It is quite ugly but should fix the problem waiting for our proper fix.

wanderwurst commented 9 years ago

Thanks for the information. Great to have that debug window! :-) Sorry for not being clear on the scene editor.

I am absolutely aware that this is in beta, so any workaround that works I'm fine with (there is no "ugly" in "it works" ;-) )

Am I supposed to close this issue now or should I do that when a future version implements the unreferenced asset loading?

xenux commented 9 years ago

Great thanks :)

You can keep the issue like this for the moment. We will close it when we fix it.