Jump to content

  • Log In with Google      Sign In   
  • Create Account

Gamedev info



The Grand List Of Console Role Playing Game Clichés

Posted by , 08 June 2016 - - - - - - · 569 views

The Grand List Of Console Role Playing Game Clichés

 

from

 

http://project-apollo.net/text/rpg.html

 


There are few people who love a good console RPG more than I. Games like Final Fantasy, Grandia, and Skies of Arcadia set a standard of majesty and wonder and immersion that American game designers are challenged to match. And yet, as I play the latest masterpiece to come out of Japan I sometimes can't help the feeling that, somehow, I've seen it all before... WARNING! There are spoilers in here for many popular CRPGs. I mean, duh.
Note that the list is closed to new entries -- this is as cliché-ridden as it's going to get. But other than that, if you have something to say about the list, this is the address to write to.

  • Sleepyhead Rule
    The teenaged male lead will begin the first day of the game by oversleeping, being woken up by his mother, and being reminded that he's slept in so late he missed meeting his girlfriend.
  • "No! My beloved peasant village!"
    The hero's home town, city, slum, or planet will usually be annihilated in a spectacular fashion before the end of the game, and often before the end of the opening scene.
  • Thinking With The Wrong Head (Hiro Rule)
    No matter what she's accused of doing or how mysterious her origins are, the hero will always be ready to fight to the death for any girl he met three seconds ago.
  • Cubic Zirconium Corollary
    The aforementioned mysterious girl will be wearing a pendant that will ultimately prove to be the key to either saving the world or destroying it.
  • Logan's Run Rule
    RPG characters are young. Very young. The average age seems to be 15, unless the character is a decorated and battle-hardened soldier, in which case he might even be as old as 18. Such teenagers often have skills with multiple weapons and magic, years of experience, and never ever worry about their parents telling them to come home from adventuring before bedtime. By contrast, characters more than twenty-two years old will cheerfully refer to themselves as washed-up old fogies and be eager to make room for the younger generation.
  • Single Parent Rule
    RPG characters with two living parents are almost unheard of. As a general rule, male characters will only have a mother, and female characters will only have a father. The missing parent either vanished mysteriously and traumatically several years ago or is never referred to at all. Frequently the main character's surviving parent will also meet an awkward end just after the story begins, thus freeing him of inconvenient filial obligations.
  • Some Call Me... Tim?
    Good guys will only have first names, and bad guys will only have last names. Any bad guy who only has a first name will become a good guy at some point in the game. Good guys' last names may be mentioned in the manual but they will never be referred to in the story.
  • Nominal Rule
    Any character who actually has a name is important in some way and must be sought out. However, if you are referred to as a part of a posessive noun ("Crono's Mom") then you are superfluous.
  • The Compulsories
    There's always a fire dungeon, an ice dungeon, a sewer maze, a misty forest, a derelict ghost ship, a mine, a glowing crystal maze, an ancient temple full of traps, a magic floating castle, and a technological dungeon.
  • Luddite Rule (or, George Lucas Rule)
    Speaking of which, technology is inherently evil and is the exclusive province of the Bad Guys. They're the ones with the robots, factories, cyberpunk megalopolises and floating battle stations, while the Good Guys live in small villages in peaceful harmony with nature. (Although somehow your guns and/or heavily armed airships are exempted from this.)
  • Let's Start From The Very Beginning (Yuna Rule)
    Whenever there is a sequel to an RPG that features the same main character as the previous game, that character will always start with beginner skills. Everything that they learned in the previous game will be gone, as will all their ultra-powerful weapons and equipment.
  • Poor Little Rich Hero (Meis Rule)
    If the hero comes from a rich and powerful family, it will have fallen on hard times and be broke and destitute by the time the game actually starts.
  • The Higher The Hair, The Closer To God (Cloud Rule)
    The more outrageous his hairstyle, the more important a male character is to the story.
  • Garrett's Principle
    Let's not mince words: you're a thief. You can walk into just about anybody's house like the door wasn't even locked. You just barge right in and start looking for stuff. Anything you can find that's not nailed down is yours to keep. You will often walk into perfect strangers' houses, lift their precious artifacts, and then chat with them like you were old neighbors as you head back out with their family heirlooms under your arm. Unfortunately, this never works in stores.
  • Hey, I Know You!
    You will accumulate at least three of these obligatory party members:
    • The spunky princess who is rebelling against her royal parent and is in love with the hero.
    • The demure, soft-spoken female mage and healing magic specialist who is not only in love with the hero, but is also the last survivor of an ancient race.
    • The tough-as-nails female warrior who is not in love with the hero (note that this is the only female character in the game who is not in love with the hero and will therefore be indicated as such by having a spectacular scar, a missing eye, cyborg limbs or some other physical deformity -- see The Good, The Bad, And The Ugly Rule.)
    • The achingly beautiful gothy swordsman who is riven by inner tragedy.
    • The big, tough, angry guy who, deep down, is a total softy.
    • The hero's best friend, who is actually much cooler than the hero.
    • The grim, selfish mercenary who over the course of the game learns what it means to really care about other people.
    • The character who is actually a spy for the bad guys but will instantly switch to your side when you find out about it.
    • The weird bonus character who requires a bizarre series of side quests to make them effective (with the ultimate result that no player ever uses this character if it can be avoided.)
    • The nauseatingly cute mascot who is useless in all battles.
  • Hey, I Know You, Too!
    You will also confront/be confronted by at least three of these obligatory antagonists:
    • The amazingly good-looking and amazingly evil long-haired prettyboy who may or may not be the ultimate villain.
    • The villain's loyal right-hand man, who comes in two versions: humorously incompetent or annoyingly persistent.
    • The villain's attractive female henchman, who is the strongest and most competent soldier in the army but always lets the party escape because she's, yes, fallen in love with the hero.
    • Your former ally who supposedly "died" and was forgotten about, until much later in the game when he/she shows up again on the villain's side and full of bitterness.
    • The irritatingly honorable foe whom you never get to kill because, upon discovering the true nature of his superiors, he either nobly sacrifices himself or joins your party.
    • The insane clown or jester who will turn out to be surprisingly difficult to subdue.
    • The mad scientist who likes creating mutated creatures and powerful weapons 'cause it's fun (and also handy if uninvited adventurers show up.)
    • The adorably cute li'l creature or six year old child who fights you and, inexplicably, kicks your butt time after time.
  • Hey, I Know You, Three!
    Furthermore, expect to encounter most of the following obligatory non-player chararcters (NPCs):
    • The townsperson or crewmember who wanders aimlessly in circles and never quite gets where he is going.
    • Hilariously incompetent or cowardly soldiers.
    • The NPC who has a crush on another NPC and can't quite work up the nerve to tell him or her, so instead tells every other person who wanders by about it at great length.
    • A group of small children playing hide-and-seek.
    • The wise and noble captain/king/high priest.
    • The wise and noble captain/king/high priest's splutteringly evil second-in-command. Nobody, including the hero, will notice the second's constant, crazed scheming until the moment when he betrays everyone to the forces of badness.
    • The NPC who is obsessed with his completely mundane job and witters on endlessly about how great it is. He's so thrilled by it that he wants to share it with everyone he sees, so given a quarter of a chance he'll make you do his job for him.
    • The (adult) NPC who has nothing better to do than play kids' games with passersby.
    • The group of young women who have formed a scarily obsessive fan club for one of your female party members.
  • Crono's Complaint
    The less the main character talks, the more words are put into his mouth, and therefore the more trouble he gets into through no fault of his own.
  • "Silly Squall, bringing a sword to a gunfight..."
    No matter what timeframe the game is set in -- past, present, or future -- the main hero and his antagonist will both use a sword for a weapon. (Therefore, you can identify your antagonist pretty easily right from the start of the game just by looking for the other guy who uses a sword.) These swords will be far more powerful than any gun and often capable of distance attacks.
  • Just Nod Your Head And Smile
    And no matter how big that big-ass sword is, you won't stand out in a crowd. Nobody ever crosses the street to avoid you or seems to be especially shocked or alarmed when a heavily armed gang bursts into their house during dinner, rummages through their posessions, and demands to know if they've seen a black-caped man. People can get used to anything, apparently.
  • Aeris's Corollary
    Just as the main male character will always use a sword or a variant of a sword, the main female character will always use a rod or a staff of some sort.
  • MacGyver Rule
    Other than for the protagonists, your choice of weapons is not limited to the prosaic guns, clubs, or swords. Given appropriate skills, you can cut a bloody swath across the continent using gloves, combs, umbrellas, megaphones, dictionaries, sketching tablets -- you name it, you can kill with it. Even better, no matter how surreal your choice of armament, every store you pass will just happen to stock an even better model of it for a very reasonable price. Who else is running around the world killing people with an umbrella?
  • O Brother, Where Art Thou? (Melfice Rule)
    If the male hero has an older sibling, the sibling will also be male and will turn out to be one of the major villains. If the hero has a younger sibling, the sibling will be female and will be kidnapped and held hostage by the villains.
  • Capitalism Is A Harsh Mistress
    Once you sell something to a shopkeeper, he instantly sells it to somebody else and you will never see the item again no matter what.
  • Dimensional Transcendence Principle
    Buildings are much, much larger on the inside than on the outside, and that doesn't even count the secret maze of tunnels behind the clock in the basement.
  • Local Control Rule
    Although the boss monster terrorizing the first city in the game is less powerful than the non-boss monsters that are only casual nuisances to cities later in the game, nobody from the first city ever thinks of hiring a few mercenaries from the later cities to kill the monster.
  • Nostradamus Rule
    All legends are 100% accurate. All rumors are entirely factual. All prophecies will come true, and not just someday but almost immediately.
  • IDKFA
    The basic ammunition for any firearms your characters have is either unlimited or very, very easy to obtain. This will apply even if firearms are extremely rare.
  • Indestructible Weapon Rule
    No matter how many times you use that sword to strike armored targets or fire that gun on full auto mode it will never break, jam or need any form of maintenance unless it is critical to the story that the weapon breaks, jams or needs maintenance.
  • Selective Paralysis
    Your characters must always keep both feet on the ground and will be unable to climb over low rock ledges, railings, chairs, cats, slightly differently-colored ground, or any other trivial objects which may happen to be in their way. Note that this condition will not prevent your characters from jumping from railroad car to railroad car later in the game.
  • Bed Bed Bed
    A good night's sleep will cure all wounds, diseases, and disabilities, up to and including death in battle.
  • You Can't Kill Me, I Quit (Seifer Rule)
    The good guys never seem to get the hang of actually arresting or killing the bad guys. Minor villains are always permitted to go free so they can rest up and menace you again later -- sometimes five minutes later. Knowing this rule, you can deduce that if you do manage to kill (or force the surrender of) a bad guy, you must be getting near the end of the game.
  • And Now You Die, Mr. Bond! (Beatrix Rule)
    Fortunately for you, the previous rule also applies in reverse. Rather than kill you when they have you at their mercy, the villains will settle for merely blasting you down to 1 hit point and leaving you in a crumpled heap while they stroll off, laughing. (This is, of course, because they're already planning ahead how they'll manipulate you into doing their bidding later in the game -- see Way To Go, Serge.)
  • Zap!
    Most villains in RPGs possess some form of teleportation. They generally use it to materialize in front of the adventurers when they reach the Obligatory Legendary Relic Room and seize the goodies just before you can. The question "if the bad guy can teleport anywhere at any time, then why doesn't (s)he just zip in, grab the artifact, and leave before the adventurers have even finished the nerve-wracking puzzle on the third floor?" is never answered.
  • Heads I Win, Tails You Lose (Grahf Rule)
    It doesn't matter that you won the fight with the boss monster; the evil task he was trying to carry out will still get accomplished somehow. Really, you might as well not have bothered.
  • Clockwork Universe Rule
    No matter how hard you try to stop it, that comet or meteor will always hit the earth.
  • Fake Ending
    There will be a sequence which pretends to be the end of the game but obviously isn't -- if for no other reason than because you're still on Disk 1 of 4.
  • You Die, And We All Move Up In Rank
    During that fake ending, the true villain of the story will kill the guy you'd thought was the villain, just to demonstrate what a badass he (the true villain) really is. You never get to kill the fake villain yourself.
  • "What are we going to do tonight, Vinsfeld?"
    The goal of every game (as revealed during the Fake Ending) is to Save the World from an evil figure who's trying to take it over or destroy it. There is no way to escape from this formidable task. No matter whether the protagonist's goal in life is to pay off a debt, to explore distant lands, or just to make time with that cute girl in the blue dress, it will be necessary for him to Save the World in order to accomplish it. Take heart, though -- once the world gets sorted out, everything else will fall into place almost immediately.
  • Zelda's Axiom
    Whenever somebody tells you about "the five ancient talismans" or "the nine legendary crystals" or whatever, you can be quite confident that Saving the World will require you to go out and find every last one of them.
  • George W. Bush Geography Simplification Initiative
    Every country in the world will have exactly one town in it, except for the country you start out in, which will have three.
  • Fodor's Guide Rule
    In the course of your adventure you will visit one desert city, one port town, one mining town, one casino city, one magic city (usually flying), one medieval castle kingdom, one clockwork city, one martial arts-based community, one thieves' slum, one lost city and one sci-fi utopia. On the way you'll also get a chance to see the cave with rocks that glow from a natural energy source, the village populated with nonhuman characters, the peaceful village where everyone knows the latest news about the hero's quest (see Guy in the Street Rule), the snow village, the magical forest/lake/mountain, the shop in the middle of nowhere, the fantastic-looking place with lots of FMVs just showing your entrance, the subtropical jungle island populated by friendly natives, the annoying cavern maze, and a place -- any place -- that was destroyed in some past disaster.
  • Midgar Principle
    The capital of the evil empire is always divided into two sections: a lower city slum filled with slaves and supporters of the rebellion, and an upper city filled with loyal fanatics and corrupt aristocrats.
  • Not Invented Here
    Trade of technology will not exist. One place in the world will have all the techno-gadgets while all the others will be harvesting dirt.
  • Law of Cartographical Elegance
    The world map always cleanly fits into a rectangular shape with no land masses that cross an edge.
  • ¿Quien Es Mas Macho? (Fargo Rule)
    Every powerful character you attempt to seek aid from will first insist upon "testing your strength" in a battle to the death.
  • We Had To Destroy The Village In Order To, Well, You Know The Rest (Selene Rule)
    No matter what happens, never call on the government, the church, or any other massive controlling authority for help. They'll just send a brigade of soldiers to burn your entire village to the ground.
  • Zidane's Curse (or, Dirty Pair Rule)
    An unlucky condition in which every major city in the game will coincidentally wind up being destroyed just after the hero arrives.
  • Maginot Line Rule
    It is easy to tell which city/nation is the next conquest of the Evil Empire: its streets are filled with citizens who brag that the Empire would never dare attack them, and would be easily defeated if it tried. (This smug nationalism always fails to take into account the Empire's new superweapon.)
  • Short Attention Span Principle
    All bookshelves contain exactly one book, which only has enough text on it to fill up half a page.
  • Planet of the Apes Rule
    All cities and countries have ancestors that were wiped out by their technological advances.
  • Insomnia Rule
    A "free stay at the inn" is never really free. Expect to be woken up in the middle of the night for a mandatory plot event.
  • The Bling-Bling Thing (Lemina Rule)
    No matter how much money and treasure you acquire, the greedy member of your party will never be satisfied and won't stop griping about the sorry state of the party's finances.
  • I Don't Like Gears Or Fighting
    There are always giant robots. Always.
  • Houdini's Postulate
    Anyone, whether they are in the player's party or not, who is placed in any kind of prison, fortress, cell, or detention block will escape immediately. Party members will be freed either by a small child they just happened to befriend earlier in the day or by an unexpected disaster that overcomes the enemy base, NPCs will be freed by the released party members, and villains will break out all by themselves because they're such badasses. Once a person has escaped from jail, no attempt will be made by the police to recapture them in the future.
  • Zeigfried's Contradiction
    Just because someone is weird doesn't mean they're important.
  • Natural Monopoly Rule
    No city will have more than two shops, unless it is crucial to the story that there be a hundred vendors which you must visit in order (see You Always Travel In The Right Circles.) All of these shops will sell the same goods for the same price.
  • But They Don't Take American Express
    Every merchant in the world -- even those living in far-off villages or hidden floating cities cut off from the outside world for centuries, even those who speak different languages or are of an entirely different species -- accepts the same currency.
  • Apathy Principle
    Your group is the only bunch of people trying to save the world. All other would-be heroes will either join your party or else turn out to be cowards and/or con men.
  • The Good, The Bad, And The Ugly Rule
    a. Any male character who is ugly, malformed, or misshapen is either evil or so moral, spiritual, and/or wise that it's a wonder no one's proposed him for sainthood yet.
    b. Any male character who has a physical disfiguration that doesn't seem to impede him (i.e. a prominent scar across the face or a bad eye) is evil, unless he is the male lead, since scars are cool and no other good guy can be as cool as the hero. An exception is made for characters who are clearly ancient, and therefore automatically not as cool as the young hero.
    c. Any female character who is ugly, malformed, mishapen, or physically disfigured is evil, since all good female characters are there to be potentially seduced by the male lead -- see Know Your Audience.
  • Henchman Quota (Nana, Saki, and Mio Rule)
    One of your antagonists will have three lovably incompetent stooges whom you fight over and over again. Although they're trusted with their boss's most important plans and equipment, they will screw up repeatedly, argue incessantly among themselves, blab secret information, and generally only come out victorious when their job was to be a diversion or a delaying tactic. A high point of the game will come when the True Villain reveals himself and you're able to convince the stooges you're all on the same side. They won't help you out any more successfully than they helped the antagonist, but at least you won't have to fight them any more.
  • Thousand Year Rule
    The Ancient Evil returns to savage the land every thousand years on the dot, and the last time it showed up was just about 999.9875 years ago. Despite their best efforts, heroes of the past were never able to do more than seal the Evil away again for the future to deal with (which brings up the question of just how exactly does this "sealing away" work anyway, but never mind.) The good news is that this time, the Evil will get destroyed permanently. The bad news is that you're the one who's going to have to do it.
  • Principle of Narrative Efficiency
    If the main villain (or the enemy you've been trying to kill for most of the game before he summons the real final villain) was ever defeated in the past by another group of adventurers, one of them will secretly be in your party and one of them will be the hero's father.
  • Ayn Rand's Revenge
    Outside the major cities, there is no government whatsoever. Of course, perhaps that explains why it's so difficult and dangerous to get anywhere outside the major cities.
  • First Law of Travel
    Anything can become a vehicle -- castles, cities, military academies, you name it -- so do not be alarmed when the stones of the ancient fortress you are visiting shake underfoot and the whole thing lifts off into the sky. As a corollary, anything is capable of flight if it would be cool, aeronautics or even basic physics be damned.
  • Second Law of Travel
    There will be only one of any non-trivial type of vehicle in the entire world. Thus, only one ocean-capable steamboat, only one airship, and so forth. Massive facilities will have been constructed all over the world to service this one vehicle.
  • Third Law of Travel
    The only way to travel by land between different areas of a continent will always be through a single narrow pass in a range of otherwise impenetrable mountains. Usually a palace or monastery will have been constructed in the pass, entirely filling it, so that all intracontinental traffic is apparently required to abandon their vehicles and go on foot up stairs and through the barracks, library and throne room to get to the other side. This may explain why most people just stay home. (In some cases a cave or underground tunnel may be substituted for the palace or monastery, but it will still be just as inconvenient with the added bonuses of cave-ins and nonsensical elevator puzzles.)
  • Fourth Law of Travel
    Three out of every four vehicles you ride on will eventually sink, derail or crash in some spectacular manner.
  • Fifth Law of Travel
    All vehicles can be driven or piloted by anyone. The main character just needs to find out where the bridge or steering wheel is, as he already knows all of the controls.
  • Sixth Law of Travel
    Nobody gets to own a cooler ride than you. If you ever do see a cooler vehicle than the one you've got now, at some point before the end of the game you will either take over this vehicle, get something even bigger and better, or else see it destroyed in a glorious blaze.
  • Seventh Law of Travel
    When on a voyage to another continent, the journey will last only as long as it takes you to talk to all the other passengers and the captain.
  • Eighth Law of Travel
    There are no shortcuts, ever -- unless you are forced to take them, in which case they will be much longer and more dangerous than your original route.
  • Last Law of Travel (Big Joe Rule)
    As has been described, you must endure great trials just to get from town to town: locating different vehicles, operating ancient transport mechanisms, evading military blockades, the list goes on. But that's just you. Every other character in the game seems to have no trouble getting to any place in the world on a moment's notice.
  • If You Meet The Buddha In A Random Encounter, Kill Him!
    When you're out wandering around the world, you must kill everything you meet. People, animals, plants, insects, fire hydrants, small cottages, anything and everything is just plain out to get you. It may be because of your rampant kleptomania (see Garrett's Principle.)
  • Law of Numbers
    There will be several items or effects which depend on the numerical value of your hit points, level, etc., which makes no sense unless the characters can see all the numbers in their world and find it perfectly normal that a spell only works on a monster whose level is a multiple of 5.
  • Magical Inequality Theorem
    In the course of your travels you may find useful-sounding spells such as Petrify, Silence, and Instant Death. However, you will end up never using these spells in combat because a) all ordinary enemies can be killed with a few normal attacks, making fancy attacks unneccessary, b) all bosses and other stronger-than-average monsters are immune to those effects so there's no point in using them for long fights where they'd actually come in handy, and c) the spells usually don't work anyway.
  • Magical Inequality Corollary
    When the enemy uses Petrify, Silence, Instant Death, et cetera spells on you, they will be effective 100% of the time.
  • Pretty Line Syndrome (or, Crash Bandicoot: The RPG)
    Seen in most modern RPGs. The key to completing your quest is to walk forward in a straight line for fifty hours, stopping along the way to look at, kill, and/or have meaningful conversations with various pretty things.
  • Xenobiology Rule
    The predatory species of the world will include representatives of all of the following: giant spiders, giant scorpions, giant snakes, giant beetles, wolves, squid, fish that float in midair, gargoyles, golems, carnivorous plants, chimeras, griffons, cockatrices, hydras, minotaurs, burrowing things with big claws, things that can paralyse you, things that can put you to sleep, things that can petrify you, at least twenty different creatures with poisonous tentacles, and dragons. Always dragons.
  • Friendly Fire Principle (or, Final Fantasy Tactics Rule)
    Any attack that can target both allies and enemies will hit half of your allies and none of your enemies.
  • Dungeon Design 101
    There's always goodies hidden behind the waterfall.
  • Dungeon Design 102
    When you are confronted by two doors, the closer one will be locked and its key will be hidden behind the farther-away one.
  • Dungeon Design 103 (or, Wallpaper Warning)
    Your progress through a dungeon will be indicated by a sudden change in decor: different wall color, different torches on the wall, et cetera.
  • Dungeon Design 201 (or, The Interior Decorators Anticipated Your Out-Of-Body Experience)
    Most dungeons will include "hidden" passages which are nearly impossible to see from a bird's-eye view, yet would be blaringly obvious from the party's perspective.
  • Dungeon Design 301
    All "puzzles" in RPG dungeons can be sorted into one of the following types:
    • finding some small item and sticking it into a slot;
    • pushing blocks (rocks, statues) onto switches;
    • pulling switches or levers to open and close doors;
    • learning the correct order/position of a group of objects;
    • entering a certain combination of doors;
    • something involving a clock or elevator;
    • something that is unsolvable because a vital clue in the dialogue was mistranslated out of Japanese.
  • Wait! That Was A Load-Bearing Boss!
    Defeating a dungeon's boss creature will frequently cause the dungeon to collapse, which is nonsensical but does make for thrilling escape scenes.
  • Supply and Demand Axiom
    Killing a powerful enemy will usually yield an item or weapon that would've been extremely useful if you had gotten it before killing that enemy.
  • Edison's Lament
    No switch is ever in the right position.
  • Well, That About Wraps It Up For God
    All major deities, assuming they actually exist and weren't just made up by the Church to delude its followers, are in reality malevolent and will have to be destroyed. The only exception to this rule is the four nature spirits who have preserved the land since time immemorial, but now due to the folly of mankind have lost virtually all of their power and need you to accomplish some ludicrous task to save them.
  • Guy in the Street Rule
    No matter how fast you travel, rumors of world events always travel faster. When you get to anywhere, the people on the street are already talking about where you've been. The stories of your past experiences will spread even if no witnesses were around to see them.
  • Wherever You Go, There They Are
    Wherever the characters go, the villains can always find them. Chances are they're asking the guy in the street (see above). But don't worry -- despite being able to find the characters with ease anytime they want to, the bad guys never get rid of them by simply blowing up the tent or hotel they're spending the night in. (Just think of it: the screen dims, the peaceful going-to-sleep-now music plays, then BOOM! Game Over!)
  • Figurehead Rule
    Whenever someone asks you a question to decide what to do, it's just to be polite. He or she will ask the question again and again until you answer "correctly."
  • Puddin' Tame Rule
    The average passer-by will always say the same thing no matter how many times you talk to them, and they certainly won't clarify any of the vaguely worded warnings or cryptic half-sentences they threw at you the previous time.
  • Franklin Covey Was Wrong, Wrong, Wrong
    Sticking to the task at hand and going directly from place to place and goal to goal is always a bad idea, and may even prevent you from being able to finish the game. It's by dawdling around, completing side quests and giving money to derelicts that you come into your real power.
  • Selective Invulnerability Principle
    RPG characters are immune from such mundane hazards as intense heat, freezing cold, or poison gas... except when they're suddenly not. Surprise!
  • I'm the NRA (Billy Lee Black Rule)
    Opposition to gun control is probably the only thing you could get all RPG characters to agree upon. Even deep religious faith and heartfelt pacifism can't compete with the allure of guns.
  • Three Females Rule
    There will always be either one or three female characters in the hero's party, no matter how many male characters there are.
  • Experience Not Required
    When the main character is forced to do some complex or dangerous task for the first time, even though he has never done it before he will still always be better than the oldest veteran.
  • Law of Reverse Evolution (Zeboim Principle)
    Any ancient civilizations are inexplicably much more advanced than the current one.
  • Science-Magic Equivalence (Citan Rule)
    Although mages' specialty is magic and scientists' specialty is technology, these skills are completely interchangeable.
  • Law of Productive Gullibility (Ruby Rule)
    Whenever anybody comes up to you with a patently ludicrous claim (such as, "I'm not a cat, I'm really an ancient Red Dragon") there's an at least two-thirds chance they're telling the truth. Therefore, it pays to humor everyone you meet; odds are you'll be glad you did later on.
  • Perversity Principle
    If you're unsure about what to do next, ask all the townspeople nearby. They will either all strongly urge you to do something, in which case you must immediately go out and do that thing, or else they will all strongly warn you against doing something, in which case you must immediately go out and do that thing.
  • Near-Death Epiphany (Fei Rule)
    If the party is not dealing damage to a boss character, then there's a better-than-even chance that someone in the party will suddenly become enlightened and instantly acquire the offensive skill that can blow the creature away in a matter of seconds.
  • Wutai Rule
    Most RPGs, no matter what their mythology, include a land based on ancient Japan. Full of pagodas, shrines, shoguns, kitsune, and sushi, this completely anachronistic place is the source of the entire world's supply of ninja and samurai characters.
  • Law of Mooks
    Soldiers and guards working for the Evil Empire are, as a rule, sloppy, cowardly and incompetent. Members of the heroic Resistance Faction are, as a rule, dreadfully weak and undertrained and will be wiped out to the last man the moment they come in contact with the enemy.
  • Law of Traps
    No matter how obvious the trap, you can't complete the game unless you fall into it.
  • Arbor Day Rule
    At some point, you're going to have to talk to a tree and do what it says.
  • You Do Not Talk About Fight Club
    Any fighting tournament or contest of skill you hear about, you will eventually be forced to enter and win.
  • Invisible Bureaucracy Rule
    Other than the royal family, its shifty advisor, and the odd mad scientist, the only government employees you will ever encounter in the course of your adventure are either guards or kitchen staff.
  • The Miracle of Automation
    Similarily, any factory, power plant, or other facility that you visit during the course of the game will be devoid of any human life except for the occasional guards. There will not be a single line worker or maintenance person in sight.
  • Principle of Archaeological Convenience
    Every ancient machine you find will work perfectly the first time you try to use it and every time thereafter. Even if its city got blasted into ruins and the machine was then sunk to the bottom of the sea and buried in mud for ten thousand years, it'll still work fine. The unfortunate corollary to this rule is that ancient guardian creatures will also turn out to be working perfectly when you try to filch their stuff.
  • They Don't Make 'Em Like They Used To (Cid Rule)
    Modern-day machinery, by contrast, will always break down at the worst possible moment (for example, when you only need one more shot from the giant cannon to defeat the final boss.)
  • Place Transvestite Joke Here (Miss Cloud Rule)
    If the male lead is required to dress up like a girl for any reason, he will be regarded by everyone as much more attractive than any "real" girl. If the female lead cross-dresses as a man, she will be immediately recognized as who she is by everyone except the male lead and the main villain.
  • Make Room! Make Room!
    There are always more people in a town or village than there are houses for them to live in. Most of the village is made up of shops, temples, bars, secret passages, inns, and the mansion that belongs to the richest man in town.
  • Law of Scientific Gratification
    If the hero needs a new invention to progress, he will find out that somewhere in the world someone has spent his or her entire life perfecting this invention, and usually just needs one more key item located in a monster-infested dungeon before it is completed.
  • You Always Travel In The Right Circles
    Whenever you meet a villager or other such incidental character who promises to give you some great piece of needed knowledge or a required object in exchange for a seemingly simple item, such as a bar of soap or a nice straw mat, be prepared to spend at least an hour chasing around the world exchanging useless innocuous item after item with bizarre strangers until you can get that elusive first item you were asked for.
  • Talk Is Cheap Rule
    Nothing is ever solved by diplomacy or politics in the world of RPGs. Any declarations of peace, summits and treaty negotiations are traps to fool the ever so gullible Good Guys into thinking the war is over, or to brainwash the remaining leaders of the world.
  • Stop Your Life (Setzer Rule)
    No matter what kind of exciting, dynamic life a character was leading before joining your party, once there they will be perfectly content to sit and wait on the airship until you choose to use them.
  • Don't Stand Out
    Any townsperson who is dressed oddly or otherwise doesn't fit in with the rest of the townsfolk will either:
    • Join your party after you complete some task,
    • Be in the employ of your enemy, or
    • Befriend any female member of the party, and then be immediately captured and held hostage by the villains.
  • Little Nemo Law
    If any sleeping character has a dream, that dream will be either a 100% accurate memory of the past, a 100% accurate psychic sending from the present, a 100% accurate prophetic vision of the future, or a combination of two or all three of these.
  • Child Protection Act (Rydia Rule)
    Children 12 and under are exempt from death. They will emerge alive from cataclysms that slaughter hundreds of sturdily-built adults, often with barely a scratch. Further protection is afforded if the catastrophe will orphan the child.
  • Missing Master Hypothesis
    Almost every strong physical fighter learned everything he/she knows from some old master or friend. Invariably, the master or friend has since turned evil, been killed, or disappeared without a trace.
  • Missing Master Corollary (Sabin Rule)
    If a fighter's master merely disappeared, you will undoubtedly find him/her at some point in your travels. The master will challenge the student to a duel, after which the student will be taught one final skill that the master had been holding back for years.
  • Gojira Axiom
    Giant monsters capable of leveling cities all have the following traits:
    • Low intelligence
    • Enormous strength
    • Projectile attacks
    • Gigantic teeth and claws, designed, presumably, to eat other giant monsters
    • Vulnerable to weapons 1/10,000th its size
    • Ecologically sensitive
  • "You Couldn't Get To Sleep Either, Huh?"
    If any character in the game ever meets any other character standing alone at night looking at the moon, those two will eventually fall in love.
  • Absolute Power Corrupts Absolutely (Althena Rule)
    If a good guy is manipulated to the side of evil, they will suddenly find a new inner strength that will enable them to wipe out your whole party with a wave of their hand.
  • All Is Forgiven (Nash Rule)
    However, when the trusted member of your party turns against you, do not give it a second thought. They will return to your side after they're done with their amnesia/mind control/hidden noble goal that caused them to give away all your omnipotent mystical artifacts.
  • First Law of Fashion
    All characters wear a single costume which does not change over the course of the game. The only exception is when characters dress up in enemy uniforms to infiltrate their base.
  • Second Law of Fashion
    Any character's costume, no matter how skimpy, complicated, or simply outlandish, is always completely suitable to wear when climbing around in caves, hiking across the desert, and slogging through the sewers. It will continue to be completely suitable right afterwards when said character goes to meet the King.
  • Third Law of Fashion
    In any futuristic setting, the standard uniform for female soldiers and special agents will include a miniskirt and thigh-high stockings. The standard uniform for all male characters, military or not, will include an extraordinarily silly and enormous hat.
  • First Rule of Politics (Chancellor's Axiom)
    Any advisor of a major ruler has been scheming after his throne for quite a while. Thanks to the miracle of timing, you will arrive at the king's inner sanctum just in time for the coup.
  • Second Rule of Politics (Scapegoat's Axiom)
    If the advisor works for an evil ruler, the advisor is as bad or even worse, and there's a good chance he's the final villain. (See Fake Ending Rule.) If the advisor works for a good ruler, he usually has the good of the kingdom at heart; not that that helps, because your party will invariably be made the scapegoat for all that's wrong with the nation and immediately thrown in the dungeon.
  • Last Rule of Politics
    Kingdoms are good. Empires are evil.
  • Inheritance of Acquired Characteristics (Ramus Rule)
    Twenty-three generations may pass, but any person's direct descendant will still look and act just like him.
  • Pinch Hitter Rule
    Whenever a member of the hero's team is killed or retires, no matter how unique or special he or she was there is a good chance someone will show up to replace them that has exactly the same abilities and can use the same weapons with the same proficiency.
  • Dealing With Beautiful Women, Part 1 (Yuffie Rule)
    All good-looking young females are there to help you. This rule holds even when the girl in question is annoying, useless, or clearly evil.
  • Dealing With Beautiful Women, Part 2 (Rouge Rule)
    All good-looking middle-aged females are out to kill you. This rule holds even when the woman in question has attained your unwavering trust and respect.
  • Well, So Much For That
    After you have completed your mighty quest to find the object that will save the known universe, it will either a) get lost, b) get stolen, or c) not work.
  • The Ominous Ring of Land
    The classic Ominous Ring of Land is a popular terrain feature that frequently doesn't show up on your world map. Just when you think things are going really well and you've got the Forces of Evil on the run, monsters, demons and mad gods will pour out of the center of the ring and the situation will get ten times worse. The main villain also usually hangs out in one of these after attaining godhood. If there are several Ominous Rings of Land or the entire world map is one big ring, you are just screwed.
  • Law of NPC Relativity (Magus Rule)
    Characters can accomplish superhuman physical feats, defeat enemies with one hand tied behind their back and use incredible abilities -- until they join your party and you can control them. Then these wonderful powers all vanish, along with most of their hit points.
  • Guards! Guards! (or, Lindblum Full Employment Act)
    Everything will be guarded and gated (elevators, docks, old rickety bridges, random stretches of roadway deep in the forest) except for the stuff that actually needs to be.
  • Thank You For Pressing The Self-Destruct Button
    All enemy installations and city-sized military vehicles will be equipped with a conveniently located, easy-to-operate self-destruct mechanism.
  • Falling Rule
    An RPG character can fall any distance onto anything without suffering anything worse than brief unconsciousness. In fact, falling a huge distance is an excellent cure for otherwise fatal wounds -- anyone who you see shot, stabbed, or mangled and then tossed off a cliff is guaranteed to return later in the game with barely a scratch.
  • Materials Science 101
    Gold, silver, and other precious metals make excellent weapons and armor even though in the real world they are too soft and heavy to use for that purpose. In fact, they work so well that nobody ever melts their solid gold suit of armor down into bullion, sells it, and retires to a tropical isle on the proceeds.
  • Materials Science 201
    Everyone you meet will talk enthusiastically about how some fantastically rare metal (iron, say) would make the best possible armor and weapons. Oh, if only you could get your hands on some! However, once you actually obtain iron -- at great personal risk, of course -- everyone will dismiss it as yesterday's news and instead start talking about some even more fantastically rare metal, such as gold. Repeat until you get to the metal after "mythril" (see The Ultimate Rule.)
  • Seventh Inning Stretch (Elc Rule)
    At some point in the game the main hero will receive a deadly story-driven injury and will be put in a hospital instead of having a mage heal him. This will leave him out of commission for at least the length of two sidequests; the female lead will also be temporarily out of commission as she steadfastly refuses to leave the hero's side. Ultimately a simple vision quest is all that will be required to bring the hero back to normal.
  • Vivi's Spellbook Principle
    Over the course of the game, you will spend countless hours learning between twenty and one hundred skills and/or spells, approximately three of which will still be useful by the end of the game.
  • Gender Equality, Part 1 (Feena Rule)
    Your average female RPG character carries a variety of deadly weapons and can effortlessly hack or magic her way through armies of monsters, killer cyborgs, and mutated boss creatures without breaking a sweat. She may be an accomplished ninja, a superpowered secret agent, or the world's greatest adventurer. However, if one of the game's villains manages to sneak up and grab her by the Standard Female Character Grab Area (her upper arm) she will be rendered utterly helpless until rescued by the hero.
  • Gender Equality, Part 2 (Tifa Rule)
    If any female character, in a burst of anger or enthusiasm, decides to go off and accomplish something on her own without the hero, she will fail miserably and again have to be rescued.
  • Gender Equality, Part 3 (Luna Rule)
    All of the effort you put into maxing out the female lead's statistics and special abilities will turn out to be for naught when she spends the final confrontation with the villain dead, ensorcelled, or held hostage.
  • Gender Equality Addendum (Rynn Rule)
    In the unlikely event that the main character of the game is female, she will not be involved in any romantic subplot whatsoever beyond getting hit on by shopkeepers.
  • Stealing The Spotlight (Edea Rule)
    The characters who join your party only briefly tend to be much cooler than your regular party members.
  • "Mommy, why didn't they just use a Phoenix Down on Aeris?"
    Don't expect battle mechanics to carry over into the "real world."
  • Gold Saucer Rule
    The strongest weapons/items/spells in the entire game can only be found by doing things like racing birds.
  • Evil May Live Forever, But It Doesn't Age Well
    Even though it took the greatest armies in the world and all of the world's greatest magicians to seal away an ancient evil in an apocalyptic war, once said ancient evil breaks free three fairly inexperienced warriors can destroy it.
  • Sephiroth Memorial Escape Clause
    Any misdeed up to and including multiple genocide is forgiveable if you're cool enough.
  • Doomed Utopia Theorem (Law of Zeal)
    All seemingly ideal, utopian societies are powered by some dark force and are therefore doomed to swift, flashy destruction.
  • Party Guidance Rule
    Somewhere in the last third of the story, the hero will make a stupid decision and the rest of the party must remind him of all that they have learned from being with him in order to return the hero to normal.
  • Bad Is Good, Baby!
    The heroes can always count on the support of good-hearted vampires, dragons, thieves, demons, and chainsaw murderers in their quest to save the world from evil. And on the other hand...
  • Good Is Bad, Baby!
    Watch out for generous priests, loyal military officers, and basically anyone in a position of authority who agrees to help you out, especially if they save your life and prove their sincerity innumerable times -- they're usually plotting your demise in secret (at least when they can fit it into their busy schedule of betraying their country, sponsoring international terrorism, and stealing candy from small children) and will stab you in the back at the most inconvenient moment, unless they fall under...
  • General Leo's Exception
    Honorable and sympathetic people who work for the Other Side are always the genuine article. Of course they'll be busily stabbing you in the front, so either way you lose. Eventually though, they'll fall prey to...
  • The Ineffectual Ex-Villain Theorem (Col. Mullen Rule)
    No matter how tough and bad-ass one of the Other Side's henchmen is, if he bails to the side of Good he'll turn out to be not quite tough and bad-ass enough. The main villain will defeat him easily. But don't weep -- usually he'll manage to escape just in time, leaving you to deal with the fate that was meant for him.
  • All The Time In The World (Rinoa Rule)
    Unless there's a running countdown clock right there on the screen, you have as long as you want to complete any task -- such as, say, rescuing a friend who's hanging by one hand from a slippery cliff edge thousands of feet in the air -- no matter how incredibly urgent it is. Dawdle or hurry as you will, you'll always make it just in the nick of time.
  • Ladies First (Belleza Rule)
    When things really start falling apart, the villain's attractive female henchman will be the first to jump ship and switch to the side of Good. Sadly, she still won't survive until the end credits, because later she will sacrifice her life out of unrequited love for the villain.
  • Trial By Fire (Cecil Rule)
    Any dark and brooding main characters will ultimately be redeemed by a long, ardous, quasi-spiritual quest that seems difficult at the time, but in the great scheme of things just wasn't that big of a deal after all.
  • Key Item Rule
    Never discard, sell, or otherwise remove permamently from your possession any items you begin the game with or acquire within the first town. This is especially true for items that seem to have no practical use, because of...
  • The Law of Inverse Practicality (Key Item Corollary)
    Any item that you can acquire will have some sort of purpose. Those that seem to be useless and have no practical value at all, always tend to have great power later on. The earlier you get the item, the later in the game it will be used. The longer the span of time between acquisition and use, the more powerful the item is.
  • Way To Go, Serge
    It will eventually turn out that, for a minimum of the first sixty percent of the game, you were actually being manipulated by the forces of evil into doing their sinister bidding for them. In extreme cases this may go as high as 90%. The clear implication is that it would have been better to not get involved in the first place.
  • Gilligan's Prescription
    Any character who has amnesia will be cured before the end of the game. They usually won't like what they find out about themselves, though.
  • Luke, I Am Your Tedious, Overused Plot Device (Lynx Rule)
    If there is any chance whatsoever that major villain X could be the male lead's father, then it will turn out that major villain X is the male lead's father.
  • World of Mild Inconvenience
    The devastating plague, noxious gas, planet-obliterating meteor or other large-scale disaster that led to the death of millions will affect your party (and your party's friends and family members) in no way whatsoever, save that a few party members may become lost and you can find them later.
  • Golden Chocobo Principle
    There will be at least one supremely ultimate improvement for your weapon or some way to make your trusted steed capable of going anywhere and doing anything, requiring hours and hours of hard work to acquire. Once you do achieve this, you will use it once, and it will be completely useless for the rest of the game.
  • Golden Chocobo Corollary
    The magic formula for acquiring this supreme upgrade will be only vaguely alluded to in the game itself. Ideally, you're supposed to shell out $19.95 for the strategy guide instead.
  • Flow of Goods Rule
    The quality of goods in the world is dependent upon the shop's distance from the final dungeon. It doesn't matter if the town you start in has a huge thriving economy and is the center of world trade, it will always have the game's worst equipment; and even if that village near the end is isolated and has only three people in it, it will have the game's best equipment.
  • Master Key Rule
    Any and all locked doors that the characters encounter will be unlocked by the end of the game.
  • "Evil will always triumph, because Good is dumb!"
    If the villain needs all ten legendary medallions to attain world domination and you have nine of them, everybody in your party still thinks it is neccessary to bring the nine to the villain's castle and get the final one, instead of hiding the ones they've already got and spoiling his plans that way. After you foolishly bring the legendary medallions to the villain's hideout, he will kidnap one of your companions (usually the main love interest) and you will trade the world away to rescue your friend.
  • Dark Helmet's Corollary
    After you give up the medallions to save your friend/parent/lover/other miscellaneous party member, don't expect to actually get that person back. Sucker!
  • It's Not My Department, Says Wernher Von Braun
    All space stations, flying cities, floating continents and so forth will without exception either be blown up or crash violently to earth before the end of the game.
  • The Best-Laid Schemes
    The final villain's grand scheme will have involved the deaths of thousands or even millions of innocent people, the clever manipulation of governments, armies, and entire populations, and will have taken anywhere from five to five thousand years to come to fruition. The hero will come up with a method of undoing this plan forever in less than five minutes.
  • Pyrrhic Victory
    By the time you've gotten it in gear, dealt with your miscellaneous personal crises and are finally ready to go Save the World once and for all, nine-tenths of it will already have been destroyed. Still, you've got to give your all to save the remaining one-tenth.
  • Poetic Villain Principle (Kefka Rule)
    All villains will suddenly become poets, philisophers, and/or dramatic actors when a) they first meet the hero, b) they are about to win or their evil plan is finally ready, c) some major event in the game is about to begin, d) right before the final battle, and e) right before they die, when they will frequently be feeling generous enough to reward you with some homespun wisdom about making the most of life while you have it.
  • Compression of Time
    As you approach the final confrontation with the villain, events will become increasingly awkward, contrived and disconnected from one another -- almost as if some cosmic Author was running up against a deadline and had to slap together the ending at the last minute.
  • Adam Smith's Revenge
    By the end of the game you are renowned everywhere as the Legendary Heroes, every surviving government and authority figure has rallied behind you, the fate of the world is obviously hanging in the balance, and out of nowhere random passers-by give you a pat on the back and heartfelt good luck wishes. However, shopkeepers won't even give you a discount, much less free supplies for the final battle with evil.
  • Adam Smith's Corollary
    No matter how thoroughly devastated the continent/planet/universe is, there's always some shopkeeper who survived the end of the world and sits outside the gates of the villain's castle, selling the most powerful equipment in the game, like nothing ever happened.
  • The Long Arm of the Plot
    Any bad guys, no matter how far they run, will always end up in one of two ways by the end of the game: obviously dead, or on your side. There is no in-between.
  • Apocalypse Any Time Now
    The best time to do side quests is while the huge meteor hovers in the sky above the planet, waiting to fall and destroy the world.
  • "So, Andross, you reveal your true form!"
    You will have to kill the evil villain at least twice at the end of the game. First the villain will look like a person or some creature and be rather easy to kill. Then he will grow to about 50 times the hero's size and be much harder to kill.
  • In Your Face, Jesus!
    Even if you manage to deal with him that time, you're not done -- the villain will then transform into his final form, which is always an angelic winged figure with background music remixed for ecstatic chorus and pipe organ.
  • The Moral Of The Story (Ghaleon Rule)
    Every problem in the universe can be solved by finding the right long-haired prettyboy and beating the crap out of him.
  • Weapon Rule
    There's always a hidden creature who is much harder to defeat than even the ultimate bad guy's final, world-annihilating form. It's lucky for all concerned that this hidden creature prefers to stay hidden rather than trying to take over the world himself, because he'd probably win. As a corollary, whatever reward you get for killing the hidden creature is basically worthless because by the time you're powerful enough to defeat him, you don't need it any more.
  • The Ultimate Rule
    Anything called "Ultima (whatever)" or "Ultimate (whatever)" isn't. There's always at least one thing somewhere in the world which is even more.
  • Know Your Audience (Vyse Rule)
    Every woman in the game will find the male lead incredibly attractive.




