Source: TestObjects.js

  1. /*jshint esversion: 11 */
  2. // @ts-check
  3. /**
  4. * CS559 3D World Framework Code
  5. *
  6. * Test Objects - these examples are for use in testing the framework
  7. * and are less generally useful
  8. *
  9. * @module TestObjects
  10. */
  11. // we need to have the BaseClass definition
  12. import { GrObject } from "./GrObject.js";
  13. // a global variable to keep track of how many objects we create
  14. // this allows us to give unique names
  15. let testobjsctr = 0;
  16. import * as T from "../CS559-Three/build/three.module.js";
  17. function degreesToRadians(deg) {
  18. return (deg * Math.PI) / 180;
  19. }
  20. /**
  21. * A simple object that is like a dump truck (with a hinge), but just made of
  22. * boxes.
  23. * A simple way to test a parametric object
  24. *
  25. * It's also a simple example of a hierarchical object
  26. */
  27. export class HingeCube extends GrObject {
  28. constructor() {
  29. const group = new T.Group();
  30. const geometry = new T.BoxGeometry(1, 0.5, 1);
  31. const mesh1 = new T.Mesh(
  32. geometry,
  33. new T.MeshStandardMaterial({ color: 0xa0a000 })
  34. );
  35. mesh1.position.y = 0.25;
  36. const mesh2 = new T.Mesh(
  37. geometry,
  38. new T.MeshStandardMaterial({ color: 0xffff00 })
  39. );
  40. mesh2.position.y = 0.25;
  41. mesh2.position.z = 0.5;
  42. // set group with origin at pivot point
  43. group.add(mesh1);
  44. const g2 = new T.Group();
  45. g2.position.set(0, 0.5, -0.5);
  46. g2.add(mesh2);
  47. group.add(g2);
  48. super(`HingeCube-${testobjsctr++}`, group, [
  49. ["x", -5, 5, 2],
  50. ["z", -5, 5, 2],
  51. ["theta", -180, 180, 0],
  52. ["tilt", 0, 90, 0]
  53. ]);
  54. this.group = group;
  55. this.mesh1 = mesh1;
  56. this.mesh2 = mesh2;
  57. this.g2 = g2;
  58. }
  59. update(paramValues) {
  60. this.group.position.x = paramValues[0];
  61. this.group.position.z = paramValues[1];
  62. this.group.rotation.y = degreesToRadians(paramValues[2]);
  63. this.g2.rotation.x = degreesToRadians(-paramValues[3]);
  64. }
  65. }
  66. // for faking deferred loading
  67. // from https://flaviocopes.com/javascript-sleep/
  68. const sleep = milliseconds => {
  69. return new Promise(resolve => setTimeout(resolve, milliseconds));
  70. };
  71. /**
  72. * test for an object that is created slowly (like loading an OBJ)
  73. *
  74. * the catch is that we need to have an object to install in the world
  75. * (since we can't defer that), but we don't have "the" object
  76. *
  77. * the trick: make a Group - when the deferred object finally arrives,
  78. * stick it in the group
  79. *
  80. * here, we fake OBJ loading with sleep
  81. */
  82. export class DelayTest extends GrObject {
  83. constructor() {
  84. const group = new T.Group();
  85. super("Delay-Test", group);
  86. this.group = group;
  87. // use sleep, rather than OBJ loader
  88. sleep(1500).then(function() {
  89. group.add(
  90. new T.Mesh(
  91. new T.TorusKnotGeometry(),
  92. new T.MeshStandardMaterial({ color: "red" })
  93. )
  94. );
  95. });
  96. }
  97. }
  98. /**
  99. * Better delayed object - put a proxy object in its place, and then remove it
  100. */
  101. export class BetterDelayTest extends GrObject {
  102. constructor() {
  103. const group = new T.Group();
  104. super("Delay-Test", group);
  105. this.group = group;
  106. // make a cube that will be there temporarily
  107. const tempCube = new T.Mesh(
  108. new T.BoxGeometry(),
  109. new T.MeshStandardMaterial()
  110. );
  111. group.add(tempCube);
  112. // use sleep, rather than OBJ loader
  113. sleep(2000).then(function() {
  114. group.remove(tempCube);
  115. group.add(
  116. new T.Mesh(
  117. new T.TorusKnotGeometry(),
  118. new T.MeshStandardMaterial({ color: "purple" })
  119. )
  120. );
  121. });
  122. }
  123. }
  124. /**
  125. * test for changing an object's material after some delay
  126. */
  127. export class MaterialDelayTest extends GrObject {
  128. constructor() {
  129. const group = new T.Group();
  130. super("Delay-Test", group);
  131. this.material = new T.MeshStandardMaterial({ color: "white" });
  132. this.geometry = new T.TorusGeometry();
  133. this.mesh = new T.Mesh(this.geometry, this.material);
  134. group.add(this.mesh);
  135. group.position.x = -3;
  136. const self = this;
  137. // use sleep, rather than OBJ loader
  138. sleep(1000).then(function() {
  139. // note: we can't use "this" because this isn't lexically scoped
  140. self.material.setValues({ color: "red" });
  141. self.material.needsUpdate = true;
  142. });
  143. }
  144. }
  145. export class CheckSign extends GrObject {
  146. /**
  147. *
  148. * @param {Object} props
  149. * @param {number} [props.checks=4] - number of squares per side
  150. * @param {string} [props.colortype="vertex"] - vertex,face,none
  151. * @param {number} [props.x]
  152. * @param {number} [props.y]
  153. * @param {number} [props.z]
  154. * @param {number} [props.scale=1]
  155. * @param {THREE.Color | string | Number} [props.materialcolor]
  156. */
  157. constructor(props = {}) {
  158. const group = new T.Group();
  159. super("CheckSign1", group);
  160. // let geometry = new T.Geometry();
  161. const geometry = new T.BufferGeometry();
  162. const nchecks = props.checks ?? 4;
  163. const nverts = nchecks + 1;
  164. const scale = props.scale > 0.0001 ? props.scale : 1; // disallow 0
  165. let colortype;
  166. switch (props.colortype && props.colortype[0]) {
  167. case "v":
  168. colortype = T.VertexColors;
  169. break;
  170. case "f":
  171. colortype = T.FaceColors;
  172. break;
  173. case "n":
  174. colortype = T.NoColors;
  175. break;
  176. default:
  177. console.log(`no or bad colortype - assuming vertex`);
  178. colortype = T.VertexColors;
  179. }
  180. const vertexIndex = []
  181. for (let i = 0; i < nverts + 1; i++) {
  182. for (let j = 0; j < nverts; j++) {
  183. vertexIndex.push([i, j, 0]);
  184. }
  185. }
  186. const vertices = []
  187. const colors = []
  188. for (let i = 0; i < nchecks; i++) {
  189. for (let j = 0; j < nchecks; j++) {
  190. vertices.push(...vertexIndex[i * nverts + j])
  191. vertices.push(...vertexIndex[i * nverts + j + 1])
  192. vertices.push(...vertexIndex[(i + 1) * nverts + j])
  193. vertices.push(...vertexIndex[i * nverts + j + 1])
  194. vertices.push(...vertexIndex[(i + 1) * nverts + j + 1])
  195. vertices.push(...vertexIndex[(i + 1) * nverts + j])
  196. const faceColor1 = (new T.Color('red')).toArray()
  197. colors.push(...faceColor1);
  198. colors.push(...faceColor1);
  199. colors.push(...faceColor1);
  200. colors.push(1, 0, 0);
  201. colors.push(1, 1, 1);
  202. colors.push(0, 0, 1);
  203. }
  204. }
  205. geometry.setAttribute('position', new T.BufferAttribute(Float32Array.from(vertices), 3))
  206. geometry.setAttribute('color', new T.BufferAttribute(Float32Array.from(colors), 3))
  207. geometry.computeVertexNormals();
  208. const materialProps = {
  209. side: T.DoubleSide,
  210. vertexColors: colortype
  211. };
  212. if (props.materialcolor) materialProps["color"] = props.materialcolor;
  213. const material = new T.MeshStandardMaterial(materialProps);
  214. const mesh = new T.Mesh(geometry, material);
  215. // center at 0,0
  216. mesh.scale.set(scale, scale, scale);
  217. // warning - scale does not affect translation!
  218. mesh.translateX(scale * (-nchecks / 2));
  219. mesh.translateY(scale * (-nchecks / 2));
  220. group.add(mesh);
  221. group.position.x = Number(props.x) || 0;
  222. group.position.y = Number(props.y) || 0;
  223. group.position.z = Number(props.z) || 0;
  224. }
  225. }