Tags

, , , , , , , , , , , , ,

Salt. Water. Salt water, mineral.

Here’s a bottle of mineral water I did do in da Blender:

oculusrift_virtualbottle_blendermodel

Arkwood said, ‘It’s da nuts.’ Just words.

I followed the Blender tutorial called: Mineral Bottle

It is one thing to create a bottle of mineral water in Blender. But how do you animate it? Easy.

Animation

I added 5 frames of animation. The 1st and 5th frame were keyframes of the bottle in its normal state. But keyframe 3 was of the bottle squished. Frames 2 and 4 automatically transform the bottle between the keyframes.

oculusrift_virtualbottle_blenderanimation

The Blender tutorial Basics of Animation showed me how to press the key “I” over the Scale settings of the bottle, to record the keyframes.

oculusrift_virtualbottle_blenderscale

I exported the bottle animation from Blender as a series of Wavefront .obj files.

oculusrift_virtualbottle_blenderexport_withanimation

So each frame of animation can be imported into my Visual Studio C++ application, ready for the Oculus SDK for Windows and OpenGL graphics library to render.

We just need a way to loop through our squishy bottle animation.

Here’s some variables which will help:

int bottleFrame = 3;
const int bottleFrameMin = 3;
const int bottleFrameMax = 7;

int bottleFrameCycle = 0;
const int bottleFrameCycleMax = 20;

There are variables to control the frames we wish to loop through, and variables to control the number of render cycles before moving onto the next frame.

Note that the Blender frames 1, 2, 3, 4 and 5 are represented as 3, 4, 5, 6 and 7 in our C++ app. That’s because our bottle has to slot in alongside other rendered objects.

Here’s how those variables are handled in each render cycle:

bottleFrameCycle++;
if (bottleFrameCycle > bottleFrameCycleMax) {
	bottleFrameCycle = 0;
			
	bottleFrame++;
	if (bottleFrame > bottleFrameMax) {
		bottleFrame = bottleFrameMin;
	}
}

As you can see, once we have reached our max amount of render cycles, we move onto the next frame of animation (and once we reach the last frame of animation, we start again).

Now all we need do is manage the rendering of our bottle model:

for (int i = 0; i < numModels; ++i) {

	if (i >= bottleFrameMin && i <= bottleFrameMax && i != bottleFrame) {
		continue;
	}

	Models[i]->Render(view, proj);
}

Simple. If the model is one of our bottle frames but not the current frame, we don’t render it. Otherwise we do.

I put on my Oculus Rift virtual reality headset and marvelled at the bottle of mineral water in my virtual world:

oculusrift_virtualbottle_virtualworld

But you want to see the squishy bottle animation? Here’s the video:

Arkwood said, ‘Drink water. Not sorrow.’ He is a poet.

So you see, some simple Blender animation can change the shape of objects in a VR experience.

Optimizations

One thing to note. If you want to increase loading speed and reduce memory (and who doesn’t) only add the bits of the object that actually change shape to the animation frames. For example, the bottle cap does not contain any animation, so why export and import it 5 times?

Instead, export the bottle cap without the Animation setting (and note the Selection setting, which we can use to export the bottle and cap separately).

oculusrift_virtualbottle_blenderexport_noanimation

But there’s more! Frames 1 and 5 of our Blender animation show the bottle in the same state. So do frames 2 and 4. Surely we can amend the import and rendering code of our C++ application to only handle frames 1, 2 and 3.

Here’s the variables, which can produce the same animation as before with only 3 frames:

int bottleFrameAnimationIndex = 0;
const std::vector<int> bottleFrameAnimation = { 3, 4, 5, 4, 3 };
const int bottleFrameMin = 3;
const int bottleFrameMax = 5;

int bottleFrameCycle = 0;
const int bottleFrameCycleMax = 20;

Here’s how those variables are handled in each render cycle:

bottleFrameCycle++;
if (bottleFrameCycle > bottleFrameCycleMax) {
	bottleFrameCycle = 0;

	bottleFrameAnimationIndex++;
	if (bottleFrameAnimationIndex >= bottleFrameAnimation.size()) {
		bottleFrameAnimationIndex = 0;
	}
}

And we manage the rendering of our bottle model:

for (int i = 0; i < numModels; ++i) {

	if (i >= bottleFrameMin && i <= bottleFrameMax && i != bottleFrameAnimation[bottleFrameAnimationIndex]) {
		continue;
	}

	Models[i]->Render(view, proj);
}

Arkwood said, ‘Begone, you substance of watery film. What you mean to me, evaporates.’ Swoon.

Ciao!

Advertisements