~ubuntu-branches/ubuntu/warty/aqsis/warty

« back to all changes in this revision

Viewing changes to render/procedural.cpp

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-08-24 07:25:04 UTC
  • Revision ID: james.westby@ubuntu.com-20040824072504-zf993vnevvisdsvb
Tags: upstream-0.9.1
ImportĀ upstreamĀ versionĀ 0.9.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Aqsis
 
2
// Copyright ļæ½ 1997 - 2001, Paul C. Gregory
 
3
//
 
4
// Contact: pgregory@aqsis.com
 
5
//
 
6
// This library is free software; you can redistribute it and/or
 
7
// modify it under the terms of the GNU General Public
 
8
// License as published by the Free Software Foundation; either
 
9
// version 2 of the License, or (at your option) any later version.
 
10
//
 
11
// This library is distributed in the hope that it will be useful,
 
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
// General Public License for more details.
 
15
//
 
16
// You should have received a copy of the GNU General Public
 
17
// License along with this library; if not, write to the Free Software
 
18
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
 
 
20
/**
 
21
        \file
 
22
        \brief Implements the classes and support structures for 
 
23
                handling RenderMan Procedural primitives.
 
24
        \author Jonathan Merritt (j.merritt@pgrad.unimelb.edu.au)
 
25
*/
 
26
 
 
27
#ifdef  WIN32
 
28
#pragma warning(disable : 4786)
 
29
#endif
 
30
 
 
31
#include <stdio.h>
 
32
#include <string.h>
 
33
#include <list>
 
34
#include <map>
 
35
 
 
36
#include "aqsis.h"
 
37
#include "rifile.h"
 
38
#include "imagebuffer.h"
 
39
#include "micropolygon.h"
 
40
#include "renderer.h"
 
41
#include "procedural.h"
 
42
#include "plugins.h"
 
43
#include "librib2ri.h"
 
44
#include "librib.h"
 
45
#include "bdec.h"
 
46
#include "libribtypes.h"
 
47
#include "parserstate.h"
 
48
 
 
49
#ifdef AQSIS_SYSTEM_WIN32
 
50
#include <io.h>
 
51
#else
 
52
#include <unistd.h>
 
53
#endif
 
54
 
 
55
 
 
56
 
 
57
START_NAMESPACE( Aqsis )
 
58
 
 
59
 
 
60
/**
 
61
 * CqProcedural constructor.
 
62
 */
 
63
CqProcedural::CqProcedural() : CqSurface()
 
64
{
 
65
    STATS_INC( GEO_prc_created );
 
66
}
 
67
 
 
68
/**
 
69
 * CqProcedural copy constructor.
 
70
 */
 
71
CqProcedural::CqProcedural(RtPointer data, CqBound &B, RtProcSubdivFunc subfunc, RtProcFreeFunc freefunc ) : CqSurface()
 
72
{
 
73
    m_pData = data;
 
74
    m_Bound = B;
 
75
    m_pSubdivFunc = subfunc;
 
76
    m_pFreeFunc = freefunc;
 
77
 
 
78
    m_pconStored = QGetRenderContext()->pconCurrent();
 
79
 
 
80
    STATS_INC( GEO_prc_created );
 
81
}
 
82
 
 
83
 
 
84
 
 
85
 
 
86
TqInt CqProcedural::Split( std::vector<boost::shared_ptr<CqBasicSurface> >& aSplits )
 
87
{
 
88
    // Store current context, set current context to the stored one
 
89
    boost::shared_ptr<CqModeBlock> pconSave = QGetRenderContext()->pconCurrent( m_pconStored );
 
90
 
 
91
        m_pconStored->m_pattrCurrent = m_pAttributes;
 
92
        ADDREF(m_pAttributes);
 
93
 
 
94
        m_pconStored->m_ptransCurrent = m_pTransform;
 
95
        ADDREF(m_pTransform);
 
96
 
 
97
    /// \note: The bound is in "raster" coordinates by now, as during posting to the imagebuffer
 
98
        /// the the Culling routines do the job for us, see CqBasicSurface::CacheRasterBound.
 
99
        CqBound bound = m_Bound;
 
100
//    bound.Transform(QGetRenderContext()->matSpaceToSpace("camera", "raster"));
 
101
    float detail = ( bound.vecMax().x() - bound.vecMin().x() ) * ( bound.vecMax().y() - bound.vecMin().y() );
 
102
    //std::cout << "detail: " << detail << std::endl;
 
103
 
 
104
    // Call the procedural secific Split()
 
105
    RiAttributeBegin();
 
106
 
 
107
    m_pSubdivFunc(m_pData, detail);
 
108
 
 
109
    RiAttributeEnd();
 
110
 
 
111
    // restore saved context
 
112
    QGetRenderContext()->pconCurrent( pconSave );
 
113
 
 
114
    STATS_INC( GEO_prc_split );
 
115
 
 
116
    return 0;
 
117
}
 
