multimodel.cpp 9.84 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// zmoelnig@iem.kug.ac.at
//
// Implementation file
//
//    Copyright (c) 1997-1999 Mark Danks.
//    Copyright (c) Gnther Geiger.
//    Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::fr::umlute. IEM
//    For information on usage and redistribution, and for a DISCLAIMER OF ALL
//    WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////
#include "multimodel.h"

#include <stdio.h>

CPPEXTERN_NEW_WITH_FOUR_ARGS(multimodel, t_symbol *, A_DEFSYM, t_floatarg, A_DEFFLOAT, t_floatarg, A_DEFFLOAT, t_floatarg, A_DEFFLOAT)

  multimodel::multiModelCache *multimodel::s_modelCache = NULL;

/////////////////////////////////////////////////////////
//
// multimodel
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
multimodel :: multimodel(t_symbol *filename, t_floatarg baseModel,
			 t_floatarg topModel, t_floatarg skipRate)
34
35
36
  : m_loadedCache(NULL), 
    m_numModels(0), m_curModel(-1), 
    m_rescaleModel(1),
37
    m_textype(GLM_TEX_DEFAULT),
38
    m_rebuild(true),
zmoelnig's avatar
zmoelnig committed
39
    m_currentH(1.f), m_currentW(1.f)
40
41
42
43
44
{
  inlet_new(this->x_obj, &this->x_obj->ob_pd, &s_float, gensym("mdl_num"));

  // make sure that there are some characters
  if (filename->s_name[0]) { 
zmoelnig's avatar
zmoelnig committed
45
46
47
48
49
50
    int skipRatei=static_cast<int>(skipRate);
    int topModeli=static_cast<int>(topModel);
    int baseModeli=static_cast<int>(baseModel);
    if (skipRatei == 0) {
      if (topModeli == 0)
        openMess(filename, 0, baseModeli, 1);
51
      else
zmoelnig's avatar
zmoelnig committed
52
        openMess(filename, baseModeli, topModeli, 1);
53
    }
zmoelnig's avatar
zmoelnig committed
54
    else openMess(filename, baseModeli, topModeli, skipRatei);
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  }
}

/////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
multimodel :: ~multimodel()
{
  cleanMultimodel();
}

/////////////////////////////////////////////////////////
// cleanMultimodel
//
/////////////////////////////////////////////////////////
void multimodel :: cleanMultimodel()
{
  if (m_numModels) {
    // decrement the reference count
    m_loadedCache->refCount--;
76
    
77
78
79
80
    // If the refCount == 0, then destroy the cache
    if (m_loadedCache->refCount == 0) {
      // find the cache
      multiModelCache *ptr = s_modelCache;
81
      
82
83
      // if the loaded cache is the first cache in the list
      if (m_loadedCache == s_modelCache) {
84
85
        s_modelCache = m_loadedCache->next;
        delete m_loadedCache;
86
87
      }
      else {
88
89
90
91
92
93
        while (ptr && ptr->next != m_loadedCache) ptr = ptr->next;
        if (!ptr) error("unable to find model cache!");
        else {
          ptr->next = m_loadedCache->next;
          delete m_loadedCache;
        }
94
95
      }
    }
96
    
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    m_loadedCache = NULL;
    m_numModels = 0;
  }
}

