Instances in Blender

Published on March 28, 2012
under Blender

Let’s say you want to create a 3D scene with lots of vegetation, like a forest, or jungle. The typical approach would be to model a tree, then duplicate it all over your terrain object. The main pitfall here is that for every tree you duplicate, you increase the amount of memory required to handle the scene. You are then forced to either settle for less trees, reduce the density of the mesh, or upgrade your ram.

Fortunately, there is a solution — instancing.

Instead of duplicating an object, you create an instance — or occurrence — of the object in your scene. In the end you could have, say, 1 tree occurring 1000 times instead of 1000 trees once. The difference is that with the former, only mesh data for a single object is held in memory, and rendered at multiple locations, whereas with the latter you have 1000 objects in memory. Both increase render times with object count, but instancing keeps memory usage at a healthy level, allowing you to push the limits of your rig to a new extreme — within limits to your patience, of course. In Blender, the easiest way to utilize instancing, is with particles.

Using instances, I’ve managed to create this scene with just a little over 1 trillion triangles.

trillion triangle forest

Big Image Alert! Click for full size.

Each tree has 416,040 triangles, and there are 3,000,000 trees. That gives us 1,248,120,000,000 (1.25 trillion) triangles in trees alone. I’ll admit it’s a little bit overkill for a forest. Grass would have been a better application. Or maybe sand in the desert.

So let’s get started on an instance heavy scene!

Setting up the scene

How about we render this in Cycles? It’ll be fun! Select Cycles Render from the menu in the top bar. It’s Blender Render by default.

Ok, let’s create some geometry. I’ll start with the terrain.

Add a plane, and in edit mode scale it up by 8. You’re probably going to want more detail than one face, so add a multiresolution modifier and subdivide until you’re happy.

multires modifier settings

I find that the Multires modifier gives the most flexibility when preparing a single faced plane for displacement.

Next let’s add a displace modifier. Click +New to create a texture and, if you like, name it something meaningful. Now you can go and find it in the textures panel.

Displace Modifer

Giving your new texture a name helps in finding it later.

Displacement Texture

You can use any texture you want. You could even model the terrain instead! I've used a Cloud procedural with the Voronoi F1 basis function.

preview of displaced terrain

This is what my terrain looks like with displacement.

Once you’re happy with the terrain, head over to layer 2 to start on the tree. Blender ships with a neat plugin for adding trees. If you don’t have it enabled yet, go do that now. Ctrl+Alt+U > Addons > Add Curve > Sapling. Once you’ve enabled that, you should have a new item under the add curve menu — “Add Tree”. Click it and you should have tree branches in the viewport. In the settings to the left click bevel, in branch splitting set levels to 3, and in leaves select show leaves. You can play around with the settings all you like, but this is good enough for me.

tree geometry settings

All I did was check Bevel. There are a few presets at the bottom, if you want to try them out.

tree leaves settigns

Checking Show Leaves generates leaves on the branches.

tree branch settigns

I don't know what all this stuff does. I just upped Levels and left.

tree preview

This is what you get.

You should be presented with two objects — the branches, and the leaves. Give them both appropriate materials.

leav material

Green. Not too vibrant, not too dark.

trunk material

You're probably never going to see the trunk anyway.

To make things easier for us later, we should combine them into a single object. The branches are a curve object while the leaves are a mesh, so select the branches and convert it to a mesh. Alt+C > Convert from Mesh/Meta/Surf/Text. Then select both objects and join them with Ctrl+J.

With the geometry out of the way, we might as well setup the camera and lights back on layer one.

light and camera result

There is a small plane to the top right with an emission material acting as the sun. The background is a sky blue material (duh).

The Particle System

Time to move on to the particles. Select the landscape mesh and give it a particle system. Set the type to hair and tick advanced. Turn down the emission number to something like 10 and normal velocity to around 0.100.

basic particle settings

The size is set with the velocity settings.

The reason you want hair instead of regular particles, is because we are going to render our tree at each particle, and with hair the object starts at the base of the strand and points to the tip. With point particles the object would float at the point location.

To render as objects instead of strands, find the render panel of the particle settings and click the object button. Set your tree as the dupli object and check rotation.

particle render settings

The rotation option aligns the object to the strand.

