multimodel.cpp 13.7 KB
Newer Older
1 2 3 4 5 6 7 8 9
////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// zmoelnig@iem.kug.ac.at
//
// 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
#include "Gem/State.h"
19 20 21
#include "plugins/modelloader.h"
#include <algorithm> // std::min
#include <string.h>
22
#include <stdio.h>
23

24
CPPEXTERN_NEW_WITH_FOUR_ARGS(multimodel, t_symbol *, A_DEFSYM, t_floatarg, A_DEFFLOAT, t_floatarg, A_DEFFLOAT, t_floatarg, A_DEFFLOAT);
25 26 27 28 29 30 31 32 33 34

/////////////////////////////////////////////////////////
//
// multimodel
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
multimodel :: multimodel(t_symbol *filename, t_floatarg baseModel,
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
35
                         t_floatarg topModel, t_floatarg skipRate)
36 37 38 39 40 41 42
  :  m_loader(NULL),
     m_size_change_flag(false),
     m_position(256,3),
     m_texture (256,2),
     m_color   (256,4),
     m_normal  (256,3),
     m_infoOut(gem::RTE::Outlet(this))
43 44
{
  inlet_new(this->x_obj, &this->x_obj->ob_pd, &s_float, gensym("mdl_num"));
45 46
  post("MULTIMODEL");

47
  // make sure that there are some characters
48
  if (filename&&filename->s_name&&*filename->s_name) {
49
    openMess(filename->s_name, baseModel, topModel, skipRate);
50
  }
51 52
}

53
/////////////////////////////////////////////////////////
54 55 56
// Destructor
//
/////////////////////////////////////////////////////////
57
multimodel :: ~multimodel(void)
58
{
59
  close();
60 61 62
}

/////////////////////////////////////////////////////////
63
// close
64 65
//
/////////////////////////////////////////////////////////
66
void multimodel :: close(void)
67
{
68 69
  unsigned int i;
  for(i=0; i<m_loaders.size(); i++) {
70 71
    if (m_loaders[i]) {
      m_loaders[i]->close();
72
      delete m_loaders[i];
73
    }
74
    m_loaders[i]=NULL;
75
  }
76
  m_loaders.clear();
77 78 79 80 81 82 83 84 85 86 87 88 89 90
  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

91
  if(m_loader) {
92
    m_loader->setProperties(m_properties);
93
  }
94 95 96
}

/////////////////////////////////////////////////////////
97
// materialMess
98 99
//
/////////////////////////////////////////////////////////
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 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 230 231 232 233 234 235 236 237 238 239 240 241 242
void multimodel :: materialMess(int material)
{
  gem::any value=material;
  m_properties.set("usematerials", value);
  applyProperties();
}

/////////////////////////////////////////////////////////
// materialMess
//
/////////////////////////////////////////////////////////
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();
}

/////////////////////////////////////////////////////////
// rescaleMess
//
/////////////////////////////////////////////////////////
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();
}

/////////////////////////////////////////////////////////
// matrialMess
//
/////////////////////////////////////////////////////////
void multimodel :: groupMess(int state)
{
  gem::any value=state;
  m_properties.set("group", value);
  applyProperties();
}

/////////////////////////////////////////////////////////
// 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;
      t_atom at;
      t_atom*ap=&at;
      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) {
243
    if (topModeli == 0) {
244
      open(filename, 0, baseModeli, 1);
245
    } else {
246
      open(filename, baseModeli, topModeli, 1);
247 248 249
    }
  } else {
    open(filename, baseModeli, topModeli, skipRatei);
250 251
  }
}
252
void multimodel :: open(const std::string&filename, int baseModel, int topModel, int skipRate)
253
{
254 255 256 257
  gem::Properties wantProps = m_properties;
  if(!m_backends.empty()) {
    wantProps.set("backends", m_backends);
  }
258
  std::vector<gem::plugins::modelloader*>loaders;
259

260
  if (!topModel) {
261
    error("requires an int for number of models");
262 263 264
    return;
  }
  if (baseModel > topModel) {
265
    error("top range less than base model");
266 267
    return;
  }
268 269 270
  if (skipRate < 1) {
    skipRate = 1;
  }
271 272
  char preName[256];
  char postName[256];
273

274
  int i = 0;
275
  const char *strPtr = filename.c_str();
276 277 278 279
  while (strPtr[i] && strPtr[i] != '*') {
    preName[i] = strPtr[i];
    i++;
  }
280

281
  if (!strPtr[i]) {
282
    error("unable to find * in file name");
283 284 285
    return;
  }

286
  preName[i] = '\0';
287 288
  strncpy(postName, &(strPtr[i+1]), 255);
  postName[255]='\0';
289

290
  // need to figure out how many filenames there are to load
291
  int numModels = (topModel + 1 - baseModel) / skipRate;
292 293 294

  int realNum = baseModel;
  char bufName[MAXPDSTRING];
zmoelnig's avatar
zmoelnig committed
295
  canvas_makefilename(const_cast<t_canvas*>(getCanvas()), preName, bufName, MAXPDSTRING);
296

IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
297 298
  char newName[MAXPDSTRING];
  newName[0]=0;
299

300
  for (i = 0; i < numModels; i++, realNum += skipRate) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
301 302 303
    snprintf(newName, MAXPDSTRING, "%s%d%s", bufName, realNum, postName);
    newName[MAXPDSTRING-1]=0;
    verbose(1, "trying to load '%s'", newName);
304 305

    gem::plugins::modelloader*loader=gem::plugins::modelloader::getInstance();
306 307 308
    if(!loader) {
      break;
    }
309

310
    if(loader->open(newName, wantProps)) {
311
      loaders.push_back(loader);
312
    } else {
313
      delete loader;
314
      break;
315
    }
316 317
  }

