technical architecture.

Interactive OR Design

by on Jun.01, 2011, under Interactive, Interactive, Portfolio, Unity3D

I’ve been working on this tool for a while now, here’s a quick captured demo of some of the features of my hospital room design application in action.

Interactive 3D OR/ICU design tool from Dave Buck on Vimeo.

here’s a current video of my OR/ICU design project, created in unity3d, with fully configurable parametric models rigged with both IK and FK, and a tool to quickly draw out any layout of walls, windows and doors to spatially visualize the placement and reach of equipment needed in an operating room space.

Edit:

This is a full featured application, including but not nearly limited to:

  • Full file save / load for easy sharing of designs within the staff
  • Easy to navigate tab based menu for selecting models and assets to insert
  • all models will give some sort of visual feedback that they can be controlled by the user; ex: some arms will highlight in blue to notify the user that they are rotatable; some pieces will highlight in green to notify that they are movable; some in red to notify that they are control points that allow you to drag or re-position the entire hierarchy chain of the selected boom.
  • many models have custom logic built in so that they move and rotate within their engineering limits (As best as we could get them in some cases, as some manufacturers were less than forthcoming with information and model details!)
  • Customizable system to draw out and place walls, with orthographic or free line drawing, snaps, and dimensions integrated
  • multiple wall styles, to quickly change and prototype room designs with windows, peek thrus, walls, headers, and soffits
  • fully configurable ‘Hero’ equipment, developed from engineering models, with tolerances for how they can move and be configured together
  • rule sets integrated that control how these items may be modified, ex: changing a light head to a flattened light head, or adding shelves to a boom
  • this ‘Hero’ equipment is generated at run time as needed, this allows us to quickly configure the various lengths and heights needed to show the flexibility of the equipment system, and turn the tool from a demonstrative sales tool, into a full on design tool
  • helpers of various types to show range of motion, overlaps, and possible collisions
  • tools to place staff within the scene, and change to their point of view, look around and see what the end user of the room will see when the full update to their room takes place.
  • output to jpg in several sizes, depending on whether the user is trying to print, email, or target a presentation size image
  • custom user configurable animation paths
  • output to an mp4 video file of a fixed size to enable easy email and sharing of the designs with the customers, architects, and planners that are involved in the process
7 Comments more...

Fun with Generics and Lists in C#

by on May.19, 2011, under .net, csharp, Scripting, Unity

So I’ve been doing a lot of List juggling lately and have run into a few tidbits that I wish I had learned a few months ago. I figure that is always a good cue to write them down for future reference and for any others who are digging into this C# world headfirst also. this is all sort of an exansion to try and get a concrete example of mike_mac’s lamda/delegates/callback articles

Say I have a custom class that I’m using to store a structured set of data, this time we’ll play with “ArmDefs”. ArmDef’s just hold a little string and int based data and a list of ArmDef’s as children, and a second list of GameObjects that contains all of the models for this section of arm for use to serialize to disk, but thats more a story for another time.

Overall this is used to build up a hierarchy that holds the “contents” of a runtime modular rig, and handles the Saving/Loading/Reloading.

To work with this I needed to set up a pile of recursive functions to edit the tree at runtime, since I don’t know the depth of the hierarchy, and rebuild the system as needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[System.Serializable]
//Inheriting from ISerializable gives us
//an easy access to Serialization to disk
public class ArmDef : ISerializable {
    public string Name;
    public List<ArmDef> Children = new List<ArmDef>();
 
    public ArmDef(string iFile)
    {
        this.Name= iFile;
        this.Children = new List<ArmDef>();
    }
 
    public void AddChild(ArmDef child)
    {
        Children.Add(child);
//just wrapping the Add so that we can do "extra"
//stuff here if we want, like add links to parents etc
    }
}

not really building these thru scripts out in the wild but just to build something to work with here.

1
2
3
4
5
6
7
8
9
10
public ArmDef config = new ArmDef("Base Level");
 
//add a couple children
config.AddChild(new ArmDef("Level2a"));
config.AddChild(new ArmDef("Level2b"));
config.AddChild(new ArmDef("Level2c"));
 
//add a couple grandchildren
config.Children[1].AddChild(new ArmDef("Level3a"));
config.Children[1].AddChild(new ArmDef("Level3b"));

ok thats all the boring stuff for now! here is a Generic Recursive function that walks down a tree and does ‘Action<>‘ to all children of ‘parent’. The key here is that the Action<> pretty much rocks. !

1
2
3
4
5
6
7
8
9
10
11
public void HandleArmDef(Action<ArmDef> item, ArmDef parent)
{
    if (parent.Children.Count > 0)
    {
        foreach (ArmDeft in parent.Children)
        {
            HandleArmDef(item, ti);
        }
    }
    item(parent);
}