Now you should have a tree for every particle. This is why we set the count to 10; performance of the viewport will drop quickly as more trees are added. We will deal with that in a bit, but first locate a tree on your landscape and notice that they are all laying down. To fix this, select your tree on layer two and rotate it by 90° on the Y axis. The positive X axis points up along the normals. Whatever.

You can adjust the size of the trees at anytime by tweaking the velocity settings.

The trees should render correctly now, but before we raise the particle count, set the particle display to path. They will still render as trees, but display as dots in the viewport. (No, I don’t know why path means dots)

particle display settings

You could opt for None, too, if you're feeling adventurous.

Time to turn up the heat! What I like to do is set the count to 1000, and use children as a multiplier. Note that if you have too many instances (is there such a thing?), the trees overlap a lot and look ugly and cluttered. No need to let that stop you, of course, just lower the count or size.

particle children settings

Every particle in our system now has 50 children. Note that I've also set the emission number to 1000. This gives us a total of 50,000 trees at render time. With each tree at 65,100 triangles we have 3,255,000,000 (3 and a quarter billion) tris worth of foliage.

result preview

This is what our forest looks like so far. Not bad! It looks more like grass at this scale, but pull the camera in, and the leaves will surely still be there.

It looks good, but all the trees look exactly the same; the scene could use some variation.

In layer two select the tree. In the object settings under the groups panel click the + button to add the tree to a new object group. If you go back to the particle system, under the render panel you’ll notice a button labeled group. Select it, and set our tree group as the dupli group, and check pick random. Now, instead of rendering each instance as the tree, it will pick a random object in our group. I hope you can tell where we’re going with this. We only have one object in the group right now, so go ahead and duplicate the tree — the duplicate will be automatically part of the same group — and change something. Anything. Rotation, scale, material, all of the above! Play around with it until you’re happy with the results. Remember, you have as many objects in the group as you want!

Tips

When rendering with Cycles and gpu acceleration, you may run into memory related errors that abort the render, or maybe video driver crashes. Tweaking the tile size in the performance panel (i.e. lowering it) can help. When all else fails, you can always resort to cpu calculations.

Don’t go too high with the particle count. If you need more polygons, creating denser meshes, or merging duplicated objects into one, are more efficient than raising the number of instances. So if you’re looking to make grass, you would be much better off instancing patches of grass rather than individual blades. Or clusters of trees instead of one at a time. In fact, that’s how I reached 1 trillion triangles in the scene up top. I tried using single trees and barely made it out alive.

And that’s pretty much it. Particles + object render = polygons everywhere.

Expand Article

Tags:
No Comments »

Tunnel Effect Demo

Published on March 16, 2012
under Flash

Just my actionscript port of this oldschool tunnel effect. I was going to add motion blur, but that really slowed things down.

Tunnel Effect

Use the left and right arrows to rotate, and the up arrow to accelerate.

Expand Article

Tags:
No Comments »

A wordpress plugin for displaying Flash and Java applets

Published on November 15, 2011
under wordpress

Ahh — don’t you just love that new blog smell? What I don’t love, however, is when I click on a link and get sent to a page that loads up dozens of flash or java applets. I don’t have the best internet connection, and I don’t want to load a large applet if I don’t have to. I have the Flashblock add–on for Firefox, so it’s not that big of a deal anymore (as far as flash goes, anyway). I still don’t know what I’m missing, though, so I end up loading a bunch only to find out I wasn’t interested.

I plan on adding a few applets here and there in my posts, and I would like to provide some kind of information about each applet so that readers have a better idea what they’re getting themselves into. Features I would like to have would be:

  • Type of applet — is this a flash or a java applet? Java seems to take longer to initialize for me, so it would be nice to know what I’m loading ahead of time.
  • Size of file — Often times I find myself in a position of limited bandwidth. In these cases, it would be nice to know how big this file is.
  • Description — a brief description of the contents and/or controls would be nice.

In the end, this is what I came up with:

1017 B

A flash applet

This is just a test of the plugin. Click the play button above to load this applet. Click the stop button to remove it.

1.46 kB

A java applet

This is just a test of the plugin. You may notice that this is a java applet.

