Jump to content

[solved] Display a primitive around a vessel


Recommended Posts

Hi!

I'm trying to display a simple sphere primitive (from GameObject.CreatePrimitive(PrimitiveType.Sphere) ) centered around a vessel in the tracking station, but to no avail... 

No fancy stuff, for a start I'd like the sphere to be a simple solid color.

Problem is I even don't know if the sphere is displayed somewhere but can't be seen or if it's not displayed at all.

Below is a minimal repro code for this problem:

 

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

namespace TestPrimitive
{
    [KSPAddon(KSPAddon.Startup.TrackingStation, false)]
    public class TestPrimitive : MonoBehaviour
    {
        GameObject m_SphereObj;
        List<Vessel> m_vesselList;
        bool m_inTrackingStation;
        bool m_isVesselFecthed;
        Vessel m_currentVessel;

        void Start()
        {

            // we want to do something only in the tracking station
            if (HighLogic.LoadedScene != GameScenes.TRACKSTATION)
                return;

            // Create a sphere object
            m_SphereObj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            m_SphereObj.layer = 10;

            // disable collider for the sphere
            var collider = m_SphereObj.GetComponent<Collider>();
            collider.enabled = false;
            //Destroy(collider); // should the collider be destroyed?
            

            // get object renderer and apply attributes
            var renderer = m_SphereObj.GetComponent<Renderer>();
            renderer.material.color = Color.red;
            renderer.receiveShadows = false;
            renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
            
            // enable the renderer later
            renderer.enabled = false;
            
        }


        void Update()
        {
            // we want to do something only in the tracking station
            if (HighLogic.LoadedScene != GameScenes.TRACKSTATION)
            {
                m_inTrackingStation = false;
                return;
            }
            
            // check mapview and its camera
            if (!MapView.MapIsEnabled || MapView.MapCamera == null)
            {
                m_inTrackingStation = false;
                return;
            }

            m_inTrackingStation = true;
            if (!m_isVesselFecthed)
            {
                FetchVessels();                
                m_isVesselFecthed = true;
            }

            if (m_currentVessel != null)
            { 
                // sphere parent tranform is the vessel transform
                m_SphereObj.transform.parent = m_currentVessel.transform;
                // not sure about this one...
                m_SphereObj.transform.position = ScaledSpace.LocalToScaledSpace(m_currentVessel.GetWorldPos3D());
                // is it big enough?
                m_SphereObj.transform.localScale = Vector3.one * 5000f;
                m_SphereObj.transform.localPosition = Vector3.zero;
                m_SphereObj.transform.localRotation = Quaternion.identity;
                // enable renderer
                var renderer = m_SphereObj.GetComponent<Renderer>();
                renderer.enabled = true;
                //m_SphereObj.SetActive(true);
            }
            else
            {
                var renderer = m_SphereObj.GetComponent<Renderer>();
                renderer.enabled = false;
                //m_SphereObj.SetActive(false);
            }

        }

        void onVesselIconClicked(Vessel vessel)
        {
            // check vessel instance
            if (vessel == null)
            {
                return;
            }

            // must be in tracking station
            if (!m_inTrackingStation)
            {
                return;
            }


            // get currently selected vessel
            m_currentVessel = vessel;
        }

        void FetchVessels()
        {
            if (m_vesselList == null)
            {
                m_vesselList = new List<Vessel>();
            }


            for (int i = 0; i < FlightGlobals.Vessels.Count; ++i)
            {                
                Vessel vessel = FlightGlobals.Vessels[i];
                // check that the vessel is known
                if (vessel.DiscoveryInfo.HaveKnowledgeAbout(DiscoveryLevels.StateVectors))
                {
                    m_vesselList.Add(vessel);
                }

                // callback called when vessel icon is clicked
                vessel.orbitRenderer.onVesselIconClicked.Add(new EventData<Vessel>.OnEvent(onVesselIconClicked));
            }
        }
    }
}

Setting debug logs around all code parts I can see that all the code is executed though, which makes me wonder what is wrong.