/////////////////////////////////////////////////////////
// openMess
//
/////////////////////////////////////////////////////////
void multimodel :: openMess(t_symbol *filename, int baseModel, int topModel, int skipRate)
{
  cleanMultimodel();
    
  if (!topModel) {
111
    error("requires an int for number of models");
112
113
114
    return;
  }
  if (baseModel > topModel) {
115
    error("top range less than base model");
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
    return;
  }
  if (skipRate < 1) skipRate = 1;

  // have we already loaded the model?
  multiModelCache *cache = s_modelCache;
  int found = 0;
  while (!found && cache) {
    if (baseModel == cache->baseModel &&
	topModel == cache->topModel &&
	skipRate == cache->skipRate &&
	!strcmp(filename->s_name, cache->modelName)) found = 1;
    else cache = cache->next;
  }
    
  // yep, we have it
  if (found) {
    m_loadedCache = cache;
    m_loadedCache->refCount++;
    m_curModel = 0;
    m_numModels = m_loadedCache->numModels;
137
    post("loaded models: %s from %d to %d skipping %d",
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
	 filename->s_name, baseModel, topModel, skipRate);
    return;
  }

  // nope, so create the new cache
  // find the * in the filename    
  char preName[256];
  char postName[256];
    
  int i = 0;
  char *strPtr = filename->s_name;
  while (strPtr[i] && strPtr[i] != '*') {
    preName[i] = strPtr[i];
    i++;
  }
    
  if (!strPtr[i]) {
155
    error("unable to find * in file name");
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
    return;
  }

  preName[i] = '\0';    
  strcpy(postName, &(strPtr[i+1]));
    
  // need to figure out how many filenames there are to load
  m_numModels = (topModel + 1 - baseModel) / skipRate;

  // create the new cache
  multiModelCache *newCache = new multiModelCache(filename->s_name);
  newCache->models = new GLint[m_numModels];
  newCache->realmodels = new GLMmodel*[m_numModels];
  newCache->numModels = m_numModels;
  newCache->baseModel = baseModel;
  newCache->topModel = topModel;
  newCache->skipRate = skipRate;

  int realNum = baseModel;
  char bufName[MAXPDSTRING];
  canvas_makefilename(getCanvas(), preName, bufName, MAXPDSTRING);

  for (i = 0; i < m_numModels; i++, realNum += skipRate) {
    char newName[256];
    sprintf(newName, "%s%d%s", bufName, realNum, postName);
    
    // read the object in
    GLMmodel *m_model = glmReadOBJ(newName);
    if (!m_model) {
      // a load failed, blow away the cache
      newCache->numModels = i;
      delete newCache;
      m_numModels = 0;
      return;
    }
    // set the size to -1 to 1
    //
    if (m_rescaleModel)
      glmUnitize(m_model);
    
    // generate normals if this
    // object doesn't have them.
    //
    glmFacetNormals (m_model);
    glmVertexNormals(m_model, 90); /* SMOOTH */
    
202
    glmTexture(m_model, m_textype, 1, 1);
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
    newCache->realmodels[i]=m_model;
  }
  m_curModel = 0;

  m_loadedCache = newCache;
  newCache->refCount++;

  // insert the cache at the end of the linked list
  multiModelCache *ptr = s_modelCache;
    
  if (!ptr) s_modelCache = newCache;
  else {
    while(ptr->next) ptr = ptr->next;
    ptr->next = newCache;
  }

219
  post("loaded models: %s %s from %d to %d skipping %d",
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
       bufName, postName, baseModel, topModel, skipRate);
  this->setModified();
}
/////////////////////////////////////////////////////////
// buildList
//
/////////////////////////////////////////////////////////
void multimodel :: buildList()
{
  int i = m_numModels;
  if (m_numModels && m_loadedCache)
    while(i--)glDeleteLists(m_loadedCache->models[i], 1);

  i=0;
  while(i<m_numModels){
    m_loadedCache->models[i]=glmList( m_loadedCache->realmodels[i], GLM_SMOOTH | GLM_TEXTURE);
236
    glmTexture(m_loadedCache->realmodels[i], m_textype, 1, 1);
237

238
239
240
241
    i++;
  }
}

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/////////////////////////////////////////////////////////
// materialMess
//
/////////////////////////////////////////////////////////
void multimodel :: textureMess(int state)
{
  switch(state) {
  case 0: 
    m_textype=GLM_TEX_LINEAR; 
    break;
  case 1: 
    m_textype=GLM_TEX_SPHEREMAP; 
    break;
  case 2:
    m_textype=GLM_TEX_UV; 
    break;
  default:
    m_textype=GLM_TEX_DEFAULT; 
  }
  m_rebuild=true;
}