I’ve used an image as a place holder for an unloaded applet. One idea would be to use a screenshot of the actual applet. What I’ve done in this article, however, is have one image for flash viewers, and another for java (my way of distinguishing applet types). By the end you should be able to modify the code to support a unique image per applet rather than per type, if that’s what you want.

Embedding applets

In order to implement something like this, the first thing you need to do is figure out how you want to embed flash and java on your page. There’s a few ways to go about this, but I’m going to use the <object> tag.

The object tag can be used to embed a wide variety of things, like images, audio, video, documents, applets etc…. If the browser, for one reason or another, cannot handle the particular object, it will instead move on to its nested contents. The content could be text letting the user know the object could not be loaded, a link to the plugin download page, or another object. This way, if one object doesn’t work, we could try a different object. If that doesn’t work, we try a different applet type, then an image, then some text.

Java

Embedding with the Object tag

<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" width="200" height="200">
	<param name="code" value="mypackage/MainClass.class" />
	<param name="archive" value="MyApp.jar" />
	<param name="codebase" value="url/to/file/" />
		<object type="application/x-java-applet" width="200" height="200">
			<param name="code" value="mypackage/MainClass.class" />
			<param name="archive" value="MyApp.jar" />
			<param name="codebase" value="url/to/file/" />
				Unable to display Java Applet
		</object>
</object>

As you can see, we have an <object> tag, some parameters, and then another <object> inside. The first object is for IE support — IE can’t (or won’t) handle nested objects, so we need to have it first, otherwise it won’t be read. Second is for everyone else, and if that doesn’t work, we probably don’t have java installed, and I’m just going to display some text. The major difference between the two is that most browsers are happy with a simple MIME type (the type attribute), were as IE needs a classid attribute. The classid points to the latest version of the plugin, or something, seems to work in IE9 so I’m not changing it. Other browsers ignore objects with a classid and move on to the second one.

On to the parameters. The parameters in both objects should be the same.

  • width & height: does what you think it does — specify the size of the applet
  • codebase: Url to the directory containing your applet. If the files are in the same directory, either use an absolute path or omit this tag entirely as an empty value causes errors.
  • archive: I always compile as a .jar archive, and this is where it’s referenced
  • code: A url — starting inside the .jar — to the main class. If you open up a .jar file, there is a folder for every package, so if your main class is in the package “helloworld” then enter “helloworld/yourmainclass.class”. This value is case-sensitive, careful.

You may notice that the object for IE comes first while the object for standards compliant browsers comes second. I don’t like it either, let’s fix that.

Good browsers come first

<!--[if !IE]>-->
	<object type="application/x-java-applet" width="200" height="200">
		<param name="code" value="mypackage/MainClass.class" />
		<param name="archive" value="MyApp.jar" />
		<param name="codebase" value="url/to/file/" />
		<!--<![endif]-->
			<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" width="200" height="200">
				<param name="code" value="mypackage/MainClass.class" />
				<param name="archive" value="MyApp.jar" />
				<param name="codebase" value="url/to/file/" />
					Unable to display Java Applet
			</object>
	<!--[if !IE]>-->
	</object>
<!--<![endif]-->

What happened? I switched the objects around so that IE is second. Because IE refuses to deal with nested objects, it won’t reach the complaint code without some help. The conditional tags on lines 1, 6, 13, and 15 fix this — they tell IE to ignore lines 2-5, and 14, which happen to be the lines containing the outer object. Now, IE only sees the object that it can handle, while a standards compliant browser will try the outer object first, and if that fails we go onto the second. That one is most likely to fail, so we would then go on the the next, which is just text in my example.

Flash

Embedding flash

<!--[if !IE]>-->
	<object type="application/x-shockwave-flash" data="MyApp.swf" width="200" height="200">
		<param name="movie" value="MyApp.swf" />
		<!--<![endif]-->
			<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="200" height="200">
				<param name="movie" value="MyApp.swf" />
					<a href="http://www.adobe.com/go/getflash">
						<img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif"
							 alt="Get Adobe Flash player"/>
					</a>
			</object>
	<!--[if !IE]>-->
	</object>
<!--<![endif]-->

Similar to the java code. We have the IE object on the inside, with its classid and whatnot, and the standard object on the outside.

