multimodel.cpp 14.5 KB
Newer Older
1 2 3 4
////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
5
// zmoelnig@iem.at
6 7 8 9
//
// Implementation file
//
//    Copyright (c) 1997-1999 Mark Danks.
zmoelnig's avatar
zmoelnig committed
10
//    Copyright (c) Günther Geiger.
zmoelnig's avatar
zmoelnig committed
11
//    Copyright (c) 2001-2011 IOhannes m zmölnig. forum::für::umläute. IEM. zmoelnig@iem.at
12 13 14 15
//    For information on usage and redistribution, and for a DISCLAIMER OF ALL
//    WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////
16

17
#include "multimodel.h"
18 19 20
#include "plugins/modelloader.h"
#include <algorithm> // std::min
#include <string.h>
21
#include <stdio.h>
22

23 24 25 26 27 28 29 30 31 32 33
namespace {
static char mytolower(char in)
{
  if(in<='Z' && in>='A') {
    return in-('Z'-'z');
  }
  return in;
}
};


34 35
CPPEXTERN_NEW_WITH_FOUR_ARGS(multimodel, t_symbol *, A_DEFSYM, t_floatarg,
                             A_DEFFLOAT, t_floatarg, A_DEFFLOAT, t_floatarg, A_DEFFLOAT);
36 37 38 39 40 41 42 43 44 45

/////////////////////////////////////////////////////////
//
// multimodel
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
multimodel :: multimodel(t_symbol *filename, t_floatarg baseModel,
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
46
                         t_floatarg topModel, t_floatarg skipRate)
47 48 49 50 51 52
  :  m_loader(NULL),
     m_size_change_flag(false),
     m_position(256,3),
     m_texture (256,2),
     m_color   (256,4),
     m_normal  (256,3),
53 54
     m_infoOut(gem::RTE::Outlet(this)),
     m_drawType(GL_TRIANGLES)
55
{
56 57 58 59 60 61 62 63
  m_drawTypes.clear();
  m_drawTypes["default"]=m_drawType;
  m_drawTypes["point"]=GL_POINTS;
  m_drawTypes["points"]=GL_POINTS;
  m_drawTypes["line"]=GL_LINES;
  m_drawTypes["lines"]=GL_LINES;
  m_drawTypes["fill"]=GL_TRIANGLES;

64
  inlet_new(this->x_obj, &this->x_obj->ob_pd, &s_float, gensym("mdl_num"));
65

66
  // make sure that there are some characters
67
  if (filename&&filename->s_name&&*filename->s_name) {
68
    openMess(filename->s_name, baseModel, topModel, skipRate);
69
  }
70 71
}

72
/////////////////////////////////////////////////////////
73 74 75
// Destructor
//
/////////////////////////////////////////////////////////
76
multimodel :: ~multimodel(void)
77
{
78
  close();
79 80 81
}

/////////////////////////////////////////////////////////
82
// close
83 84
//
/////////////////////////////////////////////////////////
85
void multimodel :: close(void)
86
{
87 88
  unsigned int i;
  for(i=0; i<m_loaders.size(); i++) {
89 90
    if (m_loaders[i]) {
      m_loaders[i]->close();
91
      delete m_loaders[i];
92
    }
93
    m_loaders[i]=NULL;
94
  }
95
  m_loaders.clear();
96 97 98 99 100 101 102 103 104 105 106 107 108 109
  m_loader = NULL;
}


void multimodel :: applyProperties(void)
{
#if 0
  std::vector<std::string>keys=m_properties.keys();
  unsigned int i;
  for(i=0; i<keys.size(); i++) {
    post("key[%d]=%s ... %d", i, keys[i].c_str(), m_properties.type(keys[i]));
  }
#endif

110
  if(m_loader) {
111
    m_loader->setProperties(m_properties);
112
  }
113 114 115
}

/////////////////////////////////////////////////////////
116
// materialMess
117 118
//
/////////////////////////////////////////////////////////
119 120 121 122 123 124 125 126
void multimodel :: materialMess(int material)
{
  gem::any value=material;
  m_properties.set("usematerials", value);
  applyProperties();
}

/////////////////////////////////////////////////////////
127
// textureMess
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
//
/////////////////////////////////////////////////////////
void multimodel :: textureMess(int state)
{
  std::string textype;
  switch(state) {
  case 0:
    textype="linear";
    break;
  case 1:
    textype="spheremap";
    break;
  case 2:
    textype="UV";
    break;
  default:
    break;
  }
  if(textype.empty()) {
    m_properties.erase("textype");
  } else {
    gem::any value=textype;
    m_properties.set("textype", value);
  }
  applyProperties();
}