Extra Credits - an online weekly video series about game design

Posted by , 29 July 2015 - - - - - - · 733 views

Extra Credits - an online weekly video series about game design

http://extra-credits.net/shows/extra-credits/



Beautiful code part 3 - bottom up design

Posted by , 30 June 2015 - - - - - - · 873 views

Beautiful code part 3 - bottom up design

an example of beautiful code using bottom up design.

with this method, you start with the lowest level stuff, then add higher level layers until you have a sufficiently high level API.

so you go from ugly low level stuff to a beautiful high level API.

This example uses the directx 9 skinned mesh code from tiny.cpp and DXUT.h macros at the low level:

http://www.gamedev.net/blog/1729/entry-2261223-skinned-mesh-code-part-1-low-level-microsoft-code/

probably not what most folks would call beautiful code. but that's the low level API i had to start with.

first step was a wrapper layer API to "beautify" things a bit:

http://www.gamedev.net/blog/1729/entry-2261224-skinned-mesh-code-part-2-rockland-skinned-mesh-api/

a little better, but still kind of ugly, given all the various routines for the different skinning methods. it should really be split up into separate API's, one for each skinning method.

mesh and controller pools were required. being new APIs, they turned out to be relatively simple and beautiful:

http://www.gamedev.net/blog/1729/entry-2261225-skinned-mesh-code-part-3-mesh-and-controller-memory-pools/