movie and data are both pointers to the .swf file. It can be an absolute or relative path. On line 3 we have a movie parameter even though the file has already been specified in the data attribute on line 2. The data attribute is required for Firefox. Opera and Chrome can use either movie or data. I cannot confirm anything for Safari though.

For both java and flash, there exists a way to pass extra attribute to the applet for use at runtime by adding more param tags, but as I have no experience in that area, I don’t have much to say.

Formatting & Styling

Layout and scripts

Our applets won’t be loaded by default, so we’ll need some kind of placeholder, something to contain the applet, controls, and metadata. Again, there are countless ways you could do this. Here’s what I’ve done.

Laying out our wrapper

<script type="text/javascript" src="wpav.js">
</script>

<div class="wpav-container type" style="width:210px;">
	<div id="wpav-applet123456" class="wpav-applet" style="height:200px;" >
		<noscript>
			Javascript is required to view this applet.
		</noscript>
	</div>

	<p class="wpav-filesize">1024 kB</p>

	<div class="wpav-buttons">
		<p class="wpav-play-button" title="Load applet" onclick="wpav_show('id', 'type', 'codebase', 'data', 'archive', width, height)"></p>
		<p class="wpav-stop-button" title="Remove applet" onclick="wpav_hide(id)"></p>
	</div>

	<p class="wpav-title"> </p>
	<p class="wpav-description"> </p>
</div>

wpav.js

/*
When called, inserts appropriate html into our wrapper

	@id			ID of div in which to insert html
	@type		string denoting applet, and therefore html, to insert
	@codebase	string for java's codebase param
	@data		code param for java, movie and data param for flash
	@archive	archive param for java
	@width		width of applet
	@height		height of applet
*/
function wpav_show(id, type, codebase, data, archive, width, height) {
	var value;	//html that goes into our wrapper
	if(type == "flash") {
		//html for flash applets
		//remember to escape (\) those quotation marks!
		value =	"<!--[if !IE]>-->" +
				"    <object type=\"application/x-shockwave-flash\" data=\"" + data + "\" width=\"" + width + "\" height=\"" + height + "\">" +
				"        <param name=\"movie\" value=\"" + data + "\" />" +
				"        <!--<![endif]-->" +
				"            <object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" width=\"" + width+ "\" height=\"" + height + "\">" +
				"                <param name=\"movie\" value=\"" + data + "\" />" +
				"                    <a href=\"http://www.adobe.com/go/getflash\">" +
				"                        <img src=\"http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif\"" +
				"                             alt=\"Get Adobe Flash player\"/>" +
				"                    </a>" +
				"            </object>" +
				"    <!--[if !IE]>-->" +
				"    </object>" +
				"<!--<![endif]-->" ;
	} else if(type == "java") {
		//html for java applets. I've assumed that 'codebase' will always be provided. Remember that
		// a blank value here will throw off the browser and will not load the applet.
		//remember to escape (\) those quotation marks!
		value = "<!--[if !IE]>-->" +
				"    <object type=\"application/x-java-applet\" width=\"" + width + "\" height=\"" + height + "\">" +
				"        <param name=\"code\" value=\"" + data + "\" />" +
				"        <param name=\"archive\" value=\"" + archive + "\" />" +
				"        <param name=\"codebase\" value=\"" + codebase + "\" />" +
				"        <!--<![endif]-->" +
				"            <object classid=\"clsid:8AD9C840-044E-11D1-B3E9-00805F499D93\" width=\"" + width + "\" height=\"" + height + "\">" +
				"                <param name=\"code\" value=\"" + data + "\" />" +
				"                <param name=\"archive\" value=\"" + archive + "\" />" +
				"                <param name=\"codebase\" value=\"" + codebase + "\" />" +
				"                    Unable to display Java Applet" +
				"            </object>" +
				"    <!--[if !IE]>-->" +
				"    </object>" +
				"<!--<![endif]-->" ;
	}

	//we get the element with the specified id and insert our html
	document.getElementById(id).innerHTML = value;
}

// remove html from our wrapper
function wpav_hide(id) {
	//just set the html to a blank string
	document.getElementById(id).innerHTML = "";
}