Thanks a lot for reading!
 

Edited by neitsa
changed topic: problem solved
Link to comment
Share on other sites

I could be wrong, but I'm thinking your scale factor may be too high.

m_SphereObj.transform.localScale = Vector3.one * 5000f;

Try smaller values.

Failing that, you should also be using one of KSP's shaders instead of the default applied when creating your primitive.

// get object renderer and apply attributes
            var renderer = m_SphereObj.GetComponent<Renderer>();
            renderer.material.color = Color.red;
            renderer.receiveShadows = false;
            renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;

Have a look at doing the following:

 Material material = new Material(Shader.Find("KSP/Diffuse"));
 material.color = Color.green;
 

 

Edited by udk_lethal_d0se
Added another probable cause.
Link to comment
Share on other sites

Hi guys !

Thanks for your answers.

Tried you suggestions but none of them  seem to make the sphere appear around the selected vessel.

  • removed layer (commented the line)
  • localScale set to:  Vector3.one * 1500f; (instead of 5000.0)
  • Using a shader as material (note that I used the materiel as the renderer material):
            var renderer = m_SphereObj.GetComponent<Renderer>();
            Material material = new Material(Shader.Find("KSP/Diffuse"));
            renderer.material = material;
            renderer.material.color = Color.red;

I tried a lot of things but I can't get even one to work though... I'm really short on ideas. Maybe I should try with a mesh instead, but that seems quite crazy to not being able to display a simple primitive in game.

If you guys have other ideas I'll be glad to test them :)

Thanks a lot !

Edited by neitsa
Link to comment
Share on other sites

9 minutes ago, neitsa said:

Hi guys !

Thanks for your answers.

Tried you suggestions but none of them  seem to make the sphere appear around the selected vessel.

  • removed layer (commented the line)
  • localScale set to:  Vector3.one * 1500f; (instead of 5000.0)
  • Using a shader as material (note that I used the materiel as the renderer material):

            var renderer = m_SphereObj.GetComponent<Renderer>();
            Material material = new Material(Shader.Find("KSP/Diffuse"));
            renderer.material = material;
            renderer.material.color = Color.red;

I tried a lot of things but I can't get even one to work though... I'm really short on ideas. Maybe I should try with a mesh instead, but that seems quite crazy to not being able to display a simple primitive in game.

If you guys have other ideas I'll be glad to test them :)

Thanks a lot !

You don't need to not use a primitive You don't need a mesh, a primitive will work, I aided the creator of RoverScience with something similar and he's using a primitive for that solution.  I think the issue should/could be boiled down to the location it's being spawned.

I'm looking at your code, and can see you're setting the parent first; I would personally set the parent after the object has been spawned and positioned. Essentially your primitive is becoming a child of the vessel, then being positioned in 3D space, but you're also resetting that position locally to zero afterwards.

Try this:

m_SphereObj.transform.position = ScaledSpace.LocalToScaledSpace(m_currentVessel.GetWorldPos3D());
m_SphereObj.transform.parent = m_currentVessel.transform;
m_SphereObj.transform.localScale = Vector3.one * 5000f;

Let me know your results, as I'm at work and don't have access to my dev laptop.

Edited by udk_lethal_d0se
Link to comment
Share on other sites

23 hours ago, udk_lethal_d0se said:

I still think it's to do with the scaled space coordinates

Yep, the problem is probably there.

I'll try various things to see what's wrong:

  • Try to display a sphere around kerbin to see if I can at least display something in the tracking station
  • Try to move the sphere a bit (if previous step was OK), near one of my sats and log their coordinates (scaled and "absolute" for both sphere and sat)

I should get an idea of what I'm doing wrong. I'll keep this thread updated :)

Thank you!

 

 

Link to comment
Share on other sites

12 minutes ago, neitsa said:

Yep, the problem is probably there.