and finally, one routine to rule them all: draw_skinmesh_caveman

http://www.gamedev.net/blog/1729/entry-2261227-skinned-mesh-code-part-4-implementation-example/

http://www.gamedev.net/blog/1729/entry-2261228-skinned-mesh-code-part-5-implementation-example-2/

so all that ugly code has been beautified into the single API call draw_skinmesh_caveman


Beautiful code - part 2 - top down design

Posted by , 30 June 2015 - - - - - - · 805 views

Beautiful code - part 2 - top down design

an example of beautiful code.

this was a result of top down design. IE high level routines fist, lowest level routines last.

first, the algo was written in comments in english.

then the highest level code was written, defining the lower level routines and their APIs as it was written, before ever actually defining the lower level code. so the high level code determined that there would be a camera_can_see_target function, and that it would take the index of a target as a parameter. the actual camera_can_see_target function was written later. the high level code defined what the lower level APIs would be. the highest level code was written to be as english-like as possible.

this is the high level code that started this process (from SIMSpace 8.0):
void draw_targets()
{
//  for each target, if in vis rng and pass frustum cull, add to render queue
//  vis rng = 100x tgt size = 200x tgt.rad
int a;
for (a=0; a<MAXTGTS; a++)
    {
    if (sstgt[a].active == 0)
        {
        continue;
        }
    if (camera_can_see_tgt(a) == 0)
        {
        continue;
        }
    add_tgt_to_queue(a);
    }
}
 

clarity, simplicity, and read-ability are hallmarks of beautiful code.


Skinned mesh code part 5 - implementation example 2

Posted by , 30 June 2015 - - - - - - · 792 views

Skinned mesh code part 5 - implementation example 2

another implementation example from caveman 3.0:

this is the actual code that draws band members and NPCs in the game now.

 
void draw_caveman2(int a)
{
//  a is animal #
int n;
//  n is npc #
location f;
//  npc location for frustum cull
int rad;
//  rad is NPC cliprad for frustum cull
int ani;
//  ani is ani # to play
int clothtex;
//  clothtex is the texture for loincloth and bra
float x,y,z;
//  x y z is the camera relative coordinates of the npc
float yr;
//  yr is the y rotation to use for drawing the caveperson
//     === do frustum cull ===
rad=6;
f.mx=animal[a].mx;;
f.mz=animal[a].mz;;
f.x=animal[a].x;;
f.z=animal[a].z;;
f.y=heightmap(f.mx,f.mz,f.x,f.z);
if (superclip4(&f,rad,animal_cliprng))
    {
    return;
    }
//    === set npc # ====
n=animal[a].butchered;
//   === set ani # ===
ani=controller_pool[npc[n].controllerID].curani;
//    === set cloth texture ===
//  159 = light hide
clothtex=159;
//   === set world mat ====
Mstart();
camera_relative_coords(animal[a].mx,animal[a].mz,animal[a].x,animal[a].z,&x,&z);
y=heightmap(animal[a].mx,animal[a].mz,animal[a].x,animal[a].z);
if ((!animal[a].alive)&&(!animal[a].falling))
    {
//  c deadani a
    MrotateRADS(1,pi);
    MrotateRADS(2,1.5f);
    y+=0.5f;
    }
else
    {
    MrotateRADS(0,animal[a].xr);
    yr=animal[a].yr+pi;
    if (yr > pi*2.0f)
        {
        yr-=pi*2.0f;
        }
    MrotateRADS(1,yr);
    MrotateRADS(2,animal[a].zr);
    }
Mmove(x,y,z);
//  === draw it ! ====
draw_skinmesh_caveman(npc[n].sex,npc[n].hairmeshID,npc[n].skintexID,npc[n].hairtexID,npc[n].eyetexID,clothtex,ani,npc[n].controllerID,0.066,&Mmat);
}
 
 
 
 

void draw_bandmember2(int a)
{
//  a is bandmember #
location f;
//  bandmember location for frustum cull
int rad;
//  rad is bandmember cliprad for frustum cull
int ani;
//  ani is ani # to play
int clothtex;
//  clothtex is the texture for loincloth and bra
float x,y,z;
//  x y z is the camera relative coordinates of the bandmember
float yr;
//  yr is the y rotation to use for drawing the bandmember
//     === do frustum cull ===
rad=6;
f.mx=cm[a].mx;;
f.mz=cm[a].mz;;
f.x=cm[a].x;;
f.z=cm[a].z;;
f.y=heightmap(f.mx,f.mz,f.x,f.z);
if ((a != cm0) || (cm[a].location != UPATREE))   // dont clip current bandmember when upatree
    {
if (superclip4(&f,rad,animal_cliprng))
    {
    return;
    }
    }
//   === set ani # ===
ani=controller_pool[cm[a].controllerID].curani;
//    === set cloth texture ===
//  159 = light hide
clothtex=159;
//   === set world mat ====
Mstart();
camera_relative_coords(cm[a].mx,cm[a].mz,cm[a].x,cm[a].z,&x,&z);
y=heightmap(cm[a].mx,cm[a].mz,cm[a].x,cm[a].z);
if ((!cm[a].alive)&&(!cm[a].falling))
    {
//  c deadani a
    MrotateRADS(1,pi);
    MrotateRADS(2,1.5f);
    y+=0.5f;
    }
else
    {
    MrotateRADS(0,cm[a].xr);
    yr=cm[a].yr+pi;
    if (yr > pi*2.0f)
        {
        yr-=pi*2.0f;
        }
    MrotateRADS(1,yr);
    MrotateRADS(2,cm[a].zr);
    }
Mmove(x,y,z);
//  === draw it ! ====
draw_skinmesh_caveman(cm[a].sex,cm[a].hairmeshID,cm[a].skintexID,cm[a].hairtexID,cm[a].eyetexID,clothtex,ani,cm[a].controllerID,0.066,&Mmat);
}
 
 
 
 
 
 
 
 
 
 
// called from initprog:

init_controller_pool();
load_all_skinned_meshes();
 
 
 
 
 
 
// called at program shutdown:

deactivate_all_controllers();
unload_all_skinned_meshes();
 
 
 
// called when an NPC is added to the simulation:

npc[npcID].controllerID=activate_controller(npc[npcID].skinmeshID);
set_sm_npc_ani(a,ANI_STAND);
 



 

 
// called when an NPC is removed from the simulation:
c=npc[b].controllerID;
deactivate_controller(c);
 
 
 



Skinned mesh code part 4 - implementation example

Posted by , 30 June 2015 - - - - - - · 860 views

Skinned mesh code part 4 - implementation example

An implementation example from Caveman 3.0:


 
//  called by draw_skinmesh_caveman
 
//  draws eyeballs attached to the headbone with an offset.
//  to draw a static mesh relative to the root of a bone, 
//  start with the transform matrix for the object relative to the root of the bone, 
//  then concatenate on the bone`s CombinedTransformationMatrix
//  don`t forget to call update_ani and add_world_transform_to_skeleton first!
//  use get_bone to get a D3DXFRAME pointer to the bone by name, and save it for 
//  later use when drawing meshes attached to that bone.
void draw_eyeballs2(int skmeshID,int texID)
{
Zdrawinfo a;
Zcdi(&a);
a.meshID=146;
a.texID=texID;
a.cull=1;
D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED *)skinned_mesh_pool[skmeshID].headbone;
//  use matrix manipulator to create bone to right eyeball transform matrix...
Mstart();
switch (skmeshID)
    {
    case 5:
        case 6:
            case 9:
                Mscale(0.013f,0.013f,-0.013f);
                Mrotate(0,-17);
                Mmove(0.0318f,0.1831f,0.082f);
                break;
            case 7:
                Mscale(0.013f,0.013f,-0.013f);
                Mrotate(0,-17);
                Mmove(0.0318f,0.1833f,0.0835f);
                break;
            case 8:
                Mscale(0.013f,0.013f,-0.013f);
                Mrotate(0,-17);
                Mmove(0.0318f,0.1833f,0.082f);
                break;
            default:
//  skmeshID 0,1,2,3,4
            Mscale(0.015f,0.015f,-0.015f);
            Mrotate(0,-20);
            Mmove(0.036f,0.099f,0.076f);
            break;
        }
//  cat combined matrix onto matrix manipulator mat to get world mat...
    D3DXMatrixMultiply(&a.mWorld,&Mmat,&pFrame->CombinedTransformationMatrix);
//  add to render queue...
    Zdraw2(&a);
//  use matrix manipulator to create bone to left eyeball transform matrix...
    Mstart();
    switch (skmeshID)
        {
        case 5:
            case 6:
                case 9:
                    Mscale(0.013f,0.013f,-0.013f);
                    Mrotate(0,-17);
                    Mmove(-0.0318f,0.1831f,0.082f);
                    break;
                case 7:
                    Mscale(0.013f,0.013f,-0.013f);
                    Mrotate(0,-17);
                    Mmove(-0.0337f,0.1825f,0.0828f);
                    break;
                case 8:
                    Mscale(0.013f,0.013f,-0.013f);
                    Mrotate(0,-17);
                    Mmove(-0.0337f,0.1825f,0.082f);
                    break;
                default:
//  skmeshID 0,1,2,3,4
                Mscale(0.015f,0.015f,-0.015f);
                Mrotate(0,-20);
                Mmove(-0.035f,0.098f,0.076f);
                break;
            }
//  cat combined matrix onto matrix manipulator mat to get world mat...
        D3DXMatrixMultiply(&a.mWorld,&Mmat,&pFrame->CombinedTransformationMatrix);
//  add to render queue...
        Zdraw2(&a);
        }
 
 
 
 
 
 
 
 
 
 
//  draws hair mesh attached to the headbone with an offset.
//  to draw a static mesh relative to the root of a bone, 
//  start with the transform matrix for the object relative to the root of the bone, 
//  then concatenate on the bone`s CombinedTransformationMatrix
//  don`t forget to call update_ani and add_world_transform_to_skeleton first!
//  use get_bone to get a D3DXFRAME pointer to the bone by name, and save it for 
//  later use when drawing meshes attached to that bone.
void draw_girl_hair(int skmeshID,int hmeshID,int texID)
{
Zdrawinfo a;
    Zcdi(&a);
    a.meshID=hmeshID;
    a.texID=texID;
D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED *)skinned_mesh_pool[skmeshID].headbone;
//  use matrix manipulator to create bone to hair mesh transform matrix...
//  start w/ identity matrix...
    Mstart();
    switch (hmeshID)
        {
        case 154:
//  long messy
            Mscale(0.925f,1.317f,1.2203f);
            Mrotate(0,-5);
            Mmove(0.002f,-0.032f,-0.07f);
            break;
        case 254:
//  short ponytail
            Mscale(1.2f,1.5f,1.15f);
            Mmove(-0.002f,0.103f,-0.05f);
            break;
        case 255:
//  short
            Mscale(1.09f,1.41f,1.1f);
            Mmove(-0.004f,0.051f,-0.028f);
            break;
        case 258:
//  short afro
            Mscale(1.224f,1.28f,1.15f);
            Mmove(-0.002f,0.06f,-0.032f);
            break;
        case 259:
//  long afro / dreads
            Mscale(1.23f,1.66f,1.27f);
            Mmove(0.004f,0.059f,-0.02f);
            break;
        default:
//  tex 260 - long straight
        Mscale(0.996f,1.381f,1.403f);
        Mrotate(0,-10);
        Mmove(0.002f,0.059f,-0.039f);
        break;
    }
//  cat combined matrix onto matrix manipulator mat to get world mat...
D3DXMatrixMultiply(&a.mWorld,&Mmat,&pFrame->CombinedTransformationMatrix);
//  add to render queue...
Zdraw2(&a);
}
 
 
 
 
 
 
 
 
 
 
 
 
//  draws hair mesh attached to the head bone with an offset.
//  to draw a static mesh relative to the root of a bone, 
//  start with the transform matrix for the object relative to the root of the bone, 
//  then concatenate on the bone`s CombinedTransformationMatrix
//  don`t forget to call update_ani and add_world_transform_to_skeleton first!
//  use get_bone to get a D3DXFRAME pointer to the bone by name, and save it for 
//  later use when drawing meshes attached to that bone.
void draw_guy_hair(int skmeshID,int hmeshID,int texID)
{
Zdrawinfo a;
Zcdi(&a);
a.meshID=hmeshID;
a.texID=texID;
D3DXFRAME_DERIVED *pFrame = (D3DXFRAME_DERIVED *)skinned_mesh_pool[skmeshID].headbone;
//  use matrix manipulator to create bone to hair mesh transform matrix...
//  start w/ identity matrix...
Mstart();
switch (hmeshID)
    {
    case 258:
        case 259:
            Mscale(1.11f,1.2f,1.2f);
            Mmove(0.0f,0.152f,-0.005f);
            break;
        case 250:
            Mscale(1.08f,1.073f,1.078f);
            Mmove(-0.0047f,0.177f,0.016f);
            break;
        case 265:
            Mscale(0.818f,0.815f,1.028f);
            Mmove(-0.0023f,0.143f,-0.039f);
            break;
        case 266:
            Mscale(1.07f,1.18f,0.8876f);
            Mrotate(0,-13);
            Mmove(-0.0023f,0.215f,-0.005f);
            break;
        default:
//  -1 = bald
        return;
        break;
    }
//  cat combined matrix onto matrix manipulator mat to get world mat...
D3DXMatrixMultiply(&a.mWorld,&Mmat,&pFrame->CombinedTransformationMatrix);
//  add to render queue...
Zdraw2(&a);
}
 
 
 
 
 
 
 
 
 