Now the fun begins when you start to chain this stuff together to “act” on all children.
we can define that “Action item” in the above function to be something else using some Lambda or Delegate type stuff. (I’m learning here, those are the reading points for the moment!)

This will go through the hierarchy and remove all children with the Name of “Level3a”.

1
2
string toDelete = "Level3a";
HandleArmDef(item => { item.Children = item.Children.Where(child => child.Name != toDelete).ToList(); }, config);

In another example, i wanted to search the hierarchy and see if there was a certain value anywhere in the hierarchy. easiest way i found was to just quickly build a flattened list and use the Linq “Any” function to see if the string was found anywhere in the hierarchy.

1
2
3
List<string> flatList = new List<string>();
HandleArmDef(item => { flatList.Add(item.Name ); }, parent);
bool isCurrentlyFlat = flatList.Any(x => x.Contains("Flat"));

in another case, i wanted to insert a new child into the hierarchy as a child of a specific object.

1
2
3
4
5
6
7
8
9
10
11
12
13
void AddChildAddon(ArmDef parent, string addAsChildOf, string newChild)
{
    //for each object 'item' down the hierarchy
    //if the item.Name contains our addAsChildOf string
    //we'll add the newChild as a child to the item.
    HandleArmDef(item =>
    {
        if (item.Name.Contains(addAsChildOf))
        {
            item.Children.Add(new ArmDef(newChild));
        }
    }, parent);
}

there’s plenty more possibilities of things to do here if I’ve explained well enough!
More info on Linq by example

1 Comment more...

Unity: Toying with Scriptable objects and Custom Editors

by on May.18, 2011, under .net, csharp, Scripting, Unity

Expanding on the previous post about Scriptable Objects as an easy way to save/edit some data at runtime, here’s a small extension looking at an example for a simple custom editor that appears when you select the Scriptable Object we built previously in the Project view.

Here’s what you get clicking on the file previously:
Before Custom Editor

And the result with the custom editor:
with editor

Now, this isn’t the sexiest of examples, but hopefully it helps someone along the way!

First a small addition to the data class, lets add a constructor to it, since our input values are going to be basically the same every time (Mimic ‘this’ camera), we’ll add a secondary constructor for the class that just takes a unity Camera as input and records the values for us.

We’ll use the same ‘CameraLocationHolder.cs’ from below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//File: CameraLocation.cs
using UnityEngine;
using System.Collections;
//the Serializable attribute is key to what makes the custom class visible in the editor !
[System.Serializable]
public class CameraLocation
{
    public string name;
    public bool isOrtho;
    public float orthoSize;
    public float fov;
    public Vector3 position;
    public Quaternion rotation;
 
    //base constructor that builds an empty CamLoc
    public CameraLocation()
    {
        name = string.Empty;
        isOrtho = false;
        orthoSize = -1;
        fov = -1;
        position = Vector3.zero;
        rotation = Quaternion.identity;
    }
    //build a CamLoc from a supplied camera
    public CameraLocation(Camera c)
    {
        if (c)
        {
            name = "New Camera";
            isOrtho = c.isOrthoGraphic;
            orthoSize = c.orthographicSize;
            fov = c.fov;
            position = c.transform.position;
            rotation = c.transform.rotation;
        }
    }
}

And we replace the ‘CameraListboxWizard’ that we previously used to edit the Scriptable Object with this (very similar) ‘Custom Editor’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//File: CameraLocationEditor.cs -- Place in an Editor directory under the unity project
 
using UnityEngine;
using UnityEditor;
 
//this limits the editor to running on object that have type CameraLocationHolder
[CustomEditor(typeof(CameraLocationHolder))]
public class CameraLocationEditor : Editor {
 
    //This will just be a shortcut to the target, ex: the object you clicked on.
    private CameraLocationHolder db;
 
	void Awake()
	{
		db=(CameraLocationHolder)target;
	}
 
	public override void OnInspectorGUI()
	{
		GUILayout.BeginVertical();
        if (GUILayout.Button("Add current Camera.main position", GUILayout.Height(50)))
        {
			Camera m = Camera.main;
			if(m)
			{
                //Add the new camera to the list
                db.content.Add(new CameraLocation(m));
                //tell the unity editor that the list has changed and needs to be serialized
                EditorUtility.SetDirty(target);
			}			
        }
 
        GUILayout.Space(5);
        DisplayCurrentCamList();
        GUILayout.Space(5);
        GUILayout.EndVertical();
    }
 