118
 
 
119
 
 
120
//---------------------------------------------------------------------
 
121
/** Transform the quadric primitive by the specified matrix.
 
122
 */
 
123
 
 
124
void    CqProcedural::Transform( const CqMatrix& matTx, const CqMatrix& matITTx, const CqMatrix& matRTx, TqInt iTime )
 
125
{
 
126
    m_Bound.Transform( matTx );
 
127
}
 
128
 
 
129
 
 
130
/**
 
131
 * CqProcedural destructor.
 
132
 */
 
133
CqProcedural::~CqProcedural()
 
134
{
 
135
    if( m_pFreeFunc ) m_pFreeFunc( m_pData );
 
136
}
 
137
 
 
138
 
 
139
//----------------------------------------------------------------------
 
140
// RiProcFree()
 
141
//
 
142
extern "C" RtVoid       RiProcFree( RtPointer data )
 
143
{
 
144
    free(data);
 
145
}
 
146
 
 
147
 
 
148
class CqRiProceduralPlugin : CqPluginBase
 
149
{
 
150
private:
 
151
    void *( *m_ppvfcts ) ( char * );
 
152
    void ( *m_pvfctpvf ) ( void *, float );
 
153
    void ( *m_pvfctpv ) ( void * );
 
154
    void *m_ppriv;
 
155
    void *m_handle;
 
156
    bool m_bIsValid;
 
157
    CqString m_Error;
 
158
 
 
159
public:
 
160
    CqRiProceduralPlugin( CqString& strDSOName )
 
161
    {
 
162
        CqString strConver("ConvertParameters");
 
163
        CqString strSubdivide("Subdivide");
 
164
        CqString strFree("Free");
 
165
 
 
166
        CqRiFile        fileDSO( strDSOName.c_str(), "procedure" );
 
167
        m_bIsValid = false ;
 
168
 
 
169
        if ( !fileDSO.IsValid() )
 
170
        {
 
171
            m_Error = CqString( "Cannot find Procedural DSO for \"" )
 
172
                      + strDSOName
 
173
                      + CqString ("\" in current searchpath");
 
174
 
 
175
            return;
 
176
        }
 
177
 
 
178
        CqString strRealName( fileDSO.strRealName() );
 
179
        fileDSO.Close();
 
180
        void *handle = DLOpen( &strRealName );
 
181
 
 
182
        if ( ( m_ppvfcts = ( void * ( * ) ( char * ) ) DLSym(handle, &strConver) ) == NULL )
 
183
        {
 
184
            m_Error = DLError();
 
185
            return;
 
186
        }
 
187
 
 
188
        if ( ( m_pvfctpvf = ( void ( * ) ( void *, float ) ) DLSym(handle, &strSubdivide) ) == NULL )
 
189
        {
 
190
            m_Error = DLError();
 
191
            return;
 
192
        }
 
193
 
 
194
        if ( ( m_pvfctpv = ( void ( * ) ( void * ) ) DLSym(handle, &strFree) ) == NULL )
 
195
        {
 
196
            m_Error = DLError();
 
197
            return;
 
198
        }
 
199
 
 
200
        m_bIsValid = true ;
 
201
    };
 
202
 
 
203
    void ConvertParameters(char* opdata)
 
204
    {
 
205
        if( m_bIsValid )
 
206
            m_ppriv = ( *m_ppvfcts ) ( opdata );
 
207
    };
 
208
 
 
209
    void Subdivide( float detail )
 
210
    {
 
211
        if( m_bIsValid )
 
212
            ( *m_pvfctpvf ) ( m_ppriv, detail );
 
213
    };
 
214
 
 
215
    void Free()
 
216
    {
 
217
        if( m_bIsValid )
 
218
            ( *m_pvfctpv ) ( m_ppriv );
 
219
    };
 
220
 
 
221
    bool IsValid(void)
 
222
    {
 
223
        return m_bIsValid;
 
224
    };
 
225
 
 
226
    const CqString Error()
 
227
    {
 
228
        return m_Error;
 
229
    };
 
230
};
 
