1 /* 2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package nbody; 26 27 import wrap.Wrap; 28 import wrap.glwrap.GLTexture; 29 import wrap.glwrap.GLWindow; 30 31 import java.io.IOException; 32 import java.lang.foreign.Arena; 33 import java.util.stream.IntStream; 34 35 import static opengl.opengl_h.GLUT_DEPTH; 36 import static opengl.opengl_h.GLUT_DOUBLE; 37 import static opengl.opengl_h.GLUT_RGB; 38 import static opengl.opengl_h.GL_COLOR_BUFFER_BIT; 39 import static opengl.opengl_h.GL_DEPTH_BUFFER_BIT; 40 import static opengl.opengl_h.GL_MODELVIEW; 41 import static opengl.opengl_h.GL_TEXTURE_2D; 42 import static opengl.opengl_h.glBindTexture; 43 import static opengl.opengl_h.glClear; 44 import static opengl.opengl_h.glClearColor; 45 import static opengl.opengl_h.glColor3f; 46 import static opengl.opengl_h.glDisable; 47 import static opengl.opengl_h.glEnable; 48 import static opengl.opengl_h.glMatrixMode; 49 import static opengl.opengl_h.glRasterPos2f; 50 import static opengl.opengl_h.glScalef; 51 import static opengl.opengl_h.glTexCoord2f; 52 import static opengl.opengl_h.glVertex3f; 53 import static opengl.opengl_h.glutBitmapCharacter; 54 import static opengl.opengl_h.glutBitmapTimesRoman24$segment; 55 import static opengl.opengl_h.glutSwapBuffers; 56 57 58 public abstract class NBodyGLWindow extends GLWindow { 59 protected final float delT = .1f; 60 61 protected final float espSqr = 0.1f; 62 63 protected final float mass = .5f; 64 65 protected final GLTexture particle; 66 protected final Wrap.Float4Arr xyzPosFloatArr; 67 protected final Wrap.Float4Arr xyzVelFloatArr; 68 69 protected int bodyCount; 70 protected int frameCount = 0; 71 protected final long startTime = System.currentTimeMillis(); 72 73 protected final Mode mode; 74 75 public NBodyGLWindow(Arena arena, int width, int height, GLTexture particle, int bodyCount, Mode mode) { 76 super(arena, width, height, "nbody", GLUT_DOUBLE() | GLUT_RGB() | GLUT_DEPTH(), particle); 77 this.particle = particle; 78 this.bodyCount = bodyCount; 79 this.xyzPosFloatArr = Wrap.Float4Arr.of(arena, bodyCount); 80 this.xyzVelFloatArr = Wrap.Float4Arr.of(arena, bodyCount); 81 82 this.mode = mode; 83 final float maxDist = 80f; 84 85 System.out.println(bodyCount + " particles"); 86 87 for (int body = 0; body < bodyCount; body++) { 88 final float theta = (float) (Math.random() * Math.PI * 2); 89 final float phi = (float) (Math.random() * Math.PI * 2); 90 final float radius = (float) (Math.random() * maxDist); 91 92 var radial = Wrap.Float4Arr.float4.of( 93 (float) (radius * Math.cos(theta) * Math.sin(phi)), 94 (float) (radius * Math.sin(theta) * Math.sin(phi)), 95 (float) (radius * Math.cos(phi)), 96 0f); 97 98 xyzPosFloatArr.set(body, radial); 99 } 100 } 101 102 103 float rot = 0f; 104 105 public static void run(int body, int size, Wrap.Float4Arr xyzPos, Wrap.Float4Arr xyzVel, float mass, float delT, float espSqr) { 106 float accx = 0.f; 107 float accy = 0.f; 108 float accz = 0.f; 109 110 final float myPosx = xyzPos.getx(body); 111 final float myPosy = xyzPos.gety(body); 112 final float myPosz = xyzPos.getz(body); 113 final float myVelx = xyzVel.getx(body); 114 final float myVely = xyzVel.gety(body); 115 final float myVelz = xyzVel.getz(body); 116 117 for (int i = 0; i < size; i++) { 118 final float dx = xyzPos.get(i).x() - myPosx; 119 final float dy = xyzPos.get(i).y() - myPosy; 120 final float dz = xyzPos.get(i).z() - myPosz; 121 final float invDist = 1 / (float) Math.sqrt((dx * dx) + (dy * dy) + (dz * dz) + espSqr); 122 final float s = mass * invDist * invDist * invDist; 123 accx = accx + (s * dx); 124 accy = accy + (s * dy); 125 accz = accz + (s * dz); 126 } 127 accx = accx * delT; 128 accy = accy * delT; 129 accz = accz * delT; 130 131 xyzPos.setx(body, myPosx + (myVelx + accx * .5f) * delT); 132 xyzPos.sety(body, myPosy + (myVely + accy * .5f) * delT); 133 xyzPos.setz(body, myPosz + (myVelz + accz * .5f) * delT); 134 135 xyzVel.setx(body, myVelx + accx); 136 xyzVel.sety(body, myVely + accy); 137 xyzVel.setz(body, myVelz + accz); 138 } 139 140 public static void runf4(int body, int size, Wrap.Float4Arr xyzPos, Wrap.Float4Arr xyzVel, float mass, float delT, float espSqr) { 141 var accf4 = Wrap.Float4Arr.float4.zero; 142 var myPosf4 = xyzPos.get(body); 143 var myVelf4 = xyzVel.get(body); 144 for (int i = 0; i < size; i++) { 145 var delta = xyzPos.get(i).sub(myPosf4); // xyz[i]-myPos 146 var dSqrd = delta.mul(delta); // delta^2 147 var invDist = 1f / (float) Math.sqrt(dSqrd.x() + dSqrd.y() + dSqrd.z() + espSqr); 148 accf4 = accf4.add(delta.mul(mass * invDist * invDist * invDist)); // accf4 += delta*(invDist^3*mass) 149 } 150 accf4 = accf4.mul(delT); 151 xyzPos.set(body, myPosf4.add(myVelf4.mul(delT)).add(accf4.mul(.5f * delT))); 152 xyzVel.set(body, myVelf4.add(accf4)); 153 } 154 155 protected void moveBodies() { 156 switch (mode) { 157 case JavaMT4 -> IntStream.range(0, bodyCount).parallel().forEach( 158 i -> runf4(i, bodyCount, xyzPosFloatArr, xyzVelFloatArr, mass, delT, espSqr) 159 ); 160 case JavaMT -> IntStream.range(0, bodyCount).parallel().forEach( 161 i -> run(i, bodyCount, xyzPosFloatArr, xyzVelFloatArr, mass, delT, espSqr) 162 ); 163 case JavaSeq4 -> IntStream.range(0, bodyCount).forEach( 164 i -> runf4(i, bodyCount, xyzPosFloatArr, xyzVelFloatArr, mass, delT, espSqr) 165 ); 166 case JavaSeq -> IntStream.range(0, bodyCount).forEach( 167 i -> run(i, bodyCount, xyzPosFloatArr, xyzVelFloatArr, mass, delT, espSqr) 168 ); 169 default -> throw new RuntimeException("Should never get here"); 170 } 171 } 172 173 protected static final float WEST = 0; 174 protected static final float EAST = 1; 175 protected static final float NORTH = 0; 176 protected static final float SOUTH = 1; 177 protected static float dx = -.5f; 178 protected static float dy = -.5f; 179 protected static float dz = -.5f; 180 181 182 @Override 183 public void display() { 184 moveBodies(); 185 glClearColor(0f, 0f, 0f, 0f); 186 glClear(GL_COLOR_BUFFER_BIT() | GL_DEPTH_BUFFER_BIT()); 187 glEnable(GL_TEXTURE_2D()); // Annoyingly important, 188 glBindTexture(GL_TEXTURE_2D(), textureBuf.get(particle.idx)); 189 glPushMatrix1(() -> { 190 glScalef(.01f, .01f, .01f); 191 glColor3f(1f, 1f, 1f); 192 glQuads(() -> { 193 for (int bodyIdx = 0; bodyIdx < bodyCount; bodyIdx++) { 194 var bodyf4 = xyzPosFloatArr.get(bodyIdx); 195 196 /* 197 * Textures are mapped to a quad by defining the vertices in 198 * the order SW,NW,NE,SE 199 & 200 * 2--->3 201 * ^ | 202 * | v 203 * 1 4 204 * 205 * Here we are describing the 'texture plane' for the body. 206 * Ideally we need to rotate this to point to the camera (see billboarding) 207 */ 208 209 glTexCoord2f(WEST, SOUTH); 210 glVertex3f(bodyf4.x() + WEST + dx, bodyf4.y() + SOUTH + dy, bodyf4.z() + dz); 211 glTexCoord2f(WEST, NORTH); 212 glVertex3f(bodyf4.x() + WEST + dx, bodyf4.y() + NORTH + dy, bodyf4.z() + dz); 213 glTexCoord2f(EAST, NORTH); 214 glVertex3f(bodyf4.x() + EAST + dx, bodyf4.y() + NORTH + dy, bodyf4.z() + dz); 215 glTexCoord2f(EAST, SOUTH); 216 glVertex3f(bodyf4.x() + EAST + dx, bodyf4.y() + SOUTH + dy, bodyf4.z() + dz); 217 218 } 219 }); 220 }); 221 glDisable(GL_TEXTURE_2D()); // Annoyingly important .. took two days to work that out 222 glMatrixMode(GL_MODELVIEW()); 223 glPushMatrix1(() -> { 224 glColor3f(0.0f, 1.0f, 0.0f); 225 var font = glutBitmapTimesRoman24$segment(); 226 long elapsed = System.currentTimeMillis() - startTime; 227 float secs = elapsed / 1000f; 228 var FPS = "Mode: " + mode.toString() + " Bodies " + bodyCount + " FPS: " + ((frameCount / secs)); 229 glRasterPos2f(-.8f, .7f); 230 for (int c : FPS.getBytes()) { 231 glutBitmapCharacter(font, c); 232 } 233 }); 234 glutSwapBuffers(); 235 frameCount++; 236 } 237 238 @Override 239 public void onIdle() { 240 // rot += 1f; 241 super.onIdle(); 242 } 243 } 244