    void DisplayCurrentCamList()
    {
		if (db.content.Count == 0)
			return;
 
        //For each of the cameralocations in the saved scriptable object db
        for (int i = 0; i < db.content.Count; i++ )
        {
            EditorGUILayout.BeginHorizontal();
 
            //A text field to rename the camera
            db.content[i].name = GUILayout.TextField(db.content[i].name, GUILayout.Width(200));
 
            //a few simple buttons to juggle the array order
            if (GUILayout.Button("UP"))
            {
                if (i > 0)
                {
                    CameraLocation item = db.content[i];
                    db.content.RemoveAt(i);
                    db.content.Insert(i - 1, item);
                }
            }
            if (GUILayout.Button("DN"))
            {
                if (i < db.content.Count)
                {
                    CameraLocation item = db.content[i];
                    db.content.RemoveAt(i);
                    db.content.Insert(i + 1, item);
                }
            }
            if (GUILayout.Button("Remove"))
            {
                db.content.Remove(db.content[i]);
            }
 
            //sets the maincamera to be at the selected cameralocation
            if (GUILayout.Button("MoveTo"))
            {
                MoveToView(db.content[i]);
            }
 
            //updates the cameralocation to match up with the Camera.main (Usable at runtime or at edit time)
            if (GUILayout.Button("UPDATE"))
            {
                UpdateView(db.content[i]);
			}	
			EditorUtility.SetDirty(target);
            EditorGUILayout.EndHorizontal();
        }
    }
 
	void MoveToView(CameraLocation cam)
	{
		Camera MainCamera = Camera.mainCamera;
	    Quaternion currentRotation = cam.rotation;
 
		//if we had an ortho cam, lets just set it there before we start moving.
		MainCamera.orthographicSize = cam.orthoSize;
		MainCamera.isOrthoGraphic = cam.isOrtho;
 
	    //a parent check incase we have a 'fpswalker' type script setup also
	    if (MainCamera.transform.parent)
	    {
	        //move the parent object
	        MainCamera.transform.parent.position = cam.position;
 
	        Vector3 ang = currentRotation.eulerAngles;
 
	        //rotate the parent object on the Y axis
	        MainCamera.transform.parent.rotation = Quaternion.Euler(0, ang.y , 0);
 
	        //rotate the child camera on the X and Z axis
	        MainCamera.transform.localRotation = Quaternion.Euler(ang.x , 0, ang.z );
 
	    }
	    else // there is no FPSWalker parent, so just move the camera itself
	    {
	        MainCamera.transform.position = cam.position;
	        MainCamera.transform.rotation = currentRotation;
	    }
 
	    MainCamera.fov = cam.fov;	
	}
 
	void UpdateView(CameraLocation cam)
	{
		Camera MainCamera = Camera.mainCamera;
	    cam.isOrtho = Camera.main.isOrthoGraphic;
		cam.orthoSize = Camera.main.orthographicSize;
		cam.position = Camera.main.transform.position;
    	cam.rotation = Camera.main.transform.rotation;
		cam.fov = MainCamera.fov;
	}	
}

Now having built that up, we can simply click on the asset file that we had created previously and see the editor in the Inspector panel, making it easier to handle multiple lists.

6 Comments more...

Some Useful Unity3D resources

by on Feb.16, 2011, under Unity, Unity3D

Since I’m doing a lot of unity here lately, and starting to lay down some teaching to coworkers, here are a couple useful references for unity3d stuff that aren’t too commonly thrown out there.

http://feedity.com/rss.aspx/unifycommunity-com/UVtQUlVb – RSS feed that updates when new scripts are added to the community WIKI (And feedity in general is pretty nifty.)

http://www.google.com/cse/home?cx=002470491425767499270:iugs1ezlsfq a google custom search engine maintained by some anonymous saint, searches the unity references, answers, forums, and a few other popular blogs.

http://www.google.com/cse/home?cx=001712401338047450041:csfhqk-trfa a second google CSE that includes MSDN and the wiki among others, for when you need that extra bit of microsoft reference.

Also, don’t neglect the boys in irc.freenode.net #unity3d, definitely the single most useful resource @ http://webchat.freenode.net/ or your friendly neighborhood irc client.

*Oops! bolger fail. edit to fix the links.

Leave a Comment : more...

Unity: toying with Scriptable Objects

by on Oct.19, 2010, under Architecture, csharp, Scripting, Unity3D

In architecture there is often a need to keep track of and have handy many different viewpoints of a model at a time. with my early unity experiments, I made the mistake of creating a Camera for every Viewpoint that I wanted to have available in my scene. this was faulty for several reasons, not the least of which was that every ‘Camera’ object in a scene is actually rendering everything in the scene at once. Each camera has a few fun/useful options in the Clear Flags that makes this useful for things like rendering UI overlays with 3d objects and other assorted multipass ideas, but thats a whole ‘nother story.

