Friday, April 26, 2013

WWDC First Timer's Guide 2013 Edition

Well, this year's WWDC announcement ended up being a little bittersweet for me, since a lot of people I'm used to seeing at WWDC didn't get tickets this year. For those of you who did, especially those of you attending for the first time, I've decided to update my First Timer's Guide to WWDC. If interested, you can read past versions of it here (2012201120102009), though they don't change all that much..

Remember that WWDC is different every year, so don't take anything written here as gospel. Things changes every year, and I expect this year that things will change, just as they've done every year. Hopefully these hints and suggestions will help some of you.

  1. Arrive on Sunday or Earlier. Registration is usually open most of the day on Sunday. You really, really want to get your badge and related swag (usually a bag and shirt or a jacket, etc) on Sunday if you plan to get in line for the keynote. The line for the keynote will start forming many hours before the doors to Moscone West open up on Monday. The past five years, people have started lining up before midnight Sunday. The last two years the line started late afternoon on Sunday. If you do not have your badge when you get to Moscone on Monday morning, you will almost certainly end up in an overflow room for the Keynote and may even miss part of the presentation. Even if you don't care about being in the main room, there's still a lot going on on Sunday and it's a good time to meet new people and catch up with old friends. You really don't want to deal with the badge process on Monday. Developers, especially those coming from overseas, will start coming into town much earlier, so it's not even a bad idea coming in Saturday or even Friday if you have developer friends to catch up with.

  2. Do not lose your badge. If you lose it, you are done. You will spend your time crying on the short steps in front of Moscone West while you watch everyone else go in to get schooled. Sure, you'll still be able to attend many of the unofficial after-hours goings-on (aka "showcializing"), but not the Thursday night party, which is often a blast (though the band quality has been in a downward spiral for several years now). Without a badge, you'll miss out on some of the really important stuff if you're a first timer. No amount of begging or pleading will get you a replacement badge, and since they sold out, no amount of money will get you another one, either. And that would suck. Treat it like gold. When I'm not in Moscone West or somewhere else where I need the badge, I put it in my backpack, clipped to my backpack's keyper (the little hook designed to hold your keys so they don't get lost in the bottom of your bag). Yes, there have been isolated stories of people managing to convince a sympathetic conference worker to print them a new badge, but don't expect it, those are exceptions. The employees are not supposed to print new badges, and most won't.

  3. Eat your fill. In the past, they've provided two meals a day; you're on your own for dinner. Breakfast (starts a half-hour before the first session, and it's most likely going to be a continental breakfast - fruit, pastries, juice, coffee, donuts, toast, and those round dinner rolls that Californians think are bagels, but really aren't. If you're diabetic, need to eat gluten-free, or are an early riser, you'll probably want to eat before-hand. Lunch used to be (IIRC) a hot lunch, but several yeas back they switched to boxed lunches. They're okay as far as boxed lunches go, but they are boxed lunches. A lot of people complain (loudly) about them and choose to go to a nearby restaurant during the lunch break, which is pretty long - at least 90 minutes.

  4. Party hard (not that you have a choice). There are lots of official and unofficial events in the evening. A list of WWDC events is maintained at http://wwdcparties.com/, but your best bet is to follow as many iPhone and Mac devs on Twitter that you can - the unofficial gatherings happen at various places downtown, often starting with a few "seed crystal" developers stopping for a drink and tweeting their whereabouts. The unofficial, spontaneous gatherings can be really fun and a great opportunity to meet people. The sponsored parties often start before WWDC - there are usually a few on Sunday, and there have been ones as early as Friday before. Pretty much any other bar within stumbling distance of Moscone West will be used for both planned and informal gatherings. As we get closer, there will be lists and calendars devoted to all the events and parties. Some are invite-only, but many are first-come, first-serve. Although there's a lot of drinking going on, these are worth attending even if you don't drink. Great people, great conversations... good times, whether you imbibe or not. And even if you do enjoy alcohol, it's not a bad idea to take a night off during the week. WWDC is a marathon, not a sprint. Learning to pace yourself is a survival skill.

  5. Everything is Crowded As you probably guessed from how quickly it sold out, WWDC is popular. This extends to pretty much any organized event, official or unofficial. WWDC parties are often invite only and whether they are or not, they often have a long line to get in. So, if you're in a long line for too long, grab a few people who are in line with you and go to a nearby bar or restaurant that doesn't have a sponsored event and tweet your whereabouts. You might be surprised at what happens.

  6. Take good notes. You are going to be drinking knowledge from a firehose there. The information will come at you fast and furious. As an attendee, you will get all the session videos on ADC on iTunes. It used to take some time before the videos were available, but hopefully they'll continue to get them out quickly as they have the last two years. The rumor is that many session videos will be available even before the conference is over. That's just a rumor, though, so make sure you write down any information you might need immediately.

  7. Collaborative note taking A few years ago, people started taking communal notes using SubEthaEdit and Panic's Coda (they are compatible with each other). That worked out really, really well. My notes from the past few years are ten times better than from previous years. With collaborative note taking, you don't have to type fast enough to catch every detail. Instead, the audience works as a team and everybody gets great notes. The license fee pays for itself in one WWDC, especially considering you can see notes being taken in other sessions, not just your own.

  8. Labs rule. If you're having a problem, find an appropriate lab. One of the concierges at any of the labs can tell you exactly which teams and/or which Apple employees will be at which labs when. If you're having an audio problem, you can easily stalk the Core Audio team until they beat the information into your skull, for example. It's unstructured, hands-on time with the people who write the frameworks and applications we use every day. It used to be that people started remembering about the labs later in the week, but now they fill up extremely quickly, so sign up early! 

  9. Buddy up, divide and conquer There will be at least a few times when you want to be at more than one presentation offered at the same time. Find someone who's attending one and go to the other (Twitter is a good way to find people), then share your notes. Also, see #6 above.

  10. Make sure to sleep on the plane. You won't get many other chances once you get there. Everybody is ragged by Friday, some of us even earlier. Everyone remains surprisingly polite given how sleep-deprived and/or hungover people are.

  11. Thank your hosts. The folks at Apple - the engineers, managers, and evangelists who give the presentations and staff the labs, kill themselves for months to make WWDC such a great event. So, do your mother proud and remember your manners. Say thank you when someone helps you, or even if they try and don't. And if you see one of them at an after hours event, it's quite alright to buy them a beer to say thanks.

  12. Remember you're under NDA. This one is hard, especially for me. We see so much exciting amazing stuff that week that it's natural to want to tweet it, blog it, or even tell the guy handing out advertisements for strip joints on the corner all about it. Don't. Everything, from morning to night except the Keynote and the Thursday night party are under NDA.

  13. Brown Bag it. Many days there are "brown bag" sessions. These are speakers not from Apple who give entertaining, enlightening, or inspiring talks at lunchtime. Check the schedule, some of them are bound to be worth your time.

  14. Monday, Monday I don't know what to say about Monday. The last few years, people started lining up before midnight the night before. I'm typically on East coast time and usually walk over around 4:15 to see what's going on. I've done the line, and I've done the have-a-leisurely-breakfast route, and both have their merits. If you straggle too much, they may start before you get in the room, however. This has happened to me twice. The tradeoff, of course, is that you'll be much better rested for the rest of the day.

    Waiting in line is not really my thing any more, but you do get to talk to a lot of very cool people while waiting in line, and there is a sense of camaraderie that develops when you do something silly with other people like that. Some people probably want me to suggest what time to get in line. I have no idea. Most people will get into the main room to see the Keynote. There will be some people diverted to the overflow rooms, but because the number of attendees is relatively low and the Presidio (the keynote room) is so big, it's a tiny percentage who have to go to the overflow rooms (maybe the last 1,000 to 1,500 or so, depending on number of VIPs in attendance). On the other hand, you'll actually get a better view in the overflow rooms unless you get in line crazy early - you'll get to watch it in real time on huge screens and you'll get to see what's happening better than the people at the back of the Presidio. So, go when you want to. If you want to get up early and go be one of the "crazy ones," cool! If you want to get up later, you'll still get to see the keynote sitting in a comfy room with other geeks.

  15. Turn off your MiFi/Clear/other wireless router. I'm so totally not kidding on this one. People will punch you if they find out you've got one turned on. Two years ago, so many people had MiFis and other mobile hotspots running during the keynote that it interfered with the conference center's (usually very good) WiFi network and disrupted some of the tech demos. Once you're in the building, you don't need it. They have a crazy fast pipe in the building, so just use the provided WiFi or wired connection and turn your wireless router off. Seriously.

  16. Park it once in a while There will be time between sessions, and maybe even one or two slots that have nothing you're interested in. Or, you might find yourself just too tired to take in the inner workings of some technology. In that case, there are several lounges around where you can crash in a bean bag chair, comfy chair, moderately-comfy chair, or patch of floor. There is good wi-fi throughout the building and crazy-fast wired connections and outlets in various spots. So, find a spot, tweet your location, and zone out for a little while or do some coding. You never know who you might end up talking with. If you move around too much, well… let's just say a moving target is harder to hit than a stationary one.

  17. Twitter is invaluable. There's really no better way to hook up with people you didn't travel with than Twitter. There used to be problems with Twitter staying up during the keynotes, but that seems to be resolved and we've had several years without major outages during the keynote.

  18. It's okay to leave. Don't worry if a few minutes into a session you decide that you've made a horrible mistake and it's too boring/advanced/simple/etc, or you're just too damn hungover. Just get up and leave quietly and go to a different session or sit down somewhere. Nobody is going to be offended if you leave politely and without causing a disturbance.

  19. Bring proof of age on Thursday night. The official party is always on Thursday night, and it's always a blast. There's good food, good drink, great company, and sometimes a pretty good band. They are pretty strict about making sure only people who are over 21 get alcohol. So, if you want to have a drink or five on Thursday, don't leave your license or passport in your hotel room, even if you're 70 years old. Also, if you're under eighteen, I have some bad news: you can't attend the bash, sorry.

  20. It's okay to take breaks. Your first time, you're going to be tempted to go to every session you possibly can. Somewhere around Wednesday or Thursday, though, that effort combined with lack of sleep, is going to take its toll on you. If you're too tired or overwhelmed to process information, it's okay to hole up on a couch or at a table instead of going to a session, or even to go back to your hotel (you did get a close one, right?). In fact, it's a darn good idea to map out a few "sacrificial" time slots that won't feel bad about missing just in case you need a break. You don't want to burn out and then miss something you are really interested in. And some of the best, more advanced sessions fall at the end of the week, so don't shoot your wad early in the week.

  21. Get a close hotel If at all possible, try and get a hotel within two blocks and definitely not more than five blocks from Moscone West. Five blocks doesn't seem like a lot, but it can become quite a hassle, especially if you're North of Moscone West because you'll be climbing up a pretty decent hill to return to your hotel each night.

  22. Official Evening Events In addition to the Thursday night Beer Bash, there are other official activities in the evening that are very entertaining and usually happen in the early evening before the parties really get going. The two stalwarts are the Apple Design Awards and Stump the Chumps (it's actually called "Stump the Experts", but most of the participants refer to it as just "Stump"). Stump the Experts is an Apple trivia game-show-like event with notable tech luminaries and former Apple employees. Lots of sharp wits and deep knowledge of Apple make for some good entertainment. There used to also be a Monday night reception and cocktail hour, but if memory serves, it hasn't happened in several years now.

  23. Take BART If you're flying into either SFO or OAK and are staying near Moscone West (or near any BART station) there's really no reason to bother with renting a car or taking a cab from the airport. Just take BART and get off at the Powell Street station and walk up 4th street (South). Moscone West will be about four blocks on your right.

  24. Bring a Sweatshirt or Jacket A lot of first-timers assume that it's California in the summer so it's going to be hot. Well, it could be, during the middle of the day, but look up Mark Twain's quote about San Francisco in the summer. It can be downright chilly in San Francisco in the summer time, especially in the evenings and early morning. Bring a sweatshirt or light jacket, and wear layers because the temperature differential over the course of the day can be forty or fifty degrees fahrenheit.

  25. Sample Code Many sessions will have sample code, usually downloadable from the schedule or class descriptions web pages. The sample code will stay up for a while, but may not stay around forever, so it's a good idea to download any code samples you want as soon as you can. Edit: It looks like starting with 2009, you can get to the old source code for years you attended by logging in to ADC on iTunes, however I always save off a copy just in case.

  26. Get a Battery Pack You might want to consider a battery pack for your iPhone and/or iPad. You'll be in for some very long days, and it's not uncommon for your phone to be bone dry by early evening if you don't remember to charge it during the day. AT&T reception in San Francisco is notoriously bad, and that takes a toll on battery life.

  27. Don't Sound Like a N00b It's technically called the "World Wide Developer's Conference", so logically, you'd expect people to refer to it as "the WWDC" (e.g. "I'm going to head over to the WWDC")… only people rarely do. It's just "WWDC" ("are you going to WWDC this year?). Less commonly, it's also called  "DubDubDeeCee" or just "Dubdub": ("Man, what an awesome Dubdub that was", or "What time are you heading over to Dubdub?").

  28. American Drinking Age if you're coming from a country with a civilized drinking age, and you're under the age of 21, you're in for a bit of an unpleasant surprise: You won't be allowed to drink here, and most places are very strict about it because they will lose their license to serve alcohol if they're caught serving to an underage person.

  29. Clean up your mess! I never thought I'd have to say this, but the last two years, I noticed a disturbing thing. People leaving trash and garbage all over Moscone. It was especially bad during the keynote line, but even during the rest of the conference, it was embarrassing. Don't. Just really don't. There are garbage cans and recycling bins. Use them. You're an adult, and even if you weren't, your mom's not at Moscone to clean up after you.

  30. Update your Avatars: I know, you like that picture from ten years ago better. I know Calvin and Hobbes are just to die for. I know everybody loves those eight-bit avatars (or not). But, if you want to people you know online to be able to find you in meatspace, it really helps if they know who they're looking for.

Have more suggestions for first-timers? Throw them in the comments!

"Fixing" WWDC

Tickets for WWDC 2013 went on sale yesterday, and what a crazy day it was. The event, which never sold out prior to 2008, and didn't sell out the day tickets went on sale until 2011, sold out in less than two minutes yesterday. I've seen estimates claiming it was as short as 68 seconds. I don't know what the "real" or "official" duration was, but it was insanely short by every account. This blog post by Dave Smith might put that into a little perspective for you.

As a matter of fact, it was so short, I didn't get a ticket. I did everything right. I had one in my cart, but when I went to pay, my bank declined my card. There was plenty of room on the card, but the purchase triggered a fraud detection alert. Before I could confirm with my bank that this was a valid transaction, Apple's payment system encountered an error and booted me out to the main page with a note telling me that an item had been removed from my cart. 

That was frustrating.

And then, a few hours later, I got "the call." At about 4:45 EDT yesterday, I received a phone call from the 408 area code. On the other end of the call was a pleasant representative from developer relations. He told me they saw that I had tried to purchase a ticket and had had problems. He then said that my ticket had been reserved for me and that I would get an e-mail telling me how to pay for it in about twelve hours. I haven't actually been able to purchase the ticket yet, but I'm operating under the assumption that it wasn't a practical joke. After everything, it looks like I won't be crying on the sidewalk outside Moscone West this year.

But it sounds like a lot of people will be. A lot of people who I'm used to seeing at WWDC and many who have been attending even longer than I have didn't get tickets this year.

Inevitably, we're going to start seeing blog posts and tweets about how WWDC is broken and how it should be fixed. In fact, Daniel Jalkut of Red Sweater Software — himself a regular fixture at WWDC for many a year now — kicked things off with a drastic call to stop having WWDC at all.

While there's a lot in Daniel's post that's true and which I agree with, I don't agree with his conclusion. I think having a lot of us third party developers and a lot of engineers and other Apple employees in the same place at the same time every year has a lot of value to the platform, to us developers, and to Apple.

But it's clear that WWDC needs some tweaking. Apple is no longer the scrappy underdog. We're no longer the downtrodden developers for a beleaguered software company. Today, we work in an ecosystem controlled by the most profitable company on Earth. While Apple has grown tremendously, WWDC's growth has been stunted by the capacity of Moscone West. Since 2003, WWDC has been limited to 5,200 attendees split roughly between 5,000 paid tickets and 200 special tickets, which include scholarship winners. In that same time period, the number of developers working on Apple's platforms has grown exponentially.

I've seen lots of recommendations bandied about ever since WWDC started to sell out, but most of the solutions I've seen seem to fall squarely into the "solution worse than problem" category. People make it out to be an easier problem to solve than it is. Just increase the capacity, right? How hard could that be. Lots of conferences are bigger than WWDC.

It is hard, though.

I've talked with a number of people at Apple about this over the last five years. It's important to realize that there are certain things about WWDC that are, for lack of a better word, sacrosanct. In programmer terms, these are static const variables that aren't going to change. If you want to propose ways to fix the event, you need to realize that some changes are going to be a no go. Compiler error. Fix and re-compile.

Null Operations

If you're going to try and change it, it's important to understand what makes WWDC different from most other conferences. There are certain characteristics that Apple will avoid changing unless there's absolutely no other choice. And, honestly, it's altogether possible that Apple might choose to take Daniel Jalkut's suggestion of ending the event before they change certain of these things.

Location, Location, Location

The most common cry for change I see is a change of venue to someplace like Las Vegas, or someplace else that can accommodate thirty thousand or more developers. Moving a large event like this to the conference capital of the U.S. seems to make a certain sense on the surface. I can say with some confidence that Apple won't do this. 

While WWDC began in Monterey, the event was most often held in San Jose prior to 2003. If you're familiar with the South Bay, you know that San Jose is just a stone's throw from Apple's headquarters in Cupertino. In fact, the Beer Bash used to be held at the Apple campus.

This proximity to Apple's headquarters was a convenience for Apple and a boon for attendees. It meant that Apple's engineers, the lions share of whom work in Cupertino, didn't need to be put up in hotels and didn't have to incur travel expenses to attend. It meant that Apple could make an awful lot of engineers available to the attendees. The engineer-to-attendee ratio has always been incredibly high at WWDC, and it's something that really made the conference what it is. It's actually an important part of the event's DNA.

In 2003, the city of San Francisco completed construction on Moscone West. Thanks to the iPod, at that time Apple was steadily rising out of its "beleaguered" status. WWDC 2003 was originally scheduled for San Jose but was moved to this new building in downtown San Francisco, and it has been the event's home ever since, though Apple did continue to have the Beer Bash down in Cupertino for the first few years after the move. While this location was slightly less convenient for Apple, it was much more convenient for attendees from outside the Bay Area. It is the World Wide developer's conference after all, so being in a large city with a large international airport made it easier for people coming from elsewhere to attend.

While San Francisco is harder than San Jose for Apple and they do have to put engineers and other employees up in hotel rooms, it's still a lot easier than it would be in any other major city. Apple engineers working in Cupertino can drive up to San Francisco. Some go up for the whole week, some only for a day or two. Some even go up just for a day without spending the night. Over the course of the week, a huge portion of Apple's technical staff are made available to attendees. Engineers from just about every team and framework are available in labs at some point and many give presentations.

"But Apple's rich and can afford to send everybody to Las Vegas!" you might be thinking. Perhaps, but probably not without raising ticket prices, and certainly not without impacting their product pipeline. You have to keep in mind that all the Apple employees who work the labs and stand up on stage do not get time off from their regular jobs to do these things. Speaking especially involves a huge time commitment and many, many practice sessions over the course of months. And, if you think about Apple's release schedule, now is a really, really bad time for most of these folks to be doing extra duty.

While I'm sure you can come up with many sound reasons why Apple should move the conference to Las Vegas, I think it's extremely unlikely to happen any time soon, and it would fundamentally change the event.

It's a Just Ton of Work

As I alluded to before, the presentations and labs are double duty for the Apple folks. They still have to meet all their work deadlines. While they're working on their slides, they're also coding. Find an Apple engineer this time of year — even one who's not presenting— and you're going to see bags under their eyes. Ask them how they're doing and they'll unconvincingly tell you things are good. In reality, they're frazzled. They're tired. They're stressed out. They're under multiple deadlines and really feeling the pressure. It's a really good idea to be kind to Apple engineers if you encounter them right now. 

The other option I see put forth a lot besides moving the location is to have multiple WWDCs throughout the year. This is a great model for the Tech Talks, which are put on by the Evangelism team, but not for WWDC. If the engineers who build all of Apple's cool stuff had to go through this four times a year, they would burn out, turnover would go through the roof and products would never ship.

You might think that it doesn't have to be the engineers who give the talks, but it really does. That's another part of what makes WWDC what it is. We hear about new stuff from people who worked on it. We have the opportunity to ask questions of those same people. At WWDC, we can hunt down the actual person who wrote the API we're having problems with to get the answer we need - an answer that may not be available anywhere else.

Quite seriously, this particular event simply can't happen more than once per year. It's insanely hard for the Apple folks involved even doing it just once.

The Mega Conference Route

Probably the next most common suggestion I see for fixing WWDC is to utilize the entire Moscone Center (West, North, and South) the way events like GDC, Macworld Exp and JavaOne do. By using all three of the Conference Center's buildings, the capacity of the event could be somewhere north of twenty thousand attendees, which would at least quadruple capacity over the current event. 

In my mind, this has a lot more validity than the previous two suggestions, but it's also something that doesn't fit well with Apple's goals for WWDC based on what I've heard.

Why? Well, that's hard to explain unless you've been to a developer mega conference like OracleWorld, Java One, PDC, Tech Ed, etc. There's a very different feel to these events. In colloquial terms, these events have no soul. Most attendees go on their employer's dime. Large groups often go from the same company and spend all of their time together. The majority of attendees don't even stay in the immediate area, so at 6:00 when the sessions are done, there's a mass exodus from the area. I've talked with many bartenders and other wait staff at Moscone-area establishments and have repeatedly been told that WWDC's 5200 attendees are far better for business then any of the much larger conferences. By and large, we stay in the area and we socialize. We eat and drink and talk within about a five square block radius. You can't stumble more than one of those blocks during WWDC without bumping into an iOS or Mac developer (figuratively, but sometimes also literally).

Also, with the mega conference approach, the attendee-to-engineer ratio goes way down. While they put speakers in bigger rooms and let more people see the talks first hand, the more people that attend, the harder it is to get real answers to problems from Apple engineers. We can all watch the videos when they come out, so the value of letting more people see the talks live is actually far less than you'd think, and the downside is that far more people would be trying to get access to the same number of engineers.

The people I've talked to at Apple really do not want to go this route. They really don't want WWDC to turn into a soulless mega conference. Maybe it will have to someday, but I'm pretty sure it's viewed as a nuclear option to be used as a last resort only.

The All Video Route

Since Apple has become so good at producing the videos quickly, many have suggested going to a session-less model. Have the new content available to stream within Moscone with lots of high-speed hard lines and then set up a lot more labs and more areas to socialize.

This has some merit, for sure. But if you take people out of the sessions, you're once again skewing that attendee-to-engineer ratio. The labs are already swamped. At any given time there are, perhaps, two dozen engineers giving presentations. Putting those two dozen back in the pool of engineers available in the labs doesn't change the ratio all that much, but taking several thousand people out of sessions does, and not in the good direction.

The Jacking the Price Route

Another suggestion I've seen, and one that I believe has some merit, is to jack the price of the conference up. We haven't seen a price increase in many years. There used to be an early bird discount up until 2009 that let you buy the ticket at a lower price, but the full price of the conference has been $1599 for as long as I can remember. I've heard that the money we pay doesn't even cover Apple's expenses any more. Even if it does, they're certainly not making money on the event if you factor in the value of the Apple employees' time and the event's impact on their productivity.

Raising the price of the conference would remove some of the demand. The more expensive they make it, the fewer people who can afford it. But Apple is very resistant to this. It's a very intentional thing that they haven't raised the price. They want to make the event as accessible as possible to less experienced and indie developers. You may think that that goal is not being served, since not everyone who wants to attend gets to attend, but think about this: Over half of WWDC attendees every year since 2008 have been first timers. 

So, What is the Answer?

Make no mistake, this is not an easy nut to crack. Apple has some very firm ideas about what the conference should be and what purposes it should serve, and some of those ideas they are not going to let go of lightly. It may be the case that they will have to eventually change the fundamental nature of WWDC, but I'm pretty sure they'll only do that when they feel they have no other options left.

I've spent a lot of time talking about what can't or shouldn't change. Now it's time for me to be constructive. Software developers are problem solvers by trade, so we should be able to come up with some way to increase capacity of the event without fundamentally altering the feel or purpose of the event, shouldn't we?

I believe we can.

I believe Apple could at least double the capacity while still keeping much of the same feel. I believe they could keep the event accessible to first timers and still meet the needs of more experienced developers. It still wouldn't be a perfect event, but I think it would be a huge step forward to meeting the ever-increasing demand.

First Timers vs. Experienced Attendees

Before I talk about how they do this, let's talk a little about the difference between people who are attending WWDC for the first time, second, or even third time, and those who are more seasoned veterans.

First and second timers: 
  • …are likely to get in line for the keynote early because they want to be in the main room. I suspect this will continue to be true even with Steve gone.
  • …will probably attend a session in every time slot until their endurance wears out.
  • …may attend labs, but are probably able to get answers to their problems from even moderately experienced engineers and even from other attendees.
  • …are more likely to attend planned events than socialize on their own at local establishments.
More experienced attendees:
  • …are more likely to be happy with an overflow room for the keynote and some are happy just watching the liveblogs from their hotel room.
  • …will attend relatively few sessions, skewing towards the more advanced and those featuring brand new technologies.
  • …will be easily discouraged by a long line for a session knowing the videos will be out shortly
  • …will attend labs and will likely have interesting edge case problems that will require the attention of a more senior engineer
  • …are more likely to do much of their socializing outside of the planned events with people they know but only get to see a few times a year
Of course, these are extreme cases and gross generalizations but, in my experience, they're generally true. Those new to the event and those who have been attending for years go to WWDC to get different things out of it. There's overlap, for sure, but some very distinct differences in the experience, and both are valuable parts of what WWDC is and what it should be. 

Tweaking WWDC

So, what about something like this:

Expand WWDC to use both Moscone West and either North or South, but not both. Not yet, at least. Increase the overall capacity to around ten, maybe twelve thousand. Increase the amount of space devoted to working, meeting, and socializing inside the Moscone buildings so that people who are not in a lab or a session are more likely to stay at the conference center rather than go work in their hotel or have a meeting a nearby restaurant.

Instead of having a single admission ticket, have a number of options tailored to the needs of different attendees. Some possibilities:
  • A very expensive all-access ticket that gets you into the main keynote room, maybe even in the VIP section, as well as full access to all live sessions
  • An experienced developer ticket that lets you watch the keynote from an overflow room, but gives you live access to the State of the Unions, What's New in… talks, and the more advanced deep dive sessions, as well as lab access to "advanced labs" staffed by senior Apple engineers
  • A first timer ticket that gives live access to the Keynote from the main room, live access to all the beginner and intermediate sessions, and to labs staffed by less experienced developers who have the ability to call in a more experienced developer if needed.
  • A "keynote only" ticket that lets you watch the keynote in realtime.
  • A "building only" pass that lets into the building, but doesn't let you into any live sessions. You can use the meeting room and work in the common areas and be part of the event
I'm sure there are ways to improve on these ticket categories, but the basic idea would be to let more people experience the event while distributing the scarce resource of engineer time to best advantage. More people would be in town which, itself, increases the value of the event, and more attendees would be able to get what they need to out of the event without sacrificing any of the sacrosanct attributes of the event. 

Apple might event want to consider having more official social and after-hour events for the benefit of the newer attendees. Being at WWDC for your first time can be daunting and sanctioned meet-and-greet opportunities would lessen that intimidation.

WWDC 2014 and Beyond

I don't pretend that there aren't problems with this model. Many people wouldn't fit nicely into one of the ticket categories, for example. Some people would want things not offered by the ticket levels they could afford. Very possibly, still some people who want to attend would still get shut out. There are, after all, 275,000 of us developers in Apple's ecosystem, so likely even doubling capacity wouldn't accommodate everybody who wanted to attend.

That being said, it does seem like a way for WWDC to scale to some extent without sacrificing the fundamental things that make WWDC what it is and what Apple wants it to be.

Thoughts? Criticisms? Improvements? Want to tell me I'm an idiot? I've decided to turn comments back on, so please feel free to chime in.

Sunday, April 21, 2013

SceneKitFun - Project and Presentation from CocoaConf San Jose

Yesterday, at CocoaConf San Jose, I gave a talk on SceneKit. This talk was based on my earlier blog post on the subject, but contained some new materials. The source code and the keynote for the presentation are available on my GitHub account.

The source code is based on the code from my earlier blog post, but contains some new examples.

One of the session attendees e-mailed me after the session with some information. In the session I said that there was no way to specify which camera was used if you had multiple cameras in the scene. This is actually incorrect. SCNViews and SCNLayers conform to a protocol called SCNSceneRenderer. That protocol has a property called pointOfView that can be used to specify on a per-view level which camera is used.

I've added a sample project to the repository called SceneKitFun/Multiple Cameras that shows this in action. There's one scene, but two different views showing the scene from different cameras. This project also shows how to use stock SCNViews with a view controller rather than creating a custom subclass of SCNView, which is probably how you'll want to structure most real-world SceneKit projects. I've also put a note in the presentation on the incorrect slide.

My sincere thanks to Thomas Goossens for pointing this out to me.

Wednesday, February 6, 2013

Broken Links

I've been getting an awful lot of e-mails lately about the broken links on the blog. Unfortunately, there are too many of these e-mails for me to reply to them individually. Instead,  I thought I'd reiterate here that all files with broken links can be found on my GitHub page:

Most of them can be found here:

https://github.com/jlamarche/Old-Blog-Code

The OpenGL ES-specific code can be found here:

https://github.com/jlamarche/iOS-OpenGLES-Stuff

I apologize for the inconvenience, but it's just not practical to go back through five years of blog posts and update the links.

Friday, January 18, 2013

Can you keep a secret?

You may have noticed something of a dearth of posts here lately. There are a few reasons behind that.

I'd like to tell you about one of those reasons today.

As some of you know (or may have surmised from my cryptic tweets), MartianCraft started a Skunkworks project about seven months ago that we've been referring to internally as "Project Boo". It's been quite an undertaking by a fairly large team. My personal involvement, other than a few small code contributions, has mostly been to keep the wheels from falling off of our existing contracting business while the skunkworks team toils away, but I'm still incredibly proud of what we've created. I have been surprised on more than one occasion by how cool the Project Boo software is, and how amazing it looks.

We started a very limited alpha a few weeks ago, and this past Monday, we shipped our first beta build to testers, code named "Tarkin". Beta 2, code named "Needa", will ship on Monday.

For almost seven months, we've done a pretty good job of keeping the existence of Project Boo under wraps. As hard as it was, we kept things quiet. We demoed early builds to some friends and acquaintances, but otherwise, mum was the word.

Then, this week, it finally slipped.

One of our beta testers talked about Project Boo during a podcast interview. We were nicely given the opportunity to have the discussion redacted, but after some discussions, Rob and I decided we were close enough to our release date to let the genie out of the bottle just a little.

Project Boo's real name may be familiar to a few of you: it will ship under the name Briefs.



So, without further ado, I'd like to point you to Project Boo's freshly updated website:

giveabrief.com

Media inquiries about Briefs can be sent to Rob.

Wednesday, January 9, 2013

GIKPopoverBackgroundView

Gordon Hughes recently released his open source GIKPopoverBackgroundView, which allows you to customize the background of a popover view. Looks handy and well-designed.  You can find the source code on GitHub.

What are you waiting for? Go check it out.

Monday, August 20, 2012

An Introduction to SceneKit

One of the things that surprises most people when they first start graphics programming with OpenGL on the Mac or OpenGL ES on iOS is that, until very recently, there wasn't any Apple-provided libraries for loading or managing objects or scenes. You had to roll your own code for loading objects, doing skeletal animation, and managing all your objects.

In iOS 5 we got GLKit, which added a lot of higher-level functionality missing from OpenGL ES. It notably does not include object loading or scene graph management, two of the most fundamental needs of 3D programs.

Then, in one of the early Mountain Lion previews, something called SceneKit showed up. I'd been hearing bits and pieces about SceneKit before it was released, but had honestly assumed it was going to show up on iOS. The documentation for SceneKit was (and still is) pretty sparse, but the functionality looks really promising.

Unfortunately, as of right now, SceneKit is a Mac-only technology. I've seen been no evidence yet that SceneKit will be included in iOS 6. It's still possible, but I'm not holding my breath. If not, well… fingers crossed for iOS 7.

Even as a Mac-only technology, SceneKit is a very cool framework. Let's take a look at what it does and how it works.

SceneKit, Conceptually

Before we start using SceneKit, let's talk a little about what SceneKit is and does. Conceptually speaking, SceneKit sits above OpenGL, but below the higher level frameworks like AppKit. SceneKit views are fully layer-aware, so the view can be animated using Core Animation. Not only can the view itself be animated using Core Animation, you can actually use Core Animation to animate the contents of the view.

SceneKit is primarily what's known as a scene graph management library. It manages one or more 3D objects plus lights and camera. These items together, comprise a "scene" that can be manipulated and displayed on screen. Although, some understanding of graphics programming is beneficial when using SceneKit, it has a much smaller learning curve and, by using a paradigm that's familiar to most people (a movie-set like scene with a camera, lights, and objects), it's conceptually much easier.

SceneKit provides some functionality for creating scenes from scratch right in your code, but is primarily designed for working with 3D scene data exported from an external application like Maya, 3DS Max, Modo, LightWave, or Blender. SceneKit uses the Collada file format, which is supported by most (if not all) major 3D programs (though with some caveats I'll discuss later). The Collada file format doesn't support just 3D objects, it can store information about an entire 3D scene including lights and cameras as well as armatures and animation data for both simple object animation and complex skeletal animation.

SceneKit is very well integrated with AppKit. As you'll see a little later, projection (how you determine if a mouse click or tap touches a 3D object in your scene) is actually handled using the standard hit testing mechanism that's already in NSView.

SceneKit is perfectly capable of rendering most scenes for you without any need to interact with the OpenGL API or write shader code. If you need more flexibility, however, SceneKit also offers the ability to let you render the scene, or any individual component of the scene, using a render delegate. This lets you get up and running fast, but also gives you the full flexibility of OpenGL should you need it. Render delegates are beyond the scope of this introductory article, but the very presence of such a mechanism shows how much thought went into the engineering of this framework.

The bottom line is that SceneKit, makes a lot of hard things really easy. But, there are a lot of undocumented nooks and crannies in SceneKit right now. I'll look at a few of those today and hopefully get you to a place where you can start exploring the rest of them.

Setup to Play Along at Home

If you want to use SceneKit, you're going to need to be using Xcode 4.4 or 4.5 and, of course, you need to be on Mountain Lion. If you feel like playing along at home, go ahead and open up Xcode. If you'd rather just download the code, you can get it right here. If you're not going to play along at home, feel free to skip ahead to the section called Setting the Scene Background Color.

In Xcode, create a new OS X Application project in Xcode by pressing ⌘⇧N and selecting the Cocoa Application project template.

Screen Shot 2012 08 17 at 11 55 02 AM

You can call your project whatever you want. I'm calling mine SceneKitFun to keep with the naming convention I started in Beginning iPhone Development. The only setting that matters here is making sure Use Automatic Reference Counting is checked. All the code I post here will be using ARC. I'm using the prefix MCP in my code, which is our internal MartianCraft class prefix. If you use a different prefix (and you should), your filenames will be slightly different than mine.

Screen Shot 2012 08 17 at 11 57 07 AM

After your project has been created, add the SceneKit framework to your project. SceneKit is not included in Xcode projects by default, and we need to use its functionality. You'll also need to add the QuartzCore framework, as SceneKit relies on several Core Animation data structures and macros.

Wiring up the Scene View

Now, we need to create a SceneKit view. SCNView, the class that implements a SceneKit view does not have a delegate, so to add our custom functionality, we're going to subclass SCNView. Alternatively, we could use a stock SCNView and set its properties from another class, such as our app delegate, but I find my code tends to be cleaner with subclassing in cases like this.

Create a new class in your project. You want to use the OS X Cocoa template called Objective-C Class.

Screen Shot 2012 08 17 at 12 26 45 PM

Call the class XXXSceneView (where XXX is the three letter prefix you want to use), and make it a subclass of SCNView.

Screen Shot 2012 08 17 at 1 07 24 PM

Next, click MainMenu.xib to open Xcode's interface builder. From the Object Library in the lower right pane (press ^⌥⌘3 to bring it up if you don't see it), Drag a Scene View object to your application's window, then resize it to take up the full window. Xcode's auto-constraints should do the right thing automatically and make the new view resize with the window, so we shouldn't need to do anything else in terms of layout.

Press ⌥⌘3 to bring up the Identity Inspector and set the class of this new view to XXXSceneView.

Single-click XXXSceneView.h to open the header up in the editor. Right now, our code won't compile because the compiler won't know where to find SCNView. Insert the following #import statement right after the existing import to tell it:

#import <SceneKit/SceneKit.h>


Now you can compile your app. It won't do much, but it will compile and run and show you an all-white window.

Setting the Scene Background Color

Let's make sure everything is wired up correctly. We can do that by implementing awakeFromNib in our scene view class and setting the scene's backgroundColor property. Setting this is exactly the same as clearing the framebuffer to a specific color in OpenGL. It will change the color of the view "behind" the objects in the scene. Essentially, this will the set the color everywhere in the view where there's no 3D object visible. Since we haven't given the view a scene to display, this will change the entire view to that color for now. This will tell us if we have everything set up correctly. If we set the background color and the window is still white, we know something's gone awry.

In XXXSceneView.m, add the following method after the @implementation line to set the view's color. I'm using a nice neutral gray, but you can use whatever color gives you a warm fuzzy.

-(void)awakeFromNib
{
self.backgroundColor = [NSColor grayColor];
}





If you run the app now and everything is wired up correctly, the app window should now be gray (or some other color) rather than white. If it is, we're ready to start building.

Building a Simple Scene

As I stated earlier, SceneKit is primarily designed for loading 3D scene data from other programs. It does have the ability to construct a scene programmatically, though. Let's construct a simple scene in code, which will introduce you to the basic tools and terminology of SceneKit.

The most basic useful SceneKit scene consists of a camera, a light, and an object. Without those basic components, there's not really much to see. You need an object to look at, a camera to look with, and a light so the object can be seen.

We'll start out by creating an empty SCNScene object. Once we have that object, in order to add items to a scene, we simply create instances of something called a "node". A node, represented by the class SCNNode, is designed to hold the different types of objects that can go into a scene. Nodes are hierarchical, so every node can contain child nodes and can be added as a child of any other node.

Every scene has a root node, imaginatively called rootNode, which is where you add the top level items of your scene. By creating nodes, assigning a 3D object, light, or camera to that node, and then adding the node as a child of rootNode or one of rootNode's children, we add objects to our scene. That's pretty straightforward, right?

For our simple example, we just need to add the objects to the scene once. Once they're added, they'll stay in the scene until removed. That means we can go ahead and add the code to create the scene right to the awakeFromNib method, which will build our scene as soon as our nib is loaded.

Our first step is to create an empty scene and assign it to our view's scene property.

// Create an empty scene
SCNScene *scene = [SCNScene scene];
self.scene = scene;


Next, let's add a camera. We have to create the camera and set some properties on it, create a node to hold the camera, assign the camera to that node, and then add the node to the scene. That looks something like this:

// Create a camera
SCNCamera *camera = [SCNCamera camera];
camera.xFov = 45; // Degrees, not radians
camera.yFov = 45;
SCNNode *cameraNode = [SCNNode node];
cameraNode.camera = camera;
cameraNode.position = SCNVector3Make(0, 0, 30);
[scene.rootNode addChildNode:cameraNode];


I've given the camera a field of view of 45° in both directions. For a wide-angle effect, you could increase the field of view by providing a larger number. For a telephoto effect, you could decrease the values. Notice that the FOV values need to be specified in degrees rather than radians unlike most other things in SceneKit or GLKit. I've also assigned the camera node a position 30 units back from the origin, so when later I add an object at the origin, the object will be in front of the camera and, therefore, visible.

If you look at SCNCamera, you'll notice that its properties fairly closely mimic those that we'd use to set up OpenGL viewport. That's not a coincidence. A "camera" in SceneKit isn't all that much more than a GL viewport along with a position and transformation in 3D space. If the camera is at position {0,0,30}, for example, then every node in the scene will be shifted -30 along the Z axis before it is rendered to account for the camera's position. The camera can also be rotated, meaning we can move the camera around the virtual world very much like you could move a physical camera around the real world. Because of this, the scene will always be rendered from the perspective of the camera.

Once we have a camera, we need something for the camera to look at. Let's use one of the built-in primitives that SceneKit provides. SceneKit has built-in classes that represent a cylinder, cube, sphere, cone, capsule, plane, tube, torus, pyramid, and text. Each of these primitives is a subclass of the class SCNGeometry, which represents any kind of renderable object. Each of these items have different properties needed to instantiate them, but they all work fundamentally the same way. You create them, set any necessary properties, assign them to a node, and then add the node to the scene. Here's the basic process using a torus:

// Create a torus
SCNTorus *torus = [SCNTorus torusWithRingRadius:8 pipeRadius:3];
SCNNode *torusNode = [SCNNode nodeWithGeometry:torus];
CATransform3D rot = CATransform3DMakeRotation(MCP_DEGREES_TO_RADIANS(45), 1, 0, 0);
torusNode.transform = rot;
[scene.rootNode addChildNode:torusNode];


The only new thing here is that we've rotated the torus forty-five degrees along the X axis. We've done this by creating a CATransform3D and assigning it to the node's transform property. A CATransformCD is a 4x4 transformation matrix similar to GLKMatrix4. It can hold information about where an object is located in 3D space as well as how it's rotated, scaled, or otherwise transformed.

Now we have an object to look at, but we still need a light in order for it to draw correctly. If you run the program now, the torus will be drawn, but it will be drawn in a completely flat white, which is probably not the behavior you want.

As you might remember from my OpenGL ES From the Ground Up blog post on light, the most basic components of light in 3D graphics are the diffuse, ambient, and specular components. Here's the description of the various components of light from that post:

The following illustration shows what effect each component has on the resulting image.

component.jpg


Source image from Wikimedia Commons, modified as allowed by license.


The specular component defines direct light that is very direct and focused and which tends to reflect back to the viewer such that it forms a "hot spot" or shine on the object. The size of the spot can vary based on a number of factors, but if you see an area of more pronounced light such as the two white dots on the yellow sphere above, that's generally going to be coming from the specular component of one or more lights.

The diffuse component defines flat, fairly even directional light that shines on the side of objects that faces the light source.

Ambient light is light with no apparent source. It is light that has bounced off of so many things that its original source can't be identified. The ambient component defines light that shines evenly off of all sides of all objects in the scene.


SceneKit's lighting options are considerably more advanced than what we were discussing in that blog post, but those descriptions are still fairly accurate. In SceneKit, specular highlights comes not from settings on the light, but rather from materials, which we'll look at later, but we can get the diffuse and ambient components just by adding some lights to our scene.

In SceneKit, lights can be one of several types. One type of light provides ambient light to the entire scene. Several other types can be used to provide different forms of direct light. These direct lights are used to calculate the diffuse and specular components of an object.

First, let's add a single ambient light to the scene by adding a light node set to be of the ambient type. Generally speaking, you should never have more than one ambient light node in a scene, though for more dramatic lighting effects, you might choose not to have an ambient light in your scene. The location and transformation of an ambient light doesn't matter because ambient light is applied evenly to all objects. Here's how we add an ambient light node:

// Create ambient light
SCNLight *ambientLight = [SCNLight light];
SCNNode *ambientLightNode = [SCNNode node];
ambientLight.type = SCNLightTypeAmbient;
ambientLight.color = [NSColor colorWithDeviceWhite:0.1 alpha:1.0];
ambientLightNode.light = ambientLight;
[scene.rootNode addChildNode:ambientLightNode];


Next, we'll add a direct light node like so:

// Create a diffuse light
SCNLight *diffuseLight = [SCNLight light];
SCNNode *diffuseLightNode = [SCNNode node];
diffuseLight.type = SCNLightTypeOmni;
diffuseLightNode.light = diffuseLight;
diffuseLightNode.position = SCNVector3Make(-30, 30, 50);
[scene.rootNode addChildNode:diffuseLightNode];


For our direct light, I've specified a type of SCNLightTypeOmni. This creates a light at a specific point in 3D space that shines out in all directions equally. Basically, it's like a bare light bulb floating somewhere in the scene. There are a couple other types we could have selected, including SCNLightTypeDirectional, which mimics a distant directional light source like the sun, and SCNLightTypeSpot which creates a light that shines in a specific direction from a specific point in space. Unlike ambient light, you often will want to add multiple direct lights to your scene to create different effects, though be aware that every light you add to the scene adds processing overhead.

At this point, we have a scene. It's nothing fancy, but it should look okay. Go ahead and run the app, and you should get something like this:

Screen Shot 2012 08 17 at 3 21 41 PM

That's pretty good for about thirty lines of code, right? But there's so much more to SceneKit. Let's keep going.

Animating with Core Animation

A little later on, we'll look at using animations imported from a 3D program, but one thing that's really cool about SceneKit is that it's fully integrated with Core Animation. I'm not just talking about being able to animate the SceneKit view itself, but being able to actually animate the contents of the view. So, for example, we can rotate our torus just by creating a CAAnimation. Add the following code to your awakeFromNib method if you want to animate your torus.

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
animation.values = [NSArray arrayWithObjects:
[NSValue valueWithCATransform3D:CATransform3DRotate(torusNode.transform, 0 * M_PI / 2, 1.f, 0.5f, 0.f)],
[NSValue valueWithCATransform3D:CATransform3DRotate(torusNode.transform, 1 * M_PI / 2, 1.f, 0.5f, 0.f)],
[NSValue valueWithCATransform3D:CATransform3DRotate(torusNode.transform, 2 * M_PI / 2, 1.f, 0.5f, 0.f)],
[NSValue valueWithCATransform3D:CATransform3DRotate(torusNode.transform, 3 * M_PI / 2, 1.f, 0.5f, 0.f)],
[NSValue valueWithCATransform3D:CATransform3DRotate(torusNode.transform, 4 * M_PI / 2, 1.f, 0.5f, 0.f)],
nil
]
;
animation.duration = 3.f;
animation.repeatCount = HUGE_VALF;



[torusNode addAnimation:animation forKey:@"transform"];


Yep, that's it. If you've ever worked with Core Animation, everything there should look pretty familiar. The only difference from what you'd be used to with Core Animation is that we're adding the animation to a node instead of to a layer.

Of course, you still have the option of setting up a CVDisplayLink or NSTimer and doing your animations manually if you're a glutton for punishment and want to risk not getting the full benefit of hardware acceleration. SceneKit doesn't take anything away, it just gives you easier ways to do stuff.

Scene Graph and Parenting

Earlier, I mentioned that SceneKit nodes can contain other nodes. This is actually a really powerful mechanism because children inherit there parent's position and transformations. If you move a node, all of its child nodes will move along with it. If the child was 5 units away before the move, it will be 5 units away after the move. If you rotate an object, all of its children will rotate also. But they won't rotate around their own centers, they'll rotate around their parents center.

It probably makes sense to talk a little about "spaces" in graphics programming. Objects can be transformed in different "spaces". The two most common spaces are "local space" and "world space".

Normally, when you rotate an object, that object rotates around its own origin. We refer to this as an "object space" (or sometimes "local space") rotation. When we talk about "world space", we're talking about the actual position of the object in our 3D world. We moved our camera 30 units, so in world space, the camera is now located at {0, 0, 30}. In object space, however, the camera is still located at {0,0,0}.

Spaces matter when transforming an object. Transformations need to be applied to the right node to get the result you want. If you apply a rotation in object space, the object rotates around its own origin. If you apply a rotation in world space (which you could do by transforming the root node, for example), the object would orbit around the world's origin. It's also possible to apply a rotation to one object in another object's space, and that's what happens with scene inheritance.

When an object inherits a transformation from a parent or other ancestor in the scene graph, that transformation happens in the ancestor's object space, not the in the child's. If that's confusing, a small code example may make it clear.

Let's add a child to the torus and see how the torus' rotation affects the child. That should help you understand how node inheritance affects object transformations. Let's create a text object. I'm choosing text because traditionally, programmatically creating 3D text is kind of hard, and SceneKit makes it extremely easy.

Before we add any code, we need to make one change to the existing code. We need to move the camera back a little further so we'll be able to see the child object as it rotates with the parent. Change this line of code:

cameraNode.position = SCNVector3Make(0, 0, 30);


to this:

cameraNode.position = SCNVector3Make(0, 0, 50);


Now, add the following code to create a text object as a child of the torus rather than a child of the root node:

// Give the Torus a child
SCNText *text = [SCNText textWithString:@"Whee!" extrusionDepth:4.f];
SCNNode *textNode = [SCNNode nodeWithGeometry:text];
textNode.position = SCNVector3Make(-1, 5, 0);
textNode.transform = CATransform3DScale(textNode.transform, .1f, .1f, .1f);
[torusNode addChildNode:textNode];


Run this now and you'll see that the text object rotates, but it orbits the parent rather than rotating around its own center. That's because the rotation is happening in its parent's space.

Screen Shot 2012 08 18 at 9 33 24 AM

Just because the node inherits transformations from the parent doesn't prevent it from being transformed on its own. We could, for example, also rotate the text in its own local space, like so:

// Animate the text
CAKeyframeAnimation *textAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
textAnimation.values = [NSArray arrayWithObjects:
[NSValue valueWithCATransform3D:CATransform3DRotate(textNode.transform, 0 * M_PI / 2, 0.f, 0.5f, 1.f)],
[NSValue valueWithCATransform3D:CATransform3DRotate(textNode.transform, 1 * M_PI / 2, 0.f, 0.5f, 1.f)],
[NSValue valueWithCATransform3D:CATransform3DRotate(textNode.transform, 2 * M_PI / 2, 0.f, 0.5f, 1.f)],
[NSValue valueWithCATransform3D:CATransform3DRotate(textNode.transform, 3 * M_PI / 2, 0.f, 0.5f, 1.f)],
[NSValue valueWithCATransform3D:CATransform3DRotate(textNode.transform, 4 * M_PI / 2, 0.f, 0.5f, 1.f)],
nil
]
;
textAnimation.duration = 3.f;
textAnimation.repeatCount = HUGE_VALF;



[textNode addAnimation:textAnimation forKey:@"transform"];


With that code added, the text will orbit around the parent and also rotate around its own origin.

Parenting is an extremely important and and powerful concept. It allows you to combine multiple objects that behave as components of a whole. Each object is capable of moving independent of its parent, but also inherits the movements of the parents. Without this structure, complex skeletal animation would be all but impossible. Think about this: when you move your upper arm, your lower arm, hand, and fingers all go along for the ride. In 3D terms, your lower arm and fingers inherit the transformations of your upper arm in your upper arm's object space. But, you can still bend your lower arm and fingers independently, however. In 3D terms, the lower arm and fingers would be transforming in their own object space in addition to inheriting the parent's transformation.

Materials

If you've ever worked with a 3D program, you're probably aware that most of them have extensive options for defining how an object looks. They allow creating "materials" using images, colors, and procedural textures to define not just the diffuse qualities of the object's surface, but also to define the specular, transparency, reflective, and emissive components (emissive components are used to simulate materials that emit light, like LED displays). These programs also allow you to affect the opacity of an object and to use something called a normal map to give the material greater detail than allowed by the number of vertices in the underlying object.

SceneKit has a lot of this same material functionality built right into it. It doesn't have the ability to define procedural textures because those tend to be computationally expensive and SceneKit is primarily designed as a realtime display technology. SceneKit does have the ability to use either images or flat colors to define several different properties of a material.

To create a material, you create an instance of SCNMaterial and set some properties on it. Let's start with a simple example. We'll create a shiny plastic material for our torus. Add the following code to awakeFromNib to do that:

// Give the Torus a shiny blue material
SCNMaterial *material = [SCNMaterial material];
material.diffuse.contents = [NSColor blueColor];
material.specular.contents = [NSColor whiteColor];
material.shininess = 1.0;
torus.materials = @[material];


If you run the app now, you'll get a blue torus with a white hotspot rather than the flat gray torus we had before. Since we defined a blue diffuse and a white specular, gave the material some shininess, and have a direct light in our scene, we get this blue plastic look:

Screen Shot 2012 08 17 at 3 51 19 PM

Instead of assigning colors to the diffuse and specular contents, we could also assign images. SceneKit will automatically map the image to the surface based on the UV values of the object's geometry. UVs, which define how an image maps onto a 3D object, are created automatically for SceneKit primitives and will be imported for objects created in an external 3D program.

Let's use an image for our diffuse value instead of a flat color. We'll leave the specular component defines as a simple color (white). I created this simple image in Photoshop using the Add Noise filter. You can download it or you can use any other image you want for the next code example.

Noise

Once you've added the image to your project, replace the existing blue material code with the following version:

// Give the Torus an image-based diffuse
SCNMaterial *material = [SCNMaterial material];
NSImage *diffuseImage = [NSImage imageNamed:@"noise"];
material.diffuse.contents = diffuseImage;
material.specular.contents = [NSColor whiteColor];
material.shininess = 1.0;
torus.materials = @[material];


The new version will look like this (assuming you've used the same image):

Screen Shot 2012 08 17 at 4 04 33 PM

All of the material properties that are defined as an SCNMaterialProperty can be set to use either an image or a color. There are also a number of properties on the SCNMaterialProperty that can be set to modify how the material is drawn. These will look familiar to anyone who has worked with OpenGL textures before.

For example, we could tell SceneKit to use trilinear filter for scaling the image. This will improve the appearance of the texture when SceneKit has to increased or decrease the texture's size:

material.diffuse.minificationFilter = SCNLinearFiltering;
material.diffuse.magnificationFilter = SCNLinearFiltering;
material.diffuse.mipFilter = SCNLinearFiltering;


You won't notice much difference in our current app if you add this code, but it's worth changing to a trilinear filter if you're using an image-based material. There is a tiny bit of additional overhead associated with doing this, but it will make images look much better when they are scaled up or down when mapped to an object. Note, you have to specify properties for each material independently.

Let's play around with a little more cool stuff in the materials settings.

Normal Maps

Normal mapping is a really cool way to make models look better without increasing their vertex count, which is a really good thing for real-time graphics where performance considerations really matter. Normal maps are typically generated out of a 3D program using a higher resolution version of the model you're displaying. Basically, a normal map uses the pixels of an image to store normal data from the higher resolution object. Normals, as you may recall, are used for doing lighting calculations. Normal maps allow a low resolution model to use the normal data from a higher resolution image in their lighting calculations, so they render as if they were much more complex models than they actually are. Because modern graphics hardware has efficient, accelerated methods for dealing with bitmap image data, this normal information can be fed to the shaders with a fairly nominal amount of overhead compared with increasing the vertex count.

That may sound like gibberish, but it'll make more sense in a moment. I've created another image. This is a normal map based on that noise image we used earlier for the diffuse texture. If you want to create your own normal maps, be aware that there are multiple types of normal maps. Make sure you're creating a tangent space normal map for SceneKit (if it's purple, it's probably correct). This is the default in most 3D packages. You can download my normal map here:

Noise normal

This particular normal map defines the normals of a rough, craggy surface. Creating this surface in geometry would require an extraordinarily high vertex count. We use this image exactly the same way we used the diffuse image earlier, except we assign it to the normal property rather than the diffuse:

NSImage *normalImage = [NSImage imageNamed:@"noise_normal"];
material.normal.contents = normalImage;


That will give us a torus that no longer has a nice smooth surface, but instead looks like it's very rough. (I've removed the diffuse texture to make the effect easier to see in the following screenshot):

Screen Shot 2012 08 18 at 9 40 11 AM

But, wait! There's more.

Transparency

We can also use an image to control which parts of an object are fully transparent, which are partially transparent, and which are opaque. Here's an image I created that has black and white stripes. The black strips have an alpha value of 1.0 (fully opaque), the gray stripes have an alpha value of 0.5 (partially transparent), and the white stripes have an alpha of 0.0 (fully transparent). Note, SceneKit will use the alpha component of each pixel and not the saturation (how dark the pixel is), I just made the transparent ones white and the opaque ones black so it would be easier to see what was going on.

Stripes

If we assign this image to the material's transparent property, this image will then control which parts of the image are opaque or transparent. That code would look like this:

NSImage *opacityImage = [NSImage imageNamed:@"stripes"];
material.transparent.contents = opacityImage;


and the result of that code would be this:

Screen Shot 2012 08 18 at 9 59 19 AM Similarly, you can also use an image to make parts of a model look like they're emitting light. The light emitted by this property will not affect other objects in the scene, but it can be used to create interesting science-fictioney looking illuminated displays. The process is exactly the same as what we did above.

NSImage *emissionImage = [NSImage imageNamed:@"h_stripes"];
material.emission.contents = emissionImage;


The white stripes you see in the following image are areas that are emitting light based on a similar image to the one we used for controlling transparency:

Screen Shot 2012 08 19 at 11 54 48 AM

Environment Maps and Reflection

Creating shiny metallic objects can also be done with SceneKit, and it's relatively trivial. The trick to creating reflective objects is having something for the object to reflect. In the real world, shiny metal objects will have the sky and clouds and all the various things around them in the world to reflect back to the viewer's eye. In our virtual world, we usually don't have all that stuff defined, and even if we did, it would be too computationally costly to do physically accurate reflection calculations in real time using modern computers.

Instead, we can create something called a "cube map" (or "environment map") which is simply a collection of six images: front, back, left, right, top bottom. These six images define a cube surrounding the viewer. That cube won't (in this case) actually be shown to the user, but it will be use for calculating the reflections off a reflective surface. Calculating reflections from a cube map is much faster than calculating real reflections can be done in real time. If you select a cube map that is appropriate to your scene, the results can be fairly convincing.

You can find tutorials for creating cube maps using photographs or by generating them from a 3D program and there are many license-free cube maps available if you search the internet. For simplicity, I'm using the environment map taken from Apple's SceneKit Material Editor sample code in this example.

To create a cube map with SceneKit, we simply build an NSArray containing the six images in the following order: right, left, top, bottom, back front. Once you have the cube map array, simply assign it to the contents of the material's reflective property, and you're done. Here's how to create a shiny metallic material this way:

// Give the Torus a metallic surface
SCNMaterial *material = [SCNMaterial material];


material.diffuse.contents = [NSColor blackColor];


NSArray *mapArray = @[
[NSImage imageNamed:@"right.jpg"],
[NSImage imageNamed:@"left.jpg"],
[NSImage imageNamed:@"top.jpg"],
[NSImage imageNamed:@"bottom.jpg"],
[NSImage imageNamed:@"back.jpg"],
[NSImage imageNamed:@"front.jpg"]
]
;
material.reflective.contents = mapArray;
material.specular.contents = [NSColor whiteColor];
material.shininess = 100.0;
torus.materials = @[material];


Run this, and your torus will be metallic looking. The torus is "reflecting" the contents of those six images back to our eye to create the illusion of a metallic surface.

Screen Shot 2012 08 18 at 10 19 04 AM

You can combine the different material properties to create all sorts of new appearances. If we combine the reflective material with the normal map from earlier, for example, we'd get something like this:

Screen Shot 2012 08 18 at 10 22 09 AM

Hit Testing

One of the common problems in 3D graphics is determining if a mouse click or tap is on a specific 3D object. To do this, you have to project an imaginary line (a vector) into the 3D world using the camera's projection matrix (the projection matrix is used to calculate how the world is displayed in two dimensions) and then do collision detection with the various objects in the world. Doing that with good performance is not always trivial. Fortunately, SceneKit does all the heavy lifting for us and it's integrated with NSView's existing hit testing mechanism.

Let's figure out if a click is on the torus. Let's make it even more fun. If there's a click on the torus, let's animate the torus' material so it glows red for a moment. Yes, Virginia, you can use Core Animation to animate material properties too.

Add the following method to XXXSceneView:

- (void)mouseDown:(NSEvent *)event
{
NSPoint mouseLocation = [self convertPoint:[event locationInWindow] fromView:nil];
NSArray *hits = [self hitTest:mouseLocation options:nil];

if ([hits count] > 0)
{
SCNHitTestResult *hit = hits[0];
SCNMaterial *material = [hit.node.geometry.materials objectAtIndex:0];

CABasicAnimation *highlightAnimation = [CABasicAnimation animationWithKeyPath:@"contents"];
highlightAnimation.toValue = [NSColor redColor];
highlightAnimation.fromValue = [NSColor blackColor];
highlightAnimation.repeatCount = 1;
highlightAnimation.autoreverses = YES;
highlightAnimation.duration = 0.35;
highlightAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

[material.emission addAnimation:highlightAnimation forKey:@"highlight"];
}


[super mouseDown:event];
}


All we have to do is get and convert to local the mouse down coordinates. Then, we pass that to hitTest:options:, which will return an array of SCNHitTestResult objects ordered based on the their proximity to the active camera. So, the first object in the array, if there are any objects in the array, will be the nearest object underneath the the cursor. If the array is empty, nothing was clicked.

In our case, there is only one object, but the technique would be the same with a more complex scene. We just grab the node from the SCNHitTestResult and have our way with it. In this case, I've animated its material to glow briefly red when it's clicked.

Loading and Using Scenes from Files

That should give you a pretty good idea of the basics of using SceneKit to build and manipulate scenes programmatically. Where SceneKit really shines, however, is when you use it to load more complex scenes created in an external 3D program.

Unfortunately, SceneKit only supports one file format - Collada - and there are some limitations in its support of that file format. It appears, for example, to only support a single animation (or "take") per file, and it doesn't support the most recent version of Collada, 1.5. Collada has the ability to support multiple animations in a file, but I haven't been able to get SceneKit to recognize more than one. Apple's own SceneKit Animations sample code uses separate Collada files for each individual animation, so my suspicion is that this is a limitation of SceneKit, though it's not document and there could be something else going on.

SceneKit also seems to be a little picky about the Collada files it will load. Loading scenes with no animations worked fine for me using scenes exported from several different applications, but once I started adding animations, SceneKit got very picky and refused to load files not exported from Maya. From looking at the metadata in the example Collada files included in the SceneKit source code, it appears Apple used Maya to create their own example files. Not only that, used a custom exporter, not the built-in AutoDesk Collada exporter. It does not appear that Apple has made this exporter public, but files with a single animation exported from Maya with the default exporter appear to work okay.

I put together a quick sample file with two animations in Blender. After several attempts at converting using several different techniques (including AutoDesk's free FBX Converter), I finally gave up and settled on a single animation file converted using the 30-day free trial of Maya. It's a rather roundabout way of getting data, so I hope Apple adds support for other file formats. FBX would be great. Despite it being a proprietary format, it seems to have jumped ahead of Collada in terms of update and features.

Anyway, if you want to use my sample Collada file, you can find it here. You can also grab Apple's sample files from their sample code projects. If you're interested in creating your own files, at the moment, I think using Maya is your best bet (Note - I've been getting reports that Cinema4D export with animations works also). If I have time, I'm going to compare the output of Maya and Blender and see if I can't create a custom Collada exporter for Blender that works with SceneKit, but I make no promises that I'll be able to get to that any time soon.

Adding a Collada file to your Xcode project is identical to adding any other resource. Just drag it into your project. If you single-click on the Collada file after it's been added to your project, you'll see that Xcode now has a nifty new editor for Collada files. It's a little crashy as of yet, but it's still very neat. Here's my simple Collada file opened up in Xcode's editor:

Screen Shot 2012 08 18 at 3 36 28 PM

Creating a SceneKit scene from a .dae file couldn't be much easier. This is all you have to do:

NSURL *url = [[NSBundle mainBundle] URLForResource:@"Bendy" withExtension:@"dae"];



NSError * __autoreleasing error;
SCNScene *scene = [SCNScene sceneWithURL:url options:nil error:&error];
if (scene)
self.scene = scene;
else
NSLog(@"Yikes! You're going to do real error handling, right?");


SceneKit will parse the file for you and create a full scene with nodes for all the objects contained in the file - lights, objects, cameras, etc. It will also play any animations that the file contains if you want it to, if you set the view to play:

self.playing = YES;


SceneKit also has built-in mouse handling if you want to let the user rotate the object. If you want to try that, it's just one additional line of code:

self.allowsCameraControl = YES;


You can go ahead an manipulate any of the nodes in a loaded scene just as you did with scenes you built programmatically. Want to animate a node? Do it exactly like we did with the torus - build a CAAnimation and assign it to the node. Finding nodes is usually best done by filtering on the node's name. You can find the name of various nodes in the Collada editor in Xcode. If we want to retrieve the Armature node in my sample file, for example, all we have to do is make a note of the name of the node, and retrieve the node like this:

SCNNode *armatureNode = [self.scene.rootNode childNodeWithName:@"Armature" recursively:YES];


SceneKit even creates CAAnimation objects for the animation contained in the Collada file. Be aware, however, that these animations are often complex and may be made up of several nested CAAnimationGroup objects. To get the animations for a node, just do this:

CAAnimation *armatureAnimation = [armatureNode animationForKey:@"Armature-anim"];


Notice that we used the animation name ("Armature-anim") from the editor screenshot to retrieve the animation. That CAAnimation it returns be used just like ones we create ourselves.

Adding the Items from Collada Files to an Existing Scenes

SceneKit also allows you to add individual parts of a Collada file to an existing scene using the SCNSceneSource class. For example, you can pull an animation out of another file and add it to a node in your current scene (at least, assuming the animation you're pulling uses the same armature as the node you're adding it to). Doing that would look like this:

SCNNode *armatureNode = [self.scene.rootNode childNodeWithName:@"Armature" recursively:YES];    
NSURL *sceneURL = [[NSBundle mainBundle] URLForResource:@"OtherFile" withExtension:@"dae"];
SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:sceneURL options:nil];
CAAnimation *animationObject = [sceneSource entryWithIdentifier:@"idle-animation" withClass:[CAAnimation class]];
[armatureNode addAnimation:animationObject forKey:@"idle-animation"];


This same technique can be used to combine any elements from any number of different files. So you could, for example, create your initial scene from a file that contains the camera, lights, and static elements, then add other objects from various other files. You could add a player from one file, enemies from another file, and moving obstacles from yet a third file, for example.

The Bottom Line

SceneKit is a really great addition to Cocoa. It's an extremely well engineered framework that's easy to use. About the only criticism I have is that the choice of Collada combined with an importer that has some limitations and isn't very fault tolerant makes creating content for SceneKit a bit of a headache. That's a minor complaint for a brand new framework, though, and I'm really hoping for an iOS version of SceneKit before too long.

Further Reading

Apple has posted three really nice pieces of SceneKit sample code. If you're interested in using SceneKit, I would highly suggest downloading these three, running them, and then dissecting the code.