void draw_hair(int sex,int skmeshID,int hmeshID,int texID)
{
if (sex == 0)
    {
    draw_guy_hair(skmeshID,hmeshID,texID);
    }
else
    {
    draw_girl_hair(skmeshID,hmeshID,texID);
    }
}
 
 
 
 
 
 
 
 
 
 
 
 
//  draws a caveperson using the specified hair mesh, textures, and animation
void draw_skinmesh_caveman(int sex,int hair,int bodT,int hairT,int eyeT,int clothT,int ani,int ctrl,double dt,D3DXMATRIX *mWorld)
{
//  sex: 0=male 1=female
//  hair is hair mesh ID. -1 = bald.
//  bodT is body mesh texture ID
//  hairT is hair texture ID
//  eyeT is eye texture ID
//  clothT is clothing texture ID
//  ani is which ani should be playing
//  ctrl is the index of the character`s controller
int a;
//  a is the skinned_mesh_pool index of the body mesh
a=controller_pool[ctrl].mesh_index;
//  1. set textures in the skinned mesh pool
skinned_mesh_pool[a].body->texID=bodT;
skinned_mesh_pool[a].loincloth->texID=clothT;
if (sex == 1)
    {
    skinned_mesh_pool[a].bra->texID=clothT;
    }
//  2. set animation
controller_pool_setani(ctrl,ani);
//  3. draw instance ( controllerID, dt, mWorld )
draw_skinned_mesh_instance(ctrl,dt,mWorld);
//  set z3d state manager current mesh to none...
Zcurrentmesh=-1;
//  reset FVF back to z3d FVF...
Zd3d_device_ptr->SetFVF(ZFVF);
//  4. draw eyes
draw_eyeballs2(a,eyeT);
//  5. draw hair
draw_hair(sex,a,hair,hairT);
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
//  tests drawing 100 instances of a skinned mesh, each with its own controller
//  uses update_and_draw_skinned_mesh w/ drawmeshcontainer5
void skinned_mesh_test3()
{
//  hair is which hair mesh to use. 0 through num hair meshes - 1.
int i,quit,et,fps,num_anisets,hair,hairmeshID,cx,cz,xr,sex,bodT,eyeT,hairT,clothT;
float x,z;
char s[100],s2[100];
bodT=0;
eyeT=0;
hairT=0;
clothT=0;
sex=0;
xr=0;
hair=0;
hairmeshID=154;
cx=0;
cz=-100;
//  no need to init stuff - its done in initprog now.
//  c init_controller_pool
//  c load_all_skinned_meshes
num_anisets=(int)skinned_mesh_pool[0].controller->GetNumAnimationSets();
//  ss s "num anisets: "
//  c i2s num_anisets s2
//  s+ s s2
//  c msg3 s
for (i=0; i<100; i++)
    {
    activate_controller(i%10);
//  set it to play a random ani...
    controller_pool_setani(i,i%num_anisets);
    }
//  set a directional light...
Zsetlite(0,0.0f,-1.0f,1.0f,1.0f);
//  turn it on...
Zlite(0,1);
quit=0;
//  set material to z3d default material....
Zsetmaterial(0);
et=0;
fps=0;
//  init matrix editor
AAinit();
//  turn on matrix editor
AAturnon();
while (!quit)
    {
//  timer 8 is used for framerate limiter.
//  like Caveman 3.0, the test runs at 15fps. i`ve also run it at 60 fps no problem.
    Zstarttimer(8);
//  set view matrix....
    Zsetcam((float)cx/10.0f,5.0f,(float)cz/10.0f,0.0f,0.0f,0.0f);
    Zclearscreen(32,0,32);
    Zclearzbuf();
    Zbeginscene();
    Zcleardrawlist();
    x=-10.0f;
    z=0.0f;
    for (i=0; i<100; i++)
        {
        sex=i%10;
        if (sex < 5)
            {
            sex=1;
            }
        else
            {
            sex=0;
            }
        if (sex == 0)
            {
//  male skin textures: 299 = mideastern   389 = sm_guy1.bmp
            bodT=389;
            eyeT=300;
            hairT=267;
            clothT=159;
            switch (hair)
                {
                case 0:
                    hairmeshID=258;
                    break;
                case 1:
                    hairmeshID=259;
                    break;
                case 2:
                    hairmeshID=250;
                    break;
                case 3:
                    hairmeshID=265;
                    break;
                case 4:
                    hairmeshID=266;
                    break;
                case 5:
                    hairmeshID=-1;
                    break;
                }
            }
        else
            {
//  girl skin textures: 387, 388
            bodT=387;
//  eye textures: 292=blue 300=brown
            eyeT=292;
//  hair textures: 291=blonde 267=black
            hairT=291;
//  cloth textures: 159=light hide 192=dark hide
            clothT=159;
            switch (hair)
                {
                case 0:
                    hairmeshID=154;
                    break;
                case 1:
                    hairmeshID=254;
                    break;
                case 2:
                    hairmeshID=255;
                    break;
                case 3:
                    hairmeshID=258;
                    break;
                case 4:
                    hairmeshID=259;
                    break;
                case 5:
                    hairmeshID=260;
                    break;
                }
            }
        Mstart();
        Mrotate(1,xr);
        Mmove(x,0.0f,z);
        draw_skinmesh_caveman(sex,hairmeshID,bodT,hairT,eyeT,clothT,i%num_anisets,i,0.066,&Mmat);
        x+=2.0f;
        if (x >= 10.0f)
            {
            x=-10.0f;
            z+=2.0f;
            }
        }
    Zdrawlist();
    Zbeginsprite();
    tx2(100,0,"press ESC to quit             press SPACE to change hair mesh");
    tx2(100,50,"use wasd to move camera      L/R arrows: rotate models");
    strcpy_s(s,100,"frame time: ");
    i2s(et,s2);
    strcat_s(s,100,s2);
    strcat_s(s,100," ms");
    tx2(100,700,s);
    if (et == 0)
        {
        fps=0;
        }
    else
        {
        fps=1000/et;
        }
    strcpy_s(s,100,"fps: ");
    i2s(fps,s2);
    strcat_s(s,100,s2);
    tx2(100,750,s);
    Zendsprite();
//  draw matrix editor
    AAdraw();
//  end scene and present...
    Zshowscene();
//  process matrix editor input
    AAprocess_input();
    if (Zkeypressed(VK_SPACE))
        {
//  c change_ani ani_controller_ptr2 num_anis
        hair++;
        if (hair > 5)
            {
            hair=0;
            }
        while (Zkeypressed(VK_SPACE))
            {
            }
        }
//  if they hit escape, quit the test...
    if (Zkeypressed(VK_ESCAPE))
        {
        quit=1;
        }
    if (Zkeypressed(0x57))
        {
//  w key
        cz++;
//  wh Zkeypressed(0x57)
//     .
        }
    if (Zkeypressed(0x41))
        {
//  a key
        cx--;
//  wh Zkeypressed(0x41)
//     .
        }
    if (Zkeypressed(0x53))
        {
//  s key
        cz--;
//  wh Zkeypressed(0x53)
//     .
        }
    if (Zkeypressed(0x44))
        {
//  d key
        cx++;
//  wh Zkeypressed(0x44)
//     .
        }
    if (Zkeypressed(VK_LEFT))
        {
        xr-=2;
        if (xr < 0)
            {
            xr=359;
            }
        }
    if (Zkeypressed(VK_RIGHT))
        {
        xr+=2;
        if (xr > 359)
            {
            xr=0;
            }
        }
//  framerate limiter: wait until 66 millisconds has passed before beginning next frame...
//     wh Zelapsedtime(8)<66
//         .
    et=Zelapsedtime(8);
    }
//  c msg3 "unloading stuff..."
//  turn off matrix editor
AAturnoff();
//  no need to uninit stuff now - its done by endprog.
//  c deactivate_all_controllers
//  c unload_all_skinned_meshes
//  c msg3 "all done!"
}
 
 
 
 
 
 
//  enum ani_type: stand, walk, run, backstep;
 
#define ANI_STAND 0
#define ANI_WALKBACK 1
#define ANI_WALK 2
#define ANI_RUN 3
 
 
 
//  set skinned mesh ani for a npc
void set_sm_npc_ani(int a,int ani)
{
//  a is animal #
//  ani is the animation # to play
int n;
//  n is npc #
int c;
//  c is controller #
n=animal[a].butchered;
c=npc[n].controllerID;
controller_pool_setani(c,ani);
}
 
 
 
//  set skinned mesh ani for a player character (a band member)
void set_sm_pc_ani(int a,int ani)
{
//  a is band member #
//  ani is the animation # to play
controller_pool_setani(cm[a].controllerID,ani);
}
 
 
 
 
 
 
 
 
 
//  ------------------------ end skinned mesh --------------------------
 
 
 
 



Skinned mesh code part 3 - mesh and controller memory pools

Posted by , 30 June 2015 - - - - - - · 725 views

Skinned mesh code part 3 - mesh and controller memory pools
 
//  ----------------------------------- skinned mesh pool API -------------------------------------------
 
 
struct skinned_mesh_master
{
LPD3DXFRAME root;
ID3DXAnimationController *controller;
LPD3DXFRAME headbone;
D3DXMESHCONTAINER_DERIVED *body, *bra, *loincloth;
};
 
#define MAX_SKINNED_MESHES 100
 
skinned_mesh_master skinned_mesh_pool[MAX_SKINNED_MESHES];
 
int num_skinned_meshes=0;
 
 
 
 
 
 
 
void load_skinned_mesh_master(char *fname)
{
int result;
LPD3DXMESHCONTAINER p;
result=load_skinned_mesh(fname,&skinned_mesh_pool[num_skinned_meshes].controller,&skinned_mesh_pool[num_skinned_meshes].root);
switch (result)
    {
    case 1:
        msg3("load skinned mesh master: invalid call!");
        break;
    case 2:
        msg3("load skinned mesh master: out of memory!");
        break;
    case 3:
        msg3("load skinned mesh master: setup bone matrix pointers error");
        break;
    }
skinned_mesh_pool[num_skinned_meshes].headbone=get_bone(skinned_mesh_pool[num_skinned_meshes].root,"metarig_head");
if (skinned_mesh_pool[num_skinned_meshes].headbone == NULL)
    {
    msg3("load skinned mesh master: metarig_head not found in root!");
    }
//  get pointer to body mesh to demonstrate interchangeable pooled textures for each mesh of a skinned mesh
p=get_mesh_container2(skinned_mesh_pool[num_skinned_meshes].root,"body");
if (p == NULL)
    {
    msg3("load skinned mesh master: body frame meshcontainer is null!");
    }
skinned_mesh_pool[num_skinned_meshes].body=(D3DXMESHCONTAINER_DERIVED*)p;
p=get_mesh_container2(skinned_mesh_pool[num_skinned_meshes].root,"bra");
if (p == NULL)
    {
//  its a male skinned mesh, no bra mesh.
    skinned_mesh_pool[num_skinned_meshes].bra=NULL;
    }
else
    {
    skinned_mesh_pool[num_skinned_meshes].bra=(D3DXMESHCONTAINER_DERIVED*)p;
    }
p=get_mesh_container2(skinned_mesh_pool[num_skinned_meshes].root,"loincloth");
if (p == NULL)
    {
    msg3("load skinned mesh master: loincloth frame meshcontainer is null!");
    }
skinned_mesh_pool[num_skinned_meshes].loincloth=(D3DXMESHCONTAINER_DERIVED*)p;
num_skinned_meshes++;
}
 
 
 
 
 
 
 
 
void load_all_skinned_meshes()
{
FILE *f;
int quit;
char s[100];
if (filefound("skinned_meshes.dat") == 0)
    {
    msg3("error: meshes.dat not found!");
    return;
    }
num_skinned_meshes=0;
f=infile("skinned_meshes.dat");
quit=0;
while (!quit)
    {
    readfile(f,s);
    if (strcmp(s,"endfile") == 0)
        {
        quit=1;
        }
    else
    if (s[0] == '/')
        {
//  do nothing, its a comment
        }
    else
        {
//  c msg2 s
        load_skinned_mesh_master(s);
        }
    }
fclose(f);
}
 
 
 
 
 
 
 
 
 
 
void unload_all_skinned_meshes()
{
int i;
for (i=0; i<num_skinned_meshes; i++)
    {
    unload_skinned_mesh(skinned_mesh_pool[i].controller,skinned_mesh_pool[i].root);
    }
}
 
 
 
 
 
 
 
 
//  -------------------------------------- controller pool API ------------------------------------------
 
 
 
 
 
 
struct skinned_mesh_instance
{
//  curani: -1 = none
int active,mesh_index,curani;
ID3DXAnimationController *controller;
};
 
#define MAX_SKINNED_MESH_INSTANCES 200
 
skinned_mesh_instance controller_pool[MAX_SKINNED_MESH_INSTANCES];
 
 
 
 
 
 
 
void init_controller_pool()
{
int i;
for (i=0; i<MAX_SKINNED_MESH_INSTANCES; i++)
    {
    controller_pool[i].active=0;
    }
}
 
 
 
 
 
int get_free_controller()
{
int i;
for (i=0; i<MAX_SKINNED_MESH_INSTANCES; i++)
    {
    if (controller_pool[i].active == 0)
        {
        return(i);
        }
    }
return(-1);
}
 
 
 
 
 
 
 
 
//  activates a controller. 
//  mesh_index is the index of the skinned mesh the controller should use.
//  returns the index of the controller.
 
 
int activate_controller(int mesh_index)
{
int j;
j=get_free_controller();
if (j == -1)
    {
    msg3("error: all animation controllers are in use!");
    return(-1);
    }
controller_pool[j].active=1;
clone_controller(skinned_mesh_pool[mesh_index].controller,&controller_pool[j].controller);
controller_pool[j].mesh_index=mesh_index;
controller_pool[j].curani=-1;
return(j);
}
 
 
 
 
 
 
 
 
 
void draw_skinned_mesh_instance(int i,double seconds,LPD3DXMATRIX mWorld)
{
//  i is the index of the controller in the controller pool.
int j;
//  j is the index of the skinned mesh in the skinned mesh pool
j=controller_pool[i].mesh_index;
update_and_draw_skinned_mesh(controller_pool[i].controller,seconds,skinned_mesh_pool[j].root,mWorld);
}
 
 
 
 
 
 
 
 
 
 
 
void deactivate_controller(int c)
{
//  c is controller #
if (controller_pool[c].active == 0)
    {
    return;
    }
if (controller_pool[c].controller == NULL)
    {
    return;
    }
controller_pool[c].controller->Release();
controller_pool[c].controller=NULL;
controller_pool[c].active=0;
}
 
 
 
 
 
 
 
 
 
void deactivate_all_controllers()
{
int i;
for (i=0; i<MAX_SKINNED_MESH_INSTANCES; i++)
    {
    if (controller_pool[i].active == 1)
        {
        deactivate_controller(i);
        }
    }
}
 
 
 
void controller_pool_setani(int cindex,int aniID)
{
//  sets the animation of a controller in the controller pool.
//  does not set if its already the current ani.
//  cIndex is the controller index.
//  aniID is the aniID.
if (controller_pool[cindex].curani == aniID)
    {
    return;
    }
set_ani(controller_pool[cindex].controller,aniID);
controller_pool[cindex].curani=aniID;
}
 
 
 
//  the controller should track which ani its playing
//  it does, via controller_pool[i].curani
 
 
 
/*
 
 
 
usage: 
// init
init_controller_pool
load_all_skinned_meshes
activate_controller
// draw
draw_skinned_mesh_instance
// shutdown
deactivate_controller -or- deactivate_all_controllers
unload_all_skinned_meshes
 
 
 
 
 
 
 
for drawing in caveman:
a cm or animal rec will hold the controller index, similar to an aniplayerID.
the mWorld for the cm or animal will be used.
seconds will usually be 0.066.
there will be a draw skinned mesh model routine that will set textures, then call draw_skinned_mesh_instance, then
draw attached stuff (eyes, hair, gear, weapons etc). texture IDs will be stored in the cm rec or the npc rec.
 
 
 
 
 
*/
 
 
 
 
 
 
 
 
//  -------------------------- end mesh pool and controller pool APIs --------------------------
 
 
 



Skinned mesh code part 2 - Rockland skinned mesh API

Posted by , 30 June 2015 - - - - - - · 690 views

Skinned mesh code part 2

Rockland skinned mesh API
//  ------------------------ Rockland skinned mesh API --------------------------
 
 
//  -------------------------------- load skinned mesh -----------------------------------
//  loads a skinned mesh from a .x file, and creates an animation controller for it.
//  NOTE: load mesh hierarchy from X automatically makes the created controller use the loaded mesh.
//  returns: 
//  0 success
//  1 invalid call
//  2 out of memory
//  3 setup bone matrix pointers error
int load_skinned_mesh(char *filename,ID3DXAnimationController** ani_controller_ptr_addr,LPD3DXFRAME* g_pFrameRoot_addr)
{
HRESULT result;          // result
CAllocateHierarchy Alloc;      // allocator routine
result=D3DXLoadMeshHierarchyFromX(filename,D3DXMESH_MANAGED,Zd3d_device_ptr,&Alloc,NULL,g_pFrameRoot_addr,ani_controller_ptr_addr);
switch (result)
    {
    case D3DERR_INVALIDCALL:
        return(1);
        break;
    case E_OUTOFMEMORY:
        return(2);
        break;
    }
//  result = D3D_OK
result=SetupBoneMatrixPointers(*g_pFrameRoot_addr,*g_pFrameRoot_addr);
if (result != S_OK)
    {
    return(3);
    }
return(0);
}
 
 
 
 
 
 
 
 
 
//  ------------------- unload skinned mesh ---------------------------------
void unload_skinned_mesh(ID3DXAnimationController* ani_controller_ptr,LPD3DXFRAME g_pFrameRoot)
{
CAllocateHierarchy Alloc;
D3DXFrameDestroy(g_pFrameRoot,&Alloc);
SAFE_RELEASE(ani_controller_ptr);
}
 
 
 
 
 
 
 
//  ------------------- set D3DINDEXEDVS projection matrix transpose ----------
//  only required by D3DINDEXEDVS skinning method
//  must be called before calling set_indexed_shader_proj_mat!
void set_D3DINDEXEDVS_proj_mat_transpose()
{
D3DXMatrixTranspose(&g_matProjT,&Zprojection_matrix);
}
 
 
 
 
 
 
//  --------------------- load effect file ------------------------------------
//  only required for shader based skinning methods
//  returns S_OK on success
HRESULT load_effect_file()
{
HRESULT hr;
DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE;
    // Read the D3DX effect file
//     WCHAR str[MAX_PATH];
//     V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"SkinnedMesh.fx" ) );
    // If this fails, there should be debug output as to why the .fx file failed to compile
    V_RETURN( D3DXCreateEffectFromFile( Zd3d_device_ptr, "SkinnedMesh.fx", NULL, NULL, dwShaderFlags,
                                        NULL, &g_pEffect, NULL ) );
return(S_OK);
}
 
 
 
 
 
 
 
 
//  -----------------------  release effect file -------------------------------------
//  only required for shader based skinning methods
void unload_effect_file()
{
SAFE_RELEASE( g_pEffect );
}
 
 
 
 
 
 
 
//  ------------------ set indexed shader proj mat -------------------------------------
//    Set the projection matrix for indexed skinning vertex shaders
//  only required for D3DINDEXEDVS and D3DINDEXEDHLSLVS skinning methods
//  must call set_D3DINDEXEDVS_proj_mat_transpose first!
void set_indexed_shader_proj_mat()
{
HRESULT hr;
if( g_SkinningMethod == D3DINDEXEDVS )
    {
    V( Zd3d_device_ptr->SetVertexShaderConstantF( 2, ( float* )&g_matProjT, 4 ) );
    }
else if( g_SkinningMethod == D3DINDEXEDHLSLVS )
    {
    V( g_pEffect->SetMatrix( "mViewProj", &Zprojection_matrix ) );
    }
}
 
 
 
 
 
 
 
 
 
 
 
//  --------------------------- set shader light -------------------------------
//    // Set Light for vertex shader
//  only required for shader based skinning methods
//  note you`ll want to modify this light to match your 
//  fixed function one or your lighting won`t look right!
void set_shader_light()
{
HRESULT hr;         
D3DXVECTOR4 vLightDir( 0.0f, 1.0f, -1.0f, 0.0f );
D3DXVec4Normalize( &vLightDir, &vLightDir );
V( Zd3d_device_ptr->SetVertexShaderConstantF( 1, ( float* )&vLightDir, 1 ) );
V( g_pEffect->SetVector( "lhtDir", &vLightDir ) );
}
 
 
 
 
 
 
 
 
 
//  ------------- update animation -----------------------------
//  advances the controller`s timer by "seconds", 
//  tweens between the two closest keyframes, then sets the bone matrices
//  of the skeleton.
void update_ani(ID3DXAnimationController* ani_controller_ptr,double seconds)
{
ani_controller_ptr->AdvanceTime( seconds, NULL );
}
 
 
 
 
 
 
 
 
//  --------------------- add world transform to skeleton ---------------------
//  call update_ani first!
void add_world_transform_to_skeleton(LPD3DXFRAME g_pFrameRoot,LPD3DXMATRIX mWorld_ptr)
{
UpdateFrameMatrices( g_pFrameRoot, mWorld_ptr );
}
 
 
 
 
 
 
 
//  ------------------------ draw skinned mesh ----------------------
//  draws a skinned mesh
//  ZmView is the view matrix
//  to port to code not based on the z3d game library, 
//  replace Zd3d_device ptr and ZmView with your own d3d device pointer and view matrix.
void draw_skinned_mesh(LPD3DXFRAME g_pFrameRoot)
{
DrawFrame( Zd3d_device_ptr, g_pFrameRoot, &ZmView );
}
 
 
 
 
 
 
 
 
 
 
//  ------------------------------ get behavior flags  ----------------------------
//  gets behavior flags (HW caps -> creation params -> behavoir flags) for indexed shading methods
//  only required for D3DINDEXED, D3DINDEXEDVS, and D3DINDEXEDHLSLVS skinning methods
void get_indexed_behavior_flags()
{
D3DDEVICE_CREATION_PARAMETERS cp;
Zd3d_device_ptr->GetCreationParameters( &cp );
g_dwBehaviorFlags = cp.BehaviorFlags;
}
 
 
 
 
 
 
 
 
 
 
 
//  ---------------------------- load shaders -----------------------------
//  returns S_OK on success
//  loads the indexed vertex shaders
//  only required for shader based skinning methods
HRESULT load_shaders()
{
HRESULT hr;
DWORD dwShaderFlags = 0;
/*
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DXSHADER_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3DXSHADER_DEBUG;
    #endif
#if defined(DEBUG_VS) || defined(DEBUG_PS)
        dwShaderFlags |= D3DXSHADER_DEBUG|D3DXSHADER_SKIPVALIDATION;
    #endif
*/
    for( DWORD iInfl = 0; iInfl < 4; ++iInfl )
        {
            LPD3DXBUFFER pCode;
            // Assemble the vertex shader file
//             WCHAR str[MAX_PATH];
//             DXUTFindDXSDKMediaFileCch( str, MAX_PATH, g_wszShaderSource[iInfl] );
            if( FAILED( hr = D3DXAssembleShaderFromFile( g_wszShaderSource[iInfl], NULL, NULL, dwShaderFlags, &pCode, NULL ) ) )
                return hr;
            // Create the vertex shader
            if( FAILED( hr = Zd3d_device_ptr->CreateVertexShader( ( DWORD* )pCode->GetBufferPointer(),
                                                             &g_pIndexedVertexShader[iInfl] ) ) )
            {
                return hr;
            }
            pCode->Release();
        }
return(S_OK);
}
 
 
 
 
 
 
 
 
 
 
 
 
//  ------------------------------------ unload shaders -------------------------
//  only required for shader based skinning methods
void unload_shaders()
{
for( DWORD iInfl = 0; iInfl < 4; ++iInfl )
    SAFE_RELEASE( g_pIndexedVertexShader[iInfl] );
}
 
 
 
 
//  ----------------------------------- get bone (by name) -----------------------------------
//  returns a pointer to a bone (D3DXFRAME), given its name (as it appears in the .x file, not blender, etc!)
//  use this to get a pointer to a bone you want to attach a mesh to, 
//  such as a hand bone you want to attach a weapon to.
//  example:
//  LPD3DXFRAME my_bone_pointer=get_bone(root_bone_pointer,"some_bone_name");
//  the bone`s world transform is then (D3DXFRAME_DERIVED*)my_bone_pointer->CombinedTransformMatrix
//  getting bone by name requires chasing pointers to travere the frame hierarchy tree, 
//  and string comparisons at each node. so you want to just do it once for bones you`re 
//  interested in when you load a skined mesh, and save the bone pointers for later use when
//  drawing meshes attached to those bones.
LPD3DXFRAME get_bone(LPD3DXFRAME rootframe,char *s)
{
return(D3DXFrameFind(rootframe,s));
}
 
 
 
 
 
 
 
 
//  -------------------- get mesh container, given the mesh container name  ---------------------
//                       NOTE:  blender does not export mesh container names! 
//                              it only exports them as comments in the .x file.
//                              use get_mesh_container2 to get the first mesh container of a frame,
//                              given the frame name, which blender does export.
 
 
//  recursively searches a linked list of meshcontainers and returns a pointer to a mesh container, given its name.
//  returns NULL if not found
//  NOTE: unfortunately, blender doesn`t seem to export mesh names except as comments. so mesh names are always "".
//  so this wont really work with files from blender.
LPD3DXMESHCONTAINER get_mesh_container_part2(LPD3DXMESHCONTAINER p,char *s)
{
msg3(p->Name);
if (strcmp(p->Name,s) == 0)
    {
    return(p);
    }
if (p->pNextMeshContainer == NULL)
    {
    return(NULL);
    }
return(get_mesh_container_part2(p->pNextMeshContainer,s));
}
 
 
 
 
//  returns a pointer to a meshcontainer in a skinned mesh, given its name.
//  returns NULL if not found
//  rootframe is the root frame of the skinned mesh
//  does a recursive traversal of the frame hierarchy. for each frame with a meshcontainer, 
//  it does a recursive search of the linked list of meshcontainers, 
//  looking for a meshcontainer with a matching name.
//  USAGE:
//  1. add a texID variable to D3DXMESHCONTAINER_DERIVED
//  2. after loading the skinned mesh, use get_mesh_container to get a pointer to a mesh container.
//  3. use the pointer to the mesh container to set the texID
//  4. use drawmeshcontainer5 to draw ( it uses the texID)
//  this gives you interchangeable poolable textures for each mesh of the skinned mesh.
//  NOTE: unfortunately, blender doesn`t seem to export mesh names except as comments. so mesh names are always "".
//  so this wont really work with files from blender.
LPD3DXMESHCONTAINER get_mesh_container(LPD3DXFRAME rootframe,char *s)
{
LPD3DXMESHCONTAINER p;
p=NULL;
if (rootframe->pMeshContainer != NULL)
    {
    p=get_mesh_container_part2(rootframe->pMeshContainer,s);
    if (p != NULL)
        {
        return(p);
        }
    }
if (rootframe->pFrameSibling != NULL)
    {
    p=get_mesh_container(rootframe->pFrameSibling,s);
    if (p != NULL)
        {
        return(p);
        }
    }
if (rootframe->pFrameFirstChild != NULL)
    {
    p=get_mesh_container(rootframe->pFrameFirstChild,s);
    if (p != NULL)
        {
        return(p);
        }
    }
return(NULL);
}
 
 
 
 
 
 
 
 
 
//  --------------------------- get first mesh container of a frame, given the frame name --------------------------
 
//  returns a pointer to the first meshcontainer of a frame, given the name of the frame.
//  returns NULL if frame not found, or frame meshcontainer is null.
//  rootframe is the root frame of the skinned mesh.
//  uses get_bone to get the frame, then returns the frame`s mesh container pointer.
//  USAGE:
//  1. add a texID variable to D3DXMESHCONTAINER_DERIVED
//  2. after loading the skinned mesh, use get_mesh_container2 to get a pointer to a mesh container.
//  3. if necessary, use meshcontainer->pNextContainer to traverse down the linked list of meshcontainers, 
//     until you get to the one you want.
//  4. just before drawing, use the pointer to the mesh container to set the texID
//  5. use drawmeshcontainer5 to draw (it uses the texID)
//  this gives you interchangeable poolable textures for each mesh of the skinned mesh.
//  AND it works with .x files with no mesh container names, such as those exported from blender.
 
LPD3DXMESHCONTAINER get_mesh_container2(LPD3DXFRAME rootframe,char *s)
{
LPD3DXFRAME p;
p=get_bone(rootframe,s);
if (p == NULL)
    {
    return(NULL);
    }
return(p->pMeshContainer);
}
 
 
 
 
 
 
 
 
 
 
 
 
//  ---------------------------------- clone controller ---------------------------
//  clones an animation controller.
//  used when drawing multiple instances of the same skinned mesh, each with its own animation.
//  use this to clone a copy of the "master" controller you used to load the skinned mesh.
//  create a clone for each instance you want to draw. only use the master for cloning, not for animation.
//  when drawing just one instance, there is no need to clone, and its ok to use the master for animation.
void clone_controller(ID3DXAnimationController* source,ID3DXAnimationController** target)
{
source->CloneAnimationController(
    source->GetMaxNumAnimationOutputs(),
    source->GetMaxNumAnimationSets(),
    source->GetMaxNumTracks(),
    source->GetMaxNumEvents(),
    target);
}
 
 
 
 
 
//  ------------------------------------ set animation --------------------------------------
//  sets a controller to the animation given by "index", and starts the animation from the beginning. 
//  sorry - no animation blending - yet! <g>.
//  use this to switch between multiple animations
void set_ani(ID3DXAnimationController* p,int index)
{
LPD3DXANIMATIONSET a;
p->GetAnimationSet((UINT)index,&a);
p->SetTrackAnimationSet(0,a);
p->SetTrackPosition(0,0);
p->ResetTime();
p->SetTrackSpeed(0,1.0f);
}
 
 
 
 
 
 
 
 
 
 
 
//  -------------------------- update and draw skinned emsh --------------------------------------
 
//  combines update_ani, add_world_transform_to_skeleton, and draw_skinned_mesh
void update_and_draw_skinned_mesh(ID3DXAnimationController* ani_controller_ptr,double seconds,LPD3DXFRAME g_pFrameRoot,LPD3DXMATRIX mWorld_ptr)
{
update_ani(ani_controller_ptr,seconds);
add_world_transform_to_skeleton(g_pFrameRoot,mWorld_ptr);
draw_skinned_mesh(g_pFrameRoot);
}
 
 
 
 
 
 
 
 
 
//  ========================== skinned mesh test routines 1 and 2 ===============================
 
 
 
 
//  toggles a controller to the next animation
//  only used with the 1st skinned mesh`s controller for the test routine
//  num_anis is the number of animation sets the controller has
void change_ani(ID3DXAnimationController* p,int num_anis)
{
static int current_ani=0;
current_ani++;
if (current_ani >= num_anis)
    {
    current_ani=0;
    }
set_ani(p,current_ani);
}
 
 
 
 
 
 
 
 
 
 
 
//  draws a quad at the root of each frame in the hierarchy of the test .x file used.
//  this tested the basic ability to use combinedtransformmatrix for attaching meshes to bones.
void draw_hat(LPD3DXFRAME g_pFrameRoot)
{
Zdrawinfo a;
Zcdi(&a);
 
D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )g_pFrameRoot;
a.mWorld=pFrame->CombinedTransformationMatrix;
Zdraw2(&a);
 
pFrame=(D3DXFRAME_DERIVED*)(pFrame->pFrameFirstChild);
a.mWorld=pFrame->CombinedTransformationMatrix;
Zdraw2(&a);
 
pFrame=(D3DXFRAME_DERIVED*)(pFrame->pFrameFirstChild);
a.mWorld=pFrame->CombinedTransformationMatrix;
Zdraw2(&a);
 
pFrame=(D3DXFRAME_DERIVED*)(pFrame->pFrameFirstChild);
a.mWorld=pFrame->CombinedTransformationMatrix;
Zdraw2(&a);
 
/*
pFrame=(D3DXFRAME_DERIVED*)(pFrame->pFrameSibling);
a.mWorld=pFrame->CombinedTransformationMatrix;
Zdraw2(&a);
*/
}
 
 
 
 
//  draws a spinning textured quad w/ alphatest, no cull, no lighting.
//  rotation is based on ET of timer #7
//  used to test drawing skinned meshes with regular meshes in the same 
//  beginscene - end scene block. this helped to determine what needs 
//  to be reset after drawing a skinned mesh.
void draw_hat2()
{
Zdrawinfo a;
Zcdi(&a);
a.texID=250;
a.sx=10.0f;
a.sy=10.0f;
a.sz=10.0f;
a.rx=pi/2.0f;
a.ry=(float)Zelapsedtime(7)/1000.0f;
a.y=5.0f;
a.alphatest=1;
Zdraw(&a);
}
 
 
 
 
 
 
//  draws a quad at the root of a bone (frame).
//  to draw a static mesh relative to the root of a bone, 
//  start with the transform matrix for the object relative to the root of the bone, 
//  then concatenate on the bone`s CombinedTransformationMatrix
//  don`t forget to call update_ani and add_world_transform_to_skeleton first!
//  use get_bone to get a D3DXFRAME pointer to the bone by name, and save it for 
//  later use when drawing meshes attached to that bone.
void draw_hat3(LPD3DXFRAME bone)
{
Zdrawinfo a;
//  clear the zdrawinfo struct. 
//  this sets it to drawtype:staticmesh, scale 1, meshID 0 (unit quad), texID 0 (grass tile 1), materialID 0 (Z3D default material), 
//  no cull, no clamp, no alphatest, and no translation or rotation...
Zcdi(&a);
D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )bone;
//  set the drawinfo`s world matrix to the combined transform matrix...
a.mWorld=pFrame->CombinedTransformationMatrix;
//  add the drawinfo to the render queue...
Zdraw2(&a);
}
 
 
 
 
 
 
 
 
//  draws eyeballs attached to bone with offset.
//  to draw a static mesh relative to the root of a bone, 
//  start with the transform matrix for the object relative to the root of the bone, 
//  then concatenate on the bone`s CombinedTransformationMatrix
//  don`t forget to call update_ani and add_world_transform_to_skeleton first!
//  use get_bone to get a D3DXFRAME pointer to the bone by name, and save it for 
//  later use when drawing meshes attached to that bone.
void draw_eyeballs(LPD3DXFRAME bone,int texID)
{
Zdrawinfo a;
Zcdi(&a);
a.meshID=8;
a.texID=texID;
a.cull=1;
D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )bone;
//  use matrix manipulator to create bone to right eyeball transform matrix...
Mstart();
Mscale(0.1f,0.1f,0.1f);
Mrotate(0,90);
Mrotate(1,320);
Mmove(-0.096f,1.55f,0.244f);
//  cat combined matrix onto matrix manipulator mat to get world mat...
D3DXMatrixMultiply(&a.mWorld,&Mmat,&pFrame->CombinedTransformationMatrix);
//  add to render queue...
Zdraw2(&a);
//  use matrix manipulator to create bone to left eyeball transform matrix...
Mstart();
Mscale(0.1f,0.1f,0.1f);
Mrotate(0,90);
Mrotate(1,320);
Mmove(0.102f,1.555f,0.206f);
//  cat combined matrix onto matrix manipulator mat to get world mat...
D3DXMatrixMultiply(&a.mWorld,&Mmat,&pFrame->CombinedTransformationMatrix);
//  add to render queue...
Zdraw2(&a);
}
 
 
 
 
 
 
 
 
 
 
 