Next attempt was to just import a large list of dummy objects in from 3dsmax that would represent camera orientations and allow the end user to select from these dummys and align the camera with the dummy objects using some UI bits in unity. This worked fine, but there is the base problem that the user cannot modify/edit these objects easily and have the changes save across play sessions.

This is where Scriptable Objects started to come in to play. (Taken partly from the unity Character Demo where they were explaining some of the joys of AssetBundles if you wish to look up more in depth examples.) this may not be the most useful demo of Scriptable objects, but this is a case study for when you are trying to store Runtime data for use at editortime.

There are several parts: First and probably the only important part is the Holder, this is really just a list of ‘Somethings’ that we save to disk. in our case, CameraLocations.

//CameraLocationHolder.cs
using System.Collections.Generic;
using UnityEngine;
 
public class CameraLocationHolder : ScriptableObject {
    public List<CameraLocation> content;
    public CameraLocationHolder(List content)
    {
        this.content = content;
    }
}

the above empty list consists of ‘CameraLocation’s which is just a simple class I used to store a few variables that are needed to re-create a Viewpoint. Camera locations is a Serializable Class (Serializable in this case means that its viewable in the inspector in Unity the same way you would view for instance a Vector3..) and it has some default values in there of pulling the maincameras location/rotation and field of view.

//CameraLocation.cs
using UnityEngine;
using System.Collections;
 
[System.Serializable]
public class CameraLocation
{
    public string name = "New Camera";
    public float fov = Camera.main.fov;
    public Vector3 position = Camera.main.transform.position;
    public Quaternion rotation = Camera.main.transform.rotation;
}

Now, we create an asset in the editor where we will store our list of CameraLocation’s, this can be placed anywhere, its a small file so i tend to just store it in Resources for easy finding later.

//CreateCameraLocationAsset.cs
using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
 
public class CreateCameraLocationAsset
{
    [MenuItem("Custom/Cameras/Create camera location holder")]
    public static void CreateMyAsset()
    {
        ListList<CameraLocation> content = new List<CameraLocation>();
        CameraLocationHolder asset = new CameraLocationHolder(content);  //scriptable object 
        AssetDatabase.CreateAsset(asset, "Assets/CameraLocationDatabase.asset");
        AssetDatabase.SaveAssets();
        EditorUtility.FocusProjectWindow();
        Selection.activeObject = asset;
    }
}

Now you have an ‘Asset’ where you can store/load/erase/whatever in a list of CameraLocations at runtime, and the results will still be available at Editor time, and at future Runs of the application.

For instance, you can browse around your scene as you would normally with your FlyingCameraScriptOfDoom, and occasionally call a script to add your current camera.main viewpoint to the list.

or accessing it as a runtime resource for use by a normal GUI pulldown:

//Excerpts from CameraListBox.cs
 
//declaring a list of stuff
            public List<CameraLocation> camList = new List<CameraLocation>(); //Declare our list of stuff
 
//....
 
//somewhere in Start() we load the list from the DB
            Object o = Resources.Load("CameraLocationDatabase", typeof(CameraLocationHolder));
            CameraLocationHolder CameraLocationDB = (CameraLocationHolder)o;
//fill our list of stuff, with the content from the Holder in the asset
            camList = CameraLocationDB.content;
 
//....
 
//and somewhere in our OnGUI()
            GUILayout.BeginVertical(GUILayout.Width(120));
            for (int i = 0; i &lt; camList.Count; i++)
            {
                if (GUILayout.Button(camList[i].name))
                {
                    SelectedListItem = i;//Set the index for our currrently selected item
                    CameraLocation cam = camList[SelectedListItem];
                    StartCoroutine(SmoothMoveCamLoc(cam.position, cam.rotation, cam.fov, duration));
                   }
            }
            GUILayout.EndVertical();
 
//the SmoothMoveCamLoc just lerps between the current positions/rotations/fov's 
//and the desired ones that were stored in the CameraLocation.
//although mine has a few other random logic items in there to decide
//which style of camera i have currently and deal with any parents/targets/initializing/etc
//needed if we move the camera.

For instance, editing the list by an Editor script, that builds a list for you to Add/Remove/Rename/and Re-Order the list, because thats oddly hard to do with the built in object interface? Maybe this is where a custom inspector should be for the .asset type? hmm! things to play with in the future.

//CameraListboxWizard.cs
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
 
public class CameraListboxWizard : ScriptableWizard
{
    public string DBLocation = "Assets/Resources/CameraLocationDatabase.asset";
    private CameraLocation camLocation;
    static private Object dbaseAsset;
    static private CameraLocationHolder db;
 
