File: vrwpick.c

package info (click to toggle)
vrwave 0.9-4
  • links: PTS
  • area: non-free
  • in suites: slink
  • size: 5,032 kB
  • ctags: 7,153
  • sloc: java: 15,050; ansic: 8,219; sh: 458; makefile: 181
file content (221 lines) | stat: -rw-r--r-- 6,954 bytes parent folder | download | duplicates (3)
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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
/*
 * vrwpick.c - VRwave picking
 * Copyright (c) 1995,96,97 IICM
 *
 * created: mpichler, 19970724 (based on VRweb picking code)
 *
 * changed: mpichler, 19970724
 *
 * $Id: vrwpick.c,v 1.1 1997/07/25 11:39:20 mpichler Exp $
 */


#ifdef __cplusplus
extern "C" {
#endif
#include "Picker.h"
#ifdef __cplusplus
} // C++
#endif


#ifdef macintosh
# include <vectors.h>
# include <ge3d.h>
# define M_PI _PI
# define PACKAGE iicm_vrml_vrwave_
#else
# include <ge3d/vectors.h>
# include <ge3d/ge3d.h>
#endif

#include "jutils.h"

#include <math.h>
#include <stdio.h>  /* debugging */


/* usage: Debug ((stderr, "format", arguments)) */
#if 0
#define Debug(body)  fprintf body
#else
#define Debug(body)  /**/
#endif


/*
 * Intersection test between a line from P towards infinity +y and and
 * edge from A to B; lines passing through a vertex point tangentially
 * do not change the intersection count
 * projection axis paxis: which coordinate to discard (0, 1, 2)
 * Author: Keith Andrews <kandrews@iicm>
 * adaption for projections of 3D points: Michael Pichler
 */

static int Intersection (
  const point3D* p,
  const point3D* a,
  const point3D* b,
  int paxis
)
{
  register point2D P, A, B;  /* test point, leftmost, and rightmost point */

#define ASSIGN(u,v)  \
{  \
  init2D (P, p->u, p->v);  \
  if (a->u < b->u)  \
    init2D (A, a->u, a->v), init2D (B, b->u, b->v);  \
  else  \
    init2D (A, b->u, b->v), init2D (B, a->u, a->v);  \
} /* now A.u <= B.u */

  if (!paxis)           /* discard x */
    ASSIGN (y,z)
  else if (paxis == 1)  /* discard y */
    ASSIGN (x,z)
  else                  /* discard z */
    ASSIGN (x,y)

#undef ASSIGN

  /* check intersection */
  if (A.x < P.x && B.x >= P.x)                              /* cases 1, 3, 5, 6 */
    if (A.y > P.y) {                                        /*   cases 1, 6 */
      if ((B.y > P.y) ||                                    /*     case 1 or */
          (A.y + ((B.y-A.y)/(B.x-A.x))*(P.x-A.x) > P.y))    /*     case 6a */
        return 1;                                           /*       count intersection */
    }
    else {                                                  /*   cases 3, 5 */
      if (A.y + ((B.y-A.y)/(B.x-A.x))*(P.x-A.x) > P.y)      /*     case 5a */
        return 1;                                           /*       count intersection */
    }

  return 0;
} /* Intersection */



/* rayhitsfaceset */
/* test whether ray hits IndexedFaceset */
/*
 * the FaceSet hit test used here projects each face to one of the
 * three planes formed by the coordinate axis (by throwing away the
 * largest absolute coordinate in the normal; see Foley/v.Dam p. 704)
 * and does a 2D intersection test. This test works also for arbitrary
 * vertex order and non-convex polygons.
 */
/* compare with QvIndexedFaceSet::pick of VRweb */