void skinned_mesh_test()
{
LPD3DXFRAME g_pFrameRoot = NULL;      // ptr to root frame of the skeleton
LPD3DXFRAME bone1 = NULL;      // ptr to bone.001 of the skeleton
ID3DXAnimationController*   ani_controller_ptr = NULL;   // pointer to the animation cotroller
ID3DXAnimationController*   ani_controller_ptr2 = NULL;   // pointer to the animation cotroller
ID3DXAnimationController*   ani_controller_ptr3 = NULL;   // pointer to the animation cotroller
//  number of animations the mesh has
int num_anis;
int result,quit;
LPD3DXMESHCONTAINER p;
//  NOTE: have to put the * with the varname, NOT the type!
//  using: int* a,b,c;   a is an int pointer, but b and c are just ints!
//  using: int *a,*b,*c;  you get a,b,and c as int pointers, the desired result.
D3DXMESHCONTAINER_DERIVED *pBody, *pBra, *pLoincloth;
result=load_skinned_mesh("tiny.x",&ani_controller_ptr,&g_pFrameRoot);
switch (result)
    {
    case 1:
        msg3("error: invalid call!");
        break;
    case 2:
        msg3("error: out of memory!");
        break;
    case 3:
        msg3("error: setup bone matrix pointers");
        break;
    }
//  get number of anis for toggle ani...
num_anis=(int)ani_controller_ptr->GetNumAnimationSets();
 
//  get pointer to bone1 for demonstrating drawing a mesh attached to a bone...
//  note that blender calls it "bone.001" but exports it as "Armature_Bone_001".
bone1=get_bone(g_pFrameRoot,"Armature_Bone_001");
if (bone1 == NULL)
    {
    msg3("error: Armature_Bone_001 not found in g_pFrameRoot!");
    }
 
//  get pointer to body mesh to demonstrate interchangeable pooled textures for each mesh of a skinned mesh
p=get_mesh_container2(g_pFrameRoot,"body");
if (p == NULL)
    {
    msg3("error: body frame meshcontainer is null!");
    }
pBody=(D3DXMESHCONTAINER_DERIVED*)p;
p=get_mesh_container2(g_pFrameRoot,"bra");
if (p == NULL)
    {
    msg3("error: bra frame meshcontainer is null!");
    }
pBra=(D3DXMESHCONTAINER_DERIVED*)p;
p=get_mesh_container2(g_pFrameRoot,"loincloth");
if (p == NULL)
    {
    msg3("error: loincloth frame meshcontainer is null!");
    }
pLoincloth=(D3DXMESHCONTAINER_DERIVED*)p;
 
//  display animation set names, and number of animations in each set.
//  looks like an animation is the keyframes for one bone (IE: D3DXFRAME), 
//  and an animationset is all the keyframes for all the bones in a skeleton (IE: a D3DXFRAME hierarchy).
//  the number of animations in an animationset should match the 
//  number of animated bones (D3DXFRAMES) in the frame hierachy.
//  NOTE: looks like animation sets appear in the controller in reverse order of their order in the .x file!
//  apparently it does an "add at front" when it adds an animation set to the list of animation sets in the controller.
 
/*
 
int i;
//  i j
LPD3DXANIMATIONSET a;
const char *s=NULL;
char s2[100];
 
for (i=0; i<num_anis; i++)
    {
    ani_controller_ptr->GetAnimationSet((UINT)i,&a);
//  NOTE: looks like getname returns a pointer to an exisiting string, use it in a readonly manner, and do NOT free it!
    s=a->GetName();
    strcpy_s(s2,100,s);
    msg3(s2);
//     cr j (int)a->GetNumAnimations
//     c i2s j s2
//     c msg3 s2
//  NOTE: GetAnimationNameByIndex also appears to return a pointer to an exisiting string, 
//  the docs are somewhat unclear.
//  use it in a readonly manner, and do NOT free it!
    }
 
*/
 
 
 
 
//  clone ani controllers:
//  apparently, you want to load one skinned mesh with a "master" controller, 
//  then clone the master controller for each instance you want to draw. 
//  so each skinned mesh instance will have a controller, plus you`ll have the master,
//  which is just used for cloning, not animating.
//  never really found out why everyone does it this way, 
//  maybe that way you don`t have to reset everything in the clone, such as track position.
//  never tested using the master for both animating and cloning.
//  so ani_controller_ptr is the master, and ani_controller_ptr2 and 3 are for the two instances the test draws.
clone_controller(ani_controller_ptr,&ani_controller_ptr2);
//  init controller to play the 1st animation
set_ani(ani_controller_ptr2,0);
clone_controller(ani_controller_ptr,&ani_controller_ptr3);
//  init controller to play the 2nd animation
set_ani(ani_controller_ptr3,1);
//  set a directional light...
Zsetlite(0,0.0f,-1.0f,1.0f,1.0f);
//  turn it on...
Zlite(0,1);
//  set view matrix....
Zsetcam(0.0f,5.0f,-10.0f,0.0f,0.0f,0.0f);
quit=0;
//  timer 7 ET is used for the rotation of the quad in drawhat2
Zstarttimer(7);
//  set material to z3d default material....
Zsetmaterial(0);
AAturnon();
while (!quit)
    {
//  timer 8 is used for framerate limiter.
//  like Caveman 3.0, the test runs at 15fps. i`ve also run it at 60 fps no problem.
    Zstarttimer(8);
    Zclearscreen(32,0,32);
    Zclearzbuf();
    Zbeginscene();
//  clear render queue...
    Zcleardrawlist();
//  draw quad relative to bone1. this call adds the quad to the render queue...
    draw_hat3(bone1);
//  turn off ligting (makes the quad easier to see)...
    Zlighting(0);
//  render the render queue...
    Zdrawlist();
//  turn lighting back on
    Zlighting(1);
 
    /*
//  update ani controller for 1st skinned mesh...
    update_ani(ani_controller_ptr2,0.066);
//  init z3d matrix manipulator to identity matrix...
    Mstart();
//  concatenate on a traslation matrix...
    Mmove(-1.0f,0.0f,0.0f);
//  add 1st skinned mesh world transform to skeleton...
//  Mmat is the z3d matrix manipulator`s matrix 
    add_world_transform_to_skeleton(g_pFrameRoot,&Mmat);
    */
 
 
//  set textures for 1st skinned mesh...
//  c Zsettex 387
    pBody->texID=387;
    pBra->texID=159;
    pLoincloth->texID=159;
 
    /*
//  draw 1st skinned mesh...
    draw_skinned_mesh(g_pFrameRoot);
    */
 
//  init z3d matrix manipulator to identity matrix...
    Mstart();
//  concatenate on a traslation matrix...
    Mmove(-1.0f,0.0f,0.0f);
//  update and draw skinned mesh with one call...
    update_and_draw_skinned_mesh(ani_controller_ptr2,0.066,g_pFrameRoot,&Mmat);
 
//  drawmeshcontainer (all versions) changes the current mesh and fvf, must reset...
//  set z3d state manager current mesh to none...
    Zcurrentmesh=-1;
//  reset FVF back to z3d FVF...
    Zd3d_device_ptr->SetFVF(ZFVF);
 
//  ok, draw some eyeballs attached to the head bone (now that its updated)...
    draw_eyeballs(bone1,266);
 
 
 
    /*
//  update ani controller for 2nd skinned mesh...
    update_ani(ani_controller_ptr3,0.066);
//  init z3d matrix manipulator to identity matrix...
    Mstart();
//  concatenate on a traslation matrix...
    Mmove(1.0f,0.0f,0.0f);
//  add 2nd skinned mesh world transform to skeleton...
//  Mmat is the z3d matrix manipulator`s matrix 
    add_world_transform_to_skeleton(g_pFrameRoot,&Mmat);
    */
 
//  set textures for 2nd skinned mesh...
//     c Zsettex 388
    pBody->texID=388;
    pBra->texID=192;
    pLoincloth->texID=192;
 
    /*
//  draw 2nd skinned mesh...
    draw_skinned_mesh(g_pFrameRoot);
    */
 
//  init z3d matrix manipulator to identity matrix...
    Mstart();
//  concatenate on a traslation matrix...
    Mmove(1.0f,0.0f,0.0f);
//  update and draw skinned mesh with one call...
    update_and_draw_skinned_mesh(ani_controller_ptr3,0.066,g_pFrameRoot,&Mmat);
 
//  drawmeshcontainer (all versions) changes the current mesh and fvf, must reset...
//  set z3d state manager current mesh to none...
    Zcurrentmesh=-1;
//  reset FVF back to z3d FVF...
    Zd3d_device_ptr->SetFVF(ZFVF);
 
 
    tx(100,800,"press ESC to quit");
    tx(100,850,"press SPACE to change animation");
    AAdraw();
//  end scene and present...
    Zshowscene();
//  if they hit spacebar, toggle the 1st skinned mesh to the next animation...
    if (Zkeypressed(VK_SPACE))
        {
        change_ani(ani_controller_ptr2,num_anis);
        while (Zkeypressed(VK_SPACE))
            {
            }
        }
//  if they hit escape, quit the test...
    if (Zkeypressed(VK_ESCAPE))
        {
        quit=1;
        }
    AAprocess_input();
//  framerate limiter: wait until 66 millisconds has passed before beginning next frame...
    while (Zelapsedtime(8)<66)
        {
        }
    }
msg3("unloading stuff...");
AAturnoff();
//  unload the mesh and master controller...
unload_skinned_mesh(ani_controller_ptr,g_pFrameRoot);
//  release the clones...
ani_controller_ptr2->Release();
ani_controller_ptr3->Release();
msg3("all done!");
}
 
 
 
 
 
 
 
 
 
 
 
 
//  uses update_and_draw_skinned_mesh w/ drawmeshcontainer5
//  code to display animation set names has been removed
void skinned_mesh_test2()
{
LPD3DXFRAME g_pFrameRoot = NULL;      // ptr to root frame of the skeleton
LPD3DXFRAME bone1 = NULL;      // ptr to bone.001 of the skeleton
ID3DXAnimationController*   ani_controller_ptr = NULL;   // pointer to the animation cotroller
ID3DXAnimationController*   ani_controller_ptr2 = NULL;   // pointer to the animation cotroller
ID3DXAnimationController*   ani_controller_ptr3 = NULL;   // pointer to the animation cotroller
//  number of animations (animation sets) the mesh has
int num_anis;
int result,quit;
LPD3DXMESHCONTAINER p;
//  NOTE: have to put the * with the varname, NOT the type!
//  using: int* a,b,c;   a is an int pointer, but b and c are just ints!
//  using: int *a,*b,*c;  you get a,b,and c as int pointers, the desired result.
D3DXMESHCONTAINER_DERIVED *pBody, *pBra, *pLoincloth;
result=load_skinned_mesh("tiny.x",&ani_controller_ptr,&g_pFrameRoot);
switch (result)
    {
    case 1:
        msg3("error: invalid call!");
        break;
    case 2:
        msg3("error: out of memory!");
        break;
    case 3:
        msg3("error: setup bone matrix pointers");
        break;
    }
//  get number of anis for toggle ani...
num_anis=(int)ani_controller_ptr->GetNumAnimationSets();
 
//  get pointer to bone1 for demonstrating drawing a mesh attached to a bone...
//  note that blender calls it "bone.001" but exports it as "Armature_Bone_001".
bone1=get_bone(g_pFrameRoot,"Armature_Bone_001");
if (bone1 == NULL)
    {
    msg3("error: Armature_Bone_001 not found in g_pFrameRoot!");
    }
 
//  get pointer to body mesh to demonstrate interchangeable pooled textures for each mesh of a skinned mesh
p=get_mesh_container2(g_pFrameRoot,"body");
if (p == NULL)
    {
    msg3("error: body frame meshcontainer is null!");
    }
pBody=(D3DXMESHCONTAINER_DERIVED*)p;
p=get_mesh_container2(g_pFrameRoot,"bra");
if (p == NULL)
    {
    msg3("error: bra frame meshcontainer is null!");
    }
pBra=(D3DXMESHCONTAINER_DERIVED*)p;
p=get_mesh_container2(g_pFrameRoot,"loincloth");
if (p == NULL)
    {
    msg3("error: loincloth frame meshcontainer is null!");
    }
pLoincloth=(D3DXMESHCONTAINER_DERIVED*)p;
 
 
 
 
 
//  clone ani controllers:
//  apparently, you want to load one skinned mesh with a "master" controller, 
//  then clone the master controller for each instance you want to draw. 
//  so each skinned mesh instance will have a controller, plus you`ll have the master,
//  which is just used for cloning, not animating.
//  never really found out why everyone does it this way, 
//  maybe that way you don`t have to reset everything in the clone, such as track position.
//  never tested using the master for both animating and cloning.
//  so ani_controller_ptr is the master, and ani_controller_ptr2 and 3 are for the two instances the test draws.
clone_controller(ani_controller_ptr,&ani_controller_ptr2);
//  init controller to play the 1st animation
set_ani(ani_controller_ptr2,0);
clone_controller(ani_controller_ptr,&ani_controller_ptr3);
//  init controller to play the 2nd animation
set_ani(ani_controller_ptr3,1);
//  set a directional light...
Zsetlite(0,0.0f,-1.0f,1.0f,1.0f);
//  turn it on...
Zlite(0,1);
//  set view matrix....
Zsetcam(0.0f,5.0f,-10.0f,0.0f,0.0f,0.0f);
quit=0;
//  timer 7 ET is used for the rotation of the quad in drawhat2
Zstarttimer(7);
//  set material to z3d default material....
Zsetmaterial(0);
AAturnon();
while (!quit)
    {
//  timer 8 is used for framerate limiter.
//  like Caveman 3.0, the test runs at 15fps. i`ve also run it at 60 fps no problem.
    Zstarttimer(8);
    Zclearscreen(32,0,32);
    Zclearzbuf();
    Zbeginscene();
 
//  set textures for 1st skinned mesh...
    pBody->texID=387;
    pBra->texID=159;
    pLoincloth->texID=159;
 
//  init z3d matrix manipulator to identity matrix...
    Mstart();
//  concatenate on a traslation matrix...
    Mmove(-1.0f,0.0f,0.0f);
//  update and draw skinned mesh with one call...
    update_and_draw_skinned_mesh(ani_controller_ptr2,0.066,g_pFrameRoot,&Mmat);
 
//  drawmeshcontainer (all versions) changes the current mesh and fvf, must reset...
//  set z3d state manager current mesh to none...
    Zcurrentmesh=-1;
//  reset FVF back to z3d FVF...
    Zd3d_device_ptr->SetFVF(ZFVF);
 
//  clear render queue...
    Zcleardrawlist();
//  ok, draw some eyeballs attached to the head bone (now that its updated)...
//  this call adds them to the render queue.
    draw_eyeballs(bone1,266);
//  turn off ligting (makes the quad easier to see)...
    Zlighting(0);
//  render the render queue...
    Zdrawlist();
//  turn lighting back on
    Zlighting(1);
 
//  set textures for 2nd skinned mesh...
    pBody->texID=388;
    pBra->texID=192;
    pLoincloth->texID=192;
 
//  init z3d matrix manipulator to identity matrix...
    Mstart();
//  concatenate on a traslation matrix...
    Mmove(1.0f,0.0f,0.0f);
//  update and draw skinned mesh with one call...
    update_and_draw_skinned_mesh(ani_controller_ptr3,0.066,g_pFrameRoot,&Mmat);
 
//  drawmeshcontainer (all versions) changes the current mesh and fvf, must reset...
//  set z3d state manager current mesh to none...
    Zcurrentmesh=-1;
//  reset FVF back to z3d FVF...
    Zd3d_device_ptr->SetFVF(ZFVF);
 
//  clear render queue...
    Zcleardrawlist();
//  draw quad relative to bone1. this call adds the quad to the render queue...
    draw_hat3(bone1);
//  turn off ligting (makes the quad easier to see)...
    Zlighting(0);
//  render the render queue...
    Zdrawlist();
//  turn lighting back on
    Zlighting(1);
 
    tx(100,800,"press ESC to quit");
    tx(100,850,"press SPACE to change animation");
    AAdraw();
//  end scene and present...
    Zshowscene();
//  if they hit spacebar, toggle the 1st skinned mesh to the next animation...
    if (Zkeypressed(VK_SPACE))
        {
        change_ani(ani_controller_ptr2,num_anis);
        while (Zkeypressed(VK_SPACE))
            {
            }
        }
//  if they hit escape, quit the test...
    if (Zkeypressed(VK_ESCAPE))
        {
        quit=1;
        }
    AAprocess_input();
//  framerate limiter: wait until 66 millisconds has passed before beginning next frame...
    while (Zelapsedtime(8)<66)
        {
        }
    }
msg3("unloading stuff...");
AAturnoff();
//  unload the mesh and master controller...
unload_skinned_mesh(ani_controller_ptr,g_pFrameRoot);
//  release the clones...
ani_controller_ptr2->Release();
ani_controller_ptr3->Release();
msg3("all done!");
}
 
 
 
 
 
 
 
 
/*
skinned mesh info:
 
exporting from blender: 
1. link the 1st ani to the armature object. leave the rest unlinked with a fake user. or leave all unlinked with fake users.
   do not link animations to meshes - only link them to the skeleton! 
2. select the armature and mesh objects, then export selected objects.
3. delete the default_animation from the exported .x file with notepad. 
   not doing so seems to interfere with changing animations.  not true! xyz eulers arent exported!
4. export fails if more than 1 armature in the scene.
5. ALL animation sets are exported from startframe through  endframe.
   for animationsets with different lengths, they must be exported to different files, then
   paseted together with notepad.
6. only quaternion rotations are exported? xyz eulers dont get exported? apparently so!
7. apply modifiers (armature modifier) is not required.
8. export fps seems to be required. unable to determine default value of anim ticks per sec.
9. export default pose seems to not matter - not required.
 
 
 
 
 
 
 
possible optimizations:
1. different skinning methods. test each. 
   only nonindexed fixed function pure device hw vertex blending has been tested so far. 
   but theres only about 6 fps difference on my pc with tiny sample between 
   non-indexed fixed function hw vertex blending and indexed shader methods.
2. AnimationSet resource pooling.
3. body mesh resource pooling w/ interchangeable heads.
4. convert the static meshmaster and controller pools to dynamic memory (vectors, etc)
5. reorganize from PODs and procedural code into into oo classes. the meshmaster and controller pools would be classes.
   pretty much everything else would be methods in those classes or part of low level classes used by the meshmaster and 
   controller pool classes.
6. add ability to draw a mesh from a skinned mesh using the render queue
 
 
 
separate static mesh attached to a bone:
hair 
clothes - might need to be rigged to the skeleton
wpns & equipment - some might need to be rigged to the skeleton
 
different textures:
skintones
eye colors
 
different meshes:
single mesh for head and body. separate mesh for eyes.
 
faces (wide / narrow nose, eye shape, etc):   
separate heads can leave a seam. 
editing complete meshes is slightly less work (dont have to detach a head). 
therefore, caveman will use a single head and body mesh. 
this means: create a complete head and body mesh for each face.
 
 
 
setting texture on a per mesh basis vs not at all makes no difference in fps when drawing 
100 meshes. the bottleneck is the number of tris, not setting textures.
 
sharing the same animation between two different skinned meshes:
1. create, rig, and animate the first skinned mesh. save and export.
2. delete the mesh, leaving the animated rig.
3. append the second mesh (load it into the existing scene)
4. rig the mesh. save and export. all done!
 
skinned mesh LOD:
1. create and export mesh as usual
2. decimate mesh, then export again.
3. load both meshes
4. cm or npc now has two meshIDs, not one. one high LOD, one low LOD.
5. when its time to draw, decide hi or low LOD based on distance. 
6. if the controller`s mesh is not the LOD you want, get the current animation time 
   from the controller, destroy the mesh instance, create a mesh instance with the 
   correct LOD mesh, then set the new controllers time to the animation time you got 
   from the old controller. this way the animation continues from where it was.
7. update and draw as usual.
 
 
 
adapting all this for use in caveman:
every skinned mesh loaded will have a "master" animation controller to go with it.
so you'll have a struct that holds a skinned mesh, and its master animation controller.
and you'll have a second type of struct for an instance of a skinned mesh, 
with a cloned animation controller, and a pointer to the skinned mesh to use.
then you'll have an array of each of these structs.
and a load_mesh routine that loads a mesh into the array of skinned meshes & master controllers.
and a activate_controller routine that activates a struct in the array of cloned controllers, 
clones the controller, and sets the skinned mesh pointer (g_pFrameRoot).
then you'll have a draw_instance routine that draws the skinned mesh using a cloned controller 
and its skinned mesh pointer.
and you'll need a deactivate_controller routine
and an unload_mesh routine
a skinned mesh struct should also hold a pointer to the head bone for drawing hair and eyes
attached to the head bone, and pointers to the various meshcontainers (body, bra, loincloth, etc)
for setting textures on the fly.
 
 
*/
 
 