    [MenuItem("Custom/Cameras/CameraListBox - Database Helper")]
    static void DoSet()
    {
        ScriptableWizard.DisplayWizard("Add view to CameraListBox", typeof(CameraListboxWizard), "DONE");
    }
 
    void OnWizardUpdate()
    {
        helpString = "This will control the Camera Location asset database\n, and allow you to 'save' the Camera.main's position\n while in the player mode\n";
 
        UpdateMyAssetLocation();
    }
 
    void AddExistingCameras()
    {
        int count = Camera.allCameras.Length;
        for (int i = 0; i &lt; count; ++i)
        {
            //except for the main camera, which we'll use to transition between the others
            if (Camera.allCameras[i] != Camera.main)
            {
                camLocation = new CameraLocation();
                camLocation.name = Camera.allCameras[i].name;
                camLocation.fov = Camera.allCameras[i].fov;
                camLocation.position = Camera.allCameras[i].transform.position;
                camLocation.rotation = Camera.allCameras[i].transform.rotation;
                AddCamera(camLocation);
            }
        }
    }
 
    void OnWizardCreate()
    {
        Debug.Log("done");
    }
 
    void AddCamera(CameraLocation cam)
    {
        db.content.Add(cam);
        EditorUtility.SetDirty(dbaseAsset);
    }
 
    void OnGUI()
    {
        EditorGUILayout.BeginVertical();
         EditorGUILayout.BeginHorizontal();
        GUILayout.Label(name, GUILayout.MaxWidth(0));
        DBLocation = EditorGUILayout.TextField(DBLocation);
        if (GUILayout.Button("Browse"))
        {
            DBLocation = EditorUtility.OpenFilePanel(name, DBLocation, "asset");
            UpdateMyAssetLocation();
        }
        EditorGUILayout.EndHorizontal();
 
        if (GUILayout.Button("Update Database Location"))
        {
            UpdateMyAssetLocation();
        }
 
        EditorGUILayout.Space();
        if (GUILayout.Button("Add current Camera.main position", GUILayout.Height(50)))
        {
            camLocation = new CameraLocation();
            AddCamera(camLocation);
        }
        EditorGUILayout.Space();
        DisplayCurrentCamList();
        EditorGUILayout.Space();
 
        if (GUILayout.Button("Add all existing 'Cameras' to the list\n this can 'import' max camera locations", GUILayout.Height(50)))
        {
            AddExistingCameras();
        }
        EditorGUILayout.EndVertical();
    }
 
    void DisplayCurrentCamList()
    {
        for (int i = 0; i &lt; db.content.Count; i++ )
        {
            EditorGUILayout.BeginHorizontal();
            db.content[i].name = GUILayout.TextField(db.content[i].name, GUILayout.Width(200));
            if (GUILayout.Button("UP"))
            {
                if (i &gt; 0)
                {
                    CameraLocation item = db.content[i];
                    db.content.RemoveAt(i);
                    db.content.Insert(i - 1, item);
                }
            }
            if (GUILayout.Button("DN"))
            {
                if (i &lt; db.content.Count)
                {
                    CameraLocation item = db.content[i];
                    db.content.RemoveAt(i);
                    db.content.Insert(i + 1, item);
                }
            }
            if (GUILayout.Button("Remove"))
            {
                db.content.Remove(db.content[i]);
            }
            //db.SetDirty();
            EditorUtility.SetDirty(dbaseAsset);
            EditorGUILayout.EndHorizontal();
        }
    }
 
    public void UpdateMyAssetLocation()
    {
        dbaseAsset = AssetDatabase.LoadAssetAtPath(DBLocation, typeof(CameraLocationHolder));
        CameraLocationHolder CameraLocationDB = (CameraLocationHolder)dbaseAsset;
        db = CameraLocationDB;
    }
    public void CreateMyAsset()
    {
        List content = new List();
        CameraLocationHolder asset = new CameraLocationHolder(content);  //scriptable object 
        AssetDatabase.CreateAsset(asset, "CameraLocationDatabase");
        AssetDatabase.SaveAssets();
        EditorUtility.FocusProjectWindow();
        Selection.activeObject = asset;
    }
}
12 Comments more...

Recursive Tiff to PDF

by on Apr.01, 2010, under Admin, Batching, utility

Another one of those semi random requests that turned out to be more of a pain in the arse than it really should be. the mission: Converting a large mass of scanned tiff CAD drawings into multi page PDF’s

There are two ways to do this fairly quickly, the easy way if you don’t have many individual sets to work with is to simply drag all the tif images into the window in Acrobat Pro and save the file from there.