Should be fairly self explanatory. I’ve prefixed everything with “wpav” (WordPress Applet Viewer) to avoid any name collisions. You need to give “wpav-applet” a unique ID because we need to reference it in the javascript. Inside the “wpav-applet” is where the applet will be once it’s loaded. In case javascript is disabled/not supported, <noscript> will be executed. The buttons have an onclick event listener attached that calls the javascript functions below, which in turn adds our applet html to the <div> pointed to by id.

I’ve hardcoded the width of the wrapper to the width of the applet, plus 10px to account for the 5px margin I’ll add to the applet. This is because <div>s take up an entire line by default, and each applet may be a different size so we can’t do it in a stylesheet. I’ve also hardcoded the height of the applet container — it would be collapsed at 0px until the applet loads otherwise. The buttons are just empty <p> tags. You could have text in there, but I’m going to make them fixed size image buttons with css in a bit.

In the javascript we just convert our earlier html for applets into strings, inject variables, and shove it into our applet container. We only need to declare these functions once, and we use unique ID’s so one set of buttons don’t control all the applets.

Stylesheet

Now we can begin styling our wrapper. I just put this in my theme’s stylesheet.

style.css

/* By setting the display to inline-block, I can put these applet wrappers in a div whose
   text-align property is center, and have them actually center, like text */

.wpav-container {
	display: inline-block;
	font-size: 0.8em;
	margin: 2em 1em;
	text-align: justify;
}

.wpav-applet {
	background-position: center;
	background-repeat: no-repeat;
	margin: 5px;
}

/* Give the applet container a different background image based on it's type */

.wpav-container.flash .wpav-applet {
	background-image: url("images/flash-type-background.png");
}

.wpav-container.java .wpav-applet {
	background-image: url("images/java-type-background.png");
}

.wpav-filesize {
	color: #999;
	margin: 4px;
	text-align: center;
}

.wpav-buttons {
	text-align: center;
}

/* Give the buttons a fixed size plus a background image */

.wpav-play-button {
	width: 20px;
	height: 20px;
	display: inline-block;
	margin: 2px;
	background-image: url("images/play-button.png");
}

.wpav-stop-button {
	width: 20px;
	height: 20px;
	display: inline-block;
	margin: 2px;
	background-image: url("images/stop-button.png");
}

/* The 'active' psuedo-class is for when an element is depressed.
   When I hold the mouse button down, I change the background image, for effect */

.wpav-play-button:active {
	background-image: url("images/play-button-pressed.png");
}

.wpav-stop-button:active {
	background-image: url("images/stop-button-pressed.png");
}

.wpav-title {
	font-weight: bold;
	font-size: 1.25em;
	text-align: center;
}

.wpav-description {
	margin: 1em 10px;
}

WordPress and Shortcodes

And that should just about do it for the applet viewer. All you have to do is insert the html for the wrapper, plug in the corrent type, sizes, unique id’s, urls, archive, etc… kind of tedious, isn’t it? Fortunately, WordPress has a solution: Shortcodes.

A shortcode is a tag you enter in the editor and WordPress parses it when rendering a page and changes it to somethings else. They follow the form [code-name param1="value" param2="value"] inner text [/code-name]. They can take parameters, none at all, can have inner text (requires closing tag), or not (self closing). A decent shortcode for our purpose would look something like this:

[applet type=" " data=" " filesize=" " archive=" " codebase=" " width=" " height=" " title=" " description=" " /]

archive and codebase would only be required for java. You would have to check the size of the file and manually enter the string — if that seems too tedious, you probably have too many applets! I tried using php functions to get the size, but I couldn’t get them to work… When you enter the above code in the post editor — with appropriate params and values — WordPress should insert the html for our wrapper. Not now, of course — we need to write the shortcode functions first. We can put them in our theme’s functions.php, but a better course of action would be to create a dedicated plugin.

Building the plugin

In your wordpress/wp-content/plugins/ directory make a new folder and name it something like wordpress-applet-viewer. Inside this is going to be files for the plugin. You can put wpav.js in there now, and add a new php file named wordpress_applet_viewer.php.

wordpress_applet_viewer.php