I'll try various things to see what's wrong:

  • Try to display a sphere around kerbin to see if I can at least display something in the tracking station
  • Try to move the sphere a bit (if previous step was OK), near one of my sats and log their coordinates (scaled and "absolute" for both sphere and sat)

I should get an idea of what I'm doing wrong. I'll keep this thread updated :)

Thank you!

 

 

Sounds like a good troubleshooting plan. I'll keep an eye on this thread, I like to help where I can. 

Link to comment
Share on other sites

I came over to post my source, but it looks like you already got the idea! Let me know if I can help you. From the looks of things it's the probably the positioning that's the issue. dw though, if udk_lethal_d0se is helping you, you're in good hands.

Link to comment
Share on other sites

Hi guys o/

quick update, I can now display a sphere around a celestial body in the tracking station, yeah :) \o/

Here's red-tomato Kerbin:

http://imgur.com/a/8bmtx

I didn't changed many things in the code. Here's the code if that can help someone:

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

namespace ComViz
{
    [KSPAddon(KSPAddon.Startup.TrackingStation, false)]
    public class ComViz : MonoBehaviour
    {
        GameObject m_SphereObj;
        MapObject m_MapObject;
        Renderer m_renderer;

        /// <summary>
        /// Overrides the Start method for a MonoBehaviour plugin.
        /// </summary>
        void Start()
        {
            //KspUtils.Logging.logLevel = KspUtils.LogLevel.DEBUG;
            //KspUtils.Logging.EnterFunction();

            // we want to do something only in the tracking station
            if (HighLogic.LoadedScene != GameScenes.TRACKSTATION)
                return;

            // Create a sphere object
            m_SphereObj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            m_SphereObj.layer = 10;

            // disable collider for the sphere
            var collider = m_SphereObj.GetComponent<Collider>();
            collider.enabled = false;
            Destroy(collider);

            // get object renderer and apply attributes
            m_renderer = m_SphereObj.GetComponent<Renderer>();
            var shader = Shader.Find("KSP/Diffuse");
            if(shader == null)
            {
                //KspUtils.Logging.Debug("no shader");
            }

            m_renderer.material = new Material(shader);
            m_renderer.material.color = new Color(Color.red.r, Color.red.g, Color.red.b, 0.5f);
            m_renderer.receiveShadows = false;
            m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
            m_renderer.enabled = false;

            GameEvents.onPlanetariumTargetChanged.Add(OnPlanetariumTargetChanged);            
        }

        // called when target in tracking station changed
        private void OnPlanetariumTargetChanged(MapObject mapObject)
        {
            if(mapObject == null)
            {
                return;
            }

            switch(mapObject.type)
            {
                // we only want to do something with CBs for a start
                case MapObject.ObjectType.CelestialBody:
                    //KspUtils.Logging.Debug("CelestialBody: " + mapObject.name);
                    m_MapObject = mapObject;
                    break;
            }
        }

        void Update()
        {
            // we want to do something only in the tracking station
            if (HighLogic.LoadedScene != GameScenes.TRACKSTATION)
            {
                m_MapObject = null;
                return;
            }

            // must have a map view and a map camera
            if (!MapView.MapIsEnabled || MapView.MapCamera == null)
            {
                m_MapObject = null;
                return;
            }

            
            // if we have a map object from the planetarium
            if (m_MapObject != null)
            {
                m_SphereObj.transform.position = ScaledSpace.LocalToScaledSpace(m_MapObject.transform.position);
                m_SphereObj.transform.parent = m_MapObject.transform;
                m_SphereObj.transform.localScale = Vector3.one * 5000f;
                m_SphereObj.transform.localPosition = Vector3.zero;
                m_SphereObj.transform.localRotation = Quaternion.identity;
                var renderer = m_SphereObj.GetComponent<Renderer>();
                renderer.enabled = true;

            }
            else
            {
                //KspUtils.Logging.Debug("No vessel");
                var renderer = m_SphereObj.GetComponent<Renderer>();
                renderer.enabled = false;

            }
        }
    }
}

