Virtual FCVW

Generally when on the road with VastPark at conferences/expos, I’m deprived of the opportunity to see many of the speakers and round table sessions that occur as I’m usually run off my feet speaking to people at the stand.

As I sit here at the VastPark booth during the Federal Consortium for Virtual Worlds during one of the few quiet periods I’m able to do both!

virtual-fcvw

If you’re at home and wanting to get in on the action as well, jump onto this link:

http://fcvw.vastpark.com/

It’s nearing the end of the day here in Washington but the feed will be live again tomorrow and you can certainly follow the twitter feeds post conference.

VastPark VNC Plugin

After some recent improvements to the core VastPark framework and the way textures are made available to the plugin API, I decided to build a VNC plugin for VastPark, basing it upon VncSharp. For those of you know don’t know what VNC is, there’s a good overview of it available on Wikipedia.

In it’s simplest form the IMML for the VNC Plugin will look something like this:

<Plugin Name="VncPlugin" Enabled="True" 
Source="http://content.vastpark.com/VastParkWS/get.vpws?publisher=craigomatic&amp;name=VncPlugin&amp;domain=vastpark&amp;context=park">
	<Element Name="TargetElement"/>
	<Element Name="OnPasswordRequired"/>
	<Parameter Key="Uri" Value="the-vnc-server-address.com"/>
	<Parameter Key="Port" Value="5900"/>
 	<Parameter Key="PasswordRequired" Value="OnPasswordRequired"/>
</Plugin>
<Script Name="OnPasswordRequired">
    function main(obj, args)
        vncplugin:setpassword('pass') --hardcoding password is bad, you should ask the user to enter one!
    end
</Script>
<Primitive Type="Box" Name="TargetElement" Size="1,1,1"/>

Here’s one I prepared earlier (well, Adrian did most of the work…):

vncplugin-in-action

Plugin Parameters

Uri