231
 
 
232
// We don't want to DLClose until we are finished rendering, since any RiProcedurals
 
233
// created from within the dynamic module may re-use the Subdivide and Free pointers so
 
234
// they must remain linked in.
 
235
static std::list<CqRiProceduralPlugin*> ActiveProcDLList;
 
236
//----------------------------------------------------------------------
 
237
// RiProcDynamicLoad() subdivide function
 
238
//
 
239
extern "C" RtVoid       RiProcDynamicLoad( RtPointer data, RtFloat detail )
 
240
{
 
241
    CqString dsoname = CqString( (( char** ) data)[0] ) + CqString(SHARED_LIBRARY_SUFFIX);
 
242
    CqRiProceduralPlugin *plugin = new CqRiProceduralPlugin( dsoname );
 
243
 
 
244
    if( !plugin->IsValid() )
 
245
    {
 
246
        std::cerr << error << "Problem loading Procedural DSO: [" << plugin->Error().c_str() << "]" << std::endl;
 
247
        return;
 
248
    }
 
249
 
 
250
    plugin->ConvertParameters( (( char** ) data)[1] );
 
251
    plugin->Subdivide( detail );
 
252
    plugin->Free();
 
253
 
 
254
    ActiveProcDLList.push_back( plugin );
 
255
 
 
256
    STATS_INC( GEO_prc_created_dl );
 
257
}
 
258
 
 
259
 
 
260
 
 
261
//----------------------------------------------------------------------
 
262
// RiProcDelayedReadArchive()
 
263
//
 
264
extern "C" RtVoid       RiProcDelayedReadArchive( RtPointer data, RtFloat detail )
 
265
{
 
266
    RiReadArchive( (RtToken) ((char**) data)[0], NULL );
 
267
    STATS_INC( GEO_prc_created_dra );
 
268
}
 
269
 
 
270
 
 
271
//----------------------------------------------------------------------
 
272
/* RiProcRunProgram()
 
273
 * Your program must writes its output to a pipe. Open this
 
274
 * pipe with read text attribute so that we can read it 
 
275
 * like a text file. 
 
276
 */
 
277
 
 
278
// TODO: This is far from ideal, we need to parse directly from the popene'd
 
279
// process.
 
280
 
 
281
#ifndef AQSIS_SYSTEM_WIN32
 
282
 
 
283
class CqRiProceduralRunProgram
 
284
{
 
285
public:
 
286
    int fd_out[2]; // aqsis -> servant
 
287
    int fd_in[2]; // servant -> aqsis
 
288
    pid_t pid;
 
289
    FILE *out, *in;
 
290
};
 
291
 
 
292
static std::map<std::string, CqRiProceduralRunProgram*> ActiveProcRP;
 
293
 
 
294
extern "C" RtVoid       RiProcRunProgram( RtPointer data, RtFloat detail )
 