Skinned mesh code part 1 - Low level Microsoft code

Posted by , 30 June 2015 - - - - - - · 930 views

skinned mesh code part 1

low level Microsoft code from DXUT.h and tiny.cpp. June 2010 DX9.0c SDK.
//  -------------------------------------------------------------------------------
//  ----------------------------- directx skinned mesh code -----------------------
//  -------------------------------------------------------------------------------
 
//  CONTENTS:
//  section 1: description of the code
//  section 2: a brief outline of how to use the code
//  section 3: low level MS code
//  section 4: high level ROCKAND skinned mesh API
//  section 5: skinned mesh test routine sample code
//  section 6: notes on working with skinned meshes
 
 
 
//  this code is based on the tiny.cpp sample from june 2010 dx sdk.
 
//  it allows easy integration of skinned meshes into existing dx9 apps.
 
//  it consists of two parts:
 
//  1. low level code copied directly from tiny.cpp that loads and unloads a mesh, 
//  and updates the skeleton with the meshe`s world matrix. this code has been 
//  modified to use ansi strings, dx9 matrices, and to supress compiler warnings
//  (one unused parameter, and one non-explicit cast).
//  its also been modified to only search the current directory for files.
//  for dx10, unicode, and DXUT path searching, just use the corresponding original 
//  tiny.cpp code instead.
//  the unicode and DXUT path searching has only been commented out, 
//  and can easily be turned back on if desired.
//  replacing D3DXMATRIX with D3DXMATRIX16A will convert it back to dx10 matrices.
 
//  2. a high level API written by ROCKLAND, based on the tiny.cpp and DXUT code.
//  this API handles loading and unloading effects and shaders, getting device caps,
//  cloning and updating controllers, setting up shader parameters for drawing a 
//  skinned mesh, adding the skinned mesh`s world matrix to the skeleton, 
//  drawing the mesh, and changing animations. it also provides a routine to get 
//  a pointer to a bone by name for later drawing of meshes attached to that bone.
 
 
//  this code includes all sample skinned mesh and DXUT code required - macros, struct 
//  definitions, etc. no DXUT required! #include <d3dx9.h> should be all thats required.
 
//  note that its not based on the multi-animation sample, so it does not have a
//  class definition for animated skinned mesh instances. an animated skinned mesh 
//  instance basically consists of the instance`s cloned controller, the instance`s 
//  world matrix, and a pointer to the root frame of the skeleton used by the instance.
//  you might want to add the instance`s texture and material if you use the
//  drawmeshcontainer4 routine, which allows materials and textures on a per
//  instance basis.
 
//  in some areas, debug version only code might be commented out. 
//  uncomment it if you`re compiling in debug mode.
 
//  note that this code can easily be turned into a stand alone module with its own 
//  header file. i simply haven`t gotten around to it yet.
//  just copy the declarations for the ROCKLAND skinned mesh API into a header file,
//  turn off the test routine, and replace any Z3d specifc variable names (IE the device 
//  pointer, the view matrix, and the projection matrix).
//  so while its not a turnkey library yet, it has everything you need to make one easily.
 
 
 
 
 
 
 
/*
 
 
a basic outline of how to use the high level ROCKLAND skinned mesh API:
 
//  init stuff...
get_indexed_behavior_flags   // only required for indexed skinning methods
load_skinned_mesh
load_effect_file     // only required for shader based skinning methods
load_shaders        // only required for shader based skinning methods
clone_cntroller    // optional - for multiple instances of a skinned mesh, each with it own animation.
get_bone            // optional - get pointer to a bone by name, used to draw meshes attached to a bone.
//  render loop...
while !quit
//  set the light for the fixed function pipeline. IE: Zsetlite and Zlite in z3d
//  set the view matrix. IE: Zsetcam in z3d
//  setup for drawing skinned mesh...
    set_D3DINDEXEDVS_proj_mat_transpose  // only required for D3DINDEXEDVS skinning method. 
                                        // must be called before set_indexed_shader_proj_mat.
    set_indexed_shader_proj_mat     // only required for indexed shaders. not required for D3DINDEXED skinning method.
    set_shader_light   // only required for shader based skinning methods
    set_ani         // optional: change to a specific animation first
//  update the animation controller...
    update_ani 
//  add the world transform for the skinned mesh to the skeleton...
    add_world_transform_to_skeleton
//  draw it!
    draw_skinned_mesh
//  drawsubset changes the current mesh and fvf! dont forget to reset them before regular drawing!
//  reset fvf and your state manager`s current mesh here, then draw as usual.
//  draw some non-skinned meshes here...
}
//  shutdown...
unload_shaders      // only required for shader based skinning methods
unload_effect_file      // only required for shader based skinning methods
unload_skinned_mesh
//  release any cloned controllers!
 
 
*/
 
 
 
 
//  ----------------------------- LOW LEVEL MS CODE ------------------------------
 
//  ---------------------- DXUT.h macros -----------------------------------------
 
#if defined(DEBUG) || defined(_DEBUG)
#ifndef V
#define V(x)           { hr = (x); if( FAILED(hr) ) { DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, true ); } }
#endif
#ifndef V_RETURN
#define V_RETURN(x)    { hr = (x); if( FAILED(hr) ) { return DXUTTrace( __FILE__, (DWORD)__LINE__, hr, L#x, true ); } }
#endif
#else
#ifndef V
#define V(x)           { hr = (x); }
#endif
#ifndef V_RETURN
#define V_RETURN(x)    { hr = (x); if( FAILED(hr) ) { return hr; } }
#endif
#endif
 
 
 
#ifndef SAFE_DELETE
#define SAFE_DELETE(p)       { if (p) { delete (p);     (p)=NULL; } }
#endif    
 
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p);   (p)=NULL; } }
#endif    
 
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p)      { if (p) { (p)->Release(); (p)=NULL; } }
#endif
 
 
 
 
 
 
//  ------------------------------- tiny.cpp code ------------------------------------
 
//--------------------------------------------------------------------------------------
// Name: struct D3DXFRAME_DERIVED
// Desc: Structure derived from D3DXFRAME so we can add some app-specific
//       info that will be stored with each frame
//--------------------------------------------------------------------------------------
struct D3DXFRAME_DERIVED : public D3DXFRAME
{
    D3DXMATRIX CombinedTransformationMatrix;
};
 
 
//--------------------------------------------------------------------------------------
// Name: struct D3DXMESHCONTAINER_DERIVED
// Desc: Structure derived from D3DXMESHCONTAINER so we can add some app-specific
//       info that will be stored with each mesh
//--------------------------------------------------------------------------------------
struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER
{
    LPDIRECT3DTEXTURE9* ppTextures;       // array of textures, entries are NULL if no texture specified    
 
    // SkinMesh info             
    LPD3DXMESH pOrigMesh;
    LPD3DXATTRIBUTERANGE pAttributeTable;
    DWORD NumAttributeGroups;
    DWORD NumInfl;
    LPD3DXBUFFER pBoneCombinationBuf;
    D3DXMATRIX** ppBoneMatrixPtrs;
    D3DXMATRIX* pBoneOffsetMatrices;
    DWORD NumPaletteEntries;
    bool UseSoftwareVP;
    DWORD iAttributeSW;     // used to denote the split between SW and HW if necessary for non-indexed skinning
    int texID;
};
 
 
 
 
// enum for various skinning modes possible
enum METHOD
{
    D3DNONINDEXED,
    D3DINDEXED,
    SOFTWARE,
    D3DINDEXEDVS,
    D3DINDEXEDHLSLVS,
    NONE
};
 
 
 
 
 
 
//--------------------------------------------------------------------------------------
// Global variables
//--------------------------------------------------------------------------------------
 
METHOD                      g_SkinningMethod = D3DNONINDEXED; // Current skinning method
 
bool                        g_bUseSoftwareVP;       // Flag to indicate whether software vp is
                                                    // required due to lack of hardware
 
UINT                        g_NumBoneMatricesMax = 0;
 
D3DXMATRIX                  *g_pBoneMatrices = NULL;
 
D3DXMATRIX                  g_matProjT;             // Transpose of projection matrix (for asm shader)
 
ID3DXEffect*                g_pEffect = NULL;       // D3DX effect interface
 
DWORD                       g_dwBehaviorFlags;      // Behavior flags of the 3D device
 
