diff options
Diffstat (limited to 'client')
| -rw-r--r-- | client/splash/cloud/datasetList.js | 1 | ||||
| -rw-r--r-- | client/splash/cloud/index.js | 52 | ||||
| -rw-r--r-- | client/splash/constants.js | 54 | ||||
| -rw-r--r-- | client/splash/face/faces.js | 13 | ||||
| -rw-r--r-- | client/splash/face/index.js | 22 | ||||
| -rw-r--r-- | client/splash/face/markers.js | 101 | ||||
| -rw-r--r-- | client/splash/face/mesh.js | 84 | ||||
| -rw-r--r-- | client/splash/face/util.js | 99 | ||||
| -rw-r--r-- | client/splash/index.js | 42 | ||||
| -rw-r--r-- | client/splash/renderer.js | 41 | ||||
| -rw-r--r-- | client/util/index.js (renamed from client/util.js) | 0 | ||||
| -rw-r--r-- | client/util/math.js | 51 | ||||
| -rw-r--r-- | client/util/vendor/DRACOLoader.js | 518 | ||||
| -rw-r--r-- | client/util/vendor/geometryHelper.js | 106 | ||||
| -rw-r--r-- | client/util/vendor/oktween.js | 159 |
15 files changed, 1343 insertions, 0 deletions
diff --git a/client/splash/cloud/datasetList.js b/client/splash/cloud/datasetList.js new file mode 100644 index 00000000..88058de7 --- /dev/null +++ b/client/splash/cloud/datasetList.js @@ -0,0 +1 @@ +export default ["10K US Adult Faces","3D-RMA","3D Dynamic","3DPeS","4DFAB","50 People One Question","aPascal","Aberdeen","Adience","AFAD","AFEW-VA","AffectNet","AFLW","AFW","AgeDB","ALERT Airport","AM-FED","APiS1.0","AR Face","AWE Ears","B3D(AC)","BBC Pose","BPAD","BFM","BioID Face","BJUT-3D","The Bosphorus","BP4D+","BP4D-Spontanous","Brainwash","BU-3DFE","BUHMAP-DB ","CAFE","Caltech 10K Web Faces","Caltech Faces","Caltech Pedestrians","CAMEL","CAS-PEAL","Casablanca","CASIA Webface","CAVIAR4REID","CelebA","CelebFaces+","CFD","ChaLearn","ChokePoint","Cityscapes","CCP","CMDP","CMU PIE","COCO","COCO-a","COCO QA","COFW","CK","CK+","Columbia Gaze","Ongoing Complex Activities","CUHK01","CUHK02","CUHK03","CVC-01","UFI","D3DFACS","Dartmouth Children","Data61 Pedestrian","DeepFashion","DISFA","Long Distance Heterogeneous Face","Duke MTMC","EmotioNet Database","ETHZ Pedestrian","EuroCity Persons","ExpW","Face Research Lab London","FaceScrub","FaceTracer","SFC","Facebook100","Face Place","Faces94","Faces95","Faces96","FIW","FDDB","FEI","FERET","FER+","CMU FiA","300-W","Florida Inmate","FRAV2D","FRAV3D","GRIMACE","FRGC","Gallagher","Gavab","GeoFaces","Georgia Tech Face","Google Makeup","Google (private)","Graz Pedestrian","H3D","HDA+","Helen","Hi4D-ADSIP","HID","Hipsterwars","HollywoodHeads","HRT Transgender","IFAD","IFDB","IIT Dehli Ear","IJB-A","IJB-B","IJB-C","","iLIDS-VID","Images of Groups","IMDB","IMFDB","IMM Face Dataset","Immediacy","imSitu","INRIA Pedestrian","iQIYI-VID dataset ","JAFFE","Jiku Mobile Video Dataset","JPL-Interaction dataset","Karpathy Instagram","KDEF","UB KinFace","KinectFaceDB","KITTI","LAG","Large Scale Person Search","Leeds Sports Pose","Leeds Sports Pose Extended","LFW","LFW-a","LFWP","m2vts","xm2vtsdb","MAFL","MALF","Mapillary","Market 1501","Market 1203","MARS","McGill Real World","Multiple Encounter Dataset","MegaAge","MegaFace","MIFS","MIKKI dataset","MIT CBCL","CBCL","CBCLSS","MIW","MMI Facial Expression Dataset","Moments in Time","MORPH Commercial","MORPH Non-Commercial","MOT","Large MPI Facial Expression","Small MPI Facial Expression","MPIIGaze","MPII Human Pose","MR2","MRP Drone","MsCeleb","MSMT17","MUCT","MUG Faces","MULTIPIE","MTFL","News Dataset","ND-2006","MID","Novaemötions Dataset","Nude Detection","ORL","Penn Fudan","PETA","PETS 2017","PPB","PIPA","PKU","PKU-Reid","Pornography DB","Precarious","PRID","PRW","PSU","PubFig","pubfig83","Put Face","GRID","QMUL-iLIDS","QMUL-SurvFace","RaFD","RAiD","RAP","ReSEED","SAIVT SoftBio","SAMM","Sarc3D","SCface","SCUT-FBP","SCUT HEAD","SDU-VID","SED Dataset","Sheffield Face","Shinpuhkan 2014","Social Relation","SOTON HiD","SVW","STAIR Action","Stanford Drone","Buffy Stickmen","We Are Family Stickmen","Stickmen PASCAL","Stirling/ESRC 3D Face","SUN","SVS","Texas 3DFRD","TinyFace","Tiny Images","TownCenter","TUD-Brussels","TUD-Campus","TUD-Crossing","TUD-Motionparis","TUD-Multiview","TUD-Pedestrian","TUD-Stadtmitte","TVHI","ND-TWINS-2009-2010","UCCS","UCF101","UCF-CC-50","UCF Selfie","UFDD","UMB","UMD","UNBC-McMaster Pain","Urban Tribes","USED Social Event Dataset","UTKFace","V47","VADANA","CIP","VGG Face","VGG Face2","Violent Flows","VIPeR","Phrasal Recognition","VMU","VOC","VQA","WARD","WGT","WIDER","WIDER FACE","WIDER Attribute","WildTrack","YaleFaces","Yale Face Database B","Extended Yale Face Database B ","YawDD","YFCC100M","UOY 3D Face Database","YouTubeFaces","YMU","YouTube Pose","WLFDB","SAL","Semaine","Belfast naturalistic","Belfast induced","VAM-faces","MAHNOB-HCI","DEAP","AMFED","Recola","AVEC13","AVEC14","Mimicry","Meissner Caucasian and African American","Nottingham Scans","Nottingham Originals","Stirling Pain","Utrecht ECVP","Mooney","Visual Commonsense Reasoning","HUFRD Pilgrims Dataset"] diff --git a/client/splash/cloud/index.js b/client/splash/cloud/index.js new file mode 100644 index 00000000..d0a39d8c --- /dev/null +++ b/client/splash/cloud/index.js @@ -0,0 +1,52 @@ +import { Object3D } from 'three' + +import TextSprite from 'three.textsprite' + +import datasetList from './datasetList' +import { choice } from '../../util' + +import { + CLOUD_SCALE, + CLOUD_COLORS, + CLOUD_ROTATION_SPEED, + CLOUD_MAX_COUNT, + CLOUD_TEXT_MIN_SIZE, + CLOUD_TEXT_MAX_SIZE, +} from '../constants' + +import { scene } from '../renderer' + +export const fontFamily = 'Helvetica, Arial, sans-serif' + +let cloud = new Object3D() + +export function init() { + let sprites = Array.from({ length: Math.min(datasetList.length, CLOUD_MAX_COUNT) }, (t, i) => { + const sprite = new TextSprite({ + textSize: CLOUD_TEXT_MIN_SIZE + Math.random() * (CLOUD_TEXT_MAX_SIZE - CLOUD_TEXT_MIN_SIZE), + redrawInterval: 1, + material: { + color: choice(CLOUD_COLORS), + }, + texture: { + text: datasetList[i], + fontFamily, + }, + }) + sprite.position + .setX(Math.random()) + .setY(Math.random()) + .setZ(Math.random()) + .subScalar(1 / 2) + .setLength(1 + Math.random()) + .multiplyScalar(CLOUD_SCALE) + cloud.add(sprite) + return sprite + }) + scene.add(cloud) + return cloud +} + +export function update() { + cloud.rotation.y += CLOUD_ROTATION_SPEED +} diff --git a/client/splash/constants.js b/client/splash/constants.js new file mode 100644 index 00000000..a3724b72 --- /dev/null +++ b/client/splash/constants.js @@ -0,0 +1,54 @@ +/* renderer */ + +export const CAMERA_NEAR = 0.001 +export const CAMERA_FAR = 1000 + +export const CAMERA_X = 0 +export const CAMERA_Y = 0.2 +export const CAMERA_Z = 2 + +export const FOG_COLOR = 0x191919 + +/* tag cloud */ + +export const CLOUD_COLORS = [ + 0xffffff, + 0xffffff, + 0xffffff, + 0xdddde6, + 0x888896, +] + +export const CLOUD_SCALE = 1 +export const CLOUD_ROTATION_SPEED = 0.001 +export const CLOUD_MAX_COUNT = 80 +export const CLOUD_TEXT_MIN_SIZE = 0.01 +export const CLOUD_TEXT_MAX_SIZE = 0.04 + +/* face */ + +export const FACE_SCALE = 1 + +/* face markers */ + +export const FACE_MARKERS_SCALE = 0.82 +export const FACE_OFFSET_X = 0 +export const FACE_OFFSET_Y = -0.0295 +export const FACE_OFFSET_Z = 0.0275 + +export const MARKER_POINT_COLOR = 0xffffff +export const MARKER_COLORS = [ + 0xff3333, + 0xff8833, + 0xffff33, + 0x338833, + 0x3388ff, + 0x3333ff, + 0x8833ff, + 0xff3388, + 0xffffff, +] + +export const POINT_SCALE = 0.01 +export const LINE_THICKNESS = 0.0075 +export const FACE_POINT_COUNT = 68 diff --git a/client/splash/face/faces.js b/client/splash/face/faces.js new file mode 100644 index 00000000..bab50c97 --- /dev/null +++ b/client/splash/face/faces.js @@ -0,0 +1,13 @@ +import { recenter } from './util' + +const rawFaces = { + putin: [[6.229589182834997700e02, 1.237765302967744901e03, 4.134526977539062500e02], [6.376896424835800872e02, 1.410526376450482758e03, 3.891479492187500000e02], [6.739611274719236462e02, 1.582250497753667105e03, 3.769988098144531250e02], [7.100658831128885140e02, 1.741763669768688942e03, 3.758922424316406250e02], [7.536649862551219030e02, 1.917029460341510003e03, 4.252069091796875000e02], [8.298141803217866936e02, 2.059907416716931493e03, 5.431895141601562500e02], [9.258791576609889944e02, 2.164709160946117208e03, 7.162614746093750000e02], [1.070414319266524672e03, 2.251194729721967633e03, 8.754176635742187500e02], [1.274411328424191879e03, 2.298006267580519307e03, 9.157481689453125000e02], [1.458846686449237723e03, 2.247376503427543867e03, 8.904161376953125000e02], [1.613419451724781766e03, 2.154859059460210574e03, 7.340153198242187500e02], [1.719510602704216581e03, 2.046877580339021051e03, 5.658121948242187500e02], [1.802992956004423377e03, 1.898674323395374358e03, 4.472657165527343750e02], [1.839803416592467329e03, 1.728563973209157211e03, 4.044224853515625000e02], [1.867230816650390807e03, 1.567819041550580550e03, 4.134500732421875000e02], [1.892678734334310093e03, 1.401836273899452635e03, 4.331260375976562500e02], [1.900552931542490114e03, 1.223502216072830606e03, 4.526267700195312500e02], [7.511424780752142851e02, 1.160890923207600963e03, 1.001351135253906250e03], [8.158950379315542705e02, 1.135870041375253777e03, 1.114991333007812500e03], [9.076874701406441091e02, 1.127822704063864421e03, 1.194016601562500000e03], [9.926902658799115216e02, 1.143210580432368261e03, 1.233907104492187500e03], [1.063656488096947669e03, 1.162017750208237658e03, 1.248264404296875000e03], [1.395103497613645231e03, 1.151143294423421366e03, 1.261149414062500000e03], [1.466387226179534309e03, 1.130542678432090725e03, 1.248993530273437500e03], [1.552676099051681376e03, 1.115153013015447868e03, 1.213308837890625000e03], [1.652305108762255031e03, 1.120968661861045803e03, 1.146292236328125000e03], [1.727642721019071814e03, 1.143332434488932449e03, 1.041585449218750000e03], [1.244122246476715645e03, 1.334647129869648325e03, 1.250885375976562500e03], [1.249189824721392597e03, 1.465660963816923413e03, 1.309278930664062500e03], [1.252202780570235745e03, 1.585706690278895167e03, 1.358756103515625000e03], [1.259493648693608066e03, 1.685694304749732282e03, 1.360432617187500000e03], [1.148455034173703552e03, 1.740496228553921810e03, 1.155312866210937500e03], [1.194674198315190097e03, 1.755804119561887546e03, 1.196942993164062500e03], [1.258795422961665054e03, 1.777236518698300188e03, 1.212644042968750000e03], [1.305280558926451249e03, 1.757635707289752190e03, 1.198189453125000000e03], [1.356348142915613380e03, 1.737675595379997958e03, 1.164412109375000000e03], [8.664502087162989028e02, 1.296397628532858562e03, 1.039572875976562500e03], [9.217092343199485640e02, 1.262228697521733466e03, 1.107280639648437500e03], [9.986516724530387137e02, 1.265434423307531233e03, 1.120204345703125000e03], [1.074714246772317438e03, 1.306653943337833425e03, 1.091015014648437500e03], [1.007696155622893571e03, 1.330022539822447698e03, 1.095014892578125000e03], [9.264212392470415125e02, 1.326119930090810840e03, 1.079521362304687500e03], [1.405216489784390433e03, 1.300602885574640140e03, 1.104635620117187500e03], [1.476379755775601325e03, 1.256362955328249654e03, 1.138428955078125000e03], [1.554564041795917774e03, 1.249119944242589781e03, 1.133978515625000000e03], [1.616356373027726704e03, 1.280854080128389342e03, 1.071557495117187500e03], [1.554167071892233480e03, 1.313725106410606259e03, 1.107841308593750000e03], [1.472497819489123913e03, 1.322406412808287541e03, 1.114204223632812500e03], [1.037582353868671362e03, 1.936206664907418599e03, 1.033433837890625000e03], [1.116472667858647128e03, 1.915096195044424576e03, 1.131442626953125000e03], [1.209116239719764735e03, 1.900456612907858926e03, 1.186649047851562500e03], [1.263021651623295838e03, 1.914385048409256797e03, 1.188636718750000000e03], [1.300469609698127215e03, 1.904690494719861135e03, 1.189560302734375000e03], [1.398002848905675364e03, 1.918324532410865231e03, 1.142420776367187500e03], [1.480066685814951143e03, 1.930527828548656316e03, 1.061488159179687500e03], [1.389899255969477508e03, 1.978273845885034234e03, 1.124004760742187500e03], [1.327246293191348741e03, 2.005233211071538562e03, 1.142569091796875000e03], [1.262139949065563542e03, 2.009971803241804992e03, 1.149364868164062500e03], [1.180817821697160070e03, 2.011372329759785316e03, 1.134800781250000000e03], [1.120554480578852463e03, 1.984728334003523742e03, 1.106011474609375000e03], [1.061098198954264262e03, 1.933346772066004633e03, 1.042356811523437500e03], [1.192023425322887761e03, 1.945863449802773175e03, 1.133595458984375000e03], [1.264533596083696921e03, 1.947978800443761884e03, 1.155409667968750000e03], [1.322869685094496617e03, 1.947868674591663421e03, 1.140181640625000000e03], [1.466934774301566449e03, 1.932516156771791202e03, 1.067155761718750000e03], [1.321161541688208445e03, 1.941148711607690529e03, 1.139805419921875000e03], [1.264645510983934400e03, 1.947900976849724657e03, 1.145079345703125000e03], [1.191629635949228032e03, 1.946293020139208465e03, 1.132726318359375000e03]], + xi: [[1.581475713243671350e02, 2.509900304577397208e02, 1.024322662353515625e02], [1.602838203130983743e02, 2.899340435970530621e02, 9.691181182861328125e01], [1.677042909659591032e02, 3.286639095949659009e02, 9.189924621582031250e01], [1.748665216932109558e02, 3.625784639964384155e02, 9.149455261230468750e01], [1.847612098245059542e02, 3.980092255955116229e02, 1.006391220092773438e02], [2.038414167516371549e02, 4.305498111620136115e02, 1.265865783691406250e02], [2.267264444687787091e02, 4.546917949123007929e02, 1.604735412597656250e02], [2.612276697794596316e02, 4.780719417916091629e02, 1.935450286865234375e02], [3.090068905699486663e02, 4.907490536080154016e02, 2.012679595947265625e02], [3.518451845056870297e02, 4.797107582361557547e02, 1.910112304687500000e02], [3.847857219621247395e02, 4.564630420879288408e02, 1.558248138427734375e02], [4.103254317339729482e02, 4.305017980957030659e02, 1.197048492431640625e02], [4.318702770756740392e02, 3.973330254169538307e02, 9.420705413818359375e01], [4.422064956365847479e02, 3.622428712253944241e02, 8.656275177001953125e01], [4.490620300891353054e02, 3.271828325578277941e02, 8.970116424560546875e01], [4.560659534529144139e02, 2.910778101423675253e02, 9.644176483154296875e01], [4.601242073208678676e02, 2.548580883071001892e02, 1.051369400024414062e02], [1.962528726615157666e02, 2.332017434931736375e02, 2.350029449462890625e02], [2.121071722591624393e02, 2.262617082603305221e02, 2.581283569335937500e02], [2.337514653224571362e02, 2.251718836885340238e02, 2.734199218750000000e02], [2.531733047485351449e02, 2.294949851660634863e02, 2.820414123535156250e02], [2.699339236988740822e02, 2.343355223233092204e02, 2.848821716308593750e02], [3.455108120787377857e02, 2.358723006125057395e02, 2.856489868164062500e02], [3.613513837627336329e02, 2.322616238283643497e02, 2.837029724121093750e02], [3.815044319900812297e02, 2.292742164881089195e02, 2.767124328613281250e02], [4.047670535218482541e02, 2.327066104784198899e02, 2.606012268066406250e02], [4.217962602802351739e02, 2.402466705142750527e02, 2.363506011962890625e02], [3.089879679361979470e02, 2.760715659167719878e02, 2.821576232910156250e02], [3.093649245318244425e02, 3.046348659859451118e02, 2.941600341796875000e02], [3.114766959994447006e02, 3.293608332435757120e02, 3.046447143554687500e02], [3.122007153080958801e02, 3.512138293816061605e02, 3.057337951660156250e02], [2.828070219152114078e02, 3.584083089312384800e02, 2.589963073730468750e02], [2.940082791197533538e02, 3.623387865373199475e02, 2.662980346679687500e02], [3.092972130868948852e02, 3.668293242331112083e02, 2.692637634277343750e02], [3.198154823452819073e02, 3.628364545754824917e02, 2.664876708984375000e02], [3.327329919553270656e02, 3.587899754064222861e02, 2.584887084960937500e02], [2.224500400618010758e02, 2.660715080829695012e02, 2.413853302001953125e02], [2.361075687184053322e02, 2.595814741366517637e02, 2.553526916503906250e02], [2.531863677380131890e02, 2.604359155512790949e02, 2.575644531250000000e02], [2.701898918002259506e02, 2.695448503202550228e02, 2.497847900390625000e02], [2.550828977098652217e02, 2.744724371337890716e02, 2.524707183837890625e02], [2.360909179089115923e02, 2.732695200422698463e02, 2.497717590332031250e02], [3.453027739281748154e02, 2.714797381053251684e02, 2.498313293457031250e02], [3.623781652113971177e02, 2.630507298607919893e02, 2.577862548828125000e02], [3.801104738721660965e02, 2.626405963194604283e02, 2.570069580078125000e02], [3.942162860945159650e02, 2.698504937983494187e02, 2.416210479736328125e02], [3.801208079020182709e02, 2.762396809117934140e02, 2.497300720214843750e02], [3.607991919424019898e02, 2.768305075970818052e02, 2.518104248046875000e02], [2.491371596093271137e02, 3.952959470980774768e02, 2.243894042968750000e02], [2.708619362027037027e02, 3.908969472967409047e02, 2.487248382568359375e02], [2.956859382180606417e02, 3.884492228908164293e02, 2.622833557128906250e02], [3.090222115310967865e02, 3.917322305836396481e02, 2.625950012207031250e02], [3.181707092883540327e02, 3.897191172401577433e02, 2.624730224609375000e02], [3.430552471086090804e02, 3.928625462670419211e02, 2.471974639892578125e02], [3.613266984528186754e02, 3.978334362673291480e02, 2.215844879150390625e02], [3.386838139552696134e02, 4.127940916293274540e02, 2.407136993408203125e02], [3.237892077277688827e02, 4.194459653487859327e02, 2.482334289550781250e02], [3.079967322256050579e02, 4.202518811513862715e02, 2.512642974853515625e02], [2.868524067299038620e02, 4.193043642051546840e02, 2.499754791259765625e02], [2.717448862412396693e02, 4.119777032710056233e02, 2.416939392089843750e02], [2.543327108644971304e02, 3.948142095348881071e02, 2.252314910888671875e02], [2.901215895409677614e02, 3.978477875473919880e02, 2.505760803222656250e02], [3.087859695135378502e02, 3.988037545716528598e02, 2.547194671630859375e02], [3.235175030876608844e02, 3.988561727606081035e02, 2.500653839111328125e02], [3.580007754456763678e02, 3.984504858996821213e02, 2.228356323242187500e02], [3.221863525989008963e02, 4.030397094247854852e02, 2.482552185058593750e02], [3.081756245332605317e02, 4.048862038526347646e02, 2.513024139404296875e02], [2.894371609257716500e02, 4.031360126091451548e02, 2.486108093261718750e02]], + may: [[2.382511024026309201e02, 2.801454149852079922e02, 5.214575576782226562e01], [2.493454499637378774e02, 3.064797704180549545e02, 5.088167190551757812e01], [2.639089957742129400e02, 3.319260479018267915e02, 5.266226196289062500e01], [2.761821218154009330e02, 3.563184045051126532e02, 5.502143859863281250e01], [2.895152865241554991e02, 3.809634247544232721e02, 6.649546051025390625e01], [3.060860955631030720e02, 4.000378012982537257e02, 9.035191345214843750e01], [3.213366342881146238e02, 4.128964293356502822e02, 1.197938156127929688e02], [3.427375466739429157e02, 4.230581409050437287e02, 1.479590148925781250e02], [3.741586932014016043e02, 4.234630673397289229e02, 1.611701660156250000e02], [4.023389876421759936e02, 4.080303395708870084e02, 1.599018707275390625e02], [4.257357296214383950e02, 3.868248218132467855e02, 1.410530548095703125e02], [4.422483188404756334e02, 3.651947661276425379e02, 1.185040130615234375e02], [4.545492390352137022e02, 3.384910384952321465e02, 1.011212463378906250e02], [4.562152171415440876e02, 3.097211315199908768e02, 9.170339202880859375e01], [4.545628575942095608e02, 2.840973517743279331e02, 8.920196533203125000e01], [4.518868203555836089e02, 2.565134695434570631e02, 8.989577484130859375e01], [4.476127057243795662e02, 2.289923642596077116e02, 9.148764801025390625e01], [2.440917306787826817e02, 2.537266974774529444e02, 1.485117950439453125e02], [2.505385673074160877e02, 2.449884134449678754e02, 1.672230224609375000e02], [2.629362676283892029e02, 2.388631448005227753e02, 1.830232696533203125e02], [2.764214070039636226e02, 2.376199662870519660e02, 1.927939453125000000e02], [2.886053881476906895e02, 2.378487686426499863e02, 1.978783874511718750e02], [3.423349636302274348e02, 2.245833317745433533e02, 2.082518310546875000e02], [3.541764831542968750e02, 2.182193829974006576e02, 2.078261871337890625e02], [3.692832874971277306e02, 2.125500047392004035e02, 2.033292694091796875e02], [3.884832659553078429e02, 2.116430788197237121e02, 1.939130706787109375e02], [4.040244427849264639e02, 2.158026554152545202e02, 1.781080627441406250e02], [3.225035565544576457e02, 2.592519715971105825e02, 2.056854705810546875e02], [3.248514710370232024e02, 2.779033468627930006e02, 2.160809936523437500e02], [3.270475357055663608e02, 2.946293671372358176e02, 2.269593200683593750e02], [3.308855010986327443e02, 3.100248983764648756e02, 2.303575286865234375e02], [3.207017386043772831e02, 3.250651369700713076e02, 1.937343902587890625e02], [3.269732386869542324e02, 3.255231738640280810e02, 2.018161010742187500e02], [3.372413344439337948e02, 3.264521555103975743e02, 2.067531127929687500e02], [3.444994117288027837e02, 3.217518894689223998e02, 2.058569641113281250e02], [3.539391475901883837e02, 3.172060759420955947e02, 2.011738739013671875e02], [2.669883459652170927e02, 2.717859535486558116e02, 1.602029724121093750e02], [2.727741014368393166e02, 2.638736764167337014e02, 1.725038604736328125e02], [2.851500773710362182e02, 2.613269290520163395e02, 1.773946533203125000e02], [2.994385292502009861e02, 2.645952199419807584e02, 1.756203308105468750e02], [2.895026475794174416e02, 2.709050594374713228e02, 1.749083251953125000e02], [2.761346585442037735e02, 2.735916131232767157e02, 1.694635925292968750e02], [3.532332682890050251e02, 2.518493531889074291e02, 1.864256134033203125e02], [3.633393915512982630e02, 2.427597525921990211e02, 1.922457427978515625e02], [3.761221859202665314e02, 2.392758025045956174e02, 1.929562835693359375e02], [3.890149852079503034e02, 2.427061907420439297e02, 1.852474365234375000e02], [3.783926052317899575e02, 2.491870305498908920e02, 1.901230163574218750e02], [3.645112599092370829e02, 2.528680367682962356e02, 1.894682617187500000e02], [3.149588652666876669e02, 3.656840930534812060e02, 1.685671386718750000e02], [3.209378256405100274e02, 3.546107831887638326e02, 1.896470184326171875e02], [3.335197990866267901e02, 3.465708392693014730e02, 2.029352569580078125e02], [3.426302308924057343e02, 3.463943742460363637e02, 2.055313262939453125e02], [3.487268890380859148e02, 3.431470490220013971e02, 2.064477233886718750e02], [3.683972360049976942e02, 3.438066943718405355e02, 1.994532775878906250e02], [3.876000764734604331e02, 3.492489317770565549e02, 1.836993255615234375e02], [3.744968906178193038e02, 3.656988448917164760e02, 1.932265167236328125e02], [3.646742408303653065e02, 3.739269323730468955e02, 1.959875488281250000e02], [3.539474262910730431e02, 3.774166064812155810e02, 1.956282958984375000e02], [3.400370959113625418e02, 3.795127504954618871e02, 1.915529479980468750e02], [3.295073663150562311e02, 3.757046441291360566e02, 1.844334564208984375e02], [3.180933715820311818e02, 3.644562714341108176e02, 1.699502258300781250e02], [3.342565650491152951e02, 3.568250763657514426e02, 1.932405548095703125e02], [3.456540888169232062e02, 3.534686874210133283e02, 1.999556121826171875e02], [3.567734405517577443e02, 3.518089711626838607e02, 1.987718353271484375e02], [3.853768515194162774e02, 3.501925692210478473e02, 1.839606781005859375e02], [3.596766138413372573e02, 3.610454082533892688e02, 1.959595794677734375e02], [3.495193201401654051e02, 3.635478616871553754e02, 1.955101776123046875e02], [3.382061392391428853e02, 3.660274766630285512e02, 1.905125427246093750e02]], +} + +export const names = Object.keys(rawFaces) + +export const faces = rawFaces + +// names.map(name => faces[name] = recenter(rawFaces[name])) diff --git a/client/splash/face/index.js b/client/splash/face/index.js new file mode 100644 index 00000000..4090a9fa --- /dev/null +++ b/client/splash/face/index.js @@ -0,0 +1,22 @@ +import { choice } from '../../util' +import { faces, names } from './faces' +import * as markers from './markers' +import * as mesh from './mesh' + +export function init() { + const name = choice(names) + const face = faces[name] + markers.build(face) + mesh.load(name) +} + +export function load() { + const name = choice(names) + const face = faces[name] + markers.swap(face) + // mesh.load(name) +} + +export function update() { + // markers.update() +} diff --git a/client/splash/face/markers.js b/client/splash/face/markers.js new file mode 100644 index 00000000..22b9966c --- /dev/null +++ b/client/splash/face/markers.js @@ -0,0 +1,101 @@ +import * as THREE from 'three' +import { MeshLine, MeshLineMaterial } from 'three.meshline' + +import oktween from '../../util/vendor/oktween' +import { scene } from '../renderer' + +import { getLineGeometry, updateFace, lerpPoints, getBboxForPoints, getBboxScaleAndCentroid } from './util' + +import { + POINT_SCALE, LINE_THICKNESS, FACE_POINT_COUNT, MARKER_COLORS, MARKER_POINT_COLOR, + FACE_SCALE, FACE_MARKERS_SCALE, FACE_OFFSET_X, FACE_OFFSET_Y, FACE_OFFSET_Z, +} from '../constants' + +const faceBuffer = Array.from({ length: FACE_POINT_COUNT }, () => new THREE.Vector3()) + +let group = new THREE.Object3D() +let cubes +let meshes + +let swapFrom +let swapTo + +export function build(points) { + swapTo = points + + const matrix = new THREE.Matrix4() + const quaternion = new THREE.Quaternion() + + const bbox = getBboxForPoints(points) + let { scale, midX, midY, midZ } = getBboxScaleAndCentroid(bbox) + + scale /= 2 + scale *= FACE_SCALE * FACE_MARKERS_SCALE + + const boxColor = new THREE.Color() + boxColor.setHex(MARKER_POINT_COLOR) + const scaledPoints = points.map((p) => new THREE.Vector3( + (p[0] - midX) * scale + FACE_OFFSET_X, + (p[1] - midY) * scale * -1 + FACE_OFFSET_Y, + (p[2] - midZ) * scale + FACE_OFFSET_Z + )) + + cubes = scaledPoints.map((position) => { + let geometry = new THREE.BoxBufferGeometry() + let rotation = new THREE.Euler() + let boxScale = new THREE.Vector3() + boxScale.x = POINT_SCALE + boxScale.y = POINT_SCALE + boxScale.z = POINT_SCALE + quaternion.setFromEuler(rotation, false) + matrix.compose(position, quaternion, boxScale) + geometry.applyMatrix(matrix) + let material = new THREE.MeshBasicMaterial({ color: boxColor }) + let cube = new THREE.Mesh(geometry, material) + group.add(cube) + return cube + }) + + meshes = getLineGeometry(scaledPoints).map((geometry, i) => { + const color = new THREE.Color() + const material = new MeshLineMaterial({ + color: color.setHex(MARKER_COLORS[i % MARKER_COLORS.length]), + }) + const line = new MeshLine() + line.setGeometry(geometry, () => LINE_THICKNESS) + const mesh = new THREE.Mesh(line.geometry, material) + mesh.geometry.dynamic = true + mesh.scale.x = 2 + mesh.scale.y = 2 + mesh.scale.z = 2 + group.add(mesh) + return [line, mesh] + }) + + group.frustumCulled = false + scene.add(group) + + updateFace(scaledPoints, cubes, meshes) +} + +export function swap(face) { + swapFrom = swapTo + swapTo = face + oktween.add({ + from: { n: 0 }, + to: { n: 1 }, + duration: 1000, + easing: oktween.easing.quad_in_out, + update: (obj) => { + lerpPoints(obj.n, swapFrom, swapTo, faceBuffer) + updateFace(faceBuffer, cubes, meshes) + }, + finished: () => { + setTimeout(swap, 2000) + } + }) +} + +export function update() { + // group.rotation.y += 0.005 +} diff --git a/client/splash/face/mesh.js b/client/splash/face/mesh.js new file mode 100644 index 00000000..69a40900 --- /dev/null +++ b/client/splash/face/mesh.js @@ -0,0 +1,84 @@ +import { Points, Mesh, MeshBasicMaterial, VertexColors, TrianglesDrawMode } from 'three' + +import { scene } from '../renderer' + +import DRACOLoader from '../../util/vendor/DRACOLoader' +import GeometryHelper from '../../util/vendor/geometryHelper' + +import { getBboxScaleAndCentroid } from './util' + +import { FACE_SCALE } from '../constants' + +DRACOLoader.setDecoderPath('/assets/js/vendor/draco/') + +const dracoLoader = new DRACOLoader() +DRACOLoader.getDecoderModule() + +export function load(name) { + dracoLoader.setVerbosity(1) + dracoLoader.setDrawMode(TrianglesDrawMode) + dracoLoader.setSkipDequantization('position', true) + dracoLoader.load('/assets/data/faces/' + name + '.drc', dracoDidLoad) +} + +export function update(name) { + load(name) +} + +function setDequantizationForMaterial(material, bufferGeometry) { + material.onBeforeCompile = (shader) => { + // Add uniform variables needed for dequantization. + const posAttribute = bufferGeometry.attributes.position + shader.uniforms.normConstant = { value: posAttribute.maxRange / (1 << posAttribute.numQuantizationBits) } + shader.uniforms.minPos = { value: posAttribute.minValues } + + shader.vertexShader = 'uniform float maxRange;\n' + + 'uniform float normConstant;\n' + + 'uniform vec3 minPos;\n' + + shader.vertexShader + shader.vertexShader = shader.vertexShader.replace( + '#include <begin_vertex>', + 'vec3 transformed = minPos + position * normConstant;' + ) + } +} + +function dracoDidLoad(bufferGeometry) { + const material = new MeshBasicMaterial({ vertexColors: VertexColors }) + material.wireframe = true + // If the position attribute is quantized, modify the material to perform + // dequantization on the GPU. + if (bufferGeometry.attributes.position.isQuantized) { + setDequantizationForMaterial(material, bufferGeometry) + } + + let geometry + // Point cloud does not have face indices. + if (bufferGeometry.index === null) { + geometry = new Points(bufferGeometry, material) + } else { + if (bufferGeometry.attributes.normal === undefined) { + const geometryHelper = new GeometryHelper() + geometryHelper.computeVertexNormals(bufferGeometry) + } + geometry = new Mesh(bufferGeometry, material) + geometry.drawMode = dracoLoader.drawMode + } + + // Compute range of the geometry coordinates for proper rendering. + bufferGeometry.computeBoundingBox() + const bbox = bufferGeometry.boundingBox + const { scale, midX, midY, midZ } = getBboxScaleAndCentroid(bbox, bufferGeometry.attributes.position) + geometry.scale.multiplyScalar(scale * FACE_SCALE) + geometry.position.x = -midX * scale + geometry.position.y = -midY * scale + geometry.position.z = -midZ * scale + geometry.frustumCulled = false + // geometry.castShadow = true + // geometry.receiveShadow = true + + const selectedObject = scene.getObjectByName("my_mesh") + scene.remove(selectedObject) + geometry.name = "my_mesh" + scene.add(geometry) +} diff --git a/client/splash/face/util.js b/client/splash/face/util.js new file mode 100644 index 00000000..38b1e376 --- /dev/null +++ b/client/splash/face/util.js @@ -0,0 +1,99 @@ + +import { Geometry } from 'three' +import { LINE_THICKNESS } from '../constants' + +export function getLineGeometry(points) { + return [ + points.slice(0, 17), + points.slice(17, 22), + points.slice(22, 27), + points.slice(27, 31), + points.slice(31, 36), + points.slice(36, 42), + points.slice(42, 48), + points.slice(48) + ].map((a, i) => { + const geometry = new Geometry() + a.forEach(p => geometry.vertices.push(p)) + if (i > 4) { + geometry.vertices.push(a[0]) + } + return geometry + }) +} + +export function updateFace(buf, cubes, meshes) { + updateCubeGeometry(buf, cubes) + updateLineGeometry(buf, meshes) +} + +export function updateLineGeometry(points, meshes) { + getLineGeometry(points).forEach((geometry, i) => { + const [line, mesh] = meshes[i] + line.setGeometry(geometry, () => LINE_THICKNESS) + mesh.geometry.vertices = line.geometry.vertices + mesh.geometry.verticesNeedUpdate = true + }) +} + +export function updateCubeGeometry(points, cubes) { + cubes.forEach((cube, i) => { + const p = points[i] + cube.position.set(p.x, p.y, p.z) + }) +} + +export function lerp(n, a, b) { + return (b - a) * n + a +} + +export function lerpPoint(n, A, B, C) { + C.x = lerp(n, A.x, B.x) + C.y = lerp(n, A.y, B.y) + C.z = lerp(n, A.z, B.z) +} + +export function lerpPoints(n, A, B, C) { + for (let i = 0, len = A.length; i < len; i++) { + lerpPoint(n, A[i], B[i], C[i]) + } +} + +export function getBboxForPoints(points) { + return points.reduce((a, p) => { + a.min.x = Math.min(a.min.x, p[0]) + a.max.x = Math.max(a.max.x, p[0]) + a.min.y = Math.min(a.min.y, p[1]) + a.max.y = Math.max(a.max.y, p[1]) + a.min.z = Math.min(a.min.z, p[2]) + a.max.z = Math.max(a.max.z, p[2]) + return a + }, { + min: { x: Infinity, y: Infinity, z: Infinity }, + max: { x: -Infinity, y: -Infinity, z: -Infinity } + }) +} + +export function getBboxScaleAndCentroid(bbox, position) { + if (position && position.isQuantized) { + // If the geometry is quantized, transform the bounding box to the dequantized + // coordinates. + const normConstant = position.maxRange / (1 << position.numQuantizationBits) + const minPos = position.minValues + bbox.max.x = minPos[0] + bbox.max.x * normConstant + bbox.max.y = minPos[1] + bbox.max.y * normConstant + bbox.max.z = minPos[2] + bbox.max.z * normConstant + bbox.min.x = minPos[0] + bbox.min.x * normConstant + bbox.min.y = minPos[1] + bbox.min.y * normConstant + bbox.min.z = minPos[2] + bbox.min.z * normConstant + } + const sizeX = bbox.max.x - bbox.min.x + const sizeY = bbox.max.y - bbox.min.y + const sizeZ = bbox.max.z - bbox.min.z + const diagonalSize = Math.sqrt(sizeX * sizeX + sizeY * sizeY + sizeZ * sizeZ) + const scale = 1.0 / diagonalSize + const midX = (bbox.min.x + bbox.max.x) / 2 + const midY = (bbox.min.y + bbox.max.y) / 2 + const midZ = (bbox.min.z + bbox.max.z) / 2 + return { scale, midX, midY, midZ } +} diff --git a/client/splash/index.js b/client/splash/index.js new file mode 100644 index 00000000..b559f5ee --- /dev/null +++ b/client/splash/index.js @@ -0,0 +1,42 @@ +// import oktween from '../util/vendor/oktween' + +import { Vector3 } from 'three' +import OrbitControls from 'three-orbitcontrols' + +import { init, render, camera, renderer } from './renderer' + +import * as cloud from './cloud' +import * as face from './face' + +const controls = new OrbitControls(camera, renderer.domElement) +controls.maxDistance = camera.far / 2 +controls.enableDamping = true +controls.dampingFactor = 1 / 8 +controls.rotateSpeed = 1 / 4 +controls.zoomSpeed = 1 +controls.keyPanSpeed = 1 / 2 + +function build() { + init() + cloud.init() + face.init() + animate() +} + +function animate() { + requestAnimationFrame(animate) + + // camera.position.z += -0.0025 + // camera.rotation.y += 0.00001 + + controls.update() + cloud.update() + face.update() + + let cameraTarget = new Vector3(0, 0, 0) + camera.lookAt(cameraTarget) + + render() +} + +document.addEventListener('DOMContentLoaded', build) diff --git a/client/splash/renderer.js b/client/splash/renderer.js new file mode 100644 index 00000000..ad111f1a --- /dev/null +++ b/client/splash/renderer.js @@ -0,0 +1,41 @@ +import * as THREE from 'three' + +import { FOG_COLOR, CAMERA_NEAR, CAMERA_FAR, CAMERA_X, CAMERA_Y, CAMERA_Z } from './constants' + +export const scene = new THREE.Scene() +scene.fog = new THREE.Fog(FOG_COLOR, 2, 15) + +export const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, CAMERA_NEAR, CAMERA_FAR) +camera.position.set(CAMERA_X, CAMERA_Y, CAMERA_Z) + +export const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) +renderer.setClearColor(scene.fog.color) +renderer.setPixelRatio(window.devicePixelRatio) +renderer.setSize(window.innerWidth, window.innerHeight) + +let lights = [ + new THREE.DirectionalLight(0xefefff, 1.5), + new THREE.DirectionalLight(0xefefff, 1.5), +] +lights[0].position.set(1, 1, 1).normalize() +lights[1].position.set(-1, -1, -1).normalize() +lights.forEach(light => scene.add(light)) + +export function init() { + const container = document.querySelector('#three_container') + container.appendChild(renderer.domElement) + window.addEventListener('resize', () => { + camera.aspect = window.innerWidth / window.innerHeight + camera.updateProjectionMatrix() + renderer.setSize(window.innerWidth, window.innerHeight) + }, false) +} + +export function render() { + renderer.render(scene, camera) +} + + +window.scene = scene +window.THREE = THREE + diff --git a/client/util.js b/client/util/index.js index d0db0d98..d0db0d98 100644 --- a/client/util.js +++ b/client/util/index.js diff --git a/client/util/math.js b/client/util/math.js new file mode 100644 index 00000000..064d37c6 --- /dev/null +++ b/client/util/math.js @@ -0,0 +1,51 @@ +export const mod = (n,m) => n-(m * Math.floor(n/m)) +export const clamp = (n,a,b) => n<a?a:n<b?n:b +export const norm = (n,a,b) => (n-a) / (b-a) +export const lerp = (n,a,b) => (b-a)*n+a +export const mix = (n,a,b) => a*(1-n)+b*n +export const randint = (n) => Math.floor(Math.random()*n) +export const randrange = (a,b) => Math.random() * (b-a) + a +export const randsign = () => Math.random() >= 0.5 ? -1 : 1 +export const choice = (a) => a[ Math.floor(Math.random() * a.length) ] +export const angle = (x0,y0,x1,y1) => Math.atan2(y1-y0, x1-x0) +export const dist = (x0,y0,x1,y1) => Math.sqrt(Math.pow(x1-x0, 2) + Math.pow(y1-y0, 2)) +export const xor = (a,b) => { a=!!a; b=!!b; return (a||b) && !(a&&b) } +export const quantize = (a,b) => Math.floor(a/b)*b +export const shuffle = (a) => { + for (var i = a.length; i > 0; i--){ + var r = randint(i) + var swap = a[i-1] + a[i-1] = a[r] + a[r] = swap + } + return a +} +// returns a gaussian random function with the given mean and stdev. +export function gaussian(mean, stdev) { + let y2; + let use_last = false; + return () => { + let y1; + if (use_last) { + y1 = y2; + use_last = false; + } + else { + let x1, x2, w; + do { + x1 = 2.0 * Math.random() - 1.0; + x2 = 2.0 * Math.random() - 1.0; + w = x1 * x1 + x2 * x2; + } while( w >= 1.0); + w = Math.sqrt((-2.0 * Math.log(w))/w); + y1 = x1 * w; + y2 = x2 * w; + use_last = true; + } + + let retval = mean + stdev * y1; + if (retval > 0) + return retval; + return -retval; + } +} diff --git a/client/util/vendor/DRACOLoader.js b/client/util/vendor/DRACOLoader.js new file mode 100644 index 00000000..67c18dcf --- /dev/null +++ b/client/util/vendor/DRACOLoader.js @@ -0,0 +1,518 @@ +// Copyright 2016 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import * as THREE from 'three'; + +/** + * @param {THREE.LoadingManager} manager + */ +const DRACOLoader = function(manager) { + this.timeLoaded = 0; + this.manager = manager || THREE.DefaultLoadingManager; + this.materials = null; + this.verbosity = 0; + this.attributeOptions = {}; + this.drawMode = THREE.TrianglesDrawMode; + // Native Draco attribute type to Three.JS attribute type. + this.nativeAttributeMap = { + 'position' : 'POSITION', + 'normal' : 'NORMAL', + 'color' : 'COLOR', + 'uv' : 'TEX_COORD' + }; +}; + +DRACOLoader.prototype = { + + constructor: DRACOLoader, + + load: function(url, onLoad, onProgress, onError) { + var scope = this; + var loader = new THREE.FileLoader(scope.manager); + loader.setPath(this.path); + loader.setResponseType('arraybuffer'); + if (this.crossOrigin !== undefined) { + loader.crossOrigin = this.crossOrigin; + } + loader.load(url, function(blob) { + scope.decodeDracoFile(blob, onLoad); + }, onProgress, onError); + }, + + setPath: function(value) { + this.path = value; + }, + + setCrossOrigin: function(value) { + this.crossOrigin = value; + }, + + setVerbosity: function(level) { + this.verbosity = level; + }, + + /** + * Sets desired mode for generated geometry indices. + * Can be either: + * THREE.TrianglesDrawMode + * THREE.TriangleStripDrawMode + */ + setDrawMode: function(drawMode) { + this.drawMode = drawMode; + }, + + /** + * Skips dequantization for a specific attribute. + * |attributeName| is the THREE.js name of the given attribute type. + * The only currently supported |attributeName| is 'position', more may be + * added in future. + */ + setSkipDequantization: function(attributeName, skip) { + var skipDequantization = true; + if (typeof skip !== 'undefined') + skipDequantization = skip; + this.getAttributeOptions(attributeName).skipDequantization = + skipDequantization; + }, + + /** + * |attributeUniqueIdMap| specifies attribute unique id for an attribute in + * the geometry to be decoded. The name of the attribute must be one of the + * supported attribute type in Three.JS, including: + * 'position', + * 'color', + * 'normal', + * 'uv', + * 'uv2', + * 'skinIndex', + * 'skinWeight'. + * The format is: + * attributeUniqueIdMap[attributeName] = attributeId + */ + decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap, + attributeTypeMap) { + var scope = this; + DRACOLoader.getDecoderModule() + .then( function ( module ) { + scope.decodeDracoFileInternal( rawBuffer, module.decoder, callback, + attributeUniqueIdMap || {}, attributeTypeMap || {}); + }); + }, + + decodeDracoFileInternal: function(rawBuffer, dracoDecoder, callback, + attributeUniqueIdMap, attributeTypeMap) { + /* + * Here is how to use Draco Javascript decoder and get the geometry. + */ + var buffer = new dracoDecoder.DecoderBuffer(); + buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength); + var decoder = new dracoDecoder.Decoder(); + + /* + * Determine what type is this file: mesh or point cloud. + */ + var geometryType = decoder.GetEncodedGeometryType(buffer); + if (geometryType == dracoDecoder.TRIANGULAR_MESH) { + if (this.verbosity > 0) { + console.log('Loaded a mesh.'); + } + } else if (geometryType == dracoDecoder.POINT_CLOUD) { + if (this.verbosity > 0) { + console.log('Loaded a point cloud.'); + } + } else { + var errorMsg = 'THREE.DRACOLoader: Unknown geometry type.' + console.error(errorMsg); + throw new Error(errorMsg); + } + callback(this.convertDracoGeometryTo3JS(dracoDecoder, decoder, + geometryType, buffer, attributeUniqueIdMap, attributeTypeMap)); + }, + + addAttributeToGeometry: function(dracoDecoder, decoder, dracoGeometry, + attributeName, attributeType, attribute, + geometry, geometryBuffer) { + if (attribute.ptr === 0) { + var errorMsg = 'THREE.DRACOLoader: No attribute ' + attributeName; + console.error(errorMsg); + throw new Error(errorMsg); + } + + var numComponents = attribute.num_components(); + var numPoints = dracoGeometry.num_points(); + var numValues = numPoints * numComponents; + var attributeData; + var TypedBufferAttribute; + + switch ( attributeType ) { + + case Float32Array: + attributeData = new dracoDecoder.DracoFloat32Array(); + decoder.GetAttributeFloatForAllPoints( + dracoGeometry, attribute, attributeData); + geometryBuffer[ attributeName ] = new Float32Array( numValues ); + TypedBufferAttribute = THREE.Float32BufferAttribute; + break; + + case Int8Array: + attributeData = new dracoDecoder.DracoInt8Array(); + decoder.GetAttributeInt8ForAllPoints( + dracoGeometry, attribute, attributeData ); + geometryBuffer[ attributeName ] = new Int8Array( numValues ); + TypedBufferAttribute = THREE.Int8BufferAttribute; + break; + + case Int16Array: + attributeData = new dracoDecoder.DracoInt16Array(); + decoder.GetAttributeInt16ForAllPoints( + dracoGeometry, attribute, attributeData); + geometryBuffer[ attributeName ] = new Int16Array( numValues ); + TypedBufferAttribute = THREE.Int16BufferAttribute; + break; + + case Int32Array: + attributeData = new dracoDecoder.DracoInt32Array(); + decoder.GetAttributeInt32ForAllPoints( + dracoGeometry, attribute, attributeData); + geometryBuffer[ attributeName ] = new Int32Array( numValues ); + TypedBufferAttribute = THREE.Int32BufferAttribute; + break; + + case Uint8Array: + attributeData = new dracoDecoder.DracoUInt8Array(); + decoder.GetAttributeUInt8ForAllPoints( + dracoGeometry, attribute, attributeData); + geometryBuffer[ attributeName ] = new Uint8Array( numValues ); + TypedBufferAttribute = THREE.Uint8BufferAttribute; + break; + + case Uint16Array: + attributeData = new dracoDecoder.DracoUInt16Array(); + decoder.GetAttributeUInt16ForAllPoints( + dracoGeometry, attribute, attributeData); + geometryBuffer[ attributeName ] = new Uint16Array( numValues ); + TypedBufferAttribute = THREE.Uint16BufferAttribute; + break; + + case Uint32Array: + attributeData = new dracoDecoder.DracoUInt32Array(); + decoder.GetAttributeUInt32ForAllPoints( + dracoGeometry, attribute, attributeData); + geometryBuffer[ attributeName ] = new Uint32Array( numValues ); + TypedBufferAttribute = THREE.Uint32BufferAttribute; + break; + + default: + var errorMsg = 'THREE.DRACOLoader: Unexpected attribute type.'; + console.error( errorMsg ); + throw new Error( errorMsg ); + + } + + // Copy data from decoder. + for (var i = 0; i < numValues; i++) { + geometryBuffer[attributeName][i] = attributeData.GetValue(i); + } + // Add attribute to THREEJS geometry for rendering. + geometry.addAttribute(attributeName, + new TypedBufferAttribute(geometryBuffer[attributeName], + numComponents)); + dracoDecoder.destroy(attributeData); + }, + + convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType, + buffer, attributeUniqueIdMap, + attributeTypeMap) { + if (this.getAttributeOptions('position').skipDequantization === true) { + decoder.SkipAttributeTransform(dracoDecoder.POSITION); + } + var dracoGeometry; + var decodingStatus; + const start_time = performance.now(); + if (geometryType === dracoDecoder.TRIANGULAR_MESH) { + dracoGeometry = new dracoDecoder.Mesh(); + decodingStatus = decoder.DecodeBufferToMesh(buffer, dracoGeometry); + } else { + dracoGeometry = new dracoDecoder.PointCloud(); + decodingStatus = + decoder.DecodeBufferToPointCloud(buffer, dracoGeometry); + } + if (!decodingStatus.ok() || dracoGeometry.ptr == 0) { + var errorMsg = 'THREE.DRACOLoader: Decoding failed: '; + errorMsg += decodingStatus.error_msg(); + console.error(errorMsg); + dracoDecoder.destroy(decoder); + dracoDecoder.destroy(dracoGeometry); + throw new Error(errorMsg); + } + + var decode_end = performance.now(); + dracoDecoder.destroy(buffer); + /* + * Example on how to retrieve mesh and attributes. + */ + var numFaces; + if (geometryType == dracoDecoder.TRIANGULAR_MESH) { + numFaces = dracoGeometry.num_faces(); + if (this.verbosity > 0) { + console.log('Number of faces loaded: ' + numFaces.toString()); + } + } else { + numFaces = 0; + } + + var numPoints = dracoGeometry.num_points(); + var numAttributes = dracoGeometry.num_attributes(); + if (this.verbosity > 0) { + console.log('Number of points loaded: ' + numPoints.toString()); + console.log('Number of attributes loaded: ' + + numAttributes.toString()); + } + + // Verify if there is position attribute. + var posAttId = decoder.GetAttributeId(dracoGeometry, + dracoDecoder.POSITION); + if (posAttId == -1) { + var errorMsg = 'THREE.DRACOLoader: No position attribute found.'; + console.error(errorMsg); + dracoDecoder.destroy(decoder); + dracoDecoder.destroy(dracoGeometry); + throw new Error(errorMsg); + } + var posAttribute = decoder.GetAttribute(dracoGeometry, posAttId); + + // Structure for converting to THREEJS geometry later. + var geometryBuffer = {}; + // Import data to Three JS geometry. + var geometry = new THREE.BufferGeometry(); + + // Add native Draco attribute type to geometry. + for (var attributeName in this.nativeAttributeMap) { + // The native attribute type is only used when no unique Id is + // provided. For example, loading .drc files. + if (attributeUniqueIdMap[attributeName] === undefined) { + var attId = decoder.GetAttributeId(dracoGeometry, + dracoDecoder[this.nativeAttributeMap[attributeName]]); + if (attId !== -1) { + if (this.verbosity > 0) { + console.log('Loaded ' + attributeName + ' attribute.'); + } + var attribute = decoder.GetAttribute(dracoGeometry, attId); + this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry, + attributeName, Float32Array, attribute, geometry, geometryBuffer); + } + } + } + + // Add attributes of user specified unique id. E.g. GLTF models. + for (var attributeName in attributeUniqueIdMap) { + var attributeType = attributeTypeMap[attributeName] || Float32Array; + var attributeId = attributeUniqueIdMap[attributeName]; + var attribute = decoder.GetAttributeByUniqueId(dracoGeometry, + attributeId); + this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry, + attributeName, attributeType, attribute, geometry, geometryBuffer); + } + + // For mesh, we need to generate the faces. + if (geometryType == dracoDecoder.TRIANGULAR_MESH) { + if (this.drawMode === THREE.TriangleStripDrawMode) { + var stripsArray = new dracoDecoder.DracoInt32Array(); + var numStrips = decoder.GetTriangleStripsFromMesh( + dracoGeometry, stripsArray); + geometryBuffer.indices = new Uint32Array(stripsArray.size()); + for (var i = 0; i < stripsArray.size(); ++i) { + geometryBuffer.indices[i] = stripsArray.GetValue(i); + } + dracoDecoder.destroy(stripsArray); + } else { + var numIndices = numFaces * 3; + geometryBuffer.indices = new Uint32Array(numIndices); + var ia = new dracoDecoder.DracoInt32Array(); + for (var i = 0; i < numFaces; ++i) { + decoder.GetFaceFromMesh(dracoGeometry, i, ia); + var index = i * 3; + geometryBuffer.indices[index] = ia.GetValue(0); + geometryBuffer.indices[index + 1] = ia.GetValue(1); + geometryBuffer.indices[index + 2] = ia.GetValue(2); + } + dracoDecoder.destroy(ia); + } + } + + geometry.drawMode = this.drawMode; + if (geometryType == dracoDecoder.TRIANGULAR_MESH) { + geometry.setIndex(new(geometryBuffer.indices.length > 65535 ? + THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute) + (geometryBuffer.indices, 1)); + } + var posTransform = new dracoDecoder.AttributeQuantizationTransform(); + if (posTransform.InitFromAttribute(posAttribute)) { + // Quantized attribute. Store the quantization parameters into the + // THREE.js attribute. + geometry.attributes['position'].isQuantized = true; + geometry.attributes['position'].maxRange = posTransform.range(); + geometry.attributes['position'].numQuantizationBits = + posTransform.quantization_bits(); + geometry.attributes['position'].minValues = new Float32Array(3); + for (var i = 0; i < 3; ++i) { + geometry.attributes['position'].minValues[i] = + posTransform.min_value(i); + } + } + dracoDecoder.destroy(posTransform); + dracoDecoder.destroy(decoder); + dracoDecoder.destroy(dracoGeometry); + + this.decode_time = decode_end - start_time; + this.import_time = performance.now() - decode_end; + + if (this.verbosity > 0) { + console.log('Decode time: ' + this.decode_time); + console.log('Import time: ' + this.import_time); + } + return geometry; + }, + + isVersionSupported: function(version, callback) { + DRACOLoader.getDecoderModule() + .then( function ( module ) { + callback( module.decoder.isVersionSupported( version ) ); + }); + }, + + getAttributeOptions: function(attributeName) { + if (typeof this.attributeOptions[attributeName] === 'undefined') + this.attributeOptions[attributeName] = {}; + return this.attributeOptions[attributeName]; + } +}; + +DRACOLoader.decoderPath = './'; +DRACOLoader.decoderConfig = {}; +DRACOLoader.decoderModulePromise = null; + +/** + * Sets the base path for decoder source files. + * @param {string} path + */ +DRACOLoader.setDecoderPath = function ( path ) { + DRACOLoader.decoderPath = path; +}; + +/** + * Sets decoder configuration and releases singleton decoder module. Module + * will be recreated with the next decoding call. + * @param {Object} config + */ +DRACOLoader.setDecoderConfig = function ( config ) { + var wasmBinary = DRACOLoader.decoderConfig.wasmBinary; + DRACOLoader.decoderConfig = config || {}; + DRACOLoader.releaseDecoderModule(); + + // Reuse WASM binary. + if ( wasmBinary ) DRACOLoader.decoderConfig.wasmBinary = wasmBinary; +}; + +/** + * Releases the singleton DracoDecoderModule instance. Module will be recreated + * with the next decoding call. + */ +DRACOLoader.releaseDecoderModule = function () { + DRACOLoader.decoderModulePromise = null; +}; + +/** + * Gets WebAssembly or asm.js singleton instance of DracoDecoderModule + * after testing for browser support. Returns Promise that resolves when + * module is available. + * @return {Promise<{decoder: DracoDecoderModule}>} + */ +DRACOLoader.getDecoderModule = function () { + var scope = this; + var path = DRACOLoader.decoderPath; + var config = DRACOLoader.decoderConfig; + var promise = DRACOLoader.decoderModulePromise; + + if ( promise ) return promise; + + // Load source files. + if ( typeof DracoDecoderModule !== 'undefined' ) { + // Loaded externally. + promise = Promise.resolve(); + } else if ( typeof WebAssembly !== 'object' || config.type === 'js' ) { + // Load with asm.js. + promise = DRACOLoader._loadScript( path + 'draco_decoder.js' ); + } else { + // Load with WebAssembly. + config.wasmBinaryFile = path + 'draco_decoder.wasm'; + promise = DRACOLoader._loadScript( path + 'draco_wasm_wrapper.js' ) + .then( function () { + return DRACOLoader._loadArrayBuffer( config.wasmBinaryFile ); + } ) + .then( function ( wasmBinary ) { + config.wasmBinary = wasmBinary; + } ); + } + + // Wait for source files, then create and return a decoder. + promise = promise.then( function () { + return new Promise( function ( resolve ) { + config.onModuleLoaded = function ( decoder ) { + scope.timeLoaded = performance.now(); + // Module is Promise-like. Wrap before resolving to avoid loop. + resolve( { decoder: decoder } ); + }; + DracoDecoderModule( config ); + } ); + } ); + + DRACOLoader.decoderModulePromise = promise; + return promise; +}; + +/** + * @param {string} src + * @return {Promise} + */ +DRACOLoader._loadScript = function ( src ) { + var prevScript = document.getElementById( 'decoder_script' ); + if ( prevScript !== null ) { + prevScript.parentNode.removeChild( prevScript ); + } + var head = document.getElementsByTagName( 'head' )[ 0 ]; + var script = document.createElement( 'script' ); + script.id = 'decoder_script'; + script.type = 'text/javascript'; + script.src = src; + return new Promise( function ( resolve ) { + script.onload = resolve; + head.appendChild( script ); + }); +}; + +/** + * @param {string} src + * @return {Promise} + */ +DRACOLoader._loadArrayBuffer = function ( src ) { + var loader = new THREE.FileLoader(); + loader.setResponseType( 'arraybuffer' ); + return new Promise( function( resolve, reject ) { + loader.load( src, resolve, undefined, reject ); + }); +}; + +export default DRACOLoader diff --git a/client/util/vendor/geometryHelper.js b/client/util/vendor/geometryHelper.js new file mode 100644 index 00000000..7f699722 --- /dev/null +++ b/client/util/vendor/geometryHelper.js @@ -0,0 +1,106 @@ +// Copyright 2017 The Draco Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import * as THREE from 'three'; + +/** + * @fileoverview Helper class implementing various utilities for THREE.js + * geometry. + */ + +function GeometryHelper() {} + +GeometryHelper.prototype = { + constructor: GeometryHelper, + + // Computes vertex normals on THREE.js buffer geometry, even when the mesh + // uses triangle strip connectivity. + computeVertexNormals: function (bufferGeometry) { + if (bufferGeometry.drawMode === THREE.TrianglesDrawMode) { + bufferGeometry.computeVertexNormals(); + return; + } else if (bufferGeometry.drawMode === THREE.TriangleStripDrawMode) { + if (bufferGeometry.attributes.position === undefined) { + return; + } + const inPositions = bufferGeometry.attributes.position.array; + if (bufferGeometry.attributes.normal === undefined) { + bufferGeometry.addAttribute( + 'normal', + new THREE.BufferAttribute(new Float32Array(inPositions.length), + 3)); + } else { + // Reset existing normals to zero. + const array = bufferGeometry.attributes.normal.array; + for (let i = 0; i < array.length; ++i) { + array[ i ] = 0; + } + } + let outNormals = bufferGeometry.attributes.normal.array; + + let pos0 = new THREE.Vector3(); + let pos1 = new THREE.Vector3(); + let pos2 = new THREE.Vector3(); + let posDif0 = new THREE.Vector3(), posDif1 = new THREE.Vector3(); + let localNormal = new THREE.Vector3(); + + const stripIndices = bufferGeometry.index.array; + for (let i = 2; i < stripIndices.length; ++i) { + let index0 = stripIndices[i - 2] * 3; + let index1 = stripIndices[i - 1] * 3; + let index2 = stripIndices[i] * 3; + // Skip degenerate triangles. + if (index0 === index1 || index0 === index2 || index1 === index2) { + continue; + } + if ((i & 1) !== 0) { + // Swap index 1 and 0 on odd indexed triangles. + const tmpIndex = index1; + index1 = index2; + index2 = tmpIndex; + } + + // Get position values. + pos0.fromArray(inPositions, index0); + pos1.fromArray(inPositions, index1); + pos2.fromArray(inPositions, index2); + + // Position differences + posDif0.subVectors(pos2, pos0); + posDif1.subVectors(pos1, pos0); + + // Weighted normal. + localNormal.crossVectors(posDif1, posDif0); + + // Update normals on vertices + outNormals[index0] += localNormal.x; + outNormals[index0 + 1] += localNormal.y; + outNormals[index0 + 2] += localNormal.z; + + outNormals[index1] += localNormal.x; + outNormals[index1 + 1] += localNormal.y; + outNormals[index1 + 2] += localNormal.z; + + outNormals[index2] += localNormal.x; + outNormals[index2 + 1] += localNormal.y; + outNormals[index2 + 2] += localNormal.z; + } + bufferGeometry.normalizeNormals(); + bufferGeometry.attributes.normal.needsUpdate = true; + } + }, +}; + +export default GeometryHelper diff --git a/client/util/vendor/oktween.js b/client/util/vendor/oktween.js new file mode 100644 index 00000000..cbf5d835 --- /dev/null +++ b/client/util/vendor/oktween.js @@ -0,0 +1,159 @@ +/* + oktween.add({ + obj: el.style, + units: "px", + from: { left: 0 }, + to: { left: 100 }, + duration: 1000, + easing: oktween.easing.circ_out, + update: function(obj){ + console.log(obj.left) + } + finished: function(){ + console.log("done") + } + }) +*/ + +import { lerp } from '../../util/math' + +const oktween = {} +let tweens = [] + +let last_t = 0 +let id = 0 + +oktween.speed = 1 +oktween.add = (tween) => { + tween.id = id++ + tween.obj = tween.obj || {} + if (tween.easing) { + if (typeof tween.easing === "string") { + tween.easing = oktween.easing[tween.easing] + } + } else { + tween.easing = oktween.easing.linear + } + if (!('from' in tween) && !('to' in tween)) { + tween.keys = [] + } else if (!('from' in tween)) { + tween.from = {} + tween.keys = Object.keys(tween.to) + tween.keys.forEach(function(prop) { + tween.from[prop] = parseFloat(tween.obj[prop]) + }) + } else { + tween.keys = Object.keys(tween.from) + } + tween.delay = tween.delay || 0 + tween.start = last_t + tween.delay + tween.done = false + tween.after = tween.after || [] + tween.then = (fn) => { tween.after.push(fn); return tween } + tween.tick = 0 + tween.skip = tween.skip || 1 + tween.dt = 0 + tweens.push(tween) + return tween +} +oktween.update = (t) => { + let done = false + requestAnimationFrame(oktween.update) + last_t = t * oktween.speed + if (tweens.length === 0) return + tweens.forEach((tween, i) => { + const dt = Math.min(1.0, (t - tween.start) / tween.duration) + tween.tick++ + if (dt < 0 || (dt < 1 && (tween.tick % tween.skip != 0))) return + const ddt = tween.easing(dt) + tween.dt = ddt + tween.keys.forEach((prop) => { + let val = lerp(ddt, tween.from[prop], tween.to[prop]) + if (tween.round) val = Math.round(val) + if (tween.units) val = (Math.round(val)) + tween.units + tween.obj[prop] = val + }) + tween.update && tween.update(tween.obj, dt) + if (dt == 1) { + tween.finished && tween.finished(tween) + if (tween.after.length) { + var twn = tween.after.shift() + twn.obj = twn.obj || tween.obj + twn.after = tween.after + oktween.add(twn) + } + if (tween.loop) { + tween.start = t + tween.delay + } + else { + done = true + tween.done = true + } + } + }) + if (done) { + tweens = tweens.filter(function(tween){ return ! tween.done }) + } +} + +requestAnimationFrame(oktween.update) + +oktween.easing = { + linear: (t) => { + return t + }, + circ_out: (t) => { + return Math.sqrt(1 - (t = t - 1) * t) + }, + circ_in: (t) => { + return -(Math.sqrt(1 - (t * t)) - 1) + }, + circ_in_out: (t) => { + return ((t*=2) < 1) ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1) + }, + quad_in: (n) => { + return Math.pow(n, 2) + }, + quad_out: (n) => { + return n * (n - 2) * -1 + }, + quad_in_out: (n) => { + n = n * 2 + if (n < 1) { return Math.pow(n, 2) / 2 } + return -1 * ((--n) * (n - 2) - 1) / 2 + }, + cubic_bezier: (mX1, mY1, mX2, mY2) => { + function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1 } + function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1 } + function C(aA1) { return 3.0 * aA1 } + + // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. + function CalcBezier(aT, aA1, aA2) { + return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT + } + + // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. + function GetSlope(aT, aA1, aA2) { + return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1) + } + + function GetTForX(aX) { + // Newton raphson iteration + let aGuessT = aX + for (let i = 0; i < 10; ++i) { + const currentSlope = GetSlope(aGuessT, mX1, mX2) + if (currentSlope == 0.0) return aGuessT + const currentX = CalcBezier(aGuessT, mX1, mX2) - aX + aGuessT -= currentX / currentSlope + } + return aGuessT + } + + return function (aX) { + if (mX1 == mY1 && mX2 == mY2) return aX // linear + return CalcBezier(aX, mY1, mY2) + } + } +} + +export default oktween |
