Jump to content

Reworking the sfr Command Pod plugin


Recommended Posts

Premise

sfr's Command Pod plugin allows Kerbal occupants inside a properly-configured command pod to be visible from outside the vessel (essentially, see-through windows from the outside). While the add-on still works in 0.23.5 for the most part, there are a number of outstanding issues:

- The camera for the internal IVA view is dependent on the orientation of the root (first) part of a vessel, rather than that of the command pod / crew cabin itself

- A vessel with more than one sfr-powered command pod / crew cabin will end up aligning the internal IVA view to the root part / first sfr-powered command pod / crew cabin defined

- It is not clear whether the plugin is restricted to actual command pods, or if crew cabins with no command authority are also able to use this plugin

- sfr only visits the forums occasionally, such that plugin maintenance issues are slow to be addressed (his only method of distribution is also the now-defunct Spaceport)

- Some add-ons, such as Alskari's Hollow Structure and Hulls and BobCat Industries' Tesla Recon Rover, have the sfr plugin as a dependency, yet suffer from the aforementioned issues.

Finally, I personally would like to make this as a dependency FusTek Station Parts / upcoming FusTek Space Exploration Vehicle part packs, but the current version of the plugin as-is is unsuitable due to the aforementioned issues.

Objective

My primary objective is to rework the plugin to address the aforementioned issues, and publish it via more publicly-available channels such as CurseForge, Dropbox or MediaFire.

My secondary objective is to seek a new, permanent home of the plugin code, preferably with a popular dependency such as Firespitter - I personally prefer to spend most of my time making new parts than wrestling code, hence the intention for the handover.

Licencing / Permissions

sfr's source code file states that his work is licenced under CC BY-SA 3.0, and so as long as the derivative provides full credit to sfr, and is redistributed under the same license, it should be kosher.

/*
* sfr
* License: BY: Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0): http://creativecommons.org/licenses/by-sa/3.0/
*/


Help Needed

Given the following source code, I would like to:

- Strip out the portions pertaining to the hatch animation, as it is unnecessary IMHO

- Figure out how to change the IVA camera to align with the module's own transform, instead of the root part

- Ensure that multiple sfr-powered pods / cabins can work simultaneously, each with their own independent IVA camera transforms

- Ensure that the plugin can also work for non-command pod crewed cabins, such as my FusTek Kupola and various non-command pod compartments Alskari made for HSH

Here's the full source code as originally included in the Spaceport download:

/*
* sfr
* License: BY: Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0): http://creativecommons.org/licenses/by-sa/3.0/
*/

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