265
266
267
268
269
270
271
272
273
void multimodel :: startRendering()
{
  // build a display list
  buildList();
}
/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
274
void multimodel :: render(GemState *state)
275
276
{
  if (!m_numModels || !m_loadedCache) return;
277
278
279
  if (state && (m_currentH != state->texCoordX(2) || m_currentW != state->texCoordY(2)))
    {
      m_rebuild=true;
280
    }
281
282
283
284
285
  if(m_rebuild) {
    m_currentH = state->texCoordX(2);
    m_currentW = state->texCoordY(2);
    buildList();
    m_rebuild=false;
286
287
288
289
  }
  if (!m_loadedCache->models[m_curModel])return;


290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  glCallList(m_loadedCache->models[m_curModel]);
}

/////////////////////////////////////////////////////////
// rescaleMess
//
/////////////////////////////////////////////////////////
void multimodel :: rescaleMess(int state)
{
  m_rescaleModel = state;
}

/////////////////////////////////////////////////////////
// changeModel
//
/////////////////////////////////////////////////////////
void multimodel :: changeModel(int modelNum)
{
  if (modelNum >= m_numModels)
    {
310
      error("selection number too high: %d (max num is %d)", modelNum, m_numModels-1);
311
312
313
314
      return;
    }
  else if (modelNum < 0)
    {
315
      error("selection number must be > 0");
316
317
318
319
320
321
322
323
324
325
326
327
      return;
    }
  m_curModel = modelNum;
  this->setModified();
}

/////////////////////////////////////////////////////////
// static member function
//
/////////////////////////////////////////////////////////
void multimodel :: obj_setupCallback(t_class *classPtr)
{
zmoelnig's avatar
zmoelnig committed
328
  class_addmethod(classPtr, reinterpret_cast<t_method>(&multimodel::openMessCallback),
329
		  gensym("open"), A_SYMBOL, A_FLOAT, A_DEFFLOAT, A_DEFFLOAT, A_NULL);
zmoelnig's avatar
zmoelnig committed
330
  class_addmethod(classPtr, reinterpret_cast<t_method>(&multimodel::changeModelCallback),
331
		  gensym("mdl_num"), A_FLOAT, A_NULL);
zmoelnig's avatar
zmoelnig committed
332
  class_addmethod(classPtr, reinterpret_cast<t_method>(&multimodel::rescaleMessCallback),
333
		  gensym("rescale"), A_FLOAT, A_NULL);
zmoelnig's avatar
zmoelnig committed
334
  class_addmethod(classPtr, reinterpret_cast<t_method>(&multimodel::textureMessCallback),
335
		  gensym("texture"), A_FLOAT, A_NULL);
336
337
338
339
}
void multimodel :: openMessCallback(void *data, t_symbol *filename, t_floatarg baseModel,
				    t_floatarg topModel, t_floatarg skipRate)
{
zmoelnig's avatar
zmoelnig committed
340
341
342
343
344
  int skipRatei=static_cast<int>(skipRate);
  int topModeli=static_cast<int>(topModel);
  int baseModeli=static_cast<int>(baseModel);

  if (skipRatei == 0)
345
    {
zmoelnig's avatar
zmoelnig committed
346
347
      if (topModeli == 0)
	GetMyClass(data)->openMess(filename, 0, topModeli, 0);
348
      else
zmoelnig's avatar
zmoelnig committed
349
	GetMyClass(data)->openMess(filename, baseModeli, topModeli, 0);
350
351
    }
  else
zmoelnig's avatar
zmoelnig committed
352
    GetMyClass(data)->openMess(filename, baseModeli, topModeli, skipRatei);
353
354
355
}
void multimodel :: changeModelCallback(void *data, t_floatarg modelNum)
{
zmoelnig's avatar
zmoelnig committed
356
  GetMyClass(data)->changeModel(static_cast<int>(modelNum));
357
358
359
}
void multimodel :: rescaleMessCallback(void *data, t_floatarg state)
{
zmoelnig's avatar
zmoelnig committed
360
  GetMyClass(data)->rescaleMess(static_cast<int>(state));
361
}
362
363
void multimodel :: textureMessCallback(void *data, t_floatarg state)
{
zmoelnig's avatar
zmoelnig committed
364
  GetMyClass(data)->textureMess(static_cast<int>(state));
365
}