IDirect3DVertexShader9*     g_pIndexedVertexShader[4];   // array of four vertex shaders
 
 
char g_wszShaderSource[4][30] =
{
    "skinmesh1.vsh",
    "skinmesh2.vsh",
    "skinmesh3.vsh",
    "skinmesh4.vsh"
};
 
 
/*
WCHAR g_wszShaderSource[4][30] =
{
    L"skinmesh1.vsh",
    L"skinmesh2.vsh",
    L"skinmesh3.vsh",
    L"skinmesh4.vsh"
};
*/
 
 
 
 
 
 
 
 
//--------------------------------------------------------------------------------------
// Called either by CreateMeshContainer when loading a skin mesh, or when 
// changing methods.  This function uses the pSkinInfo of the mesh 
// container to generate the desired drawable mesh and bone combination 
// table.
//--------------------------------------------------------------------------------------
HRESULT GenerateSkinnedMesh( IDirect3DDevice9* pd3dDevice, D3DXMESHCONTAINER_DERIVED* pMeshContainer )
{
    HRESULT hr = S_OK;
    D3DCAPS9 d3dCaps;
    pd3dDevice->GetDeviceCaps( &d3dCaps );
 
    if( pMeshContainer->pSkinInfo == NULL )
        return hr;
 
    g_bUseSoftwareVP = false;
 
    SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
    SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
 
    // if non-indexed skinning mode selected, use ConvertToBlendedMesh to generate drawable mesh
    if( g_SkinningMethod == D3DNONINDEXED )
    {
 
        hr = pMeshContainer->pSkinInfo->ConvertToBlendedMesh
            (
            pMeshContainer->pOrigMesh,
            D3DXMESH_MANAGED | D3DXMESHOPT_VERTEXCACHE,
            pMeshContainer->pAdjacency,
            NULL, NULL, NULL,
            &pMeshContainer->NumInfl,
            &pMeshContainer->NumAttributeGroups,
            &pMeshContainer->pBoneCombinationBuf,
            &pMeshContainer->MeshData.pMesh
            );
        if( FAILED( hr ) )
            goto e_Exit;
 
 
        // If the device can only do 2 matrix blends, ConvertToBlendedMesh cannot approximate all meshes to it
        // Thus we split the mesh in two parts: The part that uses at most 2 matrices and the rest. The first is
        // drawn using the device's HW vertex processing and the rest is drawn using SW vertex processing.
        LPD3DXBONECOMBINATION rgBoneCombinations = reinterpret_cast<LPD3DXBONECOMBINATION>(
            pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
 
        // look for any set of bone combinations that do not fit the caps
        for( pMeshContainer->iAttributeSW = 0; pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups;
             pMeshContainer->iAttributeSW++ )
        {
            DWORD cInfl = 0;
 
            for( DWORD iInfl = 0; iInfl < pMeshContainer->NumInfl; iInfl++ )
            {
                if( rgBoneCombinations[pMeshContainer->iAttributeSW].BoneId[iInfl] != UINT_MAX )
                {
                    ++cInfl;
                }
            }
 
            if( cInfl > d3dCaps.MaxVertexBlendMatrices )
            {
                break;
            }
        }
 
        // if there is both HW and SW, add the Software Processing flag
        if( pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups )
        {
            LPD3DXMESH pMeshTmp;
 
            hr = pMeshContainer->MeshData.pMesh->CloneMeshFVF( D3DXMESH_SOFTWAREPROCESSING |
                                                               pMeshContainer->MeshData.pMesh->GetOptions(),
                                                               pMeshContainer->MeshData.pMesh->GetFVF(),
                                                               pd3dDevice, &pMeshTmp );
            if( FAILED( hr ) )
            {
                goto e_Exit;
            }
 
            pMeshContainer->MeshData.pMesh->Release();
            pMeshContainer->MeshData.pMesh = pMeshTmp;
            pMeshTmp = NULL;
        }
    }
        // if indexed skinning mode selected, use ConvertToIndexedsBlendedMesh to generate drawable mesh
    else if( g_SkinningMethod == D3DINDEXED )
    {
        DWORD NumMaxFaceInfl;
        DWORD Flags = D3DXMESHOPT_VERTEXCACHE;
 
        LPDIRECT3DINDEXBUFFER9 pIB;
        hr = pMeshContainer->pOrigMesh->GetIndexBuffer( &pIB );
        if( FAILED( hr ) )
            goto e_Exit;
 
        hr = pMeshContainer->pSkinInfo->GetMaxFaceInfluences( pIB,
                                                              pMeshContainer->pOrigMesh->GetNumFaces(),
                                                              &NumMaxFaceInfl );
        pIB->Release();
        if( FAILED( hr ) )
            goto e_Exit;
 
        // 12 entry palette guarantees that any triangle (4 independent influences per vertex of a tri)
        // can be handled
        NumMaxFaceInfl = min( NumMaxFaceInfl, 12 );
 
        if( d3dCaps.MaxVertexBlendMatrixIndex + 1 < NumMaxFaceInfl )
        {
            // HW does not support indexed vertex blending. Use SW instead
            pMeshContainer->NumPaletteEntries = min( 256, pMeshContainer->pSkinInfo->GetNumBones() );
            pMeshContainer->UseSoftwareVP = true;
            g_bUseSoftwareVP = true;
            Flags |= D3DXMESH_SYSTEMMEM;
        }
        else
        {
            // using hardware - determine palette size from caps and number of bones
            // If normals are present in the vertex data that needs to be blended for lighting, then 
            // the number of matrices is half the number specified by MaxVertexBlendMatrixIndex.
            pMeshContainer->NumPaletteEntries = min( ( d3dCaps.MaxVertexBlendMatrixIndex + 1 ) / 2,
                                                     pMeshContainer->pSkinInfo->GetNumBones() );
            pMeshContainer->UseSoftwareVP = false;
            Flags |= D3DXMESH_MANAGED;
        }
 
        hr = pMeshContainer->pSkinInfo->ConvertToIndexedBlendedMesh
            (
            pMeshContainer->pOrigMesh,
            Flags,
            pMeshContainer->NumPaletteEntries,
            pMeshContainer->pAdjacency,
            NULL, NULL, NULL,
            &pMeshContainer->NumInfl,
            &pMeshContainer->NumAttributeGroups,
            &pMeshContainer->pBoneCombinationBuf,
            &pMeshContainer->MeshData.pMesh );
        if( FAILED( hr ) )
            goto e_Exit;
    }
        // if vertex shader indexed skinning mode selected, use ConvertToIndexedsBlendedMesh to generate drawable mesh
    else if( ( g_SkinningMethod == D3DINDEXEDVS ) || ( g_SkinningMethod == D3DINDEXEDHLSLVS ) )
    {
        // Get palette size
        // First 9 constants are used for other data.  Each 4x3 matrix takes up 3 constants.
        // (96 - 9) /3 i.e. Maximum constant count - used constants 
        UINT MaxMatrices = 26;
        pMeshContainer->NumPaletteEntries = min( MaxMatrices, pMeshContainer->pSkinInfo->GetNumBones() );
 
        DWORD Flags = D3DXMESHOPT_VERTEXCACHE;
        if( d3dCaps.VertexShaderVersion >= D3DVS_VERSION( 1, 1 ) )
        {
            pMeshContainer->UseSoftwareVP = false;
            Flags |= D3DXMESH_MANAGED;
        }
        else
        {
            pMeshContainer->UseSoftwareVP = true;
            g_bUseSoftwareVP = true;
            Flags |= D3DXMESH_SYSTEMMEM;
        }
 
        SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
 
        hr = pMeshContainer->pSkinInfo->ConvertToIndexedBlendedMesh
            (
            pMeshContainer->pOrigMesh,
            Flags,
            pMeshContainer->NumPaletteEntries,
            pMeshContainer->pAdjacency,
            NULL, NULL, NULL,
            &pMeshContainer->NumInfl,
            &pMeshContainer->NumAttributeGroups,
            &pMeshContainer->pBoneCombinationBuf,
            &pMeshContainer->MeshData.pMesh );
        if( FAILED( hr ) )
            goto e_Exit;
 
 
        // FVF has to match our declarator. Vertex shaders are not as forgiving as FF pipeline
        DWORD NewFVF = ( pMeshContainer->MeshData.pMesh->GetFVF() & D3DFVF_POSITION_MASK ) | D3DFVF_NORMAL |
            D3DFVF_TEX1 | D3DFVF_LASTBETA_UBYTE4;
        if( NewFVF != pMeshContainer->MeshData.pMesh->GetFVF() )
        {
            LPD3DXMESH pMesh;
            hr = pMeshContainer->MeshData.pMesh->CloneMeshFVF( pMeshContainer->MeshData.pMesh->GetOptions(), NewFVF,
                                                               pd3dDevice, &pMesh );
            if( !FAILED( hr ) )
            {
                pMeshContainer->MeshData.pMesh->Release();
                pMeshContainer->MeshData.pMesh = pMesh;
                pMesh = NULL;
            }
        }
 
        D3DVERTEXELEMENT9 pDecl[MAX_FVF_DECL_SIZE];
        LPD3DVERTEXELEMENT9 pDeclCur;
        hr = pMeshContainer->MeshData.pMesh->GetDeclaration( pDecl );
        if( FAILED( hr ) )
            goto e_Exit;
 
        // the vertex shader is expecting to interpret the UBYTE4 as a D3DCOLOR, so update the type 
        //   NOTE: this cannot be done with CloneMesh, that would convert the UBYTE4 data to float and then to D3DCOLOR
        //          this is more of a "cast" operation
        pDeclCur = pDecl;
        while( pDeclCur->Stream != 0xff )
        {
            if( ( pDeclCur->Usage == D3DDECLUSAGE_BLENDINDICES ) && ( pDeclCur->UsageIndex == 0 ) )
                pDeclCur->Type = D3DDECLTYPE_D3DCOLOR;
            pDeclCur++;
        }
 
        hr = pMeshContainer->MeshData.pMesh->UpdateSemantics( pDecl );
        if( FAILED( hr ) )
            goto e_Exit;
 
        // allocate a buffer for bone matrices, but only if another mesh has not allocated one of the same size or larger
        if( g_NumBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones() )
        {
            g_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones();
 
            // Allocate space for blend matrices
            delete[] g_pBoneMatrices;
            g_pBoneMatrices = new D3DXMATRIX[g_NumBoneMatricesMax];
            if( g_pBoneMatrices == NULL )
            {
                hr = E_OUTOFMEMORY;
                goto e_Exit;
            }
        }
 
    }
        // if software skinning selected, use GenerateSkinnedMesh to create a mesh that can be used with UpdateSkinnedMesh
    else if( g_SkinningMethod == SOFTWARE )
    {
        hr = pMeshContainer->pOrigMesh->CloneMeshFVF( D3DXMESH_MANAGED, pMeshContainer->pOrigMesh->GetFVF(),
                                                      pd3dDevice, &pMeshContainer->MeshData.pMesh );
        if( FAILED( hr ) )
            goto e_Exit;
 
        hr = pMeshContainer->MeshData.pMesh->GetAttributeTable( NULL, &pMeshContainer->NumAttributeGroups );
        if( FAILED( hr ) )
            goto e_Exit;
 
        delete[] pMeshContainer->pAttributeTable;
        pMeshContainer->pAttributeTable = new D3DXATTRIBUTERANGE[pMeshContainer->NumAttributeGroups];
        if( pMeshContainer->pAttributeTable == NULL )
        {
            hr = E_OUTOFMEMORY;
            goto e_Exit;
        }
 
        hr = pMeshContainer->MeshData.pMesh->GetAttributeTable( pMeshContainer->pAttributeTable, NULL );
        if( FAILED( hr ) )
            goto e_Exit;
 
        // allocate a buffer for bone matrices, but only if another mesh has not allocated one of the same size or larger
        if( g_NumBoneMatricesMax < pMeshContainer->pSkinInfo->GetNumBones() )
        {
            g_NumBoneMatricesMax = pMeshContainer->pSkinInfo->GetNumBones();
 
            // Allocate space for blend matrices
            delete[] g_pBoneMatrices;
            g_pBoneMatrices = new D3DXMATRIX[g_NumBoneMatricesMax];
            if( g_pBoneMatrices == NULL )
            {
                hr = E_OUTOFMEMORY;
                goto e_Exit;
            }
        }
    }
    else  // invalid g_SkinningMethod value
    {
        // return failure due to invalid skinning method value
        hr = E_INVALIDARG;
        goto e_Exit;
    }
 
e_Exit:
    return hr;
}
 
 
 
 
 
 
 
 
 
 
 
//--------------------------------------------------------------------------------------
// Name: class CAllocateHierarchy
// Desc: Custom version of ID3DXAllocateHierarchy with custom methods to create
//       frames and meshcontainers.
//--------------------------------------------------------------------------------------
class CAllocateHierarchy : public ID3DXAllocateHierarchy
{
public:
    STDMETHOD( CreateFrame )( THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame );
    STDMETHOD( CreateMeshContainer )( THIS_
        LPCSTR Name,
        CONST D3DXMESHDATA *pMeshData,
        CONST D3DXMATERIAL *pMaterials,
        CONST D3DXEFFECTINSTANCE *pEffectInstances,
        DWORD NumMaterials,
        CONST DWORD *pAdjacency,
        LPD3DXSKININFO pSkinInfo,
        LPD3DXMESHCONTAINER *ppNewMeshContainer );
    STDMETHOD( DestroyFrame )( THIS_ LPD3DXFRAME pFrameToFree );
    STDMETHOD( DestroyMeshContainer )( THIS_ LPD3DXMESHCONTAINER pMeshContainerBase );
 
    CAllocateHierarchy()
    {
    }
};
 
 
 
 
 
 
 
 
 
 
//--------------------------------------------------------------------------------------
// Name: AllocateName()
// Desc: Allocates memory for a string to hold the name of a frame or mesh
//--------------------------------------------------------------------------------------
HRESULT AllocateName( LPCSTR Name, LPSTR* pNewName )
{
    UINT cbLength;
 
    if( Name != NULL )
    {
        cbLength = ( UINT )strlen( Name ) + 1;
        *pNewName = new CHAR[cbLength];
        if( *pNewName == NULL )
            return E_OUTOFMEMORY;
        memcpy( *pNewName, Name, cbLength * sizeof( CHAR ) );
    }
    else
    {
        *pNewName = NULL;
    }
 
    return S_OK;
}
 
 
 
 
//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateFrame()
// Desc: 
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::CreateFrame( LPCSTR Name, LPD3DXFRAME* ppNewFrame )
{
    HRESULT hr = S_OK;
    D3DXFRAME_DERIVED* pFrame;
 
    *ppNewFrame = NULL;
 
    pFrame = new D3DXFRAME_DERIVED;
    if( pFrame == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }
 
    hr = AllocateName( Name, &pFrame->Name );
    if( FAILED( hr ) )
        goto e_Exit;
 
    // initialize other data members of the frame
    D3DXMatrixIdentity( &pFrame->TransformationMatrix );
    D3DXMatrixIdentity( &pFrame->CombinedTransformationMatrix );
 
    pFrame->pMeshContainer = NULL;
    pFrame->pFrameSibling = NULL;
    pFrame->pFrameFirstChild = NULL;
 
    *ppNewFrame = pFrame;
    pFrame = NULL;
 
e_Exit:
    delete pFrame;
    return hr;
}
 
 
 
 
//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::CreateMeshContainer()
// Desc: 
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::CreateMeshContainer(
    LPCSTR Name,
    CONST D3DXMESHDATA *pMeshData,
    CONST D3DXMATERIAL *pMaterials,
    CONST D3DXEFFECTINSTANCE *pEffectInstances,
    DWORD NumMaterials,
    CONST DWORD *pAdjacency,
    LPD3DXSKININFO pSkinInfo,
    LPD3DXMESHCONTAINER *ppNewMeshContainer )
 {
    HRESULT hr;
    D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL;
    UINT NumFaces;
    UINT iMaterial;
    UINT iBone, cBones;
    LPDIRECT3DDEVICE9 pd3dDevice = NULL;
 
    LPD3DXMESH pMesh = NULL;
 
    *ppNewMeshContainer = NULL;
 
//  ROCKLAND NOTE:
//  the pEffectInstances  parameter isnt used in the code, causing a warning!
//  so we need to use it to supress the warning.
//  pEffectInstances is  CONST, so we cant just set it to NULL.
//  we have to assign its value to another variable.
//  so we define another D3DXEFFECTINSTANCE pointer, and set it equal to pEffectInstances.
CONST D3DXEFFECTINSTANCE *tmp;
tmp=pEffectInstances;
 
    // this sample does not handle patch meshes, so fail when one is found
    if( pMeshData->Type != D3DXMESHTYPE_MESH )
 {
        hr = E_FAIL;
        goto e_Exit;
    }
 
    // get the pMesh interface pointer out of the mesh data structure
    pMesh = pMeshData->pMesh;
 
    // this sample does not FVF compatible meshes, so fail when one is found
    if( pMesh->GetFVF() == 0 )
 {
        hr = E_FAIL;
        goto e_Exit;
    }
 
    // allocate the overloaded structure to return as a D3DXMESHCONTAINER
    pMeshContainer = new D3DXMESHCONTAINER_DERIVED;
    if( pMeshContainer == NULL )
 {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }
    memset( pMeshContainer, 0, sizeof( D3DXMESHCONTAINER_DERIVED ) );
 
    // make sure and copy the name.  All memory as input belongs to caller, interfaces can be addref'd though
    hr = AllocateName( Name, &pMeshContainer->Name );
    if( FAILED( hr ) )
        goto e_Exit;
 
    pMesh->GetDevice( &pd3dDevice );
    NumFaces = pMesh->GetNumFaces();
 
    // if no normals are in the mesh, add them
    if( !( pMesh->GetFVF() & D3DFVF_NORMAL ) )
 {
        pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
 
    // clone the mesh to make room for the normals
        hr = pMesh->CloneMeshFVF( pMesh->GetOptions(),
                                    pMesh->GetFVF() | D3DFVF_NORMAL,
                                    pd3dDevice, &pMeshContainer->MeshData.pMesh );
        if( FAILED( hr ) )
            goto e_Exit;
 
    // get the new pMesh pointer back out of the mesh container to use
    // NOTE: we do not release pMesh because we do not have a reference to it yet
        pMesh = pMeshContainer->MeshData.pMesh;
 
    // now generate the normals for the pmesh
        D3DXComputeNormals( pMesh, NULL );
    }
    else  // if no normals, just add a reference to the mesh for the mesh container
 {
        pMeshContainer->MeshData.pMesh = pMesh;
        pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
 
        pMesh->AddRef();
    }
 
    // allocate memory to contain the material information.  This sample uses
    //   the D3D9 materials and texture names instead of the EffectInstance style materials
    pMeshContainer->NumMaterials = max( 1, NumMaterials );
    pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
    pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];
    pMeshContainer->pAdjacency = new DWORD[NumFaces*3];
    if( ( pMeshContainer->pAdjacency == NULL ) || ( pMeshContainer->pMaterials == NULL ) )
 {
        hr = E_OUTOFMEMORY;
        goto e_Exit;
    }
 
    memcpy( pMeshContainer->pAdjacency, pAdjacency, sizeof( DWORD ) * NumFaces*3 );
    memset( pMeshContainer->ppTextures, 0, sizeof( LPDIRECT3DTEXTURE9 ) * pMeshContainer->NumMaterials );
 
    // if materials provided, copy them
    if( NumMaterials > 0 )
 {
        memcpy( pMeshContainer->pMaterials, pMaterials, sizeof( D3DXMATERIAL ) * NumMaterials );
 
        for( iMaterial = 0; iMaterial < NumMaterials; iMaterial++ )
 {
            if( pMeshContainer->pMaterials[iMaterial].pTextureFilename != NULL )
 {
//  ROCKLAND NOTE: caveman uses ansi strings, not unicode.
//  so this code must be changed from unicode to ansi.
 
//                 WCHAR strTexturePath[MAX_PATH];
//                 WCHAR wszBuf[MAX_PATH];
//                 MultiByteToWideChar( CP_ACP, 0, pMeshContainer->pMaterials[iMaterial].pTextureFilename, -1, wszBuf, MAX_PATH );
//                 wszBuf[MAX_PATH - 1] = L'\0';
//                 DXUTFindDXSDKMediaFileCch( strTexturePath, MAX_PATH, wszBuf );
//                 if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, strTexturePath,
//                                                         &pMeshContainer->ppTextures[iMaterial] ) ) )
 
                 if( FAILED( D3DXCreateTextureFromFile( pd3dDevice, 
                                pMeshContainer->pMaterials[iMaterial].pTextureFilename,
                                &pMeshContainer->ppTextures[iMaterial] ) ) )
 
                    pMeshContainer->ppTextures[iMaterial] = NULL;
 
    // don't remember a pointer into the dynamic memory, just forget the name after loading
                pMeshContainer->pMaterials[iMaterial].pTextureFilename = NULL;
            }
        }
    }
    else // if no materials provided, use a default one
 {
        pMeshContainer->pMaterials[0].pTextureFilename = NULL;
        memset( &pMeshContainer->pMaterials[0].MatD3D, 0, sizeof( D3DMATERIAL9 ) );
        pMeshContainer->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
        pMeshContainer->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
        pMeshContainer->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
        pMeshContainer->pMaterials[0].MatD3D.Specular = pMeshContainer->pMaterials[0].MatD3D.Diffuse;
    }
 
    // if there is skinning information, save off the required data and then setup for HW skinning
    if( pSkinInfo != NULL )
 {
    // first save off the SkinInfo and original mesh data
        pMeshContainer->pSkinInfo = pSkinInfo;
        pSkinInfo->AddRef();
 
        pMeshContainer->pOrigMesh = pMesh;
        pMesh->AddRef();
 
    // Will need an array of offset matrices to move the vertices from the figure space to the bone's space
        cBones = pSkinInfo->GetNumBones();
        pMeshContainer->pBoneOffsetMatrices = new D3DXMATRIX[cBones];
        if( pMeshContainer->pBoneOffsetMatrices == NULL )
 {
            hr = E_OUTOFMEMORY;
            goto e_Exit;
        }
 
    // get each of the bone offset matrices so that we don't need to get them later
        for( iBone = 0; iBone < cBones; iBone++ )
 {
            pMeshContainer->pBoneOffsetMatrices[iBone] = *( pMeshContainer->pSkinInfo->GetBoneOffsetMatrix( iBone ) );
        }
 
    // GenerateSkinnedMesh will take the general skinning information and transform it to a HW friendly version
        hr = GenerateSkinnedMesh( pd3dDevice, pMeshContainer );
        if( FAILED( hr ) )
            goto e_Exit;
    }
 
    *ppNewMeshContainer = pMeshContainer;
    pMeshContainer = NULL;
 
e_Exit:
    SAFE_RELEASE( pd3dDevice );
 
    // call Destroy function to properly clean up the memory allocated 
    if( pMeshContainer != NULL )
 {
        DestroyMeshContainer( pMeshContainer );
    }
 
    return hr;
}
 
 
 
 
//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyFrame()
// Desc: 
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyFrame( LPD3DXFRAME pFrameToFree )
{
    SAFE_DELETE_ARRAY( pFrameToFree->Name );
    SAFE_DELETE( pFrameToFree );
    return S_OK;
}
 
 
 
 
//--------------------------------------------------------------------------------------
// Name: CAllocateHierarchy::DestroyMeshContainer()
// Desc: 
//--------------------------------------------------------------------------------------
HRESULT CAllocateHierarchy::DestroyMeshContainer( LPD3DXMESHCONTAINER pMeshContainerBase )
{
    UINT iMaterial;
    D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
 
    SAFE_DELETE_ARRAY( pMeshContainer->Name );
    SAFE_DELETE_ARRAY( pMeshContainer->pAdjacency );
    SAFE_DELETE_ARRAY( pMeshContainer->pMaterials );
    SAFE_DELETE_ARRAY( pMeshContainer->pBoneOffsetMatrices );
 
    // release all the allocated textures
    if( pMeshContainer->ppTextures != NULL )
    {
        for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
        {
            SAFE_RELEASE( pMeshContainer->ppTextures[iMaterial] );
        }
    }
 
    SAFE_DELETE_ARRAY( pMeshContainer->ppTextures );
    SAFE_DELETE_ARRAY( pMeshContainer->ppBoneMatrixPtrs );
    SAFE_RELEASE( pMeshContainer->pBoneCombinationBuf );
    SAFE_RELEASE( pMeshContainer->MeshData.pMesh );
    SAFE_RELEASE( pMeshContainer->pSkinInfo );
    SAFE_RELEASE( pMeshContainer->pOrigMesh );
    SAFE_DELETE( pMeshContainer );
    return S_OK;
}
 
 
 
 
 
 
 
 
//--------------------------------------------------------------------------------------
// Called to setup the pointers for a given bone to its transformation matrix
//--------------------------------------------------------------------------------------
HRESULT SetupBoneMatrixPointersOnMesh( LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME g_pFrameRoot)
{
UINT iBone, cBones;
D3DXFRAME_DERIVED* pFrame;
D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
if( pMeshContainer->pSkinInfo != NULL )  // if there is a skinmesh, then setup the bone matrices
    {
    cBones = pMeshContainer->pSkinInfo->GetNumBones();
    pMeshContainer->ppBoneMatrixPtrs = new D3DXMATRIX*[cBones];
    if( pMeshContainer->ppBoneMatrixPtrs == NULL ) return E_OUTOFMEMORY;
    for( iBone = 0; iBone < cBones; iBone++ )
        {
        pFrame = ( D3DXFRAME_DERIVED* )D3DXFrameFind( g_pFrameRoot,pMeshContainer->pSkinInfo->GetBoneName( iBone ) );
        if( pFrame == NULL ) return E_FAIL;
        pMeshContainer->ppBoneMatrixPtrs[iBone] = &pFrame->CombinedTransformationMatrix;
        }
    }
return S_OK;
}
 
 
 
 
 
 
 
//--------------------------------------------------------------------------------------
// Called to setup the pointers for a given bone to its transformation matrix
//--------------------------------------------------------------------------------------
HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrame, LPD3DXFRAME g_pFrameRoot )
{
HRESULT hr;
if( pFrame->pMeshContainer != NULL )
    {
    hr = SetupBoneMatrixPointersOnMesh( pFrame->pMeshContainer , g_pFrameRoot );
    if ( FAILED( hr ) )  return hr;
    }
if( pFrame->pFrameSibling != NULL )
    {
    hr = SetupBoneMatrixPointers( pFrame->pFrameSibling , g_pFrameRoot );
    if( FAILED( hr ) ) return hr;
    }
if( pFrame->pFrameFirstChild != NULL )
    {
    hr = SetupBoneMatrixPointers( pFrame->pFrameFirstChild , g_pFrameRoot );
    if( FAILED( hr ) ) return hr;
    }
return S_OK;
}
 
 
 
 
 
 
 
//--------------------------------------------------------------------------------------
// update the frame matrices
//--------------------------------------------------------------------------------------
void UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix )
{
    D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;
 
    if( pParentMatrix != NULL )
        D3DXMatrixMultiply( &pFrame->CombinedTransformationMatrix, &pFrame->TransformationMatrix, pParentMatrix );
    else
        pFrame->CombinedTransformationMatrix = pFrame->TransformationMatrix;
 
    if( pFrame->pFrameSibling != NULL )
    {
        UpdateFrameMatrices( pFrame->pFrameSibling, pParentMatrix );
    }
 
    if( pFrame->pFrameFirstChild != NULL )
    {
        UpdateFrameMatrices( pFrame->pFrameFirstChild, &pFrame->CombinedTransformationMatrix );
    }
}
 
 
 
 
 