<?php
	/*
	Plugin Name: WordPress Applet Viewer
	Plugin URI: Website for plugin
	Description: Adds a shortcode for our applet viewer
	Author: Your name
	Author URI: Your site
	Version: 1.0
	*/

	// This function includes our javascript in our page
	// We have to be carefull with relative urls because the page containing
	// this snippet is NOT going to be in the plugins folder. We can get a url starting in our
	// plugins folder with the plugins_url() function with __FILE__ as the second argument.
	function insert_wpav_js() {
		?>
			<script type="text/javascript" src="<?php echo plugins_url('/wpav.js', __FILE__); ?>">
			</script>
		<?php
	}

	// Add insert_wpav_js() to the wp_head hook
	add_action('wp_head', 'insert_wpav_js');

	// Our shortcode function
	function wpav_shortcode($atts) {
		extract(shortcode_atts(array(
			'type' => '',
			'data' => '',
			'codebase' => '',
			'archive' => '',
			'width' => 0,
			'height' => 0,
			'title' => '',
			'description' => '',
			'filesize' => '',
			), $atts));

		return '';
	}

	// Register shortcode with WordPress
	add_shortcode('applet', 'wpav_shortcode');
?>

There’s the base for our plugin. The comment up at the top provides information about our plugin. They are all optional save for “Plugin Name” — it’s required for the plugin to show up in your WordPress plugin page.

insert_wpav_js() spits out html for importing our javascript. add_action('hook_name', 'function_name') registers insert_wpav_js() to the wp_head hook. A hook is a function that calls other functions. wp_head() should be located between the <head> tags of your theme, and that’s where it’s registered functions will be called. Good place to put our javascript.

wpav_shortcode() is the function that handles our shortcode. WordPress gives it all the attributes we pass to the shortcode from the editor via the $atts argument. We use the extract() function to get these values as variables we can use. Inside array() we specify all the parameters we’re looking for and assign them a default value. The string values I’ve just set to empty strings, and the integers to 0.

The return value is what is going to replace the shortcode. We need to convert our applet viewer html into a string and insert the variables just like we did the the applet with javascript earlier.

And then there’s add_shortcode('shortcode_name', 'shortcode_function'). The first argument is the name of the shortcode, and the second is the name of the function that will handle it. For my example, we’ll use ‘applet’ as the name, so every time we enter the shortcode [applet args... /] WordPress will call wpav_shortcode() to parse it.

Here’s the new wpav_shortcode() with our wrapper html.

function wpav_shortcode($atts) {
	extract(shortcode_atts(array(
		'type' => '',
		'data' => '',
		'codebase' => '',
		'archive' => '',
		'width' => 0,
		'height' => 0,
		'title' => '',
		'description' => '',
		'filesize' => '',
		), $atts));

	// uniqid() returns a random string prefixed by, in our case, 'wpav-applet'
	// we will use this as the unique ID for the applet container
	$app_id = uniqid('wpav-applet');

	// if the 'type' parameter is a valid value, we continue, otherwise we return an empty string
	// You could add or remove support for object types. For example, you could add support for
	// images, audio, or video. You would just need to add the appropriate code in wpav.js
	//
	// For the javascript arguments, use single quotes for strings so they don't close the html
	// attributes, and escape them (\) so they don't close the php strings.
	if($type == 'java' || $type == 'flash') {
		return  '<div class="wpav-container ' . $type . '" style="width:' . ($width + 10) . 'px">' .
				'	<div id="' . $app_id . '" class="wpav-applet" style="height:' . $height . 'px;" >' .
				'		<noscript>' .
				'			Javascript is required to view this applet.' .
				'		</noscript>' .
				'	</div>' .

				'	<p class="wpav-filesize">' . $filesize . '</p>' .

				'	<div class="wpav-buttons">' .
				'		<p class="wpav-play-button" title="Load applet" onclick="wpav_show(\'' . $app_id . '\', \'' . $type . '\', \'' . $codebase . '\', \'' . $data . '\', \'' . $archive . '\', ' . $width . ', ' . $height . ')"></p>' .
				'		<p class="wpav-stop-button" title="Remove applet" onclick="wpav_hide(\'' . $app_id . '\')"></p>' .
				'	</div>' .

				'	<p class="wpav-title">' . $title . '</p>' .
				'	<p class="wpav-description">' . $description . '</p>' .
				'</div>' ;
	} else {
		return '';
	}
}

If all went as planned, your shortcode should be working now. Here are some examples:

