1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

3D Model Viewer

Discussion in 'Site Discussion' started by GhostWolf, Mar 24, 2014.

  1. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    So since there was never any official thread, here goes.

    I am working on a complete different API and user interface that should change the viewer quite a lot (hopefully for good).
    The new API should make it easy to load multiple models, move them around in any way you want, change textures on run-time (e.g. to test custom textures) and attach models to other models or nodes (e.g. attachments).
    In addition, there will be no distinction between Warcaraft 3 and Starcraft 2 models, you will be able to use both of them interchangeably.

    The new user interface would be a fullscreen menu that can be opened or closed via a button or key shortcut.
    If you have any suggestions as to how it should look, go for it, I am no designer or artist.
    The menu will be context-based. If you have multiple models, you will be able to select one and the menu will change accordingly to reflect that model.

    The animation speed is going to be removed, since it's a useless and buggy feature.

    Software rendering and one of the hardware rendering modes are going to be removed.
    This means the viewer might not work for some people with really terrible computers for which it currently does work, but the numbers should be quite small.
    If in the future I see that this affects many people, they can be re-added.
     
  2. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    Here's a small teaser.

    Starcraft 2 models seem to be approximately 100 times smaller than Warcraft 3 ones, so they are scaled by that automatically.

    The API right now is quite simple, here's the code that made the scene in the first attachment below:
    Code (Javascript):

    var zerg_wc3 = loadModel("Units/Critters/zergling/zergling.mdx");
    zerg_wc3.move([-100, 100, 0]);

    var zerg_sc2 = loadModel("Assets/Units/Zerg/Zergling/Zergling.m3");
    zerg_sc2.move([100, 100, 0]);
    zerg_sc2.rotate(0, 0, -90);

    var hydralisk_wc3 = loadModel("Units/Critters/Hydralisk/Hydralisk.mdx");
    hydralisk_wc3.move([-100, -100, 0]);

    var hydralisk_sc2 = loadModel("Assets/Units/Zerg/Hydralisk/Hydralisk.m3");
    hydralisk_sc2.move([100, -100, 0]);
    hydralisk_sc2.rotate(0, 0, -90);
     


    All the asynchronous file loading is hidden from the outside by a nice wrapper.
    The new version's model is split into two. The Model structure, which is as before with some added functionality, and a ModelInstance, which is a pointer to a Model, but with additional info - a local transformation (translation, rotation, scaling) and a possible parent.
    When you load a model, if that model is already loaded, it will just return a new instance of it, so there is no duplicated data.
    While this sounds like magic, trying to run things (e.g. changing animations, changing textures, and so on) before the model is loaded wont do anything.
    I am thinking if I should keep it this way with callbacks, or do a call queue which will fill up if you call things before the model is loaded, and then automatically apply them when the model finishes loading, because, let's face it - callbacks suck.

    Shader loading is now smarter too, and everything is cached properly.

    There will no longer be different url parameters for an absolute path / MPQ path / ID (p/mpq/q right now), the code will recognize each from a single parameter.

    Texture overriding is implemented, but until I get direct access to the texture (they are NOT skins!) section, I can't show any meaningful example, so instead have a Warcraft 3 Zergling with the diffuse texture of a Starcraft 2 Zergling.
    The code to have that effect, with the current scheme of callbacks, goes like this:
    Code (Javascript):

    var zerg_wc3 = loadModel("Units/Critters/zergling/zergling.mdx", function (model) {
      model.overrideTexture("units/Critters/zergling/Zergling.blp", "assets/textures/zergling_diffuse.dds");
    });
     


    The third image shows how you can attach a model instance to another model instance, or a model instance's node, with the following code:
    Code (Javascript):

    var peasant = loadModel("Units/Human/Peasant/peasant.mdx", function (model) {
      var shield = loadModel("f86573");
     
      // Attach to the model itself (equivalent to attaching to the Origin attachment point)
      // This can be used also before peasant finishes loading
      shield.setParent(model);

      // Attach to the 4th attachment point, which corresponds to the left hand for the peasant model
      // This must be used after peasant finished loading
      shield.setParent(model.getAttachment(3));
    });
     

    Note how you have to wait for the first model to actually load before you can get the attachment if you want to attach to nodes (which is the main usage of this feature), which makes loading slower since meanwhile you could have started getting the second model already.
    An action queue of sorts could possibly solve this, as I stated above.

    There are still many things to rewrite in nicer ways, and finally an API is needed for the outside layer, or in other words, what the user interface interacts with.
     

    Attached Files:

    Last edited: Mar 24, 2014
  3. Ralle

    Ralle

    Owner

    Joined:
    Oct 6, 2004
    Messages:
    11,223
    Resources:
    22
    Tools:
    3
    Maps:
    5
    Tutorials:
    14
    Resources:
    22
    Looking really cool. I can't wait to give you more access to data. It is my top priority, so whenever I have time to code (usually 0-2 hours per day and lots in the coming weekend) I will be working on this.
     
  4. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Good luck on updating the viewer.

    As for the current one, there is a slight issue loading textures (not technically a 'bug'). There are some models who use a path such as "Textures\<Texture Name>.blp", which means that wc3 will look under the "Textures" directory. But when it is uploaded to the hive, the textures are actually found in the same directory as the model.

    Perhaps you could add a measure where, if the texture cannot be loaded from its actual path, the viewer will look within the model's directory itself. Here are two examples of models in our db that show up as black because their textures have paths for the "Textures" directory:
    http://www.hiveworkshop.com/forums/models-530/krasus-249755/
    http://www.hiveworkshop.com/forums/models-530/mounted-paladin-47461/
     
  5. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    There was indeed a small error with the texture loading function (not related to paths), fixed it.
    As usual, do a hard refresh (Ctrl + F5) if it still fails to work properly.

    ----

    Now that Ralle gave me access to the texture section (I will NOT call it skin!), here's a more meaningful example of texture overriding.
    Texture overriding is now per-instance, and it requires no callbacks.
    Code (Javascript):

    var mountainKing = loadModel("units/human/HeroMountainKing/HeroMountainKing.mdx");

    // hiveworkshop.com/forums/skins-552/heromountainking-61742/
    mountainKing.overrideTexture("replaceabletextures/heromountainking/heromountainking.blp", "opmmml");
    mountainKing.move([0, -50, 0]);

    // This only creates an instance, since the model is already loaded/loading
    var mountainKing2 = loadModel("units/human/HeroMountainKing/HeroMountainKing.mdx");

    mountainKing2.move([0, 50, 0]);

    // Oops, this sets the animation for the model, so it affects both instances
    mountainKing.setAnimation(0);
     


    I am working on the action queue to remove the need for callbacks also for other things (already done for setAnimation as can be seen above), but it's proving tricky for setParent. I don't know if I should make a setParentNode method that will take care of the tricky part, or make even weirder behind-the-scenes things.

    In addition, I suddenly realized my code is all wrong. Model instances are really shallow wrappers right now, but in reality, they need to incorporate the model's skeleton, animation timers, and really any data that changes. Otherwise, how would you run different animations on different instances?
    This will require quite a lot of code redesigning.
     

    Attached Files:

    Last edited: Mar 26, 2014
  6. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    Small update - setParent is now synchronous too (from the API standpoint).
    The signature had to be changed for this, it is now:
    Code (Javascript):
    ModelInstance.setParent(parent [, attachment])

    That is, it sets the parent, and an optional attachment ID.
     
  7. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    So after a lot of hair tearing and frustrations, I finally managed to make everything work for MDX files. I ended up restructuring almost all of the old viewer's code.
    I still didn't work on M3, but knowing the format, it should probably be a lot easier and faster.

    Here's a new teaser - dwarven wings. :)

    code

    Code (Javascript):

    var modelPath = "units/human/HeroMountainKing/HeroMountainKing.mdx";
    var oldTexture = "replaceabletextures/heromountainking/heromountainking.blp";
    var newTexture = "opmmml";

    var mountainKing = loadModel(modelPath);
    mountainKing.setAnimation(0);

    var mountainKing2 = loadModel(modelPath);
    mountainKing2.setAnimation(0);
    mountainKing2.setParent(mountainKing, 4);

    var mountainKing3 = loadModel(modelPath);
    mountainKing3.setAnimation(0);
    mountainKing3.setParent(mountainKing2, 4);

    var mountainKing4 = loadModel(modelPath);
    mountainKing4.setAnimation(0);
    mountainKing4.setParent(mountainKing, 2);

    var mountainKing5 = loadModel(modelPath);
    mountainKing5.setAnimation(0);
    mountainKing5.setParent(mountainKing4, 2);

    // Overrides the texture for the model itself, this affects all instances
    modelCache[modelPath].overrideTexture(oldTexture, newTexture);

    // Overrides the texture just for the first instance
    mountainKing.overrideTexture(newTexture, oldTexture);

    var blade = loadModel("4fkwk3");
    blade.setParent(mountainKing, 2);

    var blade2 = loadModel("4fkwk3");
    blade2.setParent(mountainKing, 4);

     

     

    Attached Files:

  8. Ralle

    Ralle

    Owner

    Joined:
    Oct 6, 2004
    Messages:
    11,223
    Resources:
    22
    Tools:
    3
    Maps:
    5
    Tutorials:
    14
    Resources:
    22
  9. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    Working on M3 was indeed a breeze compared to MDX, though there is an issue.
    Since M3 models are scaled by 100, it means that if you attach something to them, that something is scaled by 100 too, and chaos ensues.
    I am not sure how to solve this yet.

    Time to work on the actual API.
    What would you guys want in terms of configuration and information?
    E.g. set animations, override textures, load models, parent models to other ones...
    Should I do more complicated things like list all the currently loaded models, instances and textures, or if not loaded, their loading status? show model node hierarchies? anything else?

    If you want something, this is the time to suggest it!
     

    Attached Files:

  10. moyackx

    moyackx

    Joined:
    Feb 15, 2006
    Messages:
    790
    Resources:
    7
    Maps:
    4
    Spells:
    2
    Tutorials:
    1
    Resources:
    7
    One question: is this viewer an open project or it's exclusively for Hive?
     
  11. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    While it was made for the Hive, it's a library you can use wherever you want, assuming you can supply required links to get the files.

    It is hosted on github. The version there is the same as what's on the Hive right now (except for a tiny fix I didn't bother pushing).

    url.js is used to get the paths of MPQ files and such, and this depends on the server.
    For example, if the viewer calls
    url.mpqFile("path/to/some/model.mdx")
    , then you must return an url that will lead to that model.
    For images, PNG images are expected when requesting BLP files (or really any format the browser understands and has alpha channels, PNG is the safest), and DDS files should be given as-is.
     
  12. Ralle

    Ralle

    Owner

    Joined:
    Oct 6, 2004
    Messages:
    11,223
    Resources:
    22
    Tools:
    3
    Maps:
    5
    Tutorials:
    14
    Resources:
    22
    My hope is to show multiple models in the same scene and have them animate each their thing. Perhaps even store this setup in JSON for others to see.
     
  13. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    Yes, all of that exists, though I am waiting with the scene-sharing until I know what you people want to share.

    Any information about the models etc. that anyone would like to see?
    Should bounding shapes be per-instance, or global?
    And so on...come on people, use your imagination, because I have none.
     
  14. Ralle

    Ralle

    Owner

    Joined:
    Oct 6, 2004
    Messages:
    11,223
    Resources:
    22
    Tools:
    3
    Maps:
    5
    Tutorials:
    14
    Resources:
    22
    I am not sure what bounding shapes even means.
     
  15. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    There's an option to draw the bounding shapes of models, as in cubes/spheres/whatever shapes that bound the model's geometry.
    They are used in the games for selecting units, collision detection, etc.

    On a side note, I did a small change in the M3 loading that makes it load noticeably faster, and probably render a little faster too.
     
  16. Solu9

    Solu9

    Joined:
    Jan 25, 2011
    Messages:
    2,166
    Resources:
    47
    Models:
    26
    Icons:
    16
    Skins:
    1
    Tools:
    1
    Maps:
    3
    Resources:
    47
    Alright I have a small suggestion.

    Right now when you choose an animation to be played you cannot play the same animation again before another has been chosen.
    It would be nice if you could fix that.

    Also perhaps add a loop effect so the same animation can be played over and over again without having to click?

    Anyway.
    Just small annoyances.
     
  17. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    Uhm, both of those features exist.
    To restart an animation, press on the skeleton hand icon next to the animation list. To force animation looping for animations that don't loop (e.g. attack), set the looping mode to "Always".
     
  18. Solu9

    Solu9

    Joined:
    Jan 25, 2011
    Messages:
    2,166
    Resources:
    47
    Models:
    26
    Icons:
    16
    Skins:
    1
    Tools:
    1
    Maps:
    3
    Resources:
    47
    Son of a gun! So they do.
    Can't believe I never noticed that.

    And here I thought I was contributing. Gah.
     
  19. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    :p

    Fixed all the scaling issues that put stuff out of place, or made things enormous when attaching anything to a SC2 model.
     

    Attached Files:

  20. GhostWolf

    GhostWolf

    Joined:
    Jul 29, 2007
    Messages:
    4,836
    Resources:
    2
    Tools:
    1
    Tutorials:
    1
    Resources:
    2
    Ok, the new version of the API is as follows.


    • loadModel(source)
      - Load a model.
      Source can be an absolute path to a MDX/M3 file, a path to a MDX/M3 file in any of the Warcraft 3 and Starcraft 2 MPQs, or a model ID used by the Hiveworkshop.
      Returns the ID of the loaded model.
      Note: if a model was already loaded from the given source, its ID will be returned.
    • loadInstance(source)
      - Create a new instance from an existing model or instance, or a path that will be used to load also the model if needed.
      If source is a string, it can be an absolute path to a MDX/M3 file, a path to a MDX/M3 file in any of the Warcraft 3 and Starcraft 2 MPQs, or a model ID used by the Hiveworkshop.
      If source is a number, it can be an ID of a model or an instance.
      Returns null if given an invalid ID, otherwise returns the ID of the created instance.
      Note: if the source is a string, and a model was already loaded from that string, only a new instance will be created.
    • setPosition(objectId, v)
      - Set the position of an instance.
    • move(objectId, v)
      - Move an instance.
    • setRotation(objectId, v)
      - Set the rotation of an instance.
    • rotate(objectId, v)
      - Rotate an instance.
    • setScale(objectId, n)
      - Set the scale of an instance.
    • scale(objectId, n)
      - Scale an instance.
    • setParent(objectId, parentId, attachmentId)
      - Set the parent of an instance to another instance, with an optional attachment point ID owned by that parent.
    • setTeamColor(objectId, teamId)
      - Set the team color used by an instance.
    • overrideTexture(objectId, oldPath, newPath)
      - Override a texture of an instance or a model with another texture.
      If objectId is an instance, overrides the texture locally just for that instance.
      If objectId is a model, it overrides the texture for the model, which affects all instances that don't explicitly override this texture.
    • playAnimation(objectId, animationId)
      - Set the animation of an instance.
    • stopAnimation(objectId)
      - Stop the animation of an instance.
      Equivalent to playAnimation with animation ID -1.
    • setAnimationLoop(objectId, mode)
      - Sets the animation loop mode of an instance.
      Possible values are 0 for default, 1 for never, and 2 for always.
    • getSequences(objectId)
      - Get a list of the sequences owned by a model.
      Returns null if the object ID is invalid or not a model, or if the model didn't finish loading.
    • getCameras(objectId)
      - Get a list of the cameras owned by a model.
      Returns null if the object ID is invalid or not a model, or if the model didn't finish loading.
    • applyCamera(objectId, cameraId)
      - Apply a camera of an instance.
      When an instance camera is applied, the main camera is disabled.
      If the given object ID is -1, the instance camera is disabled and the main camera becomes active.
    • setScene(mode) 
      - Set the drawn scene.
      Possible values are 0 for nothing, 1 for sky, 2 for sky and ground, and 3 for sky and water.
    • showBoundingShapes(b)
      - Shows or hides the bounding shapes on all the instances.
    • panCamera(x, y) 
      - Pan the camera on the x and y axes.
    • rotateCamera(x, y) 
      - Rotate the camera on the x and y axes.
    • zoomCamera(n) 
      - Zoom the camera by a factor.
    • resetCamera() 
      - Reset the camera back to the initial state.


    Notes:
    • A model is a heavy object that holds all the big data structures, such as the vertices and animation data.
    • An instance is a light-weight object that holds a skeleton, the selected team color, the selected animation, and so on.
    • If a model is loaded from an ID, it will automatically override its textures with custom ones if those exist.


    Anything missing? Anything else anyone would like?

    I will probably add some more information getting functions like getTextures, getLoadedModels, getLoadedInstances, etc., if anyone would want to see that information.
     
    Last edited: Mar 31, 2014