public override void drawTrack(Midi.Track midiTrack, TrackProps trackProps, MaterialProps texMaterial) { float songPosP; base.drawTrack(midiTrack, trackProps, texMaterial, out songPosP); trackProps.TrackView.OcTree.drawGeo(Project.Props.Camera); }
/// <summary> Parse a DataBlock into a MaterialProps. Any missing variables will be set to their default value. </summary> /// <param name="dataBlock"></param> /// <returns></returns> public static MaterialProps Parse(List <string> dataBlock) { MaterialProps res = MaterialProps.Default(); if (dataBlock.Count > 1) { float parsedValue; if (dataBlock.Count > (int)MatProp.maxForce && TryGetFloat(dataBlock[(int)MatProp.maxForce], out parsedValue)) { res.maxForce = (int)parsedValue; } if (dataBlock.Count > (int)MatProp.maxForceDist && TryGetFloat(dataBlock[(int)MatProp.maxForceDist], out parsedValue)) { res.maxForceDist = parsedValue; } if (dataBlock.Count > (int)MatProp.yieldDist && TryGetFloat(dataBlock[(int)MatProp.yieldDist], out parsedValue)) { if (float.IsNaN(parsedValue)) { parsedValue = float.MaxValue; } res.yieldDist = parsedValue; } if (dataBlock.Count > (int)MatProp.hapticMagn && TryGetFloat(dataBlock[(int)MatProp.hapticMagn], out parsedValue)) { res.hapticForce = (int)parsedValue; } if (dataBlock.Count > (int)MatProp.hapticDur && TryGetFloat(dataBlock[(int)MatProp.hapticDur], out parsedValue)) { res.hapticDur = (int)parsedValue; } } return(res); }
public override Material BuildMaterialFromProperties(MaterialProps mp) { // check if the material is already cached if (!_existingMaterials.TryGetValue(mp, out var material)) { // otherwise create a new material and cache it if (mp.AlphaBlended) { material = BuildMaterialBlended(mp.SrcBlendMode, mp.DstBlendMode); } else if (mp.AlphaTest) { material = BuildMaterialTested(mp.AlphaCutoff); } else { material = BuildMaterial(); } if (mp.Textures.MainFilePath != null) { material.mainTexture = _textureManager.LoadTexture(mp.Textures.MainFilePath); if (NormalGeneratorIntensity != null) { material.SetTexture("_BumpMap", GenerateNormalMap((Texture2D)material.mainTexture, NormalGeneratorIntensity.Value)); } } if (mp.Textures.BumpFilePath != null) { material.SetTexture("_BumpMap", _textureManager.LoadTexture(mp.Textures.BumpFilePath)); } _existingMaterials[mp] = material; } return(material); }
public override Material BuildMaterialFromProperties(MaterialProps mp) { // check if the material is already cached if (!_existingMaterials.TryGetValue(mp, out var material)) { // otherwise create a new material and cache it if (mp.AlphaBlended) { material = BuildMaterialBlended(mp.SrcBlendMode, mp.DstBlendMode); } else if (mp.AlphaTest) { material = BuildMaterialTested(mp.AlphaCutoff); } else { material = BuildMaterial(); } if (mp.Textures.MainFilePath != null) { material.mainTexture = _textureManager.LoadTexture(mp.Textures.MainFilePath); } _existingMaterials[mp] = material; } return(material); }
MaterialProps MeshPropertiesToMaterialProperties(Core.Material mtl) { // Create the material properties. var mp = new MaterialProps(); if (true) { if (mtl.AlphaTest != 0.0) // if flags contain the alpha test flag { mp.AlphaTest = true; mp.AlphaCutoff = (float)mtl.AlphaTest / 255F; } } else { mp.AlphaBlended = false; mp.AlphaTest = false; } // Apply textures. if (mtl.Textures != null) { mp.Textures = ConfigureTextureProperties(mtl.Textures); } return(mp); }
//---------------------------------------------------------------------------------- // Material Property loading #region MaterialProps /// <summary> Load the hard-coded properties of the material </summary> /// <param name="ofMaterial"></param> public void LoadMaterialProps(VirtualMaterial ofMaterial) { //SenseGlove_Debugger.Log("Loading material props of " + ofMaterial.ToString()); if (ofMaterial != VirtualMaterial.Custom) { MaterialProps thisProps = new MaterialProps(); switch (ofMaterial) { case VirtualMaterial.Rubber: thisProps.maxForce = 65; thisProps.maxForceDist = 0.02f; thisProps.yieldDist = float.MaxValue; thisProps.hapticForce = 60; thisProps.hapticDur = 200; break; case VirtualMaterial.Steel: thisProps.maxForce = 100; thisProps.maxForceDist = 0.00f; thisProps.yieldDist = float.MaxValue; thisProps.hapticForce = 0; thisProps.hapticDur = 0; break; case VirtualMaterial.Egg: thisProps.maxForce = 90; thisProps.maxForceDist = 0.01f; thisProps.yieldDist = 0.02f; thisProps.hapticForce = 0; thisProps.hapticDur = 0; break; } this.LoadMaterialProps(thisProps); } }
/// <summary> Actually apply materialProps to this Material. </summary> /// <param name="props"></param> private void LoadMaterialProps(MaterialProps props) { this.maxForce = props.maxForce; this.maxForceDist = props.maxForceDist; this.yieldDistance = props.yieldDist; this.hapticMagnitude = props.hapticForce; this.hapticDuration = props.hapticDur; }
//---------------------------------------------------------------------------------- // Material Property loading #region MaterialProps /// <summary> Load material properties from an external database file. </summary> private void LoadMaterialProps() { if (this.materialDataBase != null && this.materialName.Length > 0) { //SenseGlove_Debugger.Log("Loading " + this.materialName + " from a local database file " + this.materialDataBase.name); MaterialProps props = SenseGloveMats.MaterialLibraries.GetMaterial(this.materialName); this.LoadMaterialProps(props); } }
public override Material BuildMaterialFromProperties(MaterialProps mp) { // check if the material is already cached if (!_existingMaterials.TryGetValue(mp, out var material)) { // otherwise create a new material and cache it if (mp.AlphaBlended) { material = BuildMaterialBlended(mp.SrcBlendMode, mp.DstBlendMode); } else if (mp.AlphaTest) { material = BuildMaterialTested(mp.AlphaCutoff); } else { material = BuildMaterial(); } if (mp.Textures.MainFilePath != null && material.HasProperty("_MainTex")) { material.SetTexture("_MainTex", _textureManager.LoadTexture(mp.Textures.MainFilePath)); } if (mp.Textures.DetailFilePath != null && material.HasProperty("_DetailTex")) { material.SetTexture("_DetailTex", _textureManager.LoadTexture(mp.Textures.DetailFilePath)); } if (mp.Textures.DarkFilePath != null && material.HasProperty("_DarkTex")) { material.SetTexture("_DarkTex", _textureManager.LoadTexture(mp.Textures.DarkFilePath)); } if (mp.Textures.GlossFilePath != null && material.HasProperty("_GlossTex")) { material.SetTexture("_GlossTex", _textureManager.LoadTexture(mp.Textures.GlossFilePath)); } if (mp.Textures.GlowFilePath != null && material.HasProperty("_Glowtex")) { material.SetTexture("_Glowtex", _textureManager.LoadTexture(mp.Textures.GlowFilePath)); } if (mp.Textures.BumpFilePath != null && material.HasProperty("_BumpTex")) { material.SetTexture("_BumpTex", _textureManager.LoadTexture(mp.Textures.BumpFilePath)); } if (material.HasProperty("_Metallic")) { material.SetFloat("_Metallic", 0f); } if (material.HasProperty("_Glossiness")) { material.SetFloat("_Glossiness", 0f); } _existingMaterials[mp] = material; } return(material); }
/// <summary> Retrieve a 'default' material. </summary> /// <returns></returns> public static MaterialProps Default() { MaterialProps res = new MaterialProps(); res.maxForce = 100; res.maxForceDist = 0; res.yieldDist = float.MaxValue; res.hapticDur = 100; res.hapticForce = 100; return(res); }
static void MakeTexture(string path) { var textureManager = new TextureManager(Asset); var materialManager = new MaterialManager(textureManager); var obj = GameObject.Find("Cube"); // CreatePrimitive(PrimitiveType.Cube); var meshRenderer = obj.GetComponent <MeshRenderer>(); var materialProps = new MaterialProps { Textures = new MaterialTextures { MainFilePath = path }, }; meshRenderer.material = materialManager.BuildMaterialFromProperties(materialProps); }
MaterialProps MeshLodDataToMaterialProperties(SiMeshLod data) { // Create the material properties. var mp = new MaterialProps(); var tp = new MaterialTextures(); foreach (var material in data.Materials) { if (tp.MainFilePath == null) { tp.MainFilePath = material.Texture; } } mp.Textures = tp; return(mp); }
static GameObject MakeTexture(string path, int idx) { var textureManager = new TextureManager(AssetPack); var materialManager = new MaterialManager(textureManager); var obj = GameObject.CreatePrimitive(PrimitiveType.Plane); obj.name = path.Replace("/", ""); obj.transform.localPosition += new Vector3(10 * (idx % 10), 0, 10 * (idx / 10)); var meshRenderer = obj.GetComponent <MeshRenderer>(); var materialProps = new MaterialProps { Textures = new MaterialTextures { MainFilePath = path }, }; meshRenderer.material = materialManager.BuildMaterialFromProperties(materialProps); return(obj); }
public override void drawTrack(Midi.Track midiTrack, TrackProps trackProps, MaterialProps texMaterial) { float songPosP; base.drawTrack(midiTrack, trackProps, texMaterial, out songPosP); List <Midi.Note> noteList = midiTrack.Notes; //List<Midi.Note> noteList = getNotes(0, midiTrack, songDrawProps); if (noteList.Count == 0) { return; } //this.trackProps = trackProps; fx.Parameters["LineType"].SetValue((int)LineType); fx.Parameters["HlSize"].SetValue(VpHlSize / 2.0f); float radius = (float)VpLineWidth / 2.0f; fx.Parameters["Radius"].SetValue(radius); fx.Parameters["InnerHlSize"].SetValue(0.0f); fx.CurrentTechnique = fx.Techniques["Line"]; fx.Parameters["DiscardAtOnce"].SetValue(true); fx.CurrentTechnique.Passes[0].Apply(); trackProps.TrackView.OcTree.drawGeo(Project.Props.Camera); fx.Parameters["DiscardAtOnce"].SetValue(false); fx.CurrentTechnique.Passes[0].Apply(); trackProps.TrackView.OcTree.drawGeo(Project.Props.Camera); DepthStencilState oldDss = GraphicsDevice.DepthStencilState; GraphicsDevice.DepthStencilState = DepthStencilState.None; drawHighLights(midiTrack, trackProps, songPosP); GraphicsDevice.DepthStencilState = oldDss; }
MaterialProps SiAVObjectPropertiesToMaterialProperties(SiAVObject obj) { // Find relevant properties. SiTexturingProperty texturingProperty = null; foreach (var propRef in obj.Properties) { var prop = _file.Blocks[propRef.Value]; if (prop is SiTexturingProperty) { texturingProperty = (SiTexturingProperty)prop; } } // Create the material properties. var mp = new MaterialProps(); // Apply textures. if (texturingProperty != null) { mp.Textures = ConfigureTextureProperties(texturingProperty); } return(mp); }
public void createGeo(Midi.Track midiTrack, TrackProps trackProps, TrackProps globalTrackProps, MaterialProps texMaterial) { _createGeoChunk(out _geo, _bbox, midiTrack, trackProps, texMaterial); return; if (_nodes != null) { foreach (var node in _nodes) { if (node == null) { continue; } node.createGeo(midiTrack, trackProps, globalTrackProps, texMaterial); } } }
public abstract Material BuildMaterialFromProperties(MaterialProps mp);
MaterialProps NiAVObjectPropertiesToMaterialProperties(NiAVObject obj) { // Find relevant properties. NiTexturingProperty texturingProperty = null; //NiMaterialProperty materialProperty = null; NiAlphaProperty alphaProperty = null; foreach (var propRef in obj.Properties) { var prop = _obj.Blocks[propRef.Value]; if (prop is NiTexturingProperty) { texturingProperty = (NiTexturingProperty)prop; } //else if (prop is NiMaterialProperty) materialProperty = (NiMaterialProperty)prop; else if (prop is NiAlphaProperty) { alphaProperty = (NiAlphaProperty)prop; } } // Create the material properties. var mp = new MaterialProps(); if (alphaProperty != null) { #region AlphaProperty Cheat Sheet /* * 14 bits used: * * 1 bit for alpha blend bool * 4 bits for src blend mode * 4 bits for dest blend mode * 1 bit for alpha test bool * 3 bits for alpha test mode * 1 bit for zwrite bool ( opposite value ) * * Bit 0 : alpha blending enable * Bits 1-4 : source blend mode * Bits 5-8 : destination blend mode * Bit 9 : alpha test enable * Bit 10-12 : alpha test mode * Bit 13 : no sorter flag ( disables triangle sorting ) ( Unity ZWrite ) * * blend modes (glBlendFunc): * 0000 GL_ONE * 0001 GL_ZERO * 0010 GL_SRC_COLOR * 0011 GL_ONE_MINUS_SRC_COLOR * 0100 GL_DST_COLOR * 0101 GL_ONE_MINUS_DST_COLOR * 0110 GL_SRC_ALPHA * 0111 GL_ONE_MINUS_SRC_ALPHA * 1000 GL_DST_ALPHA * 1001 GL_ONE_MINUS_DST_ALPHA * 1010 GL_SRC_ALPHA_SATURATE * * test modes (glAlphaFunc): * 000 GL_ALWAYS * 001 GL_LESS * 010 GL_EQUAL * 011 GL_LEQUAL * 100 GL_GREATER * 101 GL_NOTEQUAL * 110 GL_GEQUAL * 111 GL_NEVER */ #endregion var flags = alphaProperty.Flags; var oldflags = flags; var srcbm = (byte)(BitConverter.GetBytes(flags >> 1)[0] & 15); var dstbm = (byte)(BitConverter.GetBytes(flags >> 5)[0] & 15); mp.ZWrite = BitConverter.GetBytes(flags >> 15)[0] == 1; // smush if (Utils.ContainsBitFlags(flags, 0x01)) // if flags contain the alpha blend flag at bit 0 in byte 0 { mp.AlphaBlended = true; mp.SrcBlendMode = FigureBlendMode(srcbm); mp.DstBlendMode = FigureBlendMode(dstbm); } else if (Utils.ContainsBitFlags(flags, 0x100)) // if flags contain the alpha test flag { mp.AlphaTest = true; mp.AlphaCutoff = (float)alphaProperty.Threshold / 255; } } else { mp.AlphaBlended = false; mp.AlphaTest = false; } // Apply textures. if (texturingProperty != null) { mp.Textures = ConfigureTextureProperties(texturingProperty); } return(mp); }
public override void createGeoChunk(out Geo geo, BoundingBox bbox, Midi.Track midiTrack, TrackProps trackProps, MaterialProps texMaterial) { BarGeo barGeo = new BarGeo(); geo = barGeo; List <Midi.Note> noteList = midiTrack.Notes; if (noteList.Count == 0) { return; } float halfNoteHeight = Project.Props.NoteHeight / 2; int instanceIndex = 0; for (int n = 0; n < noteList.Count; n++) { Midi.Note note = noteList[n]; if (note.start > Project.Notes.SongLengthT) //only if audio ends before the notes end { continue; } Vector2 noteStart = Project.getScreenPos(note.start, note.pitch); Vector2 noteEnd = Project.getScreenPos(note.stop, note.pitch); //Create bounding boxes Vector3 boxMin = new Vector3(noteStart.X, noteStart.Y - halfNoteHeight, 0); Vector3 boxMax = new Vector3(noteEnd.X, noteEnd.Y + halfNoteHeight, 0); geo.bboxes.Add(new BoundingBoxEx(boxMin, boxMax)); //Create inctance data Vector2 topLeft_world = new Vector2(noteStart.X, noteStart.Y - halfNoteHeight); Vector2 size_world = new Vector2(noteEnd.X - noteStart.X, halfNoteHeight * 2 - 0.001f); Vector2 topLeft_tex = topLeft_world; Vector2 size_tex = size_world; Texture2D texture = texMaterial.TexProps.Texture; if (texture != null) { Vector2 texSize = new Vector2(texture.Width, texture.Height); calcRectTexCoords(out topLeft_tex, out size_tex, texSize, topLeft_world, size_world, texMaterial); } instanceVerts[instanceIndex].destRect = new Vector4(topLeft_world.X, topLeft_world.Y, size_world.X, size_world.Y); instanceVerts[instanceIndex].srcRect = new Vector4(topLeft_tex.X, topLeft_tex.Y, size_tex.X, size_tex.Y); if (++instanceIndex >= MaxInstances - 1) { createVb(ref instanceIndex, barGeo); } } if (instanceIndex > 0) { createVb(ref instanceIndex, barGeo); } }
//static string GetName<T>(T item) where T : class //{ // return typeof(T).GetProperties()[0].Name; //} public override void createGeoChunk(out Geo geo, BoundingBox bbox, Midi.Track midiTrack, TrackProps trackProps, MaterialProps texMaterial) { LineGeo lineGeo = new LineGeo(); geo = lineGeo; if (LineWidth == 0) { return; } List <Midi.Note> noteList = midiTrack.Notes; //List<Midi.Note> noteList = getNotes(0, midiTrack, songDrawProps); if (noteList.Count == 0) { return; } int vertIndex = 3; int hLineVertIndex = 0; int completeNoteListIndex = midiTrack.Notes.IndexOf(noteList[0]); float vpLineWidth = VpLineWidth; float maxNumBboxesPerScreenWidth = 1000; float bboxMinSqLength = (float)Math.Pow(Project.Props.Camera.ViewportSize.X / maxNumBboxesPerScreenWidth, 2); //for (int i = 0; i < 100; i++) //{ // vertIndex = 30000; // createLineSegment(ref vertIndex, ref hLineVertIndex, lineGeo, vpLineWidth); //} //return; for (int n = 0; n < noteList.Count; n++) { //Get current note Midi.Note note = noteList[n], nextNote; if (note.start > Project.Notes.SongLengthT) //only if audio ends before the notes end { continue; } if (n < noteList.Count - 1) { nextNote = noteList[n + 1]; } else if (completeNoteListIndex < midiTrack.Notes.Count - 1) { nextNote = midiTrack.Notes[completeNoteListIndex + 1]; } else { nextNote = note; } Vector2 noteStart = Project.getScreenPos(note.start, note.pitch); float noteEnd = Project.getScreenPos(note.stop, note.pitch).X; Vector2 nextNoteStart = Project.getScreenPos(nextNote.start, nextNote.pitch); if (noteEnd > nextNoteStart.X && completeNoteListIndex < midiTrack.Notes.Count - 1) { noteEnd = nextNoteStart.X; } bool endOfSegment = false; if ((float)(nextNote.start - note.stop) > Qn_gapThreshold * Project.Notes.TicksPerBeat || note == nextNote) { if (nextNoteStart.X != noteStart.X) { nextNoteStart.Y = (int)MathHelper.Lerp(noteStart.Y, nextNoteStart.Y, (float)(noteEnd - noteStart.X) / (nextNoteStart.X - noteStart.X)); } nextNoteStart.X = noteEnd; endOfSegment = true; } float startDraw = noteStart.X; float endDraw = nextNoteStart.X; //Don't draw if length is 0 if (startDraw == endDraw) { continue; } float curvature = calcLinearLineAngle(n, trackProps.TrackView.Curve); //if (n > 0) //{ // float prevCurvature = calcLinearLineAngle(n - 1, trackProps.TrackView.Curve); // if (prevCurvature > curvature) // curvature = (prevCurvature + curvature) / 2; //Prevent too big jumps between levels of tesselation which will cause sharp bends //} if (n < noteList.Count - 1) { curvature = Math.Max(curvature, calcLinearLineAngle(n + 1, trackProps.TrackView.Curve)); } //curvature /= (float)Math.PI; //Map to [0,1] curvature = Math.Min(curvature, 0.97f * (float)Math.PI); //Clip below 180 degrees to avoid extreme tesselation curvature = (float)Math.Pow(curvature, 2.25) * 1000; float step; if (curvature == 0) { step = (endDraw - startDraw) * 0.999f; //Only one point for the note } else { step = 1 / curvature; } if (step >= endDraw - startDraw) { step = (endDraw - startDraw);// * 0.999f; } int iterations = (int)((endDraw - startDraw) / step); if (iterations < 0) { throw new OverflowException($"Too many vertices for note {n}, track {trackProps.TrackView.TrackNumber}."); } step = (endDraw - startDraw) / (iterations + 1); Vector3 bboxStart = Vector3.Zero; for (float x = startDraw; x <= endDraw + 0.00001f; x += step) { Vector3 center, normal, vertexOffset; getCurvePoint(out center, out normal, out vertexOffset, step, x, trackProps, vpLineWidth); //normal.X = curvature/10f; lineVerts[vertIndex].normal = lineVerts[vertIndex + 1].normal = normal; //Create vertices //adjustCurvePoint(center, vertexOffset, -1, trackProps, lineVerts, vertIndex, step, vpLineWidth); //adjustCurvePoint(center, vertexOffset, 1, trackProps, lineVerts, vertIndex + 1, step, vpLineWidth); //curvature = trackProps.TrackView.Curve.EvaluateCurvature(Project.getTimeT(x)); //center.Y = curvature / 10; lineVerts[vertIndex].pos = center - vertexOffset; lineVerts[vertIndex + 1].pos = center + vertexOffset; lineVerts[vertIndex].center = lineVerts[vertIndex + 1].center = center; float normStepFromNoteStart = (x - startDraw) / (nextNoteStart.X - noteStart.X); //lineVerts[vertIndex].normStepFromNoteStart = lineVerts[vertIndex + 1].normStepFromNoteStart = normStepFromNoteStart; lineVerts[vertIndex].normPos = new Vector2(normStepFromNoteStart, 0); lineVerts[vertIndex + 1].normPos = new Vector2(normStepFromNoteStart, 1); if (texMaterial.TexProps.Texture != null) { calcTexCoords(out lineVerts[vertIndex].texCoords, out lineVerts[vertIndex + 1].texCoords, texMaterial.TexProps, x - startDraw, normStepFromNoteStart, vpLineWidth, lineVerts[vertIndex].pos, lineVerts[vertIndex + 1].pos); } if (LineType == VisualMusic.LineType.Ribbon) { float hLineStart = center.X; float hLineEnd = hLineStart; do { hLineEnd += step; } while ((int)center.Y == (int)Project.getCurveScreenY((float)hLineEnd + step, trackProps.TrackView.Curve) && hLineEnd < endDraw); if (hLineEnd > hLineStart + vpLineWidth / 2) { hLineVerts[hLineVertIndex++] = lineVerts[vertIndex]; hLineVerts[hLineVertIndex++] = lineVerts[vertIndex + 1]; } //float horizontalFactor = Math.Abs(normal.Y); //float horizontalLimit = 0.98f; //float minThickness = 0.001f; //if (horizontalFactor > horizontalLimit) //{ //todo use vertexoffset for non-ribbon instead of normal // horizontalFactor = (horizontalFactor - horizontalLimit) / (1 - horizontalLimit); //[horizontalFactor, 1] -> [0, 1] // lineVerts[vertIndex].pos.Y -= minThickness * horizontalFactor * Math.Sign(normal.X); //} } if (x == startDraw && vertIndex > 3) { lineVerts[vertIndex].pos = lineVerts[vertIndex - 2].pos; lineVerts[vertIndex + 1].pos = lineVerts[vertIndex - 1].pos; } //Create bounding box if (bboxStart == Vector3.Zero) { bboxStart = lineVerts[vertIndex].center; } Vector3 bboxEnd = lineVerts[vertIndex].center; if (Vector3.DistanceSquared(bboxStart, bboxEnd) > bboxMinSqLength) { Vector3 bboxCenter = (bboxStart + bboxEnd) / 2; geo.bboxes.Add(new BoundingBoxEx(bboxCenter, bboxEnd - bboxCenter, bboxEnd - lineVerts[vertIndex].pos, new Vector3(0, 0, 0))); bboxStart = bboxEnd; } vertIndex += 2; if (vertIndex >= MaxLineVerts - 2 || hLineVertIndex >= MaxHLineVerts - 2) { createLineSegment(ref vertIndex, ref hLineVertIndex, lineGeo, vpLineWidth); x -= step; } //break; } if (!(bool)Continuous) { endOfSegment = true; //One draw call per note. Can be used to avoid glitches between notes because of instant IN.normStepFromNoteStart interpolation from 1 to 0. } if (endOfSegment) { createLineSegment(ref vertIndex, ref hLineVertIndex, lineGeo, vpLineWidth); } completeNoteListIndex++; } createLineSegment(ref vertIndex, ref hLineVertIndex, lineGeo, vpLineWidth); }