4

I'm creating a simple WebGL project and need a way to load in models. I decided to use OBJ format so I need a way to load it in. The file is (going to be) stored on the server and my question is: how does one in JS load in a text file and scan it line by line, token by token (like with streams in C++)? I'm new to JS, hence my question. The easier way, the better.

UPDATE: I used your solution, broofa, but I'm not sure if I did it right. I load the data from a file in forEach loop you wrote but outside of it (i.e. after all your code) the object I've been filling data with is "undefined". What am I doing wrong? Here's the code:

var materialFilename;

function loadOBJModel(filename)
{
    // ...

    var req = new XMLHttpRequest();
    req.open('GET', filename);
    req.responseType = 'text';
    req.onreadystatechange = function()
    {
        if (req.readyState == 4)
        {
            var lines = req.responseText.split(/\n/g);
            lines.forEach(function(line)
            {
                readLine(line);
            });
        }
    }
    req.send();

    alert(materialFilename);

    // ...
}

function readLine(line)
{
    // ...

    else if (tokens[0] == "mtllib")
    {
        materialFilename = tokens[1];
    }

    // ...
}
NPS
  • 6,003
  • 11
  • 53
  • 90
  • It may or may not be an option, but you could let http://mrdoob.github.com/three.js/ do some of the heavy lifting for you. It has multiple loader options to take advantage of. – gotohales Nov 26 '12 at 23:37
  • @mookamafoob I've tried that already but the lack of actual documentation rendered it unusable for me. – NPS Nov 26 '12 at 23:40
  • @broofa Of course, added now. – NPS Nov 27 '12 at 21:03
  • The problem is onreadystatechange is an asynchronous callback - it doesn't get called until after the current event loop completes (i.e. _after_ you've tried to alert the value of `materialFilename`. Move your alert to the end of the onreadystatechange function and you should be good to go. P.S. you've inspired me to ask a minor question on SO etiquette :) - http://meta.stackexchange.com/questions/156897/when-is-it-okay-not-okay-to-revoke-an-answer – broofa Nov 27 '12 at 21:13
  • @broofa I knew it was async but thought it might work anyway since I put alert right after `materialFilename = tokens[1];` and it always got executed (with correct value) before the alert in `loadOBJModel` function. Anyway, `alert` was just for debugging causes and I need the `loadOBJModel` function to return data loaded from the file, how do I do that? – NPS Nov 27 '12 at 21:40
  • @NPS - you can make the XHR request synchronous by doing `req.open('GET', filename, false);` (note 3rd argument). This will block the send() method until the response has been received. Note, however, that use of sync XHR requests like this is often discouraged because they can hang IE browsers. The better solution is to pass a callback function into loadObjModel, which is then invoked once the request has been received/procesed. See http://goo.gl/CEmrW amd http://goo.gl/BJLaL for more info. (This is why this would have been better asked as a separate question, btw. But no worries! :) ) – broofa Nov 27 '12 at 22:07

2 Answers2

3

You can use XMLHttpRequest to fetch the file, assuming it's coming from the same domain as your main web page. If not, and you have control over the server hosting your file, you can enable CORS without too much trouble. E.g.

To scan line-by-line, you can use split(). E.g. Something like this ...

var req = new XMLHttpRequest();
req.open('GET', '/your/url/goes/here');
req.onreadystatechange = function() {
  if (req.readyState == 4) {
    if (req.status == 200) {
      var lines = req.responseText.split(/\n/g);
      lines.forEach(function(line, i) {
        // 'line' is a line of your file, 'i' is the line number (starting at 0)
      });
    } else {
      // (something went wrong with the request)
    }
  }
}

req.send();
broofa
  • 37,461
  • 11
  • 73
  • 73
  • 1
    'Not sure why @Brad's answer was downvoted. XHR/CORS is probably what you want and, as he notes, you're pretty much on your own for parsing at the token level, if that's what you want. If you google around you can probably find utilities for generating JS parsers AST definitions, but that's out of scope here. – broofa Nov 26 '12 at 23:38
  • I've tried this solution, I added "console.log(req.responseText);" but my browser (firefox) shows me: only "ill-formed" (I'm not sure of the translation, I have polish-languaged brwoser) in the developer toolbar. – NPS Nov 27 '12 at 00:04
  • @NPS: was missing a closing '}' on the else block. Fixed code - please try again. – broofa Nov 27 '12 at 01:04
  • Nope, it was a minor typo, I didn't even bother to mention that. Of course, I fixed that before running the code. Your code started working for me after removing "if (req.status == 200)" (it gets some other code afair and yet it works). And the warning I mentioned disappeared after adding: "req.responseType = 'text';". Thx for help anyway. – NPS Nov 27 '12 at 14:01
  • I added an update to my question, I would be grateful if you could answer that. – NPS Nov 27 '12 at 20:12
0

If you can't simply load the data with XHR or CORS, you could always use the JSON-P method by wrapping it with a JavaScript function and dynamically attaching the script tag to your page.

You would have a server-side script that would accept a callback parameter, and return something like callback1234(/* file data here */);.

Once you have the data, parsing should be trivial, but you will have to write your own parsing functions. Nothing exists for that out of the box.

Brad
  • 159,648
  • 54
  • 349
  • 530