The host address of the VNC server. You should avoid using the scheme at the start of this address (ie: don’t put the http://)

Port

Defaults to the VNC default of 5900

ConnectionFailed

Name of a Script/Timeline/other ITimelineExecutable element to execute when a VNC connection has failed

PasswordRequired

Name of a Script/Timeline/other ITimelineExecutable element to execute when a password is required to connect to the VNC server

UpdateFrequency

The number of milliseconds to wait before automatically refreshing the remote screen when ManualUpdate is false. Defaults to 500.

ManualUpdate

True/False. When true, you need to call the vncplugin:requestupdate(true) for a fullscreen update. Defaults to False

You can download an example IMML document here: basic-vnc.imml and view the source code for the VNC Plugin over here: http://vastpark-svn.cvsdude.com/public/trunk/Plugins/Plugin.VNC/

A good free windows VNC Server is TightVNC

VastPark Masterclass 2

Some video from the second masterclass I spoke at. Adrian Shepherd also features first up with some info on the content pipeline. Was quite unprepared for this one, so definitely isn’t my best work 🙂


Virtual Worlds Down Under – Part 4 of 4 – Masterclass 2 from VastPark on Vimeo.

VastPark Masterclass 1

Here’s the video of a recent lecture I spoke at which was given in-world:


VastPark Masterclass1 from VastPark on Vimeo.

Building a stock market ticker in VastPark

Update: Revised IMML here: http://theparkisvast.com/2010/02/17/updated-imml-for-stock-market-ticker/

As I’m sure you’ve probably noticed by now, the stock markets around the world have crashed and burned with lots of dollars drained off the portfolio’s of many an investor, myself included.

To avoid future surprises, I’ve decided to pay a little more attention to what’s happening with the stock market and what better way to do so than via VastPark 🙂

The first step was to work out a reliable data source for the stock prices that would let me retrieve at least the following info:

As an Aussie, I’m primarily interested in stocks from the asx – but to allow others to re-use the code, something that would work worldwide was desirable.

Yahoo Finance

After a search lead me to the blog of Mads Kristensen and this post I realised Yahoo Finance would work perfectly using the request csv method:

http://finance.yahoo.com/d/quotes.csv?s=<StockName>&f=sl1c1ohgv 

For the first version of the ticker, I wanted to keep things simple. It would provide the following visual for each stock:

I also wanted to avoid writing a plugin to do the work and instead make use of the scene.web:loadstring method in VastScript, which would return a string containing all of the data to process.

Step 1: A Loaded trigger and target script
Use a document level Trigger and an inline script, get things rolling:

<Trigger Event="Loaded" Target="GenerateStocks" />
<Script Name="GenerateStocks">
function main(obj, args)
  --todo: retrieve the data
  --todo: parse the response data
  --todo: generate the visuals
end
</Script>

Step 2: Retrieve data from Yahoo!
To make life easier when changing stocks, I’m going to store a scene variable called stocks containing all of the stock codes I’m interested in.

--store a scene variable for the stocks to retrieve
  scene:set('stocks', 'IPL.AX, CFE.AX,FMG.AX,RIO.AX,BHP.AX,BBI.AX,BBP.AX,BOQ.AX,BRM.AX,FGL.AX,NAB.AX,TAM.AX,TLS.AX,WOW.AX')
  
  --create the http request
  request = httprequest('http://finance.yahoo.com/d/quotes.csv?s='..scene:get('stocks')..'&amp;f=sl1c1ohgv')
  
  --load the response 
  response = scene.web:loadstring(request)

Step 3: Parse the data into something useful
The response of the above turns out to be a large string containing all of the data for each stock separated by a comma and all stocks separated by a new line.

--reponse is returned as each stock on a new line, break into a table entry for each stock
  stocks = explode('\r\n', response)
 
  count = 1
 
  --stocks is a dictionary of all listed stocks, need to explode each listing to generate a representation
  --format: stock code, last trade,change, open, high, low, volume
  for key,value in pairs(stocks) do
    if(string.len(value) > 0) then
      scene.ui:writeline(value)
      stock = explode(',', value)
 
      --todo: generate the visual for this stock
 
    end
  end

Step 4: Generate the visual
We get a little tricky here, generating a red primitive if the stock is worth less than the open, green otherwise.

element = _generateStock(string.sub(stock[0],2,-2), stock[1], stock[2], stock[3], stock[4], stock[5], stock[6])
 
element.position = vector3(-20 + count*4.5, -9, 30)
scene:add(element)
 
function _generateStock(code, lastTrade, change, open, high, low, volume)
	scene.ui:writeline('generating representation for: '..code)
	emissive = rgb("#00ff00") --default to green
 
	changeFromOpen = string.sub(change, 1,1)
	if(changeFromOpen == "-") then
		emissive = rgb("#ff0000") --red
		scene.ui:writeline('stock trading lower than open!')
	end
 
	--representation for the last trade
	lastTradePrim = primitive()
	lastTradePrim.type = primitivetype.cylinder
	lastTradePrim.complexity = primitivecomplexity.high
	lastTradePrim.name = code.."_last"
	lastTradePrim.material.emissive = emissive
	lastTradePrim.size = vector3(0.5, tonumber(lastTrade), 0.5)
	lastTradePrim.position = vector3(0.5,0.5,0)
 
	--representation for the opening price
	openPricePrim = primitive()
	openPricePrim.type = primitivetype.cylinder
	openPricePrim.complexity = primitivecomplexity.high
	openPricePrim.name = code.."_open"
	openPricePrim.material.emissive = rgb("#ffffff")
	openPricePrim.size = vector3(0.5, tonumber(open), 0.5)
	openPricePrim.position = vector3(-0.5,0.5,0)
 
	--stock code text
	stockCodeText = text()
	stockCodeText.name = code.."_text"
	stockCodeText.value = code
	stockCodeText.billboard = true
	stockCodeText.position = vector3(0,-1,-3) --1 unit lower than the parent and 3 units fwd
 
	--parent prim for the platform the 2 colums will sit on
	basePrim = primitive()		
	basePrim.name = code.."_parent"
	basePrim.material.emissive = rgb("#000000")
	basePrim.size = vector3(3,0.5,3)
	basePrim:add(lastTradePrim)
	basePrim:add(openPricePrim)
	basePrim:add(stockCodeText)
 
	return basePrim
end

Once the stocks have been generated, they now need to be updated with the new values. Yahoo Finance appears to operate on a slight delay but the data changes real-time, offset by that delay, so we can query for new data quite often. The best way to do this is with a looping timeline that runs a script every n seconds. I’ve chosen to do this every 2 seconds:

<Timeline Name="Timeline" Enabled="True" Speed="1" Loop="True" AutoTween="False">
    <ExecuteEvent Time="00:00:02" Element="UpdateStocks" />
  </Timeline>

My UpdateStocks script looks something like this:

<Script Name="UpdateStocks">function main(obj, args)
	request = httprequest('http://finance.yahoo.com/d/quotes.csv?s='..scene:get('stocks')..'&amp;f=sl1c1ohgv')
	response = scene.web:loadstring(request)
 
	--reponse is returned as each stock on a new line, break into a table entry for each stock
	stocks = explode('\r\n', response)
 
	count = 1
 
	--stocks is a dictionary of all listed stocks, need to explode each listing to generate a representation
	--format: stock code, last trade,change, open, high, low, volume
	for key,value in pairs(stocks) do
		if(string.len(value) &gt; 0) then
			stock = explode(',', value)
			_updateStock(string.sub(stock[0],2,-2), stock[1], stock[2], stock[3], stock[4], stock[5], stock[6])
		end
	end
end
 
function _updateStock(code, lastTrade, change, open, high, low, volume)
 
	emissive = rgb("#00ff00") --default to green
 
	changeFromOpen = string.sub(change, 1,1)
	if(changeFromOpen == "-") then
		emissive = rgb("#ff0000") --red
	end
 
	--update the last trade visual
	lastTradeVisual = scene:getelement(code..'_last')
	lastTradeVisual.material.emissive = emissive
	lastTradeVisual.size = vector3(0.5, tonumber(lastTrade), 0.5)
	
	--update the % change
	percentageChange = (tonumber(change) / tonumber(open)) * 100
	stockText = scene:getelement(code..'_text')
	stockText.value = code.."\r\n$"..lastTrade.."\r\n".._round(percentageChange,2).."%"
end
	
function _round(num, idp)
  local mult = 10^(idp or 0)
  return math.floor(num * mult + 0.5) / mult
end</Script>

Putting all of the above together does the trick and we now have a good basis to make something more advanced.

The finished product

The stock ticker in action

Download the IMML document here (right-click, save as): Stock ticker.park

← Previous Page