295
{
 
296
    std::map<std::string, CqRiProceduralRunProgram*>::iterator it;
 
297
 
 
298
    it = ActiveProcRP.find(CqString( ((char**)data)[0] )) ;
 
299
    if( it == ActiveProcRP.end() )
 
300
    {
 
301
        // We don't have an active RunProgram for the specifed
 
302
        // program. We need to try and fork a new one
 
303
        CqRiProceduralRunProgram *run_proc = new CqRiProceduralRunProgram;
 
304
        pipe( run_proc->fd_in ) ;
 
305
        pipe( run_proc->fd_out ) ;
 
306
 
 
307
        run_proc->pid = fork() ;
 
308
 
 
309
        if (run_proc->pid < 0)
 
310
        {
 
311
            // problem forking
 
312
            return;
 
313
        }
 
314
        else if( run_proc->pid != 0 )
 
315
        {
 
316
            // main process
 
317
            // fix up the filehandles.
 
318
 
 
319
            close(run_proc->fd_out[0]); // we write to fd_out[1]
 
320
            close(run_proc->fd_in[1]);  // we read from fd_in[0]
 
321
 
 
322
            run_proc->out = fdopen(dup(run_proc->fd_out[1]), "wb");
 
323
            //                  setvbuf(run_proc->out, NULL, _IONBF, 0);
 
324
            run_proc->in = fdopen(dup(run_proc->fd_in[0]), "rb");
 
325
            //                  setvbuf(run_proc->in, NULL, _IONBF, 0);
 
326
 
 
327
            ActiveProcRP[std::string(((char**)data)[0])] = run_proc;
 
328
            it = ActiveProcRP.find(std::string(((char**)data)[0]));
 
329
        }
 
330
        else
 
331
        {
 
332
            // Split up the RunProgram program name string
 
333
            int arg_count = 1;
 
334
            char *arg_values[32];
 
335
            arg_values[0] = ((char**)data)[0] ;
 
336
            char *i = arg_values[0];
 
337
            for( ; *i != '\0' ; i++ )
 
338
            {
 
339
                if( *i == ' ' )
 
340
                {
 
341
                    arg_count++ ;
 
342
                    *i = '\0' ;
 
343
                    arg_values[arg_count - 1] = i + 1 ;
 
344
                }
 
345
            };
 
346
            arg_values[arg_count] = NULL;
 
347
 
 
348
            // fix up the filehandles.
 
349
 
 
350
            close(run_proc->fd_out[1]);
 
351
            close(run_proc->fd_in[0]);
 
352
 
 
353
            close( STDIN_FILENO );
 
354
            dup( run_proc->fd_out[0] );
 
355
            //                  setvbuf( stdin, NULL, _IONBF, 0 );
 
356
            close( STDOUT_FILENO );
 
357
            dup( run_proc->fd_in[1] );
 
358
            //                  setvbuf( stdout, NULL, _IONBF, 0 );
 
359
 
 
360
            // We should use the procedurals searchpath here
 
361
            execvp( arg_values[0], arg_values );
 
362
        };
 
363
    };
 
364
 
 
365
    FILE *fileout = (it->second)->out;
 
366
    // Write out detail and data to the process
 
367
    fprintf( fileout, "%g %s\n", detail, ((char**)data)[1] );
 
368
    fflush( fileout );
 
369
    // should check for a SIGPIPE here incase the RunProgram died.
 
370
 
 
371
    // This is not pretty, gzopen attempts to read a gzip header
 
372
    //from the stream, if we try and do this before we have sent
 
373
    //the request the we get deadlock, so we have to create the decoder
 
374
    //after the request, we use a buffer size of one to prevent any
 
375
    //potential blocking.
 
376
    CqRibBinaryDecoder *decoder;
 
377
    FILE *filein = (it->second)->in;
 
378
    decoder = new CqRibBinaryDecoder( filein, 1);
 
379
 
 
380
    // Parse the resulting block of RIB.
 
381
    CqString strRealName( ((char**)data)[0] );
 
382
    CqRIBParserState currstate = librib::GetParserState();
 
383
 
 
384
    if( currstate.m_pParseCallbackInterface == NULL ) currstate.m_pParseCallbackInterface = new librib2ri::Engine;
 
385
 
 
386
    librib::ParseOpenStream( decoder, strRealName.c_str(), *(currstate.m_pParseCallbackInterface), *(currstate.m_pParseErrorStream), (RtArchiveCallback) RI_NULL );
 
387
 
 
388
    librib::SetParserState( currstate );
 
389
 
 
390
    delete( decoder );
 
391
 
 
392
    STATS_INC( GEO_prc_created_prp );
 
393
 
 
394
    return;
 
395
}
 
396
 
 
397
#else
 
398
 
 
399
#include <windows.h>
 
400
#include <fcntl.h>
 
401
 
 
402
class CqRiProceduralRunProgram
 
403
{
 
404
public:
 
405
    HANDLE hChildStdinWrDup;
 
406
    HANDLE hChildStdoutRdDup;
 
407
        bool m_valid;
 
408
};
 
409
 
 
410
static std::map<std::string, CqRiProceduralRunProgram*> ActiveProcRP;
 
411
 
 
412
 
 
413
extern "C" RtVoid       RiProcRunProgram( RtPointer data, RtFloat detail )
 
414
{
 
415
    HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr;
 
416
    PROCESS_INFORMATION piProcInfo;
 
417
    STARTUPINFO siStartInfo;
 
418
    BOOL bFuncRetn = FALSE;
 
419
 
 
420
    std::map<std::string, CqRiProceduralRunProgram*>::iterator it;
 
421
 
 
422
    it = ActiveProcRP.find(CqString( ((char**)data)[0] )) ;
 
423
    if( it == ActiveProcRP.end() )
 
424
    {
 
425
        // We don't have an active RunProgram for the specifed
 
426
        // program. We need to try and fork a new one
 
427
        CqRiProceduralRunProgram *run_proc = new CqRiProceduralRunProgram;
 
428
                // Proc is invalid until fully resolved.
 
429
                run_proc->m_valid = TqFalse;
 
430
 
 
431
        ActiveProcRP[std::string(((char**)data)[0])] = run_proc;
 
432
        it = ActiveProcRP.find(std::string(((char**)data)[0]));
 
433
 
 
434
        // Create a pipe for the child process's STDOUT.
 
435
 
 
436
        if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, NULL, 0) ||
 
437
                ! CreatePipe(&hChildStdinRd,  &hChildStdinWr,  NULL, 0))
 