If however, like us, you have to convert 42 gigs or so of tiff’s spread across 204 folders and group the PDF’s by the folders, you can programmatically do it via some batch scripting and the use of a few free command line utilities from the internet.

First thing to do is convert each individual TIF file to a PDF, for this, we use Imagemagick this is a tool that can do many things but we’re really just going to abuse the ‘convert’ application for now.

After installation, you can then in the dos prompt type ‘convert file.tif file.pdf’

For our purposes we want to convert ALL of the files, so we can set it up in a recursive for loop using

Saved into a file “Convert all Tiffs in Subdirectory to PDFs.bat” or something equally exciting:
FOR /R %%a IN (*.tif) DO convert "%%a" "%%a.pdf"

After that is done we will use a second utility to ‘join’ the pdf’s the PDF toolkit

With this util we can modify pdf files quickly; we’ll use the Append function

pdftk *.pdf cat output combined.pdf

This will concatenate all pdf’s in the current directory and write them into ‘combined.pdf’ in the same directory. Great!

To expand this into a large subdirectory we do a little more batch scripting

The below is saved into a file “Join all PDF in directory tree.bat”

FOR /F "delims=" %%a in ('DIR /S /B /AD ^|SORT') DO (
CD "%%a"
pdftk *.pdf cat output "%%a_joined.pdf"
)

This will output a combined PDF for each of the individual directories.

A good time was had by all.

Imagemagick looks to have quite a few useful applications and will warrant some more exploration soon.

1 Comment more...

Interactive Lab Demo

by on Feb.10, 2010, under Architecture, csharp, Interactive, Unity3D

Here is another Architectural interactive test project. This time, a laboratory setup, a tighter interior space, set up without any real lighting (1 direct light + ambient colors + Ambient Occlusion currently) created with the goal of customizing the unity asset pipeline to be a little more friendly for architectural work.

First, the Project, roughly 6mb, clicking on the screenshot will open the project in a new window. its another quickly evolving work in progress, Warning: No consideration has been taken currently for speed on older hardware, ~100fps on an 8800GT was the target.
Screenshot

Some findings and random ramblings on arch and interactive 3d:

  • unlike with general gaming, with architecture you are in most cases going to already have a model provided in some form or another. odds are, the model is going to be pretty horrific, as arch models tend to be built for the purpose of either:
    • A: Viz… the Viz model will require a lot of cleanup in terms of material work, and stripping down a lot of the high poly fluff required for rendering.
    • B: Design Development… the DD model is normally plagued more by bad modeling conventions, no naming, poor materials, materials not fully assigned, or mangled geometry due to being stretched and squished for days on end to keep up with a design.
  • you will probably still want to break up the file by some logical layer type system, by default this will land you with a unique material for each material of each fbx file
  • a small AssetPostProcessor script that works on the OnMaterialAssign function can make the process of sharing materials across multiple FBX files much easier to handle, with the added bonus of being able to easily progressively build up a library of reusable realtime materials
 
//file: MaterialsPostProcessor.cs
//goal: Share a library of materials across different assets based on the name of material supplied in the FBX file
// Dave Buchhofer - 02.10.2010
 
using UnityEngine;
using UnityEditor;
using System.Collections;
 
public class MaterialsPostProcessor : AssetPostprocessor
{
    Material OnAssignMaterialModel (Material material, Renderer renderer)
    {
        //The path where you keep your "Standard" library of materials
        string StandardMatPath = "Assets/DMGStandardMaterials/" + material.name + ".mat";
 
        //the path where you want to keep your "temp" materials, that weren't found above
        //So you have a logical place to look for materials that need editing and tweaking for realtime work.
        string TemporaryMatPath = "Assets/Mesh/Materials/" + material.name + ".mat";
 
        //Check for it in the standard 
        if (AssetDatabase.LoadAssetAtPath(StandardMatPath, typeof(Material)))
        {
            Debug.Log("FOUND: " + StandardMatPath);
            return (Material)AssetDatabase.LoadAssetAtPath(StandardMatPath, typeof(Material));
        }
        //Else check to see if we've already built one in the temp path
        if (AssetDatabase.LoadAssetAtPath(TemporaryMatPath, typeof(Material)))
        {
            Debug.Log("FOUND temp: " + TemporaryMatPath);
            return (Material)AssetDatabase.LoadAssetAtPath(TemporaryMatPath, typeof(Material));
        }
        //Or create it?
        material.shader = Shader.Find("Diffuse");
        AssetDatabase.CreateAsset(material, TemporaryMatPath);
        Debug.Log("CREATED temp: " + TemporaryMatPath);
        return material;
    }
}

also check out the previous arch experiment a few posts down: Basketball Arena