/*
 
 
 
 
//--------------------------------------------------------------------------------------
// Called to render a mesh in the hierarchy
//--------------------------------------------------------------------------------------
void DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase, D3DXMATRIX *g_matView)
{
HRESULT hr;
D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;
UINT iMaterial;
UINT NumBlend;
UINT iAttrib;
DWORD AttribIdPrev;
LPD3DXBONECOMBINATION pBoneComb;
 
UINT iMatrixIndex;
UINT iPaletteEntry;
D3DXMATRIX matTemp;
D3DCAPS9 d3dCaps;
pd3dDevice->GetDeviceCaps( &d3dCaps );
 
// first check for skinning
if( pMeshContainer->pSkinInfo != NULL )
    {
    if( g_SkinningMethod == D3DNONINDEXED )
        {
        AttribIdPrev = UNUSED32;
        pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
        // Draw using default vtx processing of the device (typically HW)
        for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
            {
            NumBlend = 0;
            for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
                {
                if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
                    {
                    NumBlend = i;
                    }
                }
            if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
                {
                // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
                for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
                    {
                    iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
                    if( iMatrixIndex != UINT_MAX )
                        {
                        D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
                                                pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                        V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
                        }
                    }
                V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );
                // lookup the material used for this subset of faces
                if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) )
                    {
                    V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
                    V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
                    AttribIdPrev = pBoneComb[iAttrib].AttribId;
                    }
                // draw the subset now that the correct material and matrices are loaded
                V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
                }
            }
        // If necessary, draw parts that HW could not handle using SW
        if( pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups )
            {
            AttribIdPrev = UNUSED32;
            V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) );
            for( iAttrib = pMeshContainer->iAttributeSW; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
                {
                NumBlend = 0;
                for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
                    {
                    if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
                        {
                        NumBlend = i;
                        }
                    }
                if( d3dCaps.MaxVertexBlendMatrices < NumBlend + 1 )
                    {
                    // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
                    for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
                        {
                        iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
                        if( iMatrixIndex != UINT_MAX )
                            {
                            D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
                                                    pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                            V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
                            }
                        }
                    V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );
                    // lookup the material used for this subset of faces
                    if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) )
                        {
                        V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
                        V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
                        AttribIdPrev = pBoneComb[iAttrib].AttribId;
                        }
                    // draw the subset now that the correct material and matrices are loaded
                    V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
                    }
                }
            V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) );
            }
        V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 ) );
        }
    else if( g_SkinningMethod == D3DINDEXED )
        {
        // if hw doesn't support indexed vertex processing, switch to software vertex processing
        if( pMeshContainer->UseSoftwareVP )
            {
            // If hw or pure hw vertex processing is forced, we can't render the
            // mesh, so just exit out.  Typical applications should create
            // a device with appropriate vertex processing capability for this
            // skinning method.
            if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING )
                    return;
            V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) );
            }
        // set the number of vertex blend indices to be blended
        if( pMeshContainer->NumInfl == 1 )
            {
            V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_0WEIGHTS ) );
            }
        else
            {
            V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, pMeshContainer->NumInfl - 1 ) );
            }
        if( pMeshContainer->NumInfl )
            V( pd3dDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE ) );
        // for each attribute group in the mesh, calculate the set of matrices in the palette and then draw the mesh subset
        pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
        for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
            {
            // first calculate all the world matrices
            for( iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries; ++iPaletteEntry )
                {
                iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry];
                if( iMatrixIndex != UINT_MAX )
                    {
                    D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
                        pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                    V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( iPaletteEntry ), &matTemp ) );
                    }
                }
            // setup the material of the mesh subset - REMEMBER to use the original pre-skinning attribute id to get the correct material id
            V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
            V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
            // finally draw the subset with the current world matrix palette and material state
            V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
            }
        // reset blending state
        V( pd3dDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ) );
        V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, 0 ) );
        // remember to reset back to hw vertex processing if software was required
        if( pMeshContainer->UseSoftwareVP )
            {
            V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) );
            }
        }
    else if( g_SkinningMethod == D3DINDEXEDVS )
        {
            // Use COLOR instead of UBYTE4 since Geforce3 does not support it
            // vConst.w should be 3, but due to COLOR/UBYTE4 issue, mul by 255 and add epsilon
            D3DXVECTOR4 vConst( 1.0f, 0.0f, 0.0f, 765.01f );
 
            if( pMeshContainer->UseSoftwareVP )
            {
                // If hw or pure hw vertex processing is forced, we can't render the
                // mesh, so just exit out.  Typical applications should create
                // a device with appropriate vertex processing capability for this
                // skinning method.
                if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING )
                    return;
 
                V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) );
            }
 
            V( pd3dDevice->SetVertexShader( g_pIndexedVertexShader[pMeshContainer->NumInfl - 1] ) );
 
            pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer
                                                                 () );
            for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
            {
                // first calculate all the world matrices
                for( iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries; ++iPaletteEntry )
                {
                    iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry];
                    if( iMatrixIndex != UINT_MAX )
                    {
                        D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
                                            pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                        D3DXMatrixMultiplyTranspose( &matTemp, &matTemp, g_matView );
                        V( pd3dDevice->SetVertexShaderConstantF( iPaletteEntry * 3 + 9, ( float* )&matTemp, 3 ) );
                    }
                }
 
                // Sum of all ambient and emissive contribution
                D3DXCOLOR color1( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Ambient );
                D3DXCOLOR color2( .25, .25, .25, 1.0 );
                D3DXCOLOR ambEmm;
                D3DXColorModulate( &ambEmm, &color1, &color2 );
                ambEmm += D3DXCOLOR( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Emissive );
 
                // set material color properties 
                V( pd3dDevice->SetVertexShaderConstantF( 8,
                                                         ( float* )&(
                                                         pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Diffuse ), 1 ) );
                V( pd3dDevice->SetVertexShaderConstantF( 7, ( float* )&ambEmm, 1 ) );
                vConst.y = pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Power;
                V( pd3dDevice->SetVertexShaderConstantF( 0, ( float* )&vConst, 1 ) );
 
                V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
 
                // finally draw the subset with the current world matrix palette and material state
                V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
            }
 
            // remember to reset back to hw vertex processing if software was required
            if( pMeshContainer->UseSoftwareVP )
            {
                V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) );
            }
            V( pd3dDevice->SetVertexShader( NULL ) );
        }
        else if( g_SkinningMethod == D3DINDEXEDHLSLVS )
        {
            if( pMeshContainer->UseSoftwareVP )
            {
                // If hw or pure hw vertex processing is forced, we can't render the
                // mesh, so just exit out.  Typical applications should create
                // a device with appropriate vertex processing capability for this
                // skinning method.
                if( g_dwBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING )
                    return;
 
                V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) );
            }
 
            pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer
                                                                 () );
            for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
            {
                // first calculate all the world matrices
                for( iPaletteEntry = 0; iPaletteEntry < pMeshContainer->NumPaletteEntries; ++iPaletteEntry )
                {
                    iMatrixIndex = pBoneComb[iAttrib].BoneId[iPaletteEntry];
                    if( iMatrixIndex != UINT_MAX )
                    {
                        D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
                                            pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                        D3DXMatrixMultiply( &g_pBoneMatrices[iPaletteEntry], &matTemp, g_matView );
                    }
                }
                V( g_pEffect->SetMatrixArray( "mWorldMatrixArray", g_pBoneMatrices,
                                              pMeshContainer->NumPaletteEntries ) );
 
                // Sum of all ambient and emissive contribution
                D3DXCOLOR color1( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Ambient );
                D3DXCOLOR color2( .25, .25, .25, 1.0 );
                D3DXCOLOR ambEmm;
                D3DXColorModulate( &ambEmm, &color1, &color2 );
                ambEmm += D3DXCOLOR( pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Emissive );
 
                // set material color properties 
                V( g_pEffect->SetVector( "MaterialDiffuse",
                                         ( D3DXVECTOR4* )&(
                                         pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D.Diffuse ) ) );
                V( g_pEffect->SetVector( "MaterialAmbient", ( D3DXVECTOR4* )&ambEmm ) );
 
                // setup the material of the mesh subset - REMEMBER to use the original pre-skinning attribute id to get the correct material id
                V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
 
                // Set CurNumBones to select the correct vertex shader for the number of bones
//  ROCKLAND NOTE: had to add explict cast from DWORD to int to suppress warning.
                V( g_pEffect->SetInt( "CurNumBones", (int)(pMeshContainer->NumInfl - 1) ) );
 
                // Start the effect now all parameters have been updated
                UINT numPasses;
                V( g_pEffect->Begin( &numPasses, D3DXFX_DONOTSAVESTATE ) );
                for( UINT iPass = 0; iPass < numPasses; iPass++ )
                {
                    V( g_pEffect->BeginPass( iPass ) );
 
                    // draw the subset with the current world matrix palette and material state
                    V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
 
                    V( g_pEffect->EndPass() );
                }
 
                V( g_pEffect->End() );
 
                V( pd3dDevice->SetVertexShader( NULL ) );
            }
 
            // remember to reset back to hw vertex processing if software was required
            if( pMeshContainer->UseSoftwareVP )
            {
                V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) );
            }
        }
        else if( g_SkinningMethod == SOFTWARE )
        {
            D3DXMATRIX Identity;
            DWORD cBones = pMeshContainer->pSkinInfo->GetNumBones();
            DWORD iBone;
            PBYTE pbVerticesSrc;
            PBYTE pbVerticesDest;
 
            // set up bone transforms
            for( iBone = 0; iBone < cBones; ++iBone )
            {
                D3DXMatrixMultiply
                    (
                    &g_pBoneMatrices[iBone],                 // output
                    &pMeshContainer->pBoneOffsetMatrices[iBone],
                    pMeshContainer->ppBoneMatrixPtrs[iBone]
                    );
            }
 
            // set world transform
            D3DXMatrixIdentity( &Identity );
            V( pd3dDevice->SetTransform( D3DTS_WORLD, &Identity ) );
 
            V( pMeshContainer->pOrigMesh->LockVertexBuffer( D3DLOCK_READONLY, ( LPVOID* )&pbVerticesSrc ) );
            V( pMeshContainer->MeshData.pMesh->LockVertexBuffer( 0, ( LPVOID* )&pbVerticesDest ) );
 
            // generate skinned mesh
            pMeshContainer->pSkinInfo->UpdateSkinnedMesh( g_pBoneMatrices, NULL, pbVerticesSrc, pbVerticesDest );
 
            V( pMeshContainer->pOrigMesh->UnlockVertexBuffer() );
            V( pMeshContainer->MeshData.pMesh->UnlockVertexBuffer() );
 
            for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
            {
                V( pd3dDevice->SetMaterial( &(
                                            pMeshContainer->pMaterials[pMeshContainer->pAttributeTable[iAttrib].AttribId].MatD3D ) ) );
                V( pd3dDevice->SetTexture( 0,
                                           pMeshContainer->ppTextures[pMeshContainer->pAttributeTable[iAttrib].AttribId] ) );
                V( pMeshContainer->MeshData.pMesh->DrawSubset( pMeshContainer->pAttributeTable[iAttrib].AttribId ) );
            }
        }
        else // bug out as unsupported mode
        {
            return;
        }
    }  // end if skin info != NULL
else  // standard mesh, just draw it after setting material properties
    {
    V( pd3dDevice->SetTransform( D3DTS_WORLD, &pFrame->CombinedTransformationMatrix ) );
    for( iMaterial = 0; iMaterial < pMeshContainer->NumMaterials; iMaterial++ )
        {
        V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[iMaterial].MatD3D ) );
        V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[iMaterial] ) );
        V( pMeshContainer->MeshData.pMesh->DrawSubset( iMaterial ) );
        }
    }
 
}
 
 
 
*/
 
 
 
 
 
 
 
//  ROCKLAND NOTE:  the Z3d game library uses hardware vertex processing only, so the 
//  software vertex processing code in the following routine has been turned off.
//  uncomment it if you want to use software vertex preocssing as a fallback method.
//--------------------------------------------------------------------------------------
// Called to render a mesh in the hierarchy. skinned only, non-indexed.
//--------------------------------------------------------------------------------------
void DrawMeshContainer2( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase)
{
HRESULT hr;
D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
//  D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )pFrameBase;
//  UINT iMaterial;
UINT NumBlend;
UINT iAttrib;
DWORD AttribIdPrev;
LPD3DXBONECOMBINATION pBoneComb;
 
UINT iMatrixIndex;
//  UINT iPaletteEntry;
D3DXMATRIX matTemp;
D3DCAPS9 d3dCaps;
pd3dDevice->GetDeviceCaps( &d3dCaps );
 
AttribIdPrev = UNUSED32;
pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
// Draw using default vtx processing of the device (typically HW)
for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
    {
    NumBlend = 0;
    for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
        {
        if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
            {
            NumBlend = i;
            }
        }
    if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
        {
        // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
        for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
            {
            iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
            if( iMatrixIndex != UINT_MAX )
                {
                D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
                                        pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
                }
            }
        V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );
        // lookup the material used for this subset of faces
        if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) )
            {
            V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
            V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
            AttribIdPrev = pBoneComb[iAttrib].AttribId;
            }
        // draw the subset now that the correct material and matrices are loaded
        V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
        }
    }
 
/*
 
// If necessary, draw parts that HW could not handle using SW
if( pMeshContainer->iAttributeSW < pMeshContainer->NumAttributeGroups )
    {
    AttribIdPrev = UNUSED32;
    V( pd3dDevice->SetSoftwareVertexProcessing( TRUE ) );
    for( iAttrib = pMeshContainer->iAttributeSW; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )
        {
        NumBlend = 0;
        for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
            {
            if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
                {
                NumBlend = i;
                }
            }
        if( d3dCaps.MaxVertexBlendMatrices < NumBlend + 1 )
            {
            // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
            for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
                {
                iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
                if( iMatrixIndex != UINT_MAX )
                    {
                    D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],
                                            pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                    V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
                    }
                }
            V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );
            // lookup the material used for this subset of faces
            if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) )
                {
                V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
                V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
                AttribIdPrev = pBoneComb[iAttrib].AttribId;
                }
            // draw the subset now that the correct material and matrices are loaded
            V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );
            }
        }
    V( pd3dDevice->SetSoftwareVertexProcessing( FALSE ) );
    }
 
*/
V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ) );
}
 
 
 
 
 
 
 
 
 
 
 
 
 
//  ROCKLAND NOTE:
//  this routine was written by rockland, not MS.
//  bascially its drawmeshcontainer2 with the commented out software vertex procesing code removed.
//------------------------------------------------------------------------------------------------
// Called to render a mesh in the hierarchy. skinned only, non-indexed. HW vertex processing only.
//------------------------------------------------------------------------------------------------
void DrawMeshContainer3( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase)
{
HRESULT hr;
D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
UINT NumBlend;
UINT iAttrib;
DWORD AttribIdPrev;
LPD3DXBONECOMBINATION pBoneComb;
UINT iMatrixIndex;
D3DXMATRIX matTemp;
D3DCAPS9 d3dCaps;
pd3dDevice->GetDeviceCaps( &d3dCaps );
AttribIdPrev = UNUSED32;
pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )  // Draw using default vtx processing of the device (typically HW)
    {
    NumBlend = 0;
    for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
        {
        if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
            {
            NumBlend = i;
            }
        }
    if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
        {
        for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )         // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
            {
            iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
            if( iMatrixIndex != UINT_MAX )
                {
                D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
                }
            }
        V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );
        if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) // lookup the material used for this subset of faces
            {
            V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
            V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
            AttribIdPrev = pBoneComb[iAttrib].AttribId;
            }
        V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );         // draw the subset now that the correct material and matrices are loaded
        }
    }
V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ) );
}
 
 
 
 
 
//  ROCKLAND NOTE:
//  this routine was written by rockland, not MS.
//  its the same as drawmeshcontainer3, but uses the current texture and material
//  bound to the fixed function pipeline instead of those specified by the mesh (if any).
//  this lets you change the textures and materials used to draw a skinned mesh - and 
//  those textures and materials can be pooled!
//  note that this is only designd to work with skinned meshes that use the same texture for all subsets.
//---------------------------------------------------------------------------------------------------------------------
// Called to render a mesh in the hierarchy. skinned only, non-indexed. HW vertex processing only. 
// uses current material and texture
//---------------------------------------------------------------------------------------------------------------------
void DrawMeshContainer4( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase)
{
HRESULT hr;
D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
UINT NumBlend;
UINT iAttrib;
DWORD AttribIdPrev;
LPD3DXBONECOMBINATION pBoneComb;
UINT iMatrixIndex;
D3DXMATRIX matTemp;
D3DCAPS9 d3dCaps;
pd3dDevice->GetDeviceCaps( &d3dCaps );
AttribIdPrev = UNUSED32;
pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )  // Draw using default vtx processing of the device (typically HW)
    {
    NumBlend = 0;
    for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
        {
        if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
            {
            NumBlend = i;
            }
        }
    if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
        {
        for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )         // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
            {
            iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
            if( iMatrixIndex != UINT_MAX )
                {
                D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
                }
            }
        V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );
        if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) // lookup the material used for this subset of faces
            {
//             V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
//             V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
            AttribIdPrev = pBoneComb[iAttrib].AttribId;
            }
        V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );         // draw the subset now that the correct material and matrices are loaded
        }
    }
V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ) );
}
 
 
 
 
 
 
 
 
 
 
 
//  ROCKLAND NOTE:
//  this routine was written by rockland, not MS.
//  its the same as drawmeshcontainer4, but uses the meshcontainer`s texID, along with the current material
//  bound to the fixed function pipeline.
//  this lets you change the textures used to draw each mesh of a skinned mesh - and 
//  those textures can be pooled!
//  note that this is only designd to work with skinned meshes that use the same texture for all subsets.
//---------------------------------------------------------------------------------------------------------------------
// Called to render a mesh in the hierarchy. skinned only, non-indexed. HW vertex processing only. 
// uses current material and texture
//---------------------------------------------------------------------------------------------------------------------
void DrawMeshContainer5( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase)
{
HRESULT hr;
D3DXMESHCONTAINER_DERIVED* pMeshContainer = ( D3DXMESHCONTAINER_DERIVED* )pMeshContainerBase;
UINT NumBlend;
UINT iAttrib;
DWORD AttribIdPrev;
LPD3DXBONECOMBINATION pBoneComb;
UINT iMatrixIndex;
D3DXMATRIX matTemp;
D3DCAPS9 d3dCaps;
pd3dDevice->GetDeviceCaps( &d3dCaps );
AttribIdPrev = UNUSED32;
pBoneComb = reinterpret_cast<LPD3DXBONECOMBINATION>( pMeshContainer->pBoneCombinationBuf->GetBufferPointer() );
for( iAttrib = 0; iAttrib < pMeshContainer->NumAttributeGroups; iAttrib++ )  // Draw using default vtx processing of the device (typically HW)
    {
    NumBlend = 0;
    for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )
        {
        if( pBoneComb[iAttrib].BoneId[i] != UINT_MAX )
            {
            NumBlend = i;
            }
        }
    if( d3dCaps.MaxVertexBlendMatrices >= NumBlend + 1 )
        {
        for( DWORD i = 0; i < pMeshContainer->NumInfl; ++i )         // first calculate the world matrices for the current set of blend weights and get the accurate count of the number of blends
            {
            iMatrixIndex = pBoneComb[iAttrib].BoneId[i];
            if( iMatrixIndex != UINT_MAX )
                {
                D3DXMatrixMultiply( &matTemp, &pMeshContainer->pBoneOffsetMatrices[iMatrixIndex],pMeshContainer->ppBoneMatrixPtrs[iMatrixIndex] );
                V( pd3dDevice->SetTransform( D3DTS_WORLDMATRIX( i ), &matTemp ) );
                }
            }
        V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, NumBlend ) );
        if( ( AttribIdPrev != pBoneComb[iAttrib].AttribId ) || ( AttribIdPrev == UNUSED32 ) ) // lookup the material used for this subset of faces
            {
//             V( pd3dDevice->SetMaterial( &pMeshContainer->pMaterials[pBoneComb[iAttrib].AttribId].MatD3D ) );
//             V( pd3dDevice->SetTexture( 0, pMeshContainer->ppTextures[pBoneComb[iAttrib].AttribId] ) );
Zsettex(pMeshContainer->texID);
            AttribIdPrev = pBoneComb[iAttrib].AttribId;
            }
        V( pMeshContainer->MeshData.pMesh->DrawSubset( iAttrib ) );         // draw the subset now that the correct material and matrices are loaded
        }
    }
V( pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE ) );
}
 
 
 
 
 
 
 
 
 
 
 
 
//  ROCKLAND NOTE: use your choice of drawmeshcontainer, drawmeshcontainer2, 
//  drawmeshcontainer3 , drawmeshcontainer4, or drawmeshcontainer5 in this routine.
//--------------------------------------------------------------------------------------
// Called to render a frame in the hierarchy
//--------------------------------------------------------------------------------------
void DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame, D3DXMATRIX *g_matView )
{
LPD3DXMESHCONTAINER pMeshContainer;
pMeshContainer = pFrame->pMeshContainer;
while( pMeshContainer != NULL )
    {
//  DrawMeshContainer( pd3dDevice, pMeshContainer, pFrame, g_matView );
//  DrawMeshContainer2( pd3dDevice, pMeshContainer);
//  DrawMeshContainer3( pd3dDevice, pMeshContainer);
//  drawmeshcontainer4 uses the current material and texture. set material and texture, then call draw_skinned_mesh.
//  DrawMeshContainer4( pd3dDevice, pMeshContainer);
//  drawmeshcontainer5 uses the current material and the meshcontainer`s texID. set material and texID`s, then call draw_skinned_mesh.
     DrawMeshContainer5( pd3dDevice, pMeshContainer);
    pMeshContainer = pMeshContainer->pNextMeshContainer;
    }
if( pFrame->pFrameSibling != NULL )
    {
    DrawFrame( pd3dDevice, pFrame->pFrameSibling, g_matView );
    }
if( pFrame->pFrameFirstChild != NULL )
    {
    DrawFrame( pd3dDevice, pFrame->pFrameFirstChild, g_matView );
    }
}
 
 
//  ----------------------------- end LOW LEVEL MS CODE -----------------------
 
 
 
 









PARTNERS