[applet type="flash" data="media/2011/11/file.swf" filesize="1024 kB" width="550" height="400" title="A flash app" description="Some description" /]

[applet type="java" data="package/MainClass.class" filesize="1024 kB" archive="MyApp.jar" codebase="/media/2011/11/" width="256" height="256" title="A java applet" description="Some description" /]

Editor buttons

Another thing you could do is add some buttons to the toolbar above the html editor that inserts a skeleton for our not-so-shortcode. We’ll be adding the button to the html editor, not the visual editor. The proper way to add a button to the visual editor is to register a plugin for TinyMCE, and I don’t know how to do that yet. For the html editor, we can just get the toolbar with javascript and insert a button input object.

Add this somewhere in wordpress_applet_viewer.php

// Just inserts a javascript file
function wpav_quicktag_buttons() {
	?>
		<script type="text/javascript" src="<?php echo plugins_url('/wpav_quicktags.js', __FILE__); ?>">
		</script>
	<?php
}

// register the function to the 'admin_footer' hook
add_action('admin_footer', 'wpav_quicktag_buttons');

wpav_quicktags.js

// Check for DOM readiness
var alreadyrunflag=0

if (document.addEventListener)
  document.addEventListener("DOMContentLoaded", function(){alreadyrunflag=1; addWPAVButtons()}, false)
else if (document.all && !window.opera){
  document.write('<script type="text/javascript" id="contentloadtag" defer="defer" src="javascript:void(0)"><\/script>')
  var contentloadtag=document.getElementById("contentloadtag")
  contentloadtag.onreadystatechange=function(){
    if (this.readyState=="complete"){
      alreadyrunflag=1
      addWPAVButtons()
    }
  }
}

window.onload=function(){
  setTimeout("if (!alreadyrunflag) addWPAVButtons()", 0)
}

function addWPAVButtons() {
	// Get reference to the element housing all the buttons. In this case
	// it has the ID "ed_toolbar"
	var tlbr = document.getElementById("ed_toolbar");

	// This is a reference to the text area
	var edtr = document.getElementById("content");

    // Create some input elements
    var jButton = document.createElement("input");
    var fButton = document.createElement("input");

    jButton.type = "button";
    jButton.value = "Java Applet";

    // edInsertContent(field, content) is a function that wordpress uses to insert
    // text into the editor.
    // There is not supposed to be a space between '[' and "applet"
    // but it gets treated as a shortcode and dissapears, so I needed to add it
    // to show you
    jButton.onclick = function() { edInsertContent(edtr, "[ applet type=\"java\" codebase=\"\" archive=\"\" data=\"\" width=\"\" height=\"\" filesize=\"\" title=\"\" description=\"\" /]") };
    jButton.className = "ed_button";
    jButton.title = "Insert Java Applet";
    jButton.id = "ed_java";

    fButton.type = "button";
    fButton.value = "Flash Applet";
    fButton.onclick = function() { edInsertContent(edtr, "[ applet type=\"flash\" data=\"\" width=\"\" height=\"\" filesize=\"\" title=\"\" description=\"\" /]") };
    fButton.className = "ed_button";
    fButton.title = "Insert Flash Applet";
    fButton.id = "ed_flash";

    // Add elements to toolbar
    tlbr.appendChild(jButton);
    tlbr.appendChild(fButton);
}

wpav_quicktag_buttons() simply includes a javascript file, then add_action() adds it to the footer in the admin page.

wpav_quicktags.js creates two buttons — one for inserting a snippet for flash, and the other for java.

Lines 1–19 are essencially cut & pasted from http://www.javascriptkit.com/htmltutors/domready.shtml and check if the DOM is ready. If the DOM is ready, then the toolbar and text area are available for manipulation. If the DOM isn’t ready, then we can’t be so sure our function will find those components.

Note that I have a space between "[" and "applet" on lines 41 and 48. That is because it was being parsed as an actual shortcode. You don’t want the space in the actual javascript.

You should now have two extra buttons in the html editor toolbar.

A more user-friendly approach would be to have a single button that opens a popup window — or a lightbox — with a form that outputs the correct snippet. I haven’t figured out how that works yet, so I’ll leave that up to you.

Expand Article

Tags: , ,
1 Comment »