Ignore:
Timestamp:
Apr 24, 2006, 9:59:10 AM (18 years ago)
Author:
qiaow
Message:

Added ScreenSnapper? class and VolumeRenderer? class.
VolumeRenderer? class can handle motiple volumes. Volume rendering code is seperated
from nanovis.cpp file.

Location:
trunk/gui/vizservers/nanovis
Files:
4 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/gui/vizservers/nanovis/Makefile

    r404 r406  
    33                PerfQuery.o TransferFunction.o ControlPoint.o ColorGradient.o ColorPaletteWindow.o\
    44                ColorGradientGLUTWindow.o TransferFunctionGLUTWindow.o MainWindow.o Event.o \
    5                 Lic.o Renderable.o Camera.o
     5                Lic.o Renderable.o Camera.o ScreenSnapper.o VolumeRenderer.o
     6OBJ_VOLUMERENDERER = Volume.o ConvexPolygon.o TransferFunction.o Mat4x4.o  Camera.o
    67
    78OBJ_CLIENT = Socket.o ClientSocket.o RenderClient.o Event.o
     
    6162        #g++ -g $(INCLUDES) $(NANOSCALESRC)/EventPlayer.cpp $(NANOSCALESRC)/clientlib.cpp -o simclient Event.o
    6263
    63 Color.o: Color.cpp
     64Color.o: Color.cpp Color.h
    6465        gcc $(CFLAG) Color.cpp
    6566
    66 Event.o: Event.cpp
     67Event.o: Event.cpp Event.h
    6768        gcc $(CFLAG) Event.cpp
    6869
     
    7071        gcc $(CFLAG) $(TFSRC)/ControlPoint.cpp
    7172
    72 Sphere.o: Renderable.o Color.o
     73Sphere.o: Renderable.o Color.o Sphere.cpp Sphere.h
    7374        gcc $(CFLAG) Sphere.cpp
    7475
     
    7677        gcc $(CFLAG) TransferFunction.cpp
    7778
    78 Texture1D.o: Texture1D.cpp
     79Texture1D.o: Texture1D.cpp Texture1D.h
    7980        gcc $(CFLAG) Texture1D.cpp
    8081
    81 Texture2D.o: Texture2D.cpp $(AUXSRC)
     82Texture2D.o: Texture2D.cpp Texture2D.h $(AUXSRC)
    8283        gcc $(CFLAG) Texture2D.cpp
    8384
    84 Texture3D.o: Texture3D.cpp $(AUXSRC)
     85Texture3D.o: Texture3D.cpp Texture3D.h $(AUXSRC)
    8586        gcc $(CFLAG) Texture3D.cpp
    8687
     
    8889        gcc $(CFLAG) ParticleSystem.cpp
    8990
    90 Renderable.o: Vector3.o Renderable.cpp
     91Renderable.o: Vector3.o Renderable.cpp Renderable.h
    9192        gcc $(CFLAG) Renderable.cpp
    9293
    93 Camera.o: Vector3.o Camera.cpp
     94Camera.o: Vector3.o Camera.cpp Camera.h
    9495        gcc $(CFLAG) Camera.cpp
     96
     97ScreenSnapper.o: define.h ScreenSnapper.cpp ScreenSnapper.h
     98        gcc $(CFLAG) ScreenSnapper.cpp
    9599
    96100Lic.o: Renderable.o Lic.cpp Lic.h $(AUXSRC)
     
    100104        gcc $(CFLAG) Volume.cpp
    101105
    102 Mat4x4.o: Mat4x4.cpp
     106VolumeRenderer.o: $(OBJ_VOLUMERENDERER)  VolumeRenderer.h VolumeRenderer.cpp
     107        gcc $(CFLAG) VolumeRenderer.cpp
     108
     109Mat4x4.o: Mat4x4.cpp Mat4x4.h
    103110        gcc $(CFLAG) Mat4x4.cpp
    104111
    105 Vector4.o: Vector4.cpp
     112Vector4.o: Vector4.cpp Vector4.h
    106113        gcc $(CFLAG) Vector4.cpp
    107114
    108 Vector3.o: Vector3.cpp
     115Vector3.o: Vector3.cpp Vector3.h
    109116        gcc $(CFLAG) Vector3.cpp
    110117
    111 Plane.o: Plane.cpp
     118Plane.o: Plane.cpp Plane.h
    112119        gcc $(CFLAG) Plane.cpp
    113120
    114 ConvexPolygon.o: ConvexPolygon.cpp
     121ConvexPolygon.o: ConvexPolygon.cpp ConvexPolygon.h
    115122        gcc $(CFLAG) ConvexPolygon.cpp
    116123
    117 PerfQuery.o: PerfQuery.cpp
     124PerfQuery.o: PerfQuery.cpp PerfQuery.h
    118125        gcc $(CFLAG) PerfQuery.cpp
    119126
  • trunk/gui/vizservers/nanovis/Plane.h

    r374 r406  
    2727    Plane();
    2828   
    29         void get_normal(Vector3 &normal);
     29    void get_normal(Vector3 &normal);
    3030    void get_point(Vector3 &point);
    3131    Vector4 get_coeffs();
    32         void set_coeffs(float _a, float _b, float _c, float _d);
    33         void transform(Mat4x4 mat);
    34         void transform(float *m);
    35         bool retains(Vector3 point);
    36         //bool clips(float point[3]) const { return !retains(point); }
     32    void set_coeffs(float _a, float _b, float _c, float _d);
     33    void transform(Mat4x4 mat);
     34    void transform(float *m);
     35    bool retains(Vector3 point);
     36    //bool clips(float point[3]) const { return !retains(point); }
    3737
    3838};
  • trunk/gui/vizservers/nanovis/Renderable.h

    r401 r406  
    2121#include "Vector3.h"
    2222
     23struct BoundBox{
     24  Vector3 low; //lower coordinates
     25  Vector3 high; //higher coordinates
     26
     27  BoundBox(){}
     28  BoundBox(float low_x, float low_y, float low_z,
     29          float high_x, float high_y, float high_z):
     30          low(Vector3(low_x, low_y, low_z)),
     31          high(Vector3(high_x, high_y, high_z)){}
     32};
     33
    2334class Renderable{
    2435protected:
    2536  Vector3 location;     //the location (x,y,z) of the object
    2637  bool enabled;         //display is enabled
     38  BoundBox boundary;    //the bounding box
    2739
    2840public:
  • trunk/gui/vizservers/nanovis/Sphere.cpp

    r404 r406  
    2626        stack(_stack),
    2727        slice(_slice),
    28         color(Color(r,g,b)) { }
     28        color(Color(r,g,b))
     29{
     30  boundary = BoundBox(x-r, y-r, z-r, x+r, y+r, z+r);
     31}
    2932
    3033Sphere::~Sphere(){}
  • trunk/gui/vizservers/nanovis/define.h

    r273 r406  
    2727#define NVIS_FLOAT GL_FLOAT
    2828#define NVIS_UNSIGNED_INT GL_UNSIGNED_INT
     29#define NVIS_UNSIGNED_BYTE GL_UNSIGNED_BYTE
    2930
    3031#define NVIS_LINEAR_INTERP GL_LINEAR
    3132#define NVIS_NEAREST_INTERP GL_NEAREST
    32 
    3333
    3434typedef GLuint NVISdatatype;            //OpenGL datatype: unsigned int
  • trunk/gui/vizservers/nanovis/nanoscale/EventPlayer.cpp

    r391 r406  
    6565}
    6666
     67
     68/*
     69 *Client communicates with the server
     70 */
    6771void idle(void)
    6872{
     
    165169}
    166170
     171
    167172int init_client(char* host, char* port, char* file){
    168173
  • trunk/gui/vizservers/nanovis/nanovis.cpp

    r404 r406  
    3535//render server
    3636
     37VolumeRenderer* vol_render;
    3738Camera* cam;
     39
    3840float color_table[256][4];     
    3941
     
    5961Lic* lic;
    6062
    61 char* screen_buffer = new char[3*NPIX*NPIX+1];          //buffer to store data read from the screen
     63unsigned char* screen_buffer = new unsigned char[3*win_width*win_height+1];     //buffer to store data read from the screen
    6264NVISid final_fbo, final_color_tex, final_depth_rb;      //frame buffer for final rendering
    6365NVISid vel_fbo, slice_vector_tex;       //for projecting 3d vector to 2d vector on a plane
     
    735737    g_context = cgCreateContext();
    736738
    737     //load programs that can be used in all render modes
    738    
    739     //standard vertex program
    740     m_vert_std_vprog = loadProgram(g_context, CG_PROFILE_VP30, CG_SOURCE, "./shaders/vertex_std.cg");
    741     m_mvp_vert_std_param = cgGetNamedParameter(m_vert_std_vprog, "modelViewProjMatrix");
    742     m_mvi_vert_std_param = cgGetNamedParameter(m_vert_std_vprog, "modelViewInv");
    743 
    744     m_passthru_fprog = loadProgram(g_context, CG_PROFILE_FP30, CG_SOURCE, "./shaders/passthru.cg");
    745     m_passthru_scale_param = cgGetNamedParameter(m_passthru_fprog, "scale");
    746     m_passthru_bias_param  = cgGetNamedParameter(m_passthru_fprog, "bias");
    747 
    748     m_copy_texcoord_fprog = loadProgram(g_context, CG_PROFILE_FP30, CG_SOURCE, "./shaders/copy_texcoord.cg");
    749 
    750739#ifdef NEW_CG
    751740    m_posvel_fprog = loadProgram(g_context, CG_PROFILE_FP40, CG_SOURCE, "./shaders/update_pos_vel.cg");
     
    763752
    764753  switch (choice){
    765 
    766754    case 0:
    767       //render one volume
    768       m_one_volume_fprog = loadProgram(g_context, CG_PROFILE_FP30, CG_SOURCE, "./shaders/one_volume.cg");
    769       m_vol_one_volume_param = cgGetNamedParameter(m_one_volume_fprog, "volume");
    770       cgGLSetTextureParameter(m_vol_one_volume_param, volume[0]->id);
    771       m_tf_one_volume_param = cgGetNamedParameter(m_one_volume_fprog, "tf");
    772       cgGLSetTextureParameter(m_tf_one_volume_param, tf[0]->id);
    773       m_mvi_one_volume_param = cgGetNamedParameter(m_one_volume_fprog, "modelViewInv");
    774       m_mv_one_volume_param = cgGetNamedParameter(m_one_volume_fprog, "modelView");
    775       m_render_param_one_volume_param = cgGetNamedParameter(m_one_volume_fprog, "renderParameters");
    776755      break;
    777756
    778757   case 1:
    779       //render two volumes
     758      break;
    780759     
    781760   default:
    782761      break;
    783 
    784 
    785762  }
    786763}
     
    891868   init_lic();  //init line integral convolution
    892869
    893    //load the default one volume shader
    894    switch_shader(cur_shader);
    895 
     870   //create volume renderer
     871   vol_render = new VolumeRenderer(cam, volume[1], tf[0], g_context);
    896872   
    897873   psys = new ParticleSystem(NMESH, NMESH, g_context, volume[0]->id,
     
    904880   init_particles();    //fill initial particles
    905881}
    906 
    907 
    908882
    909883
     
    927901void read_screen(){
    928902  //glBindTexture(GL_TEXTURE_2D, 0);
    929   //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
    930   glReadPixels(0, 0, win_width, win_height, GL_RGB, /*GL_COLOR_ATTACHMENT0_EXT*/ GL_UNSIGNED_BYTE, screen_buffer);
     903  //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
     904  glReadPixels(0, 0, win_width, win_height, GL_RGB, GL_UNSIGNED_BYTE, screen_buffer);
    931905  assert(glGetError()==0);
     906 
     907  for(int i=0; i<win_width*win_height; i++){
     908    if(screen_buffer[3*i]!=0 || screen_buffer[3*i+1]!=0 || screen_buffer[3*i+2]!=0)
     909      fprintf(stderr, "(%d %d %d) ", screen_buffer[3*i], screen_buffer[3*i+1],  screen_buffer[3*i+2]);
     910  }
     911 
    932912}
    933913
     
    10681048   glLoadIdentity();
    10691049
    1070    glColor3f(1.,1.,1.);         //MUST HAVE THIS LINE!!!
     1050   //glColor3f(1.,1.,1.);               //MUST HAVE THIS LINE!!!
    10711051   glBegin(GL_QUADS);
    10721052   glTexCoord2f(0, 0); glVertex2f(-1, -1);
     
    12851265#endif
    12861266
    1287 void get_near_far_z(Mat4x4 mv, double &zNear, double &zFar)
    1288 {
    1289 
    1290   double x0 = 0;
    1291   double y0 = 0;
    1292   double z0 = 0;
    1293   double x1 = 1;
    1294   double y1 = 1;
    1295   double z1 = 1;
    1296 
    1297   double zMin, zMax;
    1298   zMin =  10000;
    1299   zMax = -10000;
    1300 
    1301   double vertex[8][4];
    1302 
    1303   vertex[0][0]=x0; vertex[0][1]=y0; vertex[0][2]=z0; vertex[0][3]=1.0;
    1304   vertex[1][0]=x1; vertex[1][1]=y0; vertex[1][2]=z0; vertex[1][3]=1.0;
    1305   vertex[2][0]=x0; vertex[2][1]=y1; vertex[2][2]=z0; vertex[2][3]=1.0;
    1306   vertex[3][0]=x0; vertex[3][1]=y0; vertex[3][2]=z1; vertex[3][3]=1.0;
    1307   vertex[4][0]=x1; vertex[4][1]=y1; vertex[4][2]=z0; vertex[4][3]=1.0;
    1308   vertex[5][0]=x1; vertex[5][1]=y0; vertex[5][2]=z1; vertex[5][3]=1.0;
    1309   vertex[6][0]=x0; vertex[6][1]=y1; vertex[6][2]=z1; vertex[6][3]=1.0;
    1310   vertex[7][0]=x1; vertex[7][1]=y1; vertex[7][2]=z1; vertex[7][3]=1.0;
    1311 
    1312   for(int i=0;i<8;i++)
    1313   {
    1314     Vector4 tmp = mv.transform(Vector4(vertex[i][0], vertex[i][1], vertex[i][2], vertex[i][3]));
    1315     tmp.perspective_devide();
    1316     vertex[i][2] = tmp.z;
    1317     if (vertex[i][2]<zMin) zMin = vertex[i][2];
    1318     if (vertex[i][2]>zMax) zMax = vertex[i][2];
    1319   }
    1320 
    1321   zNear = zMax;
    1322   zFar = zMin;
    1323 }
    1324 
    1325 
    1326 void activate_one_volume_shader(int volume_index, int n_actual_slices){
    1327 
    1328   cgGLSetStateMatrixParameter(m_mvp_vert_std_param, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
    1329   cgGLSetStateMatrixParameter(m_mvi_vert_std_param, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_INVERSE);
    1330   cgGLBindProgram(m_vert_std_vprog);
    1331   cgGLEnableProfile(CG_PROFILE_VP30);
    1332 
    1333   cgGLSetStateMatrixParameter(m_mvi_one_volume_param, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_INVERSE);
    1334   cgGLSetStateMatrixParameter(m_mv_one_volume_param, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_IDENTITY);
    1335   cgGLSetTextureParameter(m_vol_one_volume_param, volume[volume_index]->id);
    1336   cgGLEnableTextureParameter(m_vol_one_volume_param);
    1337   cgGLEnableTextureParameter(m_tf_one_volume_param);
    1338 
    1339   //hack!
    1340   //if parameter.y == 0 : volume :0
    1341   //if parameter.y == 1 : volume :1
    1342   if(volume_index==0)
    1343     cgGLSetParameter4f(m_render_param_one_volume_param, n_actual_slices, 0., live_diffuse, live_specular);
    1344   else if(volume_index==1)
    1345     cgGLSetParameter4f(m_render_param_one_volume_param, n_actual_slices, 1., live_diffuse, live_specular);
    1346   cgGLBindProgram(m_one_volume_fprog);
    1347   cgGLEnableProfile(CG_PROFILE_FP30);
    1348 }
    1349 
    1350 
    1351 void deactivate_one_volume_shader(){
    1352   cgGLDisableProfile(CG_PROFILE_VP30);
    1353   cgGLDisableProfile(CG_PROFILE_FP30);
    1354 
    1355   cgGLDisableTextureParameter(m_vol_one_volume_param);
    1356   cgGLDisableTextureParameter(m_tf_one_volume_param);
    1357 }
    1358 
    1359 
    1360 //render volumes
    1361 void render_volume(int volume_index, int n_slices){
    1362 
    1363   //volume start location
    1364   Vector4 shift_4d(volume[volume_index]->location.x, volume[volume_index]->location.y, volume[volume_index]->location.z, 0);
    1365 
    1366   double x0 = 0;
    1367   double y0 = 0;
    1368   double z0 = 0;
    1369 
    1370   Mat4x4 model_view;
    1371   Mat4x4 model_view_inverse;
    1372 
    1373   double zNear, zFar;
    1374 
    1375   //initialize volume plane with world coordinates
    1376   Plane volume_planes[6];
    1377 
    1378   volume_planes[0].set_coeffs(1, 0, 0, -x0);
    1379   volume_planes[1].set_coeffs(-1, 0, 0, x0+1);
    1380   volume_planes[2].set_coeffs(0, 1, 0, -y0);
    1381   volume_planes[3].set_coeffs(0, -1, 0, y0+1);
    1382   volume_planes[4].set_coeffs(0, 0, 1, -z0);
    1383   volume_planes[5].set_coeffs(0, 0, -1, z0+1);
    1384 
    1385   glPushMatrix();
    1386 
    1387   glScalef(volume[volume_index]->aspect_ratio_width,
    1388           volume[volume_index]->aspect_ratio_height,
    1389           volume[volume_index]->aspect_ratio_depth);
    1390 
    1391   glEnable(GL_DEPTH_TEST);
    1392 
    1393   //draw volume bounding box
    1394   draw_bounding_box(x0+shift_4d.x, y0+shift_4d.y, z0+shift_4d.z, x0+shift_4d.x+1, y0+shift_4d.y+1, z0+shift_4d.z+1, 0.8, 0.1, 0.1, 1.5);
    1395 
    1396   GLfloat mv[16];
    1397   glGetFloatv(GL_MODELVIEW_MATRIX, mv);
    1398 
    1399   model_view = Mat4x4(mv);
    1400   model_view_inverse = model_view.inverse();
    1401 
    1402   glPopMatrix();
    1403 
    1404   //transform volume_planes to eye coordinates.
    1405   for(int i=0; i<6; i++)
    1406     volume_planes[i].transform(model_view);
    1407 
    1408   get_near_far_z(mv, zNear, zFar);
    1409   //fprintf(stderr, "zNear:%f, zFar:%f\n", zNear, zFar);
    1410   //fflush(stderr);
    1411 
    1412   //compute actual rendering slices
    1413   float z_step = fabs(zNear-zFar)/n_slices;             
    1414   int n_actual_slices = (int)(fabs(zNear-zFar)/z_step + 1);
    1415   //fprintf(stderr, "slices: %d\n", n_actual_slices);
    1416   //fflush(stderr);
    1417 
    1418   static ConvexPolygon staticPoly;     
    1419   float slice_z;
    1420 
    1421   Vector4 vert1 = (Vector4(-10, -10, -0.5, 1));
    1422   Vector4 vert2 = (Vector4(-10, +10, -0.5, 1));
    1423   Vector4 vert3 = (Vector4(+10, +10, -0.5, 1));
    1424   Vector4 vert4 = (Vector4(+10, -10, -0.5, 1));
    1425 
    1426   glEnable(GL_DEPTH_TEST);
    1427   glEnable(GL_BLEND);
    1428  
    1429   for (int i=0; i<n_actual_slices; i++){
    1430     slice_z = zFar + i * z_step;        //back to front
    1431        
    1432     ConvexPolygon *poly;
    1433     poly = &staticPoly;
    1434     poly->vertices.clear();
    1435 
    1436     //Setting Z-coordinate
    1437     vert1.z = slice_z;
    1438     vert2.z = slice_z;
    1439     vert3.z = slice_z;
    1440     vert4.z = slice_z;
    1441                
    1442     poly->append_vertex(vert1);
    1443     poly->append_vertex(vert2);
    1444     poly->append_vertex(vert3);
    1445     poly->append_vertex(vert4);
    1446        
    1447     for(int k=0; k<6; k++){
    1448       poly->clip(volume_planes[k]);
    1449     }
    1450 
    1451     poly->copy_vertices_to_texcoords();
    1452     //move the volume to the proper location
    1453 
    1454     poly->transform(model_view_inverse);
    1455     poly->translate(shift_4d);
    1456     poly->transform(model_view);
    1457 
    1458     glPushMatrix();
    1459     glScalef(volume[volume_index]->aspect_ratio_width, volume[volume_index]->aspect_ratio_height, volume[volume_index]->aspect_ratio_depth);
    1460    
    1461     //glPushMatrix();
    1462     //glMatrixMode(GL_MODELVIEW);
    1463     //glLoadIdentity();
    1464     //glScaled(volume[volume_index]->aspect_ratio_width, volume[volume_index]->aspect_ratio_height, volume[volume_index]->aspect_ratio_depth);
    1465 
    1466     /*
    1467     //draw slice lines only
    1468     glDisable(GL_BLEND);
    1469     glDisable(GL_TEXTURE_3D);
    1470     glDisable(GL_TEXTURE_2D);
    1471     glLineWidth(1.0);
    1472     glColor3f(1,1,1);
    1473     glBegin(GL_LINE_LOOP);
    1474       poly->Emit(false);
    1475     glEnd();
    1476     */
    1477    
    1478     activate_one_volume_shader(volume_index, n_actual_slices);
    1479     glPopMatrix();
    1480 
    1481     glBegin(GL_POLYGON);
    1482       poly->Emit(true);
    1483     glEnd();
    1484 
    1485     deactivate_one_volume_shader();
    1486                
    1487   }
    1488 
    1489   glDisable(GL_BLEND);
    1490   glDisable(GL_DEPTH_TEST);
    1491   //glPopMatrix();
    1492 }
    1493 
    14941267
    14951268void draw_3d_axis(){
     
    16501423     //render volume :1
    16511424     volume[1]->location =Vector3(0., 0., 0.);
    1652      render_volume(1, 256);
     1425     //render_volume(1, 256);
     1426     vol_render->render(0);
     1427
    16531428     //fprintf(stderr, "%lf\n", get_time_interval());
    16541429   perf->disable();
     
    16631438#endif
    16641439
    1665    display_final_fbo();
     1440   display_final_fbo(); //display the final rendering on screen
    16661441
    16671442#ifdef XINETD
    16681443   read_screen();
    1669    //glClear(GL_COLOR_BUFFER_BIT);
    1670    //glDrawPixels(win_width, win_height, GL_RGB, /*GL_COLOR_ATTACHMENT0_EXT*/ GL_UNSIGNED_BYTE, screen_buffer);
    1671 #endif
     1444#else
     1445   //read_screen();
     1446#endif   
     1447
    16721448   glutSwapBuffers();
    16731449}
     
    17951571                live_specular+=1;
    17961572                fprintf(stderr, "specular: %f\n", live_specular);
     1573                vol_render->set_specular(live_specular);
    17971574                break;
    17981575        case 'p':
    17991576                live_specular-=1;
    18001577                fprintf(stderr, "specular: %f\n", live_specular);
     1578                vol_render->set_specular(live_specular);
    18011579                break;
    18021580        case '[':
    18031581                live_diffuse+=0.5;
     1582                vol_render->set_diffuse(live_diffuse);
    18041583                break;
    18051584        case ']':
    18061585                live_diffuse-=0.5;
     1586                vol_render->set_diffuse(live_diffuse);
    18071587                break;
    18081588
     
    18451625      left_last_y = y;
    18461626
    1847       update_rot(delta_y, delta_x);
     1627      update_rot(-delta_y, -delta_x);
    18481628    }
    18491629    else if (right_down){
  • trunk/gui/vizservers/nanovis/nanovis.h

    r404 r406  
    3434#include "Texture1D.h"
    3535#include "TransferFunction.h"
    36 #include "ConvexPolygon.h"
    3736#include "Mat4x4.h"
    3837#include "Volume.h"
     
    4140#include "Event.h"
    4241#include "Lic.h"
     42#include "VolumeRenderer.h"
    4343
    4444#include "config.h"
Note: See TracChangeset for help on using the changeset viewer.