namespace sfrPartModules1
{
class sfrUtility
{
public static Camera FindCamera(string name)
{
foreach (Camera c in Camera.allCameras)
if (c.name == name)
return c;
return null;
}

public static void LogCameras()
{
int i = 0;
foreach (Camera c in Camera.allCameras)
{
Debug.Log("Cam " + i++ + " " + c.name + " " + c.cullingMask);
Debug.Log(c.transform.position + " " + c.transform.rotation.eulerAngles + " " + c.fov + " " + c.depth + " " + c.clearFlags);
}
}

public static void LogModules(Part part)
{
int i = 0;
foreach (PartModule p in part.Modules)
Debug.Log("Mod " + i++ + " " + p.name + " " + p.GUIName + " " + p.guiText);
}
}

public class sfrCommandPod : CommandPod
{
public string doorTransformName;
public float openAngle = 45f;
Transform doorTransform;

Shader glassShader, metalShader, diffuseShader;

void AddShader(string transformName, Shader s)
{
Transform t = FindModelTransform(transformName);
t.renderer.material.shader = s;
}

int CrewNumber = 0;
bool DoorOpen = false;

int CrewBoarding()
{
int Result = 0;

if (CrewNumber < protoModuleCrew.Count)
Result = 1;
else if (CrewNumber > protoModuleCrew.Count)
Result = -1;
else if (CrewNumber == protoModuleCrew.Count)
Result = 0;

CrewNumber = protoModuleCrew.Count;
return Result;
}

IEnumerator<WaitForSeconds> OpenDoor()
{
if (!DoorOpen)
doorTransform.Rotate(0, 0, openAngle, Space.Self);
yield return new WaitForSeconds(2);
// Debug.Log("Crew member on board");
doorTransform.Rotate(0, 0, -openAngle, Space.Self);
}

protected override void onPartAwake()
{
// Debug.LogWarning("onPartAwake " + GameInfo.gameScene);
base.onPartAwake();

metalShader = Shader.Find("Specular");
glassShader = Shader.Find("Transparent/Specular");
diffuseShader = Shader.Find("Diffuse");

AddShader("Pod", metalShader);
AddShader("DoorAxle", metalShader);
AddShader("Hatch", metalShader);
AddShader("WindGlass", glassShader);
AddShader("SideGlass", glassShader);
AddShader("UpGlass", glassShader);

Transform t = FindModelTransform("DoorLight");
Light l = t.GetComponent<Light>();
l.shadows = LightShadows.Soft;
}

protected override void onPartStart()
{
// Debug.LogWarning("onPartStart " + GameInfo.gameScene);
base.onPartStart();

doorTransform = FindModelTransform(doorTransformName);
// Debug.Log(doorTransform);

if (protoModuleCrew.Count == 0)
{
doorTransform.Rotate(0, 0, openAngle, Space.Self);
DoorOpen = true;
}
}

protected override void onStartComplete()
{
base.onStartComplete();
// Debug.LogWarning("onStartComplete " + GameInfo.gameScene);
}

protected override void onFlightStart()
{
base.onFlightStart();
// Debug.LogWarning("onFlightStart " + GameInfo.gameScene);

sfrUtility.LogModules(this);
}

protected override bool onPartActivate()
{
return base.onPartActivate();
// Debug.LogWarning("onPartActivate " + GameInfo.gameScene);
}

protected override void onPartDeactivate()
{
base.onPartDeactivate();
// Debug.LogWarning("onPartActivate " + GameInfo.gameScene);
}

protected override void onPartUpdate()
{
// Debug.LogWarning("OnPartUpdate " + GameInfo.gameScene);
base.onPartUpdate();

int Boarding = CrewBoarding();
if (Boarding > 0)
{
// Debug.Log("Crew member boarded");
StartCoroutine(OpenDoor());
DoorOpen = false;
}
else if (Boarding < 0)
{
if (protoModuleCrew.Count == 0)
{
doorTransform.Rotate(0, 0, openAngle, Space.Self);
DoorOpen = true;
}
else
StartCoroutine(OpenDoor());
}

}

protected override void onEditorUpdate()
{
// Debug.LogWarning("OnEditorUpdate " + GameInfo.gameScene);
base.onEditorUpdate();

}
}

public class sfrInternal : PartModule
{

Transform kspParent;
Vector3 kspPosition = new Vector3(18.3f, -6.9f, 0);
Quaternion kspRotation = new Quaternion(0, 0.7f, -0.7f, 0);
bool isActive = true;

public override void OnAwake()
{
base.OnAwake();
// Debug.LogWarning("sfrInternal OnAwake " + GameInfo.gameScene + " " + part + " " + vessel);
}

public override void OnLoad(ConfigNode node)
{
base.OnLoad(node);
// Debug.LogWarning("sfrInternal OnLoad " + node.name);

}

public override void OnSave(ConfigNode node)
{
base.OnSave(node);
// Debug.LogWarning("sfrInternal OnSave " + node.name);

}

public override void OnStart(StartState state)
{
base.OnStart(state);
// Debug.LogWarning("sfrInternal OnStart, StartState: " + state);

Camera c = sfrUtility.FindCamera("Main Camera");
if (c)
c.cullingMask |= 1 << 16 | 1 << 20;

if(!part.internalModel)
{
try
{
part.CreateInternalModel();
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}

if(part.internalModel)
{
if (state != StartState.Editor)
kspParent = part.internalModel.transform.parent;

part.internalModel.transform.parent = part.transform;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
part.internalModel.transform.localPosition = Vector3.zero;
}
}

public override void OnUpdate()
{
base.OnUpdate();
// Debug.LogWarning("sfrInternal OnUpdate");

if (isActive != part.vessel.isActiveVessel)
{
Camera c = sfrUtility.FindCamera("Main Camera");
if (c)
c.cullingMask |= 1 << 16 | 1 << 20;

part.CreateInternalModel();
kspParent = InternalCamera.Instance.transform.parent; // part.internalModel.transform.parent;

part.internalModel.SetVisible(true);
part.vessel.SetActiveInternalPart(part);

if (part.protoModuleCrew.Count > 0 && isActive)
{
part.vessel.SpawnCrew();
}

part.internalModel.transform.parent = part.transform;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
part.internalModel.transform.localPosition = Vector3.zero;
}

if(!part.internalModel)
{
part.CreateInternalModel();
kspParent = InternalCamera.Instance.transform.parent; // part.internalModel.transform.parent;

part.internalModel.SetVisible(true);
part.vessel.SetActiveInternalPart(part);

if (part.protoModuleCrew.Count > 0 && isActive)
{
part.vessel.SpawnCrew();
}

part.internalModel.transform.parent = part.transform;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
part.internalModel.transform.localPosition = Vector3.zero;
}

try
{
if (sfrUtility.FindCamera("InternalCamera"))
{
if (part.vessel.isActiveVessel)
{
part.internalModel.transform.position = kspPosition; // Vector3.zero;
part.internalModel.transform.parent = kspParent;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
}
}
else
{
part.internalModel.transform.parent = part.transform;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
part.internalModel.transform.localPosition = Vector3.zero;
}
}
catch (Exception e)
{
Debug.LogException(e, this);
}

}

}

}

And here's my early attempt at stripping out the hatch animation code and removing the non-glass shaders:

/*
* TransparentViewports plugin (transView)
*
* Author: Robin "sumghai" Chang
* and members of the Kerbal Space Program user community
*
* Based on: sfr Command Pod plugin by sfr
*
* License: Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
* http://www.creativecommons.org/licenses/by-sa/3.0/
*/

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


namespace transViewPartModules1
{
class transViewUtility
{
public static Camera FindCamera(string name)
{
foreach (Camera c in Camera.allCameras)
if (c.name == name)
return c;
return null;
}

public static void LogCameras()
{
int i = 0;
foreach (Camera c in Camera.allCameras)
{
Debug.Log("Cam " + i++ + " " + c.name + " " + c.cullingMask);
Debug.Log(c.transform.position + " " + c.transform.rotation.eulerAngles + " " + c.fov + " " + c.depth + " " + c.clearFlags);
}
}

public static void LogModules(Part part)
{
int i = 0;
foreach (PartModule p in part.Modules)
Debug.Log("Mod " + i++ + " " + p.name + " " + p.GUIName + " " + p.guiText);
}
}

public class transViewCommandPod : CommandPod
{
Shader glassShader, metalShader, diffuseShader;

void AddShader(string transformName, Shader s)
{
Transform t = FindModelTransform(transformName);
t.renderer.material.shader = s;
}

protected override void onPartAwake()
{
// Debug.LogWarning("onPartAwake " + GameInfo.gameScene);
base.onPartAwake();

//metalShader = Shader.Find("Specular");
glassShader = Shader.Find("Transparent/Specular");
//diffuseShader = Shader.Find("Diffuse");

//AddShader("Pod", metalShader);
//AddShader("DoorAxle", metalShader);
//AddShader("Hatch", metalShader);
AddShader("WindGlass", glassShader);
AddShader("SideGlass", glassShader);
AddShader("UpGlass", glassShader);

//Transform t = FindModelTransform("DoorLight");
//Light l = t.GetComponent<Light>();
//l.shadows = LightShadows.Soft;
}

protected override void onPartStart()
{
// Debug.LogWarning("onPartStart " + GameInfo.gameScene);
base.onPartStart();
}

protected override void onStartComplete()
{
base.onStartComplete();
// Debug.LogWarning("onStartComplete " + GameInfo.gameScene);
}

protected override void onFlightStart()
{
base.onFlightStart();
// Debug.LogWarning("onFlightStart " + GameInfo.gameScene);

transViewUtility.LogModules(this);
}

protected override bool onPartActivate()
{
return base.onPartActivate();
// Debug.LogWarning("onPartActivate " + GameInfo.gameScene);
}

protected override void onPartDeactivate()
{
base.onPartDeactivate();
// Debug.LogWarning("onPartActivate " + GameInfo.gameScene);
}

protected override void onPartUpdate()
{
// Debug.LogWarning("OnPartUpdate " + GameInfo.gameScene);
base.onPartUpdate();


}

protected override void onEditorUpdate()
{
// Debug.LogWarning("OnEditorUpdate " + GameInfo.gameScene);
base.onEditorUpdate();

}
}

public class transViewInternal : PartModule
{

Transform kspParent;
Vector3 kspPosition = new Vector3(18.3f, -6.9f, 0);
Quaternion kspRotation = new Quaternion(0, 0.7f, -0.7f, 0);
bool isActive = true;

public override void OnAwake()
{
base.OnAwake();
// Debug.LogWarning("transViewInternal OnAwake " + GameInfo.gameScene + " " + part + " " + vessel);
}

public override void OnLoad(ConfigNode node)
{
base.OnLoad(node);
// Debug.LogWarning("transViewInternal OnLoad " + node.name);

}

public override void OnSave(ConfigNode node)
{
base.OnSave(node);
// Debug.LogWarning("transViewInternal OnSave " + node.name);

}

public override void OnStart(StartState state)
{
base.OnStart(state);
// Debug.LogWarning("transViewInternal OnStart, StartState: " + state);

Camera c = transViewUtility.FindCamera("Main Camera");
if (c)
c.cullingMask |= 1 << 16 | 1 << 20;

if(!part.internalModel)
{
try
{
part.CreateInternalModel();
}
catch (Exception e)
{
Debug.LogException(e, this);
}
}

if(part.internalModel)
{
if (state != StartState.Editor)
kspParent = part.internalModel.transform.parent;

part.internalModel.transform.parent = part.transform;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
part.internalModel.transform.localPosition = Vector3.zero;
}
}

public override void OnUpdate()
{
base.OnUpdate();
// Debug.LogWarning("transViewInternal OnUpdate");

if (isActive != part.vessel.isActiveVessel)
{
Camera c = transViewUtility.FindCamera("Main Camera");
if (c)
c.cullingMask |= 1 << 16 | 1 << 20;

part.CreateInternalModel();
kspParent = InternalCamera.Instance.transform.parent; // part.internalModel.transform.parent;

part.internalModel.SetVisible(true);
part.vessel.SetActiveInternalPart(part);

if (part.protoModuleCrew.Count > 0 && isActive)
{
part.vessel.SpawnCrew();
}

part.internalModel.transform.parent = part.transform;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
part.internalModel.transform.localPosition = Vector3.zero;
}

if(!part.internalModel)
{
part.CreateInternalModel();
kspParent = InternalCamera.Instance.transform.parent; // part.internalModel.transform.parent;

part.internalModel.SetVisible(true);
part.vessel.SetActiveInternalPart(part);

if (part.protoModuleCrew.Count > 0 && isActive)
{
part.vessel.SpawnCrew();
}

part.internalModel.transform.parent = part.transform;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
part.internalModel.transform.localPosition = Vector3.zero;
}

try
{
if (transViewUtility.FindCamera("InternalCamera"))
{
if (part.vessel.isActiveVessel)
{
part.internalModel.transform.position = kspPosition; // Vector3.zero;
part.internalModel.transform.parent = kspParent;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
}
}
else
{
part.internalModel.transform.parent = part.transform;
part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);
part.internalModel.transform.localPosition = Vector3.zero;
}
}
catch (Exception e)
{
Debug.LogException(e, this);
}

}

}

}