/////////////////////////////////////////////////////////
// smoothMess
//
/////////////////////////////////////////////////////////
void multimodel :: smoothMess(t_float fsmooth)
{
  m_properties.set("smooth", fsmooth);
  applyProperties();
}

/////////////////////////////////////////////////////////
166
// reverseMess
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
//
/////////////////////////////////////////////////////////
void multimodel :: reverseMess(bool reverse)
{
  gem::any value=(double)reverse;
  m_properties.set("reverse", value);
  applyProperties();
}
/////////////////////////////////////////////////////////
// matrialMess
//
/////////////////////////////////////////////////////////
void multimodel :: rescaleMess(bool state)
{
  gem::any value=(double)state;
  m_properties.set("rescale", value);
  applyProperties();
}

/////////////////////////////////////////////////////////
187
// groupMess
188 189 190 191 192 193 194 195 196
//
/////////////////////////////////////////////////////////
void multimodel :: groupMess(int state)
{
  gem::any value=state;
  m_properties.set("group", value);
  applyProperties();
}

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
/////////////////////////////////////////////////////////
// drawStyle
//
/////////////////////////////////////////////////////////
void multimodel :: drawMess(int type)
{
  /* raw */
  m_drawType = type;
}

void multimodel :: drawMess(std::string name)
{
  if(0==m_drawTypes.size()) {
    error("unable to change drawstyle");
    return;
  }

  std::transform(name.begin(), name.end(), name.begin(), mytolower);

  std::map<std::string, GLenum>::iterator it=m_drawTypes.find(name);
  if(m_drawTypes.end() == it) {
    error ("unknown draw style '%s'... possible values are:", name.c_str());
    it=m_drawTypes.begin();
    while(m_drawTypes.end() != it) {
      error("\t %s", it->first.c_str());
      ++it;
    }
    return;
  }
  m_drawType=it->second;
}


230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
/////////////////////////////////////////////////////////
// backendMess
//
/////////////////////////////////////////////////////////
void multimodel :: backendMess(t_symbol*s, int argc, t_atom*argv)
{
#if 0
  gem::any value=ids;
  m_properties.set("backends", value);
  applyProperties();
#endif
  int i;

  m_backends.clear();
  if(argc) {
    for(i=0; i<argc; i++) {
      if(A_SYMBOL == argv->a_type) {
        t_symbol *b=atom_getsymbol(argv+i);
        m_backends.push_back(b->s_name);
      } else {
        error("%s must be symbolic", s->s_name);
      }
    }
  } else {
    /* no backend requested, just enumerate them */
    if(m_loader) {
      std::vector<gem::any>atoms;
      gem::any value;
      gem::Properties props;
      std::vector<std::string> backends;
      props.set("backends", value);
      m_loader->getProperties(props);
      if(props.type("backends")!=gem::Properties::UNSET) {
        props.get("backends", backends);
      }
      atoms.clear();
      atoms.push_back(value=(int)(backends.size()));
      m_infoOut.send("loaders", atoms);
      if(!backends.empty()) {
        for(i=0; i<backends.size(); i++) {
          atoms.clear();
          atoms.push_back(value=backends[i]);
          post("loader[%d] %s", i, backends[i].c_str());
          m_infoOut.send("loader", atoms);
        }
      } else {
        post("no model-loading backends found!");
      }
    }
  }
}

/////////////////////////////////////////////////////////
// openMess
//
/////////////////////////////////////////////////////////
void multimodel :: openMess(const std::string&filename,
                            float baseModel, float topModel, float skipRate)
{
  int skipRatei=static_cast<int>(skipRate);
  int topModeli=static_cast<int>(topModel);
  int baseModeli=static_cast<int>(baseModel);
  if (skipRatei == 0) {
293
    if (topModeli == 0) {
294
      open(filename, 0, baseModeli, 1);
295
    } else {
296
      open(filename, baseModeli, topModeli, 1);
297 298 299
    }
  } else {
    open(filename, baseModeli, topModeli, skipRatei);
300 301
  }
}
302 303
void multimodel :: open(const std::string&filename, int baseModel,
                        int topModel, int skipRate)