The important thing is that the layer level is very important. If it's not set, the sphere (around kerbin as of now) is not displayed.

m_SphereObj.layer = 10;

As a side note, I did have "strange" results when the layer wasn't set while going back to the KSC scene (I didn't disabled the sphere renderer while going out of the tracking station scene as a test):

http://imgur.com/a/TZKvk

Now, I'll try to see if I can display a sphere around a vessel!

Link to comment
Share on other sites

@theSpeare Thank you! 

Do you mean I should replace my

m_SphereObj.GetComponent

with something else?

As far as I understand the post you linked to, the new way in KSP 1.x+ is to use x.GetComponent<>(). Did I missed something?

Quote

Then Unity 5 also changed some of its API. Mainly some of the Components shortcut are gone. So :
Screen.showCursor => Cursor.visible 
x.rigidbody => x.GetComponent<Rigidbody>()  (for Part you can use part.rb)
x.light => x.GetComponent<Light>()
x.collider => x.GetComponent<Collider>() 

btw, your mod is really great, love it :D 

Link to comment
Share on other sites

Thank you :)

Nah, it was in reference to this:

Quote

private Rigidbody _rigidbody;

// It can infer the type for brevity...if you use it this way, I recommend very explicit names like _rigidbody.
part.GetComponentCached(ref _rigidbody).velocity = Vector3.zero;

// Or you can be more explicit...
part.GetComponentCached<Rigidbody>(ref _rigidbody).velocity = Vector3.zero;

Apparently GetComponent calls are no longer free (if I understand correctly) and every call will create a new reference which will pile up over time. Arsonide developed a way of caching the call to save resources. I still have to do this on my plugin; need to get around to doing it and testing that nothing's broken. Let me know how yours goes.

Link to comment
Share on other sites

Finally, I was able to display a sphere around a vessel \o/

Very big thank you to @udk_lethal_d0se and @theSpeare for their help!

Code was tested in 1.2-pre but it should work on 1.1.x:

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

namespace TestSphere
{
    [KSPAddon(KSPAddon.Startup.TrackingStation, false)]
    public class TestSphere : MonoBehaviour
    {
        GameObject m_SphereObj;
        List<Vessel> m_vesselList;
        Vessel m_currentVessel;
        MapObject m_MapObject;
        MapObject m_currentMapObject;
        Renderer m_renderer;

        /// <summary>
        /// Overrides the Start method for a MonoBehaviour plugin.
        /// </summary>
        void Start()
        {
            KspUtils.Logging.logLevel = KspUtils.LogLevel.DEBUG;
            KspUtils.Logging.EnterFunction();

            // we want to do something only in the tracking station
            if (HighLogic.LoadedScene != GameScenes.TRACKSTATION)
                return;

            // Create a sphere object
            m_SphereObj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            m_SphereObj.layer = 10;

            // disable collider for the sphere
            var collider = m_SphereObj.GetComponent<Collider>();
            collider.enabled = false;
            Destroy(collider);

            // get object renderer and apply attributes
            m_renderer = m_SphereObj.GetComponent<Renderer>();
            var shader = Shader.Find("KSP/Diffuse");
            if(shader == null)
            {
                KspUtils.Logging.Debug("no shader");
            }

            m_renderer.material = new Material(shader);
            m_renderer.material.color = new Color(Color.red.r, Color.red.g, Color.red.b, 0.5f);
            m_renderer.receiveShadows = false;
            m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
            m_renderer.enabled = false;

            GameEvents.onPlanetariumTargetChanged.Add(OnPlanetariumTargetChanged);            
        }

        private void OnPlanetariumTargetChanged(MapObject mapObject)
        {
            if(mapObject == null)
            {
                return;
            }

            switch(mapObject.type)
            {
                case MapObject.ObjectType.CelestialBody:
                    KspUtils.Logging.Debug("OnPlanetariumTargetChanged: CelestialBody: " + mapObject.name);
                    m_MapObject = mapObject;
                    break;

                case MapObject.ObjectType.Vessel:
                    KspUtils.Logging.Debug("OnPlanetariumTargetChanged: Vessel: " + mapObject.name);
                    m_MapObject = mapObject;
                    break;
            }
        }