3 Comments :, , , more...

Recursive file size listing output to excel

by on Dec.30, 2009, under Admin, Batching, Scripting, vbscript

Today I had to find all JPG files over a certain size in a large directory tree of a few thousand images, to find some poorly compressed jpg’s, fix the compression, and replace them on a website. Todays cavaet, the CMS is pretty weak and only lets you update 1 image at a time, so you can’t just blindly resave everything and reupload, todays Second cavaet, I dont have server level access to said website, so all the normal automated ways are out of luck.

joy.

So I found a little snip of code online, and threw a pretty simple hack job together to narrow the scope some. (PS: If you do end up doing any vbscript, I’d suggest VbsEdit for an IDE)

Anyway! the code:

It searches through a directory tree of your choosing
for any file matching the filetype: “jpg”
that is above a size threshold: fileSizeThreshold (In this case, 2kb.. so essentially everything)
and outputs it into an excell sheet with full path and size information
so that you can easily Sort, Filter, Delegate, or use as data for further automating!

' // **************************************
' //    ComputerHighGuys recursive search
' //     
' //    Date Created: 20 Aug 07
' //	http://www.tek-tips.com/faqs.cfm?fid=6716
' //
' //	Adjusted to a jpg file search and 
' //	added excel output for easy sorting/filtering
' //	
' // **************************************
 
' // If we'd like to save the output into an excel sheet
WriteExcel = "True"
 
Dim objexcel
excelRow=2
 
' // value the filesize needs to exceed to be visible in the output
fileSizeThreshold=2
 
If WriteExcel = "True" Then
	' // Create the Excel sheet to drop the information into
	Set objExcel = createobject("Excel.application")   
	objexcel.Workbooks.add
	objexcel.Cells(1, 1).Value = "Folder Name"
	objexcel.Cells(1, 2).Value = "Filename"
	objexcel.Cells(1, 3).Value = "Filesize"
	objexcel.Cells(1, 4).Value = "Filesize Unit"
	objexcel.Visible = True
	Wscript.Sleep 300
End If
 
' // Directory to search
searchDir = "C:\"
 
set objFSO=CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(searchDir)
Set colFiles = objFolder.Files
 
' // Launch the function
ScanSubFolders(objFolder)
 
' // recursive function that will search through all subfolders for a specified
' // filetype and output some information about the files to an Excel Sheel
Sub scanSubFolders(objFolder)
	' // Grab sub folders    
    Set colFolders = objFolder.SubFolders
 
    For Each objSubFolder In colFolders
 
    	' // the files to search
        Set colFiles = objSubFolder.Files
        For Each objFile in colFiles
 
            ' // the extension of the filetype to search for  
            If lcase(Right(objFile.Name,3)) = "jpg" Then
 
                ' // Getting File size in KB
                If round(objFile.Size/1024,1) > fileSizeThreshold Then
 
                	' // echo the files to the console
                	WScript.Echo objSubFolder.Path & "  " & objFile.Name & "  " & round(objFile.Size/1024,1)  & "KB"
 
					' // write various shit to excel~                    
                    If WriteExcel = "True" then
                        wscript.Echo objSubFolder.Path & "  " & objFile.Name & "  " & round(objFile.Size/1024,1)  & "KB"
                    	objexcel.Cells(excelRow, 1).Value = objSubFolder.Path
    					objexcel.Cells(excelRow, 2).Value = objFile.Name
    					objexcel.Cells(excelRow, 3).Value =  round(objFile.Size/1024,1)
    					objexcel.Cells(excelRow, 4).Value =  "KB" 
    					excelRow=excelRow+1    
                    End If
                End if    
            End If
        Next
        ScanSubFolders(objSubFolder)
    Next
End Sub
2 Comments :, , , , , more...

Architectural Interactive Demo

by on Dec.12, 2009, under Architecture, Interactive, Unity3D

Here’s a current work in progress Interactive Architectural type demo of a college Basketball Arena

it hasn’t been terribly optimized yet, so it requires a fairly hefty video card to play smoothly, that said, it runs at ~150fps on my 8800GT here, but at 5fps on my parents 3 year old dell. So your mileage may vary while I experiment!

http://www.buchhofer.com/upload/files/labs/drexel/ ~5mb

screenshot

Everything has been done with ingame lighting and shaders, nothing is baked, and was meant as a test specifically for that purpose, to see how far it can be pushed before requiring Texture Baking on a fair sized scene. (With the aim to be able to quickly iterate models in earlier phases of design without having to unwrap and bake!)

To the game! There is a quality button on the lower left that toggles between various settings of Anti Aliasing, Shader Quality, Shadow Quality.. so you can tailor it to a point to your hardware.