304
{
305 306 307 308
  gem::Properties wantProps = m_properties;
  if(!m_backends.empty()) {
    wantProps.set("backends", m_backends);
  }
309
  std::vector<gem::plugins::modelloader*>loaders;
310

311
  if (!topModel) {
312
    error("requires an int for number of models");
313 314 315
    return;
  }
  if (baseModel > topModel) {
316
    error("top range less than base model");
317 318
    return;
  }
319 320 321
  if (skipRate < 1) {
    skipRate = 1;
  }
322 323
  char preName[256];
  char postName[256];
324

325
  int i = 0;
326
  const char *strPtr = filename.c_str();
327 328 329 330
  while (strPtr[i] && strPtr[i] != '*') {
    preName[i] = strPtr[i];
    i++;
  }
331

332
  if (!strPtr[i]) {
333
    error("unable to find * in file name");
334 335 336
    return;
  }

337
  preName[i] = '\0';
338 339
  strncpy(postName, &(strPtr[i+1]), 255);
  postName[255]='\0';
340

341
  // need to figure out how many filenames there are to load
342
  int numModels = (topModel + 1 - baseModel) / skipRate;
343 344 345

  int realNum = baseModel;
  char bufName[MAXPDSTRING];
346 347
  canvas_makefilename(const_cast<t_canvas*>(getCanvas()), preName, bufName,
                      MAXPDSTRING);
348

IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
349 350
  char newName[MAXPDSTRING];
  newName[0]=0;
351

352
  for (i = 0; i < numModels; i++, realNum += skipRate) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
353 354 355
    snprintf(newName, MAXPDSTRING, "%s%d%s", bufName, realNum, postName);
    newName[MAXPDSTRING-1]=0;
    verbose(1, "trying to load '%s'", newName);
356 357

    gem::plugins::modelloader*loader=gem::plugins::modelloader::getInstance();
358 359 360
    if(!loader) {
      break;
    }
361

362
    if(loader->open(newName, wantProps)) {
363
      loaders.push_back(loader);
364
    } else {
365
      delete loader;
366
      break;
367
    }
368 369
  }

370
  if(loaders.size()!=numModels) {
371
    /* ouch, something went wrong! */
372 373
    error("failed to load model#%d of %d (%s)...resetting to original models",
          i, numModels, newName);
374 375
    unsigned int ui;
    for(ui=0; ui<loaders.size(); ui++) {
376
      if(loaders[ui]) {
377
        delete loaders[ui];
378
      }
379
      loaders[ui]=NULL;
380 381 382
    }
    loaders.clear();
    return;
383 384
  }

385
  close();
386
  m_loaders=loaders;
387
  if(m_loaders.size()>0) {
388
    m_loader = m_loaders[0];
389
  }
390

391
  post("loaded models: %s %s from %d to %d skipping %d",
392 393
       bufName, postName, baseModel, topModel, skipRate);

394 395
  getVBOarray();
  setModified();
396 397
}

398
/////////////////////////////////////////////////////////
399
// changeModel
400 401
//
/////////////////////////////////////////////////////////
402
void multimodel :: changeModel(int modelNum)
403
{
404 405 406
  if (modelNum < 0 || ((unsigned int)modelNum) >= m_loaders.size()) {
    error("selection %d out of range: 0..%d", modelNum, m_loaders.size()-1);
    return;
407
  }
408
  m_loader = m_loaders[modelNum];
409

410 411
  getVBOarray();
  setModified();
412
}
413

414 415 416
void multimodel :: startRendering()
{
  if (m_loader) {
417 418 419 420 421
    copyArray(m_loader->getVector("vertices"), m_position);
    copyArray(m_loader->getVector("texcoords"), m_texture);
    copyArray(m_loader->getVector("normals"), m_normal);
    copyArray(m_loader->getVector("colors"), m_color);
  }
422 423 424 425 426
}
/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
427
void multimodel :: render(GemState *state)
428
{
429 430 431
  if(!m_loader) {
    return;
  }
432

433 434
  if ( !m_position.vbo || !m_texture.vbo || !m_color.vbo || !m_normal.vbo
       || m_size_change_flag ) {
435 436 437 438
    createVBO();
    m_size_change_flag = false;
  }
  getVBOarray();
439

440
  std::vector<unsigned int> sizeList;
441

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
  if(m_position.render()) {
    glVertexPointer(m_position.dimen, GL_FLOAT, 0, 0);
    glEnableClientState(GL_VERTEX_ARRAY);
    sizeList.push_back(m_position.size);
  }
  if(m_texture.render()) {
    glTexCoordPointer(m_texture.dimen, GL_FLOAT, 0, 0);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    sizeList.push_back(m_texture.size);
  }
  if(m_color.render()) {
    glColorPointer(m_color.dimen, GL_FLOAT, 0, 0);
    glEnableClientState(GL_COLOR_ARRAY);
    sizeList.push_back(m_color.size);
  }
  if(m_normal.render()) {
    glNormalPointer(GL_FLOAT, 0, 0);
    glEnableClientState(GL_NORMAL_ARRAY);
    sizeList.push_back(m_normal.size);
461
  }
462

463 464
  if ( sizeList.size() > 0 ) {
    unsigned int npoints = *std::min_element(sizeList.begin(),sizeList.end());
465
    glDrawArrays(m_drawType, 0, npoints);
466 467 468 469 470 471 472 473 474 475 476 477 478
  }

  if ( m_position.enabled ) {
    glDisableClientState(GL_VERTEX_ARRAY);
  }
  if ( m_color.enabled    ) {
    glDisableClientState(GL_COLOR_ARRAY);
  }
  if ( m_texture.enabled  ) {
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  }
  if ( m_normal.enabled   ) {
    glDisableClientState(GL_NORMAL_ARRAY);
479
  }
480 481 482 483 484 485 486 487
}

