It is fun to develop and experience virtual worlds at home. Eventually, though, you want the world to see your creation. To do that, we need to package and publish our app. In the course of development, upgrades to React may come along; before publishing, you will need to decide whether you need to "code freeze" and ship with a stable version, or upgrade to a new version. This is a design decision. In today’s tutorial, we will learn to upgrade React VR and bundle the code in order to publish on the web.
This article is an excerpt from a book written by John Gwinner titled Getting Started with React VR. This book will get you well-versed with Virtual Reality (VR) and React VR components to create your own VR apps.
One of the neat things, although it can be frustrating, is that web projects are frequently updated. There are a couple of different ways to do an upgrade:
You can install/create a new app with the same name
You will then go to your old app and copy everything over
This is a facelift upgrade or Rip and Replace
Do an update. Mostly, this is an update to package.json, and then delete node_modules and rebuild it. This is an upgrade in place.
It is up to you which method you use, but the major difference is that an upgrade in place is somewhat easier—no source code to modify and copy—but it may or may not work. A Facelift upgrade also relies on you using the correct react-vr-cli. There is a notice that runs whenever you run React VR from the Command Prompt that will tell you whether it's old:
The error or warning that comes up about an upgrade when you run React VR from a Command Prompt may fly by quickly. It takes a while to run, so you may go away for a cup of coffee.
Pay attention to red lines, seriously.
To do an upgrade in place, you will typically get an update notification from Git if you have subscribed to the project. If you haven't, you should go to: http://bit.ly/ReactVR, create an account (if you don't have one already), and click on the eyeball icon to join the watch list. Then, you will get an email every time there is an upgrade. We will cover the most straightforward way to do an upgrade—upgrade in place, first.
Upgrading in place
How do you know what version of React you have installed? From a Node.js prompt, type this:
npm list react-vr
Also, check the version of react-vr-web:
npm list react-vr-web
Check the version of react-vr-cli (the command-line interface, really only for creating the hello world app).
npm list react-vr-cli
Check the version of ovrui (open VR's user interface):
npm list ovrui
You can check these against the versions on the documentation. If you've subscribed to React VR on GitHub (and you should!), then you will get an email telling you that there is an upgrade. Note that the CLI will also tell you if it is out of date, although this only applies when you are creating a new application (folder/website).
The release notes are at: http://bit.ly/VRReleases . There, you will find instructions to upgrade. The upgrade instructions usually have you do the following:
Delete your node_modules directory.
Open your package.json file.
Update react-vr, react-vr-web, and ovrui to "New version number" for example, 2.0.0.
Update react to "a.b.c".
Update react-native to "~d.e.f".
Update three to "^g.h.k".
Run npm install or yarn.
Note the ~ and ^ symbols; ~version means approximately equivalent to version and ^version means compatible with version. This is a help, as you may have other packages that may want other versions of react-native and three, specifically. To get the values of {a...k}, refer to the release notes. I have also found that you may need to include these modules in the devDependencies section of package.json:
"react-devtools": "^2.5.2",
"react-test-renderer": "16.0.0",
You may see this error:
module.js:529
throw err;
^
Error: Cannot find module './node_modules/react-native/packager/blacklist'
If you do, make the following changes in your projects root folder in the
rncli.config.js file.
Replace the var blacklist = require('./node_modules/react-native/packager/blacklist'); line with var blacklist = require('./node_modules/metro-bundler/src/blacklist');.
Third-party dependencies
If you have been experimenting and adding modules with npm install <something>, you may find, after an upgrade, that things do not work. The package.json file also needs to know about all the additional packages you installed during experimentation. This is the project way (npm way) to ensure that Node.js knows we need a particular piece of software. If you have this issue, you'll need to either repeat the install with the—save parameter, or edit the dependencies section in your package.json file.
{
"name": "WalkInAMaze",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node -e "console.log('open browser at http://localhost:8081/vr/\n\n');" && node node_modules/react-native/local-cli/cli.js start",
"bundle": "node node_modules/react-vr/scripts/bundle.js",
"open": "node -e "require('xopen')('http://localhost:8081/vr/')"",
"devtools": "react-devtools",
"test": "jest"
},
"dependencies": {
"ovrui": "~2.0.0",
"react": "16.0.0",
"react-native": "~0.48.0",
"three": "^0.87.0",
"react-vr": "~2.0.0",
"react-vr-web": "~2.0.0",
"mersenne-twister": "^1.1.0"
},
"devDependencies": {
"babel-jest": "^19.0.0",
"babel-preset-react-native": "^1.9.1",
"jest": "^19.0.2",
"react-devtools": "^2.5.2",
"react-test-renderer": "16.0.0",
"xopen": "1.0.0"
},
"jest": {
"preset": "react-vr"
}
}
Again, this is the manual way; a better way is to use npm install <package> -save.
The -s qualifier saves the new package you've installed in package.json. The manual edits can be handy to ensure that you've got the right versions if you get a version mismatch.
If you mess around with installing and removing enough packages, you will eventually mess up your modules. If you get errors even after removing node_modules, issue these commands:
npm cache clean --force
npm start -- --reset-cache
The cache clean won't do it by itself; you need the reset-cache, otherwise, the problem packages will still be saved, even if they don't physically exist!
Really broken upgrades – rip and replace
If, however, after all that work, your upgrade still does not work, all is not lost. We can do a rip and replace upgrade. Note that this is sort of a "last resort", but it does work fairly well. Follow these steps:
Ensure that your react-vr-cli package is up to date, globally:
[F:ReactVR]npm install react-vr-cli -g
C:UsersJohnAppDataRoamingnpmreact-vr -> C:UsersJohnAppDataRoamingnpmnode_modulesreact-vr-cliindex.js
+ react-vr-cli@0.3.6
updated 8 packages in 2.83s
This is important, as when there is a new version of React, you may not have the most up-to-date react-vr-cli. It will tell you when you use it that there is a newer version out, but that line frequently scrolls by; if you get bored and don't note, you can spend a lot of time trying to install an updated version, to no avail.
An npm generates a lot of verbiage, but it is important to read what it says, especially red formatted lines.
Ensure that all CLI (DOS) windows, editing sessions, Node.js running CLIs, and so on, are closed. (You shouldn't need to reboot, however; just close everything using the old directory).
Rename the old code to MyAppName140 (add a version number to the end of the old react-vr directory).
Create the application, using react-vr init MyAppName, in other words, the original app name.
The next step is easiest using a diff program (refer to http://bit.ly/WinDiff). I use Beyond Compare, but there are other ones too. Choose one and install it, if needed.
Compare the two directories, .MyAppName (new) and .MyAppName140, and see what files have changed.
Move over any new files from your old app, including assets (you can probably copy over the entire static_assets folder).
Merge any files that have changed, except package.json. Generally, you will need to merge these files:
index.vr.js
client.js (if you changed it)
For package.json, see what lines have been added, and install those packages in the new app via npm install <missed package> --save, or start the app and see what is missing.
Remove any files seeded by the hello world app, such as chess-world.jpg (unless you are using that background, of course).
Usually, you don't change the rn-cli.config.js file (unless you modified the seeded version).
Most code will move directly over. Ensure that you change the application name if you changed the directory name, but with the preceding directions, you won't have to.
The preceding list of upgrade steps may be slightly easier if there are massive changes to React VR; it will require some picking through source files. The source is pretty straightforward, so this should be easy in practice.
I found that these techniques will work best if the automatic upgrade did not work.
As mentioned earlier, the time to do a major upgrade probably is not right before publishing the app, unless there is some new feature you need. You want to adequately test your app to ensure that there aren't any bugs.
I'm including the upgrade steps here, though, but not because you should do it right before publishing.
Getting your code ready to publish
Honestly, you should never put off organizing your clothes until, oh, wait, we're talking about code. You should never put off organizing your code until the night you want to ship it. Even the code you think is throw away may end up in production. Learn good coding habits and style from the beginning.
Good code organization
Good code, from the very start, is very important for many reasons:
If your code uses sloppy indentation, it's more difficult to read. Many code editors, such as Visual Studio Code, Atom, and Webstorm, will format code for you, but don't rely on these tools.
Poor naming conventions can hide problems.
An improper case on variables can hide problems, such as using this.State instead of this.state.
Most of the time spent coding, as much as 80%, is in maintenance. If you can't read the code, you can't maintain it. When you're a starting out programmer, you frequently think you'll always be able to read your own code, but when you pick up a piece years later and say "Who wrote this junk?" and then realize it was you, you will quit doing things like a, b, c, d variable names and the like.
Most software at some point is maintained, read, copied, or used by someone other than the author.
Most programmers think code standards are for "the other guy," yet complain when they have to code well. Who then does?
Most programmers will immediately ask for the code documentation and roll their eyes when they don't find it. I usually ask to see the documentation they wrote for their last project. Every programmer I've hired usually gives me a deer in the headlights look. This is why I usually require good comments in the code.
A good comment is not something like this:
//count from 99 to 1
for (i=99; i>0; i--)
...
A good comment is this:
//we are counting bottles of beer
for (i=99; i>0; i--)
...
Cleaning the lint trap (checking code standards)
When you wash clothes, the lint builds up and will eventually clog your washing machine or dryer, or cause a fire. In the PC world, old code, poorly typed names, and all can also build up.
Refactoring is one way to clean up the code. I highly recommend that you use some form of version control, such as Git or bitbucket to check your code; while refactoring, it's quite possible to totally mess up your code and if you don't use version control, you may lose a lot of work.
A great way to do a code review of your work, before you publish, is to use a linter. Linters go through your code and point out problems (crud), improper syntax, things that may work differently than you intend, and generally try to pick up your room after you, like your mom does. While you might not like it if your mom does that, these tools are invaluable. Computers are, after all, very picky and why not use the machines against each other?
One of the most common ways to let software check your software for JavaScript is a program called ESLint. You can read about it at: http://bit.ly/JSLinter. To install ESLint, you can do it via npm like most packages—npm install eslint --save-dev.
The --save-dev option puts a requirement in your project while you are developing. Once you've published your app, you won't need to pack the ESLint information with your project!
There are a number of other things you need to get ESLint to work properly; read the configuration pages and go through the tutorials. A lot depends on what IDE you use. You can use ESLint with Visual Studio, for example.
Once you've installed ESLint, you need to configure a local configuration file. Do this with
eslint --init.
The --init command will display a prompt that will ask you how to configure the rules it will follow. It will ask a series of questions, and ask what style to use. AirBNB is fairly common, although you can use others; there's no wrong choice. If you are working for a company, they may already have standards, so check with management. One of the prompts will ask if you need React.
React VR coding style
Coding style can be nearly religious, but in the JavaScript and React world, some standards are very common. AirBNB has one good, fairly well–regarded style guide at: http://bit.ly/JStyle.
For React VR, some style options to consider are as follows:
Use lowercase for the first letter of a variable name. In other words, this.props.currentX, not this.props.CurrentX, and don't use underscores (this is called camelCase).
Use PascalCase only when naming constructors or classes.
As you're using PascalCase for files, make the filename match the class, so
import MyClass from './MyClass'.
Be careful about 0 vs {0}. In general, learn JavaScript and React.
Always use const or let to declare variables to avoid polluting the global namespace.
Avoid using ++ and --. This one was hard for me, being a C++ programmer. Hopefully, by the time you've read this, I've fixed it in the source examples. If not, do as I say, not as I do!
Learn the difference between == and ===, and use them properly, another thing that is new for C++ and C# programmers.
In general, I highly recommend that you pour over these coding styles and use a linter when you write your code:
Third-party dependencies
For your published website/application to really work reliably, we also need to update package.json; this is sort of the "project" way to ensure that Node.js knows we need a particular piece of software. We will edit the "dependencies" section to add the last line,(bold emphasis mine, bold won't show up in a text editor, obviously!):
{
"name": "WalkInAMaze",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node -e "console.log('open browser at http://localhost:8081/vr/\n\n');" && node node_modules/react-native/local-cli/cli.js start",
"bundle": "node node_modules/react-vr/scripts/bundle.js",
"open": "node -e "require('xopen')('http://localhost:8081/vr/')"",
"devtools": "react-devtools",
"test": "jest"
},
"dependencies": {
"ovrui": "~2.0.0",
"react": "16.0.0",
"react-native": "~0.48.0",
"three": "^0.87.0",
"react-vr": "~2.0.0",
"react-vr-web": "~2.0.0",
"mersenne-twister": "^1.1.0"
},
"devDependencies": {
"babel-jest": "^19.0.0",
"babel-preset-react-native": "^1.9.1",
"jest": "^19.0.2",
"react-devtools": "^2.5.2",
"react-test-renderer": "16.0.0",
"xopen": "1.0.0"
},
"jest": {
"preset": "react-vr"
}
}
This is the manual way; a better way is to use npm install <package> -s.
The -s qualifier saves the new package you've installed in package.json. The manual edits can be handy to ensure that you've got the right versions, if you get a version mismatch.
If you mess around with installing and removing enough packages, you will eventually mess up your modules. If you get errors, even after removing node_modules, issue these commands:
npm start -- --reset-cache
npm cache clean --force
The cache clean won't do it by itself; you need the reset–cache, otherwise the problem packages will still be saved, even if they don't physically exist!
Bundling for publishing on the web
Assuming that you have your project dependencies set up correctly to get your project to run from a web server, typically through an ISP or service provider, you need to "bundle" it. React VR has a script that will package up everything into just a few files.
Note, of course, that your desktop machine counts as a "web server", although I wouldn't recommend that you expose your development machine to the web. The better way to have other people experience your new Virtual Reality is to bundle it and put it on a commercial web service.
Packaging React VR for release on a website
The basic process is easy with the React VR provided script:
Go to the VR directory where you normally run npm start, and run the npm run bundle command:
You will then go to your website the same way you normally upload files, and create a directory called vr.
In your project directory, in our case f:ReactVRWalkInAMaze, find the following files in .VRBuild:
client.bundle.js
index.bundle.js
Copy those to your website.
Make a directory called static_assets.
Copy all of your files (that your app uses) from AppNamestatic_assets to the new static_assets folder.
Ensure that you have MIME mapping set up for all of your content; in particular, .obj, .mtl, and .gltf files may need new mappings. Check with your web server documentation:
For gltf files, use model/gltf-binary
Any .bin files used by gltf should be application/octet-stream
For .obj files, I've used application/octet-stream
The official list is at http://bit.ly/MimeTypes
Very generally, application/octet-stream will send the files "exactly" as they are on the server, so this is sort of a general purpose "catch all"
Copy the index.html from the root of your application to the directory on your website where you are publishing the app; in our case, it'll be the vr directory, so the file is alongside the two .js files.
Modify index.html for the following lines (note the change to ./index.vr):
<html>
<head>
<title>WalkInAMaze</title>
<style>body { margin: 0; }</style>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
<body>
<!-- When you're ready to deploy your app, update this line to point to your compiled client.bundle.js -->
<script src="./client.bundle?platform=vr"></script>
<script>
// Initialize the React VR application
ReactVR.init(
// When you're ready to deploy your app, update this line to point to
// your compiled index.bundle.js
'./index.vr.bundle?platform=vr&dev=false',
// Attach it to the body tag
document.body
);
</script>
</body>
</html>
Note for a production release, which means if you're pointing to a prebuilt bundle on a static web server and not the React Native bundler, the dev and platform flags actually won't do anything, so there's no difference between dev=true, dev=false, or even dev=foobar.
Obtaining releases and attribution
If you used any assets from anywhere on the web, ensure that you have the proper release. For example, many Daz3D or Poser models do not include the rights to publish the geometry information; including these on your website as an OBJ or glTF file may be a violation of that agreement. Someone could easily download the model, or nearly all the geometry fairly easily, and then use it for something else.
I am not a lawyer; you should check with wherever you get your models to ensure that you have permission, and if necessary, attribute properly.
Attribution licenses are a little difficult with a VR world, unless you embed the attribution into a graphic somewhere; as we've seen, adding text can sometimes be distracting, and you will always have scale issues. If you embed a VR world in a page with <iframe>, you can always give proper attribution on the HTML side. However, this isn't really VR.
Checking image sizes and using content delivery sites
Some of the images you use, especially the ones in a <pano> statement, can be quite large. You may need to optimize these for proper web speed and responsiveness.
This is a fairly general topic, but one thing that can help is a content delivery network (CDN), especially if your world will be a high-volume one.
Adding a CDN to your web server is easy. You host your asset files from a separate location, and you pass the root directory as the assetRoot at the ReactVR.init() call. For example, if your files were hosted at https://cdn.example.com/vr_assets/, you would change the method call in index.html to include the following third argument:
ReactVR.init(
'./index.bundle.js?platform=vr&dev=false',
document.body,
{ assetRoot: 'https://cdn.example.com/vr_assets/' }
);
Optimizing your models
If you were watching the web console, you may have noted this model being loaded over and over. It is not necessarily the most efficient way. Consider other techniques such as passing a model for the various child components as a prop.
Polygon decimation is another technique that is very valuable in optimizing models for the web and VR. With the glTF file format, you can use "normal maps" and still make a low polygon model look like a high-resolution one. Techniques to do this are well documented in the game development field. These techniques really do work well.
You should also optimize models to not display unseen geometry. If you are showing a car model with blacked out windows, for example, there is no need to have engine detail and interior details loaded (unless the windows are transparent). This sounds obvious, although I found the lamp that I used to illustrate the lighting examples had almost tripled the number of polygons than was needed; the glass lamp shade had inner and outer polygons that were inside the model.
We learned to do version upgrades, and if need be, how to do rip and replace upgrades. We further discussed when to do an upgrade and how to publish it on the web.
If you are interested to know about how to include existing high-performance web code into a VR app, you may refer to the book Getting Started with React VR.
Build a Virtual Reality Solar System in Unity for Google Cardboard
Understanding the hype behind Magic Leap’s New Augmented Reality Headsets
Leap Motion open sources its $100 augmented reality headset, North Star
Read more