438
        {
 
439
            std::cerr << error << "RiProcRunProgram: Stdout pipe creation failed" << std::endl;
 
440
            return;
 
441
        }
 
442
 
 
443
        SetHandleInformation(hChildStdoutWr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
 
444
        SetHandleInformation(hChildStdinRd,  HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
 
445
 
 
446
        ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
 
447
        siStartInfo.cb = sizeof(STARTUPINFO);
 
448
        siStartInfo.hStdOutput = hChildStdoutWr;
 
449
        siStartInfo.hStdInput = hChildStdinRd;
 
450
        siStartInfo.dwFlags = STARTF_USESTDHANDLES;
 
451
        ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
 
452
 
 
453
        // Create the child process.
 
454
 
 
455
        bFuncRetn = CreateProcess(NULL,
 
456
                                  ((char**)data)[0],       // command line
 
457
                                  NULL,          // process security attributes
 
458
                                  NULL,          // primary thread security attributes
 
459
                                  TRUE,          // handles are inherited
 
460
                                  0,             // creation flags
 
461
                                  NULL,          // use parent's environment
 
462
                                  NULL,          // use parent's current directory
 
463
                                  &siStartInfo,  // STARTUPINFO pointer
 
464
                                  &piProcInfo);  // receives PROCESS_INFORMATION
 
465
 
 
466
        if (bFuncRetn == 0)
 
467
        {
 
468
            std::cerr << error << "RiProcRunProgram: CreateProcess failed" << std::endl;
 
469
            return;
 
470
        }
 
471
 
 
472
        CloseHandle(piProcInfo.hProcess);
 
473
        CloseHandle(piProcInfo.hThread);
 
474
 
 
475
        CloseHandle(hChildStdoutWr);
 
476
        CloseHandle(hChildStdinRd);
 
477
 
 
478
        // Store the handles.
 
479
        (it->second)->hChildStdinWrDup = hChildStdinWr;
 
480
        (it->second)->hChildStdoutRdDup = hChildStdoutRd;
 
481
 
 
482
                // Proc seems to be valid.
 
483
                run_proc->m_valid = TqTrue;
 
484
    }
 
485
        else
 
486
        {
 
487
                if( !(it->second)->m_valid )
 
488
                        return;
 
489
        }
 
490
 
 
491
    int fd_hChildStdinWrDup = _open_osfhandle((long)(it->second)->hChildStdinWrDup, 0);
 
492
    FILE *fileout = _fdopen( fd_hChildStdinWrDup, "w");
 
493
    // Write out detail and data to the process
 
494
    fprintf( fileout, "%g %s\n", detail, ((char**)data)[1] );
 
495
    fflush( fileout );
 
496
 
 
497
    CqRibBinaryDecoder *decoder;
 
498
    int fd_hChildStdoutRdDup = _open_osfhandle((long)(it->second)->hChildStdoutRdDup, O_RDONLY);
 
499
    FILE *filein = _fdopen( fd_hChildStdoutRdDup, "r" );
 
500
    decoder = new CqRibBinaryDecoder( filein, 1);
 
501
 
 
502
    // Parse the resulting block of RIB.
 
503
    CqString strRealName( ((char**)data)[0] );
 
504
    CqRIBParserState currstate = librib::GetParserState();
 
505
 
 
506
    if( currstate.m_pParseCallbackInterface == NULL ) currstate.m_pParseCallbackInterface = new librib2ri::Engine;
 
507
 
 
508
    librib::ParseOpenStream( decoder, strRealName.c_str(), *(currstate.m_pParseCallbackInterface), *(currstate.m_pParseErrorStream), (RtArchiveCallback) RI_NULL );
 
509
 
 
510
    librib::SetParserState( currstate );
 
511
 
 
512
    delete( decoder );
 
513
 
 
514
    STATS_INC( GEO_prc_created_prp );
 
515
 
 
516
    return;
 
517
}
 
518
 
 
519
 
 
520
#endif
 
521
 
 
522
END_NAMESPACE( Aqsis )
 
523