I have yet to compile this, but in the meantime, I'd like to figure out how to fix the IVA camera transforms first - specifically, which parts of the code are responsible for defining the IVA camera transform orientation, and how would I change it so that it works on a per-module basis rather than via the vessel's root part?

Thanks in advance.

Link to comment
Share on other sites

The internal model has a hard coded rotation of 90,180,0, so if you add that to the part's rotation, that might work.

(I have not read through the source yet, but just thought I'd chime in with that piece of non obvious info)

Link to comment
Share on other sites

The internal model has a hard coded rotation of 90,180,0, so if you add that to the part's rotation, that might work.

(I have not read through the source yet, but just thought I'd chime in with that piece of non obvious info)

Ah, I see. I'm guessing this means I have to go through, find the following lines:

part.internalModel.transform.localRotation = new Quaternion(0, 0.7f, -0.7f, 0);

and apply the hard-coded internal rotation to the existing quaternions somehow?

I'll look into this a little later this week - thanks!

(NB Advice and suggestions still welcome - I dabbled in C# for work-related purposes, just not nearly enough for KSP plugin authoring)

Link to comment
Share on other sites

  • 2 weeks later...

any news or an eta release on this plugin ?

another thing maybe it helps (but probably just a shot in the dark)

i;am using this plugin and bobcat home 3 (tesla rover) and my log is spammed with NullReferenceException: Object reference not set to an instance of an object

i think its cause the source code doesn't have damage colliders defined in it maybe you can take a look at that if you have spare time but like i said its probably just a shot in the dark

take care

Edited by dtoxic
additional info
Link to comment
Share on other sites

any news or an eta release on this plugin ?

I've spoken to Mihara, and he's working on his own version of a PartModule to support transparent pods, which will probably be bundled with JSI RasterPropMonitor.

WIP tests can be seen here, and while there are still a few minor issues, for the most part it resolves the main problem mentioned in this thread. Not sure about the NullReferenceExceptions, however.

Link to comment
Share on other sites

I've spoken to Mihara, and he's working on his own version of a PartModule to support transparent pods, which will probably be bundled with JSI RasterPropMonitor.

WIP tests can be seen here, and while there are still a few minor issues, for the most part it resolves the main problem mentioned in this thread. Not sure about the NullReferenceExceptions, however.

ok thx for the info :)

Link to comment
Share on other sites

when can we get the compiled version to try it out? :)

Mihara just sent me an updated dev build with a few fixes - he and I agreed that we need to do some more stress testing with regards to how docking, crew transfer, potential conflicts with the core JSI RPM props etc may affect the transparent pod PartModule.

Once he and I feel we're confident we've sussed out these issues, Mihara may make the auxiliary function available for wider testing.

Link to comment
Share on other sites

Mihara and I have verified that his new PartModule is working properly, and resolved practically all of the issues we've encountered (other than the inability to view the insides of other transparent pods while in IVA):

http://forum.kerbalspaceprogram.com/threads/57603-0-23-5-RasterPropMonitor-make-your-IVA-a-glass-cockpit-%28v0-16%29-20-Apr?p=1211267&viewfull=1#post1211267

As such, I believe this thread has served its purpose :)

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...