318
  if(loaders.size()!=numModels) {
319
    /* ouch, something went wrong! */
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
320
    error("failed to load model#%d of %d (%s)...resetting to original models", i, numModels, newName);
321 322
    unsigned int ui;
    for(ui=0; ui<loaders.size(); ui++) {
323
      if(loaders[ui]) {
324
        delete loaders[ui];
325
      }
326
      loaders[ui]=NULL;
327 328 329
    }
    loaders.clear();
    return;
330 331
  }

332
  close();
333
  m_loaders=loaders;
334
  if(m_loaders.size()>0) {
335
    m_loader = m_loaders[0];
336
  }
337

338
  post("loaded models: %s %s from %d to %d skipping %d",
339 340
       bufName, postName, baseModel, topModel, skipRate);

341 342
  getVBOarray();
  setModified();
343 344
}

345
/////////////////////////////////////////////////////////
346
// changeModel
347 348
//
/////////////////////////////////////////////////////////
349
void multimodel :: changeModel(int modelNum)
350
{
351 352 353
  if (modelNum < 0 || ((unsigned int)modelNum) >= m_loaders.size()) {
    error("selection %d out of range: 0..%d", modelNum, m_loaders.size()-1);
    return;
354
  }
355
  m_loader = m_loaders[modelNum];
356

357 358
  getVBOarray();
  setModified();
359
}
360

361 362 363
void multimodel :: startRendering()
{
  if (m_loader) {
364 365 366 367 368
    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);
  }
369 370 371 372 373
}
/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
374
void multimodel :: render(GemState *state)
375
{
376 377 378
  if(!m_loader) {
    return;
  }
379 380 381 382 383 384

  if ( !m_position.vbo || !m_texture.vbo || !m_color.vbo || !m_normal.vbo || m_size_change_flag ) {
    createVBO();
    m_size_change_flag = false;
  }
  getVBOarray();
385

386
  std::vector<unsigned int> sizeList;
387

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
  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);
407
  }
408

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
  if ( sizeList.size() > 0 ) {
    unsigned int npoints = *std::min_element(sizeList.begin(),sizeList.end());
    glDrawArrays(GL_TRIANGLES, 0, npoints);
  }

  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);
425
  }
426 427 428 429 430 431 432 433
}

/////////////////////////////////////////////////////////
// static member function
//
/////////////////////////////////////////////////////////
void multimodel :: obj_setupCallback(t_class *classPtr)
{
zmoelnig's avatar
zmoelnig committed
434
  class_addmethod(classPtr, reinterpret_cast<t_method>(&multimodel::openMessCallback),
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
435
                  gensym("open"), A_SYMBOL, A_FLOAT, A_DEFFLOAT, A_DEFFLOAT, A_NULL);
436
  CPPEXTERN_MSG1(classPtr, "mdl_num", changeModel, int);
437

438 439
  CPPEXTERN_MSG1(classPtr, "rescale", rescaleMess, bool);
  CPPEXTERN_MSG1(classPtr, "smooth", smoothMess, float);
440 441 442 443 444
  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);
445
}
446
void multimodel :: openMessCallback(void *data, t_symbol *filesymbol, t_float baseModel,
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
447
                                    t_floatarg topModel, t_floatarg skipRate)
448
{
449
  GetMyClass(data)->openMess(filesymbol->s_name, baseModel, topModel, skipRate);
450
}
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465


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

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

  //~std::vector<std::vector<float> > tab = m_loader->getVector(vectorName);
466 467 468
  if ( tab.empty() ) {
    return;
  }
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
  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;
}

485 486 487
void multimodel :: copyAllArrays()
{
  if (m_loader && m_loader->needRefresh()) {
488 489 490 491 492 493 494 495
    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();
  }
}

496 497 498
void multimodel :: getVBOarray()
{
  if (m_loader && m_loader->needRefresh()) {
499 500 501

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

502
    if ( vboArray.empty() ) {
503 504
      copyAllArrays();
    } else {
505 506
      for (int i = 0; i<vboArray.size(); i++) {
        switch (vboArray[i].type) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
507 508 509 510 511 512 513 514 515 516 517 518 519 520
        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);
521 522 523 524 525 526
        }
      }
      m_loader->unsetRefresh();
    }
  }
}