/////////////////////////////////////////////////////////
// static member function
//
/////////////////////////////////////////////////////////
void multimodel :: obj_setupCallback(t_class *classPtr)
{
488
  CPPEXTERN_MSG4(classPtr, "open", openMess, std::string, float, float, float);
489
  CPPEXTERN_MSG1(classPtr, "mdl_num", changeModel, int);
490

491 492
  CPPEXTERN_MSG1(classPtr, "rescale", rescaleMess, bool);
  CPPEXTERN_MSG1(classPtr, "smooth", smoothMess, float);
493 494 495 496 497 498
  CPPEXTERN_MSG1(classPtr, "revert", reverseMess, bool);
  CPPEXTERN_MSG1(classPtr, "material", materialMess, int);
  CPPEXTERN_MSG1(classPtr, "texture", textureMess, int);
  CPPEXTERN_MSG1(classPtr, "group", groupMess, int);
  CPPEXTERN_MSG (classPtr, "loader", backendMess);

499 500
  CPPEXTERN_MSG1(classPtr, "draw", drawMess, std::string);
}
501 502 503 504 505 506 507 508 509

void multimodel :: createVBO(void)
{
  m_position.create();
  m_texture .create();
  m_color   .create();
  m_normal  .create();
}

510
void multimodel :: copyArray(const std::vector<std::vector<float> >&tab,
511
                             gem::VertexBuffer&vb)
512 513 514 515
{
  unsigned int size(0), i(0), npts(0);

  //~std::vector<std::vector<float> > tab = m_loader->getVector(vectorName);
516 517 518
  if ( tab.empty() ) {
    return;
  }
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
  size=tab.size();

  if(size!=vb.size) {
    vb.resize(size);
    m_size_change_flag=true;
  }

  for ( i = 0 ; i < size ; i++ ) {
    for ( int j=0 ; j< std::min(vb.dimen,(unsigned int)tab[i].size()) ; j++) {
      vb.array[i*vb.dimen + j] = tab[i][j];
    }
  }
  vb.dirty=true;
  vb.enabled=true;
}

535 536 537
void multimodel :: copyAllArrays()
{
  if (m_loader && m_loader->needRefresh()) {
538 539 540 541 542 543 544 545
    copyArray(m_loader->getVector("vertices"), m_position);
    copyArray(m_loader->getVector("texcoords"), m_texture);
    copyArray(m_loader->getVector("normals"), m_normal);
    copyArray(m_loader->getVector("colors"), m_color);
    m_loader->unsetRefresh();
  }
}

546 547 548
void multimodel :: getVBOarray()
{
  if (m_loader && m_loader->needRefresh()) {
549

550 551
    std::vector<gem::plugins::modelloader::VBOarray>  vboArray =
      m_loader->getVBOarray();
552

553
    if ( vboArray.empty() ) {
554 555
      copyAllArrays();
    } else {
556 557
      for (int i = 0; i<vboArray.size(); i++) {
        switch (vboArray[i].type) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
558 559 560 561 562 563 564 565 566 567 568 569 570 571
        case gem::VertexBuffer::GEM_VBO_VERTICES:
          copyArray(*vboArray[i].data, m_position);
          break;
        case gem::VertexBuffer::GEM_VBO_TEXCOORDS:
          copyArray(*vboArray[i].data, m_texture);
          break;
        case gem::VertexBuffer::GEM_VBO_NORMALS:
          copyArray(*vboArray[i].data, m_normal);
          break;
        case gem::VertexBuffer::GEM_VBO_COLORS:
          copyArray(*vboArray[i].data, m_color);
          break;
        default:
          error("VBO type %d not supported\n",vboArray[i].type);
572 573 574 575 576 577
        }
      }
      m_loader->unsetRefresh();
    }
  }
}