        void Update()
        {
            // we want to do something only in the tracking station
            if (HighLogic.LoadedScene != GameScenes.TRACKSTATION)
            {
                m_MapObject = null;
                var renderer = m_SphereObj.GetComponent<Renderer>();
                renderer.enabled = false;
                return;
            }

            if (!MapView.MapIsEnabled || MapView.MapCamera == null)
            {
                m_MapObject = null;
                var renderer = m_SphereObj.GetComponent<Renderer>();
                renderer.enabled = false;
                return;
            }


            if (m_MapObject != null)
            {
                // TEST ; scale down a bit if chosen target is a vessel
                float scale_multiplier = 0f;
                Color object_color = Color.white;
                switch(m_MapObject.type)
                {
                    case MapObject.ObjectType.Vessel:
                        scale_multiplier = 100f;
                        object_color = Color.green;
                        break;

                    case MapObject.ObjectType.CelestialBody:
                        scale_multiplier = 3000f;
                        object_color = Color.red;
                        break;

                    default:
                        return;
                }
                
                m_SphereObj.transform.position = ScaledSpace.LocalToScaledSpace(m_MapObject.transform.position);
                m_SphereObj.transform.parent = m_MapObject.transform;
                m_SphereObj.transform.localScale = Vector3.one * scale_multiplier;
                m_SphereObj.transform.localPosition = Vector3.zero;
                m_SphereObj.transform.localRotation = Quaternion.identity;
                var renderer = m_SphereObj.GetComponent<Renderer>();
                renderer.material.color = object_color;
                renderer.enabled = true;

                if (m_MapObject != m_currentMapObject)
                {
                    m_currentMapObject = m_MapObject;
                    KspUtils.Logging.Debug("m_MapObject: " + m_MapObject.name);
                    KspUtils.Logging.Debug("position: " + m_MapObject.transform.position.ToString());
                    KspUtils.Logging.Debug("localPosition: " + m_MapObject.transform.localPosition.ToString());
                    KspUtils.Logging.Debug("localScale: " + m_MapObject.transform.localScale.ToString());

                    KspUtils.Logging.Debug("m_SphereObj: " + m_SphereObj.name);
                    KspUtils.Logging.Debug("position: " + m_SphereObj.transform.position.ToString());
                    KspUtils.Logging.Debug("localPosition: " + m_SphereObj.transform.localPosition.ToString());
                    KspUtils.Logging.Debug("localScale: " + m_SphereObj.transform.localScale.ToString());
                }
            }
            else
            {
                var renderer = m_SphereObj.GetComponent<Renderer>();
                renderer.enabled = false;
            }
        }
    }
}


Note: You'll need to replace all the "KspUtils.Logging." (which is an private logging system I use) by "UnityEngine.Debug.Log()"

tUE1hm0.png

4 minutes ago, theSpeare said:

Thank you :)

Nah, it was in reference to this:

Apparently GetComponent calls are no longer free (if I understand correctly) and every call will create a new reference which will pile up over time. Arsonide developed a way of caching the call to save resources. I still have to do this on my plugin; need to get around to doing it and testing that nothing's broken. Let me know how yours goes.

Oh OK @theSpeare! Got it! Thanks for the clarification, that's good to know. I'll check that :)

Link to comment
Share on other sites

@theSpeare

1) Those calls were never "free" (as mentioned by sarbian in the linked post), they just looked like they should have been cached or linked internally in older versions of Unity which caught more than a few people out (presumably why they've been removed)

2) The GetComponent<>() function is a convenient wrapper on a null check to save a few lines of code. ie.

T componentRef; // global scope for cache

if (componentRef == null)
{
  componentRef = GetComponent<T>();
}
// use componentRef...

// replaced with
GetComponentCached<T>(ref componentRef);
// use componentRef...

 

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...