float name2(PACKAGE,Picker_rayhitsfaceset) (struct name3(H,PACKAGE,Picker)* handle,
  HArrayOfFloat* hraystart, HArrayOfFloat* hraydir, float raynear, float rayfar,  /* picking ray */
  HArrayOfFloat* hverts,  /* vertices */
  jn_int32 numcoordinds, HArrayOfInt* hcoordinds,  /* vertex indices */
  HArrayOfFloat* hfnormals,  /* face normal vectors */
  jn_boolean twosided,  /* whether to pick backfaces too */
  HArrayOfFloat* hhitnormal  /* out: normal vector at hitpoint (if non-NULL) */
) /* return value: hittime or 0 if no hit */
{
  const vector3D* ray_A = (const vector3D*) unhand (hraystart)->body;
  const vector3D* ray_b = (const vector3D*) unhand (hraydir)->body;
  const point3D* vertexlist = (const point3D*) unhand (hverts)->body;
  int nv = numcoordinds;
  const int* cind = (const int*) unhand (hcoordinds)->body;
  const vector3D* fn = (const vector3D*) unhand (hfnormals)->body;
  vector3D* hitnormal = hhitnormal ? (vector3D*) unhand (hhitnormal) : 0;
  point3D hitpoint;
  const point3D *p, *q;
  float hit, denom;  /* for current face */
  float hittime = 0.0f;  /* return value */
  int paxis;


  while (nv)
  {
    /* here at the beginning of a new face */
    int v0 = cind [0];
    int v1 = cind [1];
    int v2 = cind [2];

    if (v0 >= 0 && v1 >= 0 && v2 >= 0 && nv > 2)
    {
      Debug ((stderr, "picking face %d, %d, %d ...: ", v0, v1, v2));
      /* *fn ... curent face normal */
      if ((denom = dot3D (*fn, *ray_b)) < 0 || (twosided && denom))  /* entering ray or twosided */
      {
        /* hit time: <n.(P-A)>/<n.b> */
        p = vertexlist + v0;
        sub3D (*p, *ray_A, hitpoint);  /* not yet the hitpoint */
        hit = dot3D (*fn, hitpoint) / denom;

        if (raynear < hit && hit < rayfar)  /* this face plane hit first */
        {
          unsigned intersect = 0;  /* odd-even intersection test */

          /* compute the hit point and check wheter it lies within the face */
          pol3D (*ray_A, hit, *ray_b, hitpoint);

          /* projection direction: largest abs. value of face normal */
          /* might precompute this during build */
          paxis = 0;  /* x */
          {
            float nmax = fabs (fn->x);
            if (fabs (fn->y) > nmax)
            { paxis = 1;  /* y */
              nmax = fabs (fn->y);
            }
            if (fabs (fn->z) > nmax)
              paxis = 2;  /* z */
          }
          Debug ((stderr, "+%d ", paxis));
          q = vertexlist + v0;
          while (nv > 1 && cind[1] >= 0)
          { /* test edge cind[0] to cind[1] (well defined when nv > 1) */
            p = q;
            Debug ((stderr, "{%d/%d}", cind[0], cind[1]));
            q = vertexlist + *++cind;
            nv--;
            intersect += Intersection (&hitpoint, p, q, paxis);
            /* int val = Intersection (&hitpoint, p, q, paxis);  intersect += val;  cerr << '=' << val << ' '; */
          }

          p = q;  /* last edge (back to v0) */
          q = vertexlist + v0;
          Debug ((stderr, "{%d/%d}", cind[0], v0));
          intersect += Intersection (&hitpoint, p, q, paxis);
          /* int val = Intersection (&hitpoint, p, q, paxis);  intersect += val;  cerr << '=' << val << ' '; */

          Debug ((stderr, " i = %d\n", intersect));
          if (intersect & 0x1)  /* hitpoint found */
          { /* HITENCOUNTERED */
            hittime = rayfar = hit;  /* search next hit */
            if (hitnormal)
              *hitnormal = *fn;
          }

        } /* nearest face */
      } /* entering ray or twosided */
    } /* face with at least 3 vertices */

    /* faces with less than 3 vertices are not drawn and therefore not picked */
    /* (see OpenGL Programming Guide, p. 36.: GL_POLYGON) */

    /* goto next face */
    fn++;
    while (*cind >= 0 && nv)
      cind++, nv--;
    if (nv)  /* skip index -1 (face separator) */
      cind++, nv--;
  } /* for all faces */


  /* reverse normal vector if hit occured from back side */
  if (hittime && hitnormal)
  {
    if (dot3D (*hitnormal, *ray_b) > 0)
      neg3D (*hitnormal);
  }

  return hittime;

} /* rayhitsfaceset */