On the upper left is a selection of preset Camera views, clicking and dragging the mouse button in the viewport anywhere will ‘look’ the camera from the preset view.

On the upper right is some fun stuff, toggles for Ambient occlusion, Bloom shaders (glow) and a couple of sliders to adjust those primary values

there is also an alternate Daylighting scheme, if you do this i suggest toggling the roof, then you can adjust the time of day with the ‘TimeOfDay’ slider, and the ambient lighting with the ambient.

the rest are just toggles to show/hide various layers of objects

3 Comments more...

python based model/texture/matlib browser

by on Jul.24, 2009, under Uncategorized

This was a learning experiment a few months back that showed some promise, and turned into a pretty useful script, I debated passing it around due to its current ‘raw’ness, but it has already been very helpful here, and I think some feedback may motivate me to tighten it up some more!

pyAssetbrowser Screenshot

DOWNLOAD: pyAssetBrowser.zip

Setting it up

You pick a ‘Root Directory’ by editing the settings.ini file and changing the “rootPath=X:\\Model_Library\\” to match up with your chosen path. ex: if your model library is on C:\Assets, then you will make the settings.ini file read as: “rootPath=C:\\Assets\\” every directory under this ‘root’ directory will show up in the Treeview interface on the left hand side.

(and here comes the part that made me wary of distributing it)

For the connection with 3dsmax to work, python needs to know which version of max to connect to when ‘Max.Application’ is invoked! the cleanest way to do this is to just run Adams script: registerPythonCom.ms, this should work for most people! the cases I’ve found where it doesn’t work, are where there are more restrictive windows user permissions, in which case it may be required to manually do this. I’ve included a couple example .reg files that can connect to max 2008 / 2009, all that’s needed to suit them to your particular installation is to edit the paths under the LocalServer32 reg key for your particular max version.

On the 3dsmax side of things, there are a couple scripts included, the pyAssetbrowser.ms script is a Struct including some simple functions for things like importing, merging, converting to vrmesh, billboarding image maps, collapsing, re-linking maps using a few stripped down bits of modified code from an older version of ColinSenner‘s Relink Bitmaps script

File Structure

for the browser to work right as is, there are a few ‘format’ type issues that are needed to make it even remotely useful.

  • To use it as a Model Library:
    • A directory that includes .Max files.
    • If there is a .JPG file with the same name as the .MAX file, then it will be displayed as the thumbnail for the item, if there is no preview jpg the maxfile will show with a 3dsmax icon.

The material library setup is kind of strange, but its modeled somewhat after easily adding items from the VrayMaterials.de site, by just unzipping the library and ensuring that the directory has the same name as the thumbnail.

  • Material Library:
    • A Directory of .JPG thumbnails (Any size)
    • In the same directory, a Folder with the same name as the .JPG thumbnail
    • in the named folder (Yea, this gets a bit messy) a 3dsmax .mat material library file (Any name), and any textures required
  • Billboard/texture Library:
    • A directory of images, Most formats should work, Tiff’s wont show thumbnails due to some open source licensing silliness with PIL i believe.
    • One of the good things here, is that it can build a thumbnail list very very fast. not quite picasa fast, but far quicker than explorer does.

I’m sure there’ll be many issues, so call it a beta, and let me know if anyone has feedback!

I’m including the source also, as 90% of it is all bits and pieces of other peoples work!

A little source info

  • I compiled with python 2.5
  • the awesome ThumbnailCtrl from Andrea Gavanna
    • I did some *very* slight modifications on it to allow it to show .max files as an icon
  • used PIL 1.1.6+
  • Requires pyWin32 v212+ for the COM bridging to talk to 3dsMax
  • Interface in wxwidgets, using wxPython 2.8
  • ‘lazy’ directory treeview from here
  • exe compiled with py2exe 0.6.9

Some things that currently aren’t possible, but are obvious additions:

Multiple Root items in the treeview to allow for the fact of the various libraries to be in seperate locations/drives…

This can be done, but is a bit of a pain in the arse, how you would do it would be to create a virtual root, which is hidden (As the current root item is now) and add in your sub items below it with specific paths, the trick is just to adjust the path sent to the thumbnail control to account for the ‘virtual’ root. Not difficult, but possibly a little time consuming, so its been shelved unless someone wants to buy me a beer or two to polish it up 🙂

There is currently no way to retrieve the explorer bitmap thumbnails for .max files through python/pywin32, there is a few hints at there being possible workarounds and ideas that could work on Mark Hammond’s mailing list, but most of the talk about this is several years stale! any thoughts on this from the python heads would be more than welcome!

there are a few errors that pop up after closing but they dont affect